├── .gitignore ├── .travis.yml ├── ADDITIONAL-LICENSES ├── LICENSE ├── README.md ├── TODO.md ├── appimage-build.sh ├── daemon.sh ├── default_menus ├── .autoswitch.menu ├── .games.menu ├── .windowlist.menu ├── Default.menu └── Profiles.menu ├── default_profiles ├── .scc-osd.keyboard.sccprofile ├── .scc-osd.profile_editor.sccprofile ├── Desktop.sccprofile ├── XBox Controller with High Precision Camera.sccprofile └── XBox Controller.sccprofile ├── docs ├── actions.md ├── menu-file.md ├── osd-keyboard.png ├── profile-file.md ├── protocol.md ├── screenshot1-tn.png ├── screenshot1.png ├── screenshot2-tn.png ├── screenshot2.png ├── screenshot3-tn.png ├── screenshot3.png ├── screenshot4-tn.png └── screenshot4.png ├── gamecontrollerdb.txt ├── generate-icons.py ├── generate_svg.py ├── glade ├── about.glade ├── action_editor.glade ├── ae │ ├── axis.glade │ ├── axis_action.glade │ ├── buttons.glade │ ├── custom.glade │ ├── dpad.glade │ ├── first_page.glade │ ├── gesture.glade │ ├── gyro.glade │ ├── gyro_action.glade │ ├── menu_only.glade │ ├── osk_action.glade │ ├── per_axis.glade │ ├── recent_list.glade │ ├── special_action.glade │ ├── tilt.glade │ └── trigger.glade ├── app.glade ├── controller_settings.glade ├── creg.glade ├── global_settings.glade ├── icon_chooser.glade ├── import_export.glade ├── key_grabber.glade ├── macro_editor.glade ├── menu_editor.glade ├── modeshift_editor.glade ├── osk_binding_editor.glade ├── ring_editor.glade └── simple_chooser.glade ├── images ├── 24x24 │ └── status │ │ ├── scc-alive.png │ │ ├── scc-dead.png │ │ ├── scc-error.png │ │ └── scc-unknown.png ├── 256x256 │ └── status │ │ ├── scc-alive.png │ │ ├── scc-alive.tmp.png │ │ ├── scc-dead.png │ │ ├── scc-dead.tmp.png │ │ ├── scc-error.png │ │ ├── scc-error.tmp.png │ │ ├── scc-statusicon.png │ │ ├── scc-unknown.png │ │ └── scc-unknown.tmp.png ├── A.svg ├── AXISX.svg ├── AXISY.svg ├── B.svg ├── BACK.svg ├── C.svg ├── CPAD.svg ├── DOUBLECLICK.svg ├── DPAD_DOWN.svg ├── DPAD_LEFT.svg ├── DPAD_RIGHT.svg ├── DPAD_UP.svg ├── GYRO.svg ├── HOLD.svg ├── LB.svg ├── LGRIP.svg ├── LPAD.svg ├── LT.svg ├── RB.svg ├── RGRIP.svg ├── RPAD.svg ├── RSTICK.svg ├── RT.svg ├── START.svg ├── STICK.svg ├── X.svg ├── Y.svg ├── all_buttons.svg ├── axistrigger.svg ├── binding-display.svg ├── button-images │ ├── 1.bw.svg │ ├── 1.svg │ ├── 2.bw.svg │ ├── 2.svg │ ├── 3.bw.svg │ ├── 3.svg │ ├── 4.bw.svg │ ├── 4.svg │ ├── A.bw.svg │ ├── A.svg │ ├── B.bw.svg │ ├── B.svg │ ├── BACK.bw.svg │ ├── BACK.svg │ ├── C.bw.svg │ ├── C.svg │ ├── CIRCLE.bw.svg │ ├── CIRCLE.svg │ ├── CPAD.svg │ ├── CROSS.bw.svg │ ├── CROSS.svg │ ├── DOTS.bw.svg │ ├── DOTS.svg │ ├── DPAD.bw.svg │ ├── ELIPSE.bw.svg │ ├── ELIPSE.svg │ ├── GYRO.svg │ ├── LB.svg │ ├── LGRIP.svg │ ├── LGRIP2.svg │ ├── LPAD.svg │ ├── LT.svg │ ├── MENU.svg │ ├── RB.svg │ ├── RECTANGLE.svg │ ├── RGRIP.svg │ ├── RGRIP2.svg │ ├── RPAD.svg │ ├── RSTICK.bw.svg │ ├── RT.svg │ ├── RTRIANGLE.svg │ ├── SQUARE.bw.svg │ ├── SQUARE.svg │ ├── START.bw.svg │ ├── START.svg │ ├── STICK.bw.svg │ ├── TOUCHPAD.svg │ ├── TRIANGLE.bw.svg │ ├── TRIANGLE.svg │ ├── VIEW.svg │ ├── X.bw.svg │ ├── X.svg │ ├── Y.bw.svg │ ├── Y.svg │ ├── ds4SHARE.svg │ ├── groups.json │ ├── snesA.svg │ ├── snesB.svg │ ├── snesSTART.svg │ ├── snesX.svg │ └── snesY.svg ├── buttons.svg ├── controller-icon.svg ├── controller-icons │ ├── deck-0.svg │ ├── ds4-0.svg │ ├── ds4-1.svg │ ├── ds4-2.svg │ ├── ds4-3.svg │ ├── ds4-4.svg │ ├── ds4-5.svg │ ├── ds4-6.svg │ ├── ds4evdev-0.svg │ ├── ds4evdev-1.svg │ ├── ds4evdev-2.svg │ ├── ds4evdev-3.svg │ ├── ds4evdev-4.svg │ ├── ds4evdev-5.svg │ ├── ds4evdev-6.svg │ ├── evdev-0.svg │ ├── evdev-1.svg │ ├── evdev-2.svg │ ├── evdev-3.svg │ ├── evdev-4.svg │ ├── evdev-5.svg │ ├── evdev-6.svg │ ├── fake-0.svg │ ├── fake-1.svg │ ├── fake-2.svg │ ├── fake-3.svg │ ├── fake-4.svg │ ├── fake-5.svg │ ├── fake-6.svg │ ├── hid-0.svg │ ├── hid-1.svg │ ├── hid-2.svg │ ├── hid-3.svg │ ├── hid-4.svg │ ├── hid-5.svg │ ├── hid-6.svg │ ├── rpad-0.svg │ ├── rpad-1.svg │ ├── rpad-2.svg │ ├── rpad-3.svg │ ├── rpad-4.svg │ ├── rpad-5.svg │ ├── rpad-6.svg │ ├── sc-0.svg │ ├── sc-1.svg │ ├── sc-2.svg │ ├── sc-3.svg │ ├── sc-4.svg │ ├── sc-5.svg │ ├── sc-6.svg │ ├── scbt-0.svg │ ├── scbt-1.svg │ ├── scbt-2.svg │ ├── scbt-3.svg │ ├── scbt-4.svg │ ├── scbt-5.svg │ ├── scbt-6.svg │ └── unknown.svg ├── controller-images │ ├── deck.svg │ ├── ds4.svg │ ├── ps1.svg │ ├── psx.svg │ ├── remotepad.svg │ ├── sc.svg │ ├── snes.svg │ └── x360.svg ├── controller-side.svg ├── deck.config.json ├── ds4-config.json ├── ds4-small.svg ├── inputdisplay-cursor.svg ├── inputdisplay.svg ├── keyboard.svg ├── keyboard.svg.json ├── menu-cursor.svg ├── menu-icons │ ├── buttons │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── A.png │ │ ├── A.svg │ │ ├── B.png │ │ ├── B.svg │ │ ├── BACK.png │ │ ├── BACK.svg │ │ ├── CIRCLE.png │ │ ├── CROSS.png │ │ ├── LB.png │ │ ├── LB.svg │ │ ├── LICENCES │ │ ├── RB.png │ │ ├── RB.svg │ │ ├── SQUARE.png │ │ ├── START.png │ │ ├── START.svg │ │ ├── TRIANGLE.png │ │ ├── X.png │ │ ├── X.svg │ │ ├── Y.png │ │ ├── Y.svg │ │ ├── snesA.png │ │ ├── snesB.png │ │ ├── snesX.png │ │ └── snesY.png │ ├── driving │ │ ├── .gearbox.svg │ │ ├── LICENCES │ │ ├── autoshift-drive.bw.png │ │ ├── autoshift-reverse.bw.png │ │ ├── autoshift.svg │ │ ├── gearbox-1.bw.png │ │ ├── gearbox-2.bw.png │ │ ├── gearbox-3.bw.png │ │ ├── gearbox-4.bw.png │ │ ├── gearbox-5.bw.png │ │ ├── gearbox-6.bw.png │ │ ├── gearbox-N.bw.png │ │ ├── gearbox-R.bw.png │ │ ├── pedal1.bw.png │ │ ├── pedal2.bw.png │ │ └── steering-wheel.bw.png │ ├── items │ │ ├── LICENCES │ │ ├── apple.bw.png │ │ ├── bottle.bw.png │ │ ├── coffee.bw.png │ │ ├── flask.bw.png │ │ ├── heart-bottle.bw.png │ │ ├── medpack.bw.png │ │ ├── pill.bw.png │ │ ├── potion.bw.png │ │ └── syringe.bw.png │ ├── media │ │ ├── LICENCES │ │ ├── mute.bw.png │ │ ├── next.bw.png │ │ ├── pause.bw.png │ │ ├── play-pause-etc.svg │ │ ├── play.bw.png │ │ ├── prev.bw.png │ │ ├── stop.bw.png │ │ ├── volume-down.bw.png │ │ └── volume-up.bw.png │ ├── system │ │ ├── .binding-display.bw.svg │ │ ├── .sc-controller.bw.svg │ │ ├── .turn-off.svg │ │ ├── LICENCES │ │ ├── autoswitch.bw.png │ │ ├── binding-display.bw.png │ │ ├── cog.bw.png │ │ ├── controller.bw.png │ │ ├── edit.bw.png │ │ ├── expand.bw.png │ │ ├── keyboard.bw.png │ │ ├── mouse.bw.png │ │ ├── profiles.bw.png │ │ ├── profiles.bw.xcf │ │ ├── refresh.bw.png │ │ ├── sc-controller.bw.png │ │ ├── sc-controller.png │ │ ├── turn-off.bw.png │ │ ├── turn-off.png │ │ ├── window.bw.png │ │ └── windowlist.bw.png │ └── weapons │ │ ├── LICENCES │ │ ├── baseball-bat.bw.png │ │ ├── bow-arrow.bw.png │ │ ├── brass-knuckles.bw.png │ │ ├── crowbar.bw.png │ │ ├── gladius.bw.png │ │ ├── grenade.bw.png │ │ ├── katana.bw.png │ │ ├── minigun.bw.png │ │ ├── missile-swarm.bw.png │ │ ├── mp5.bw.png │ │ ├── pistol-gun.bw.png │ │ ├── reticule.bw.png │ │ ├── sawed-off-shotgun.bw.png │ │ ├── steyr-aug.bw.png │ │ ├── stun-grenade.bw.png │ │ ├── switch-weapon.bw.png │ │ └── uzi.bw.png ├── radial-menu.svg ├── radial-menu.svg.json ├── remotepad.json ├── sc-config.json ├── sc-controller-small.svg ├── sc-controller.svg ├── scc-alive.svg ├── scc-dead.svg ├── scc-error.svg ├── scc-statusicon-alive.svg ├── scc-statusicon-dead.svg ├── scc-statusicon-error.svg ├── scc-statusicon-unknown.svg ├── scc-statusicon.svg ├── scc-unknown.svg ├── test-cursor.svg └── x360-config.json ├── osd-styles ├── Blue.colors.json ├── Classic.gtkstyle.css ├── Cyan.colors.json ├── Green.colors.json ├── Red.colors.json ├── Reloaded.gtkstyle.css └── Yellow.colors.json ├── profile_examples ├── DeSmuME.sccprofile ├── DiRT Rally.sccprofile ├── GZDoom.sccprofile ├── Kodi.sccprofile ├── No Man Sky.sccprofile ├── Portal 2 coop.sccprofile ├── Portal 2.sccprofile ├── README.md ├── Stardew Valley.sccprofile ├── Tomb Raider 2013.sccprofile ├── Undertale.sccprofile ├── Vanguard Princess.sccprofile └── firefox.sccprofile ├── run.sh ├── scc-mime-types.xml ├── scc ├── __init__.py ├── actions.py ├── aliases.py ├── c_branch.h ├── cemuhook_server.c ├── cemuhook_server.py ├── cheader.py ├── config.py ├── constants.py ├── controller.py ├── custom.py ├── device_monitor.py ├── drivers │ ├── __init__.py │ ├── ds4drv.py │ ├── evdevdrv.py │ ├── fake.py │ ├── hiddrv.c │ ├── hiddrv.py │ ├── remotepad.h │ ├── remotepad.py │ ├── remotepad_controller.c │ ├── sc_by_bt.c │ ├── sc_by_bt.py │ ├── sc_by_cable.py │ ├── sc_dongle.py │ ├── scc_future.h │ ├── steamdeck.py │ └── usb.py ├── foreign │ ├── __init__.py │ ├── vdf.py │ └── vdffz.py ├── gestures.py ├── gui │ ├── __init__.py │ ├── aboutdialog.py │ ├── action_editor.py │ ├── ae │ │ ├── __init__.py │ │ ├── axis.py │ │ ├── axis_action.py │ │ ├── buttons.py │ │ ├── custom.py │ │ ├── dpad.py │ │ ├── first_page.py │ │ ├── gesture.py │ │ ├── gyro.py │ │ ├── gyro_action.py │ │ ├── menu_action.py │ │ ├── menu_only.py │ │ ├── osk_action.py │ │ ├── osk_buttons.py │ │ ├── per_axis.py │ │ ├── recent_list.py │ │ ├── special_action.py │ │ ├── tilt.py │ │ └── trigger.py │ ├── app.py │ ├── area_to_action.py │ ├── binding_editor.py │ ├── chooser.py │ ├── controller_image.py │ ├── controller_settings.py │ ├── controller_widget.py │ ├── creg │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── data.py │ │ ├── dialog.py │ │ ├── grabs.py │ │ └── tester.py │ ├── daemon_manager.py │ ├── dwsnc.py │ ├── editor.py │ ├── gdk_to_key.py │ ├── gestures.py │ ├── global_settings.py │ ├── icon_chooser.py │ ├── importexport │ │ ├── __init__.py │ │ ├── dialog.py │ │ ├── export.py │ │ ├── import_sccprofile.py │ │ └── import_vdf.py │ ├── key_grabber.py │ ├── keycode_to_key.py │ ├── macro_editor.py │ ├── menu_editor.py │ ├── modeshift_editor.py │ ├── osd_mode.py │ ├── osk_binding_editor.py │ ├── parser.py │ ├── profile_switcher.py │ ├── ribar.py │ ├── ring_editor.py │ ├── simple_chooser.py │ ├── statusicon.py │ ├── svg_widget.py │ └── userdata_manager.py ├── lib │ ├── __init__.py │ ├── daemon.py │ ├── enum.py │ ├── eudevmonitor.py │ ├── hidparse.py │ ├── hidparse_data.py │ ├── hidraw.py │ ├── ioctl_opt.py │ ├── jsonencoder.py │ ├── libusb1.py │ ├── usb1.py │ ├── vdf.py │ ├── xinput.py │ └── xwrappers.py ├── macros.py ├── mapper.py ├── menu_data.py ├── modifiers.py ├── osd │ ├── __init__.py │ ├── area.py │ ├── binding_display.py │ ├── dialog.py │ ├── gesture_display.py │ ├── grid_menu.py │ ├── hmenu.py │ ├── inputdisplay.py │ ├── keyboard.py │ ├── launcher.py │ ├── menu.py │ ├── menu_generators.py │ ├── message.py │ ├── osk_actions.py │ ├── quick_menu.py │ ├── radial_menu.py │ ├── slave_mapper.py │ └── timermanager.py ├── parser.py ├── paths.py ├── poller.py ├── profile.py ├── sccdaemon.py ├── scheduler.py ├── scripts.py ├── special_actions.py ├── tools.py ├── uinput.c ├── uinput.py └── x11 │ ├── __init__.py │ ├── autoswitcher.py │ ├── scc-autoswitch-daemon.py │ ├── scc-osd-daemon.py │ └── scc_autoswitch_daemon.py ├── scripts ├── 69-sc-controller.rules ├── appimage-AppRun.sh ├── sc-controller ├── sc-controller.appdata.xml ├── sc-controller.desktop ├── scc ├── scc-daemon ├── scc-osd-dialog ├── scc-osd-keyboard ├── scc-osd-launcher ├── scc-osd-menu ├── scc-osd-message ├── scc-osd-radial-menu └── scc-osd-show-bindings ├── setup.py ├── tests ├── README.md ├── test_boolean.py ├── test_compress.py ├── test_docs.py ├── test_glade.py ├── test_inputs.py ├── test_parser │ ├── __init__.py │ ├── test_actions.py │ ├── test_macros.py │ ├── test_modifiers.py │ └── test_special_actions.py ├── test_profile │ ├── __init__.py │ ├── test_actions.py │ ├── test_modeshift.py │ ├── test_modifiers.py │ └── test_special_actions.py ├── test_setup.py ├── test_strings │ ├── __init__.py │ └── test_modifiers.py ├── test_vdf.py └── vdfs │ ├── 241100_674847325.vdf │ ├── 272850516041983257_legacy.bin.vdf │ ├── 272850516042312980_legacy.bin.vdf │ ├── 272850516042315772_legacy.bin.vdf │ └── dummy.vdf └── update-wiki.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .* 3 | *~ 4 | *.so 5 | *.patch 6 | !scc/*tester* 7 | test.* 8 | test[0-9].* 9 | build/ 10 | .atomignore 11 | commit* 12 | profiles 13 | .idea/ 14 | out.svg 15 | *.AppImage 16 | *.cpp 17 | *.vdf 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | install: 5 | - pip install -U pytest pylibacl setuptools 6 | - python setup.py build 7 | - python setup.py install 8 | script: PYTHONPATH="." py.test 9 | # needed by pylibacl 10 | addons: 11 | apt: 12 | packages: 13 | - libacl1-dev 14 | - libusb-1.0-0-dev 15 | -------------------------------------------------------------------------------- /ADDITIONAL-LICENSES: -------------------------------------------------------------------------------- 1 | All images in images/ directory, profile files in default_profiles/ 2 | and profile_examples/ and menu files in default_menus/ directories 3 | are licensed under CC0 license - https://creativecommons.org/publicdomain/zero/1.0/ 4 | 5 | Images in images/menu-icons subdirectories are licensed as described 6 | in 'LICENSES' files in their respective subdirectories. 7 | 8 | lib/enum.py is licensed under BSD license, as described in that file. 9 | lib/usb1.py and lib/libusb1.py are licensed under LGPL 2.1, as described in those files. 10 | lib/jsonencoder.py is licensed under PSFL, https://www.python.org/download/releases/2.7/license/ 11 | scripts/gamecontrollerdb.txt is taken from Simple DirectMedia Layer library and licensed under zlib license. 12 | lib/xwrappers.py and lib/vdf.py are part of SC-Controller, licensed under GPL2 as rest of the project. 13 | 14 | Everything else is licensed under GPL2, as described in 'LICENCE' file. 15 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | List of (possibly) planned features in no particular order: 2 | 3 | - Multiple on-screen menus (and possibly keyboards) when using multiple controllers 4 | - Injecting emulated xbox controller into wine 5 | 6 | Hard stuff: 7 | - Injecting emulated xbox controller into PlayOnLinux 8 | 9 | Very hard stuff: 10 | - Visual feedback in binding editor ( [what this guy says](https://www.reddit.com/r/linux_gaming/comments/5pcdmr/sc_controller_use_steam_controller_without_steam/dcqpvf4/) ) 11 | 12 | **Done** stuff: 13 | - Multicontroller support 14 | - Configurable gamepad type (e.g. 4 axes and 16 buttons) 15 | - Steam Profile import 16 | - Radial Menu for the Joystick/Trackpad 17 | - Copy & paste 18 | - Cycling Buttons 19 | - Process monitor (or active window monitor) with switch 20 | - Mouse regions 21 | - Touch-Menu 22 | - Menu in OSD 23 | - OSD 24 | - double click 25 | - on-screen keyboard 26 | - Spining mouse wheel rotation 27 | - Haptic feedback support 28 | - Gyroscope input 29 | - Gamepad button as modifier (modeshift) 30 | - Macros 31 | - Turbo 32 | - Trigger settings 33 | - DPAD that acts only when clicked 34 | - 8-way DPAD 35 | - Selector for media keys -------------------------------------------------------------------------------- /daemon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Ensure correct cwd 4 | cd "$(dirname "$0")" 5 | 6 | # Set PATH 7 | SCRIPTS="$(pwd)/scripts" 8 | export PATH="$SCRIPTS":"$PATH" 9 | export PYTHONPATH=".":"$PYTHONPATH" 10 | export SCC_SHARED="$(pwd)" 11 | 12 | if [ x"$1" == x"lldb" ] ; then 13 | shift 14 | lldb python2 -- 'scripts/scc-daemon' debug $@ 15 | else 16 | python2 'scripts/scc-daemon' $@ 17 | fi 18 | -------------------------------------------------------------------------------- /default_menus/.autoswitch.menu: -------------------------------------------------------------------------------- 1 | [{ 2 | "separator": true, 3 | "name": "Autoswitch Options" 4 | }, { 5 | "generator": "autoswitch" 6 | }] 7 | -------------------------------------------------------------------------------- /default_menus/.games.menu: -------------------------------------------------------------------------------- 1 | [{ 2 | "separator": true, 3 | "name": "Games" 4 | }, { 5 | "generator" : "games" 6 | }] 7 | -------------------------------------------------------------------------------- /default_menus/.windowlist.menu: -------------------------------------------------------------------------------- 1 | [{ 2 | "separator": true, 3 | "name": "Switch to" 4 | }, { 5 | "generator" : "windowlist" 6 | }] 7 | -------------------------------------------------------------------------------- /default_menus/Default.menu: -------------------------------------------------------------------------------- 1 | [{ 2 | "separator": true, 3 | "name": "Recent profiles" 4 | }, { 5 | "generator": "recent", 6 | "rows": 3 7 | }, { 8 | "submenu": "Profiles.menu", 9 | "icon": "system/profiles", 10 | "name": "All Profiles" 11 | }, { 12 | "separator": true, 13 | "name": "Options" 14 | }, { 15 | "submenu": ".autoswitch.menu", 16 | "icon": "system/autoswitch", 17 | "name": "Autoswitch Options" 18 | }, { 19 | "action": "turnoff()", 20 | "id": "item4", 21 | "icon": "system/turn-off", 22 | "name": "Turn Controller OFF", 23 | "osd": true 24 | }, { 25 | "action": "keyboard()", 26 | "id": "item5", 27 | "icon": "system/keyboard", 28 | "name": "Display Keyboard" 29 | }] 30 | -------------------------------------------------------------------------------- /default_menus/Profiles.menu: -------------------------------------------------------------------------------- 1 | [{ 2 | "separator": true, 3 | "name": "All profiles" 4 | }, { 5 | "generator" : "profiles" 6 | }] 7 | -------------------------------------------------------------------------------- /default_profiles/.scc-osd.keyboard.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "buttons": { 3 | "A": { "action": "button(Keys.KEY_ENTER)" }, 4 | "B": { "action": "button(Keys.KEY_ESC)" }, 5 | "C": { "action": "OSK.close()" }, 6 | "LB": { "action": "button(Keys.KEY_BACKSPACE)" }, 7 | "RB": { "action": "button(Keys.KEY_SPACE)" }, 8 | "LGRIP": { "action": "button(Keys.KEY_LEFTSHIFT)" }, 9 | "LPAD": { "action": "OSK.press(LEFT)" }, 10 | "RGRIP": { "action": "button(Keys.KEY_RIGHTALT)" }, 11 | "RPAD": { "action": "OSK.press(RIGHT)" }, 12 | "START": { "action": "button(Keys.KEY_TAB)" }, 13 | "X": { "action": "OSK.close()" }, 14 | "Y": { "action": "OSK.close()" } 15 | }, 16 | 17 | "pad_left": { "action": "OSK.cursor(LEFT)" }, 18 | "pad_right": { "action": "OSK.cursor(RIGHT)" }, 19 | "trigger_left": { "action": "button(Keys.KEY_LEFTSHIFT)" }, 20 | "trigger_right": { "action": "button(Keys.KEY_LEFTCTRL)" }, 21 | 22 | "stick": { 23 | "action": "dpad(button(Keys.KEY_UP), button(Keys.KEY_DOWN), button(Keys.KEY_LEFT), button(Keys.KEY_RIGHT))", 24 | "modes": { "RGRIP": { "action": "OSK.move()" } } 25 | }, 26 | 27 | "gyro": {}, 28 | "menus": {}, 29 | 30 | "version": 1 31 | } 32 | -------------------------------------------------------------------------------- /default_profiles/.scc-osd.profile_editor.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "_": "", 3 | "buttons": { 4 | "A": { 5 | "action": "button(Keys.KEY_SPACE)" 6 | }, 7 | "B": { 8 | "action": "button(Keys.KEY_ESC)" 9 | }, 10 | "C": { 11 | "action": "restart()" 12 | }, 13 | "LB": { 14 | "action": "button(Keys.KEY_BACKSPACE)" 15 | }, 16 | "LPAD": { 17 | "action": "button(Keys.BTN_RIGHT)" 18 | }, 19 | "RB": { 20 | "action": "button(Keys.KEY_SPACE)" 21 | }, 22 | "RPAD": { 23 | "action": "button(Keys.BTN_LEFT)" 24 | }, 25 | "Y": { 26 | "action": "button(Keys.KEY_F2)" 27 | } 28 | }, 29 | "cpad": {}, 30 | "gyro": {}, 31 | "is_template": false, 32 | "menus": {}, 33 | "pad_left": { 34 | "action": "feedback(LEFT, 4090, 16, ball(XY(mouse(Rels.REL_HWHEEL), mouse(Rels.REL_WHEEL))))" 35 | }, 36 | "pad_right": { 37 | "action": "smooth(8, 0.78, 2.0, feedback(RIGHT, 256, ball(mouse())))" 38 | }, 39 | "stick": { 40 | "action": "dpad(button(Keys.KEY_UP), button(Keys.KEY_DOWN), button(Keys.KEY_LEFT), button(Keys.KEY_RIGHT))" 41 | }, 42 | "trigger_left": { 43 | "action": "trigger(50, 50, button(Keys.BTN_RIGHT))" 44 | }, 45 | "trigger_right": { 46 | "action": "trigger(50, 255, button(Keys.BTN_LEFT))" 47 | }, 48 | "version": 1.4 49 | } -------------------------------------------------------------------------------- /default_profiles/Desktop.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "_": "", 3 | "buttons": { 4 | "A": { 5 | "action": "button(Keys.KEY_ENTER)" 6 | }, 7 | "B": { 8 | "action": "button(Keys.KEY_ESC)" 9 | }, 10 | "BACK": { 11 | "action": "button(Keys.KEY_BACKSPACE)" 12 | }, 13 | "C": { 14 | "action": "hold(menu('Default.menu'), menu('Default.menu'))" 15 | }, 16 | "CPADPRESS": { 17 | "action": "button(Keys.BTN_LEFT)" 18 | }, 19 | "LB": { 20 | "action": "button(Keys.KEY_LEFTCTRL)" 21 | }, 22 | "RB": { 23 | "action": "button(Keys.KEY_LEFTALT)" 24 | }, 25 | "RPAD": { 26 | "action": "button(Keys.BTN_LEFT)" 27 | }, 28 | "START": { 29 | "action": "button(Keys.KEY_LEFTSHIFT)" 30 | }, 31 | "X": { 32 | "action": "button(Keys.KEY_SPACE)" 33 | }, 34 | "Y": { 35 | "action": "button(Keys.KEY_TAB)" 36 | } 37 | }, 38 | "cpad": { 39 | "action": "mouse()" 40 | }, 41 | "gyro": { 42 | "action": "cemuhook" 43 | }, 44 | "is_template": false, 45 | "menus": {}, 46 | "pad_left": { 47 | "action": "feedback(LEFT, 4096, 16, ball(XY(mouse(Rels.REL_HWHEEL, 1.0), mouse(Rels.REL_WHEEL, 1.0))))" 48 | }, 49 | "pad_right": { 50 | "action": "smooth(8, 0.78, 2.0, feedback(RIGHT, 256, ball(mouse())))" 51 | }, 52 | "stick": { 53 | "action": "dpad(button(Keys.KEY_UP), button(Keys.KEY_DOWN), button(Keys.KEY_LEFT), button(Keys.KEY_RIGHT))" 54 | }, 55 | "trigger_left": { 56 | "action": "button(Keys.BTN_RIGHT)" 57 | }, 58 | "trigger_right": { 59 | "action": "button(Keys.BTN_LEFT)" 60 | }, 61 | "version": 1.4 62 | } 63 | -------------------------------------------------------------------------------- /default_profiles/XBox Controller with High Precision Camera.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "_": "", 3 | "buttons": { 4 | "A": { 5 | "action": "button(Keys.BTN_GAMEPAD)" 6 | }, 7 | "B": { 8 | "action": "button(Keys.BTN_EAST)" 9 | }, 10 | "BACK": { 11 | "action": "button(Keys.BTN_SELECT)" 12 | }, 13 | "C": { 14 | "action": "hold(menu('Default.menu'), menu('Default.menu'))" 15 | }, 16 | "LB": { 17 | "action": "button(Keys.BTN_TL)" 18 | }, 19 | "LGRIP": { 20 | "action": "button(Keys.BTN_GAMEPAD)" 21 | }, 22 | "RB": { 23 | "action": "button(Keys.BTN_TR)" 24 | }, 25 | "RGRIP": { 26 | "action": "button(Keys.BTN_NORTH)" 27 | }, 28 | "RPAD": { 29 | "action": "button(Keys.BTN_THUMBR)" 30 | }, 31 | "START": { 32 | "action": "button(Keys.BTN_START)" 33 | }, 34 | "STICKPRESS": { 35 | "action": "button(Keys.BTN_THUMBL)" 36 | }, 37 | "X": { 38 | "action": "button(Keys.BTN_NORTH)" 39 | }, 40 | "Y": { 41 | "action": "button(Keys.BTN_WEST)" 42 | } 43 | }, 44 | "cpad": {}, 45 | "gyro": {}, 46 | "is_template": false, 47 | "menus": {}, 48 | "pad_left": { 49 | "action": "click(dpad(hatup(Axes.ABS_HAT0Y), hatdown(Axes.ABS_HAT0Y), hatleft(Axes.ABS_HAT0X), hatright(Axes.ABS_HAT0X)))" 50 | }, 51 | "pad_right": { 52 | "action": "smooth(8, 0.78, feedback(RIGHT, 256, ball(mouse())))" 53 | }, 54 | "stick": { 55 | "action": "sens(1.2, 1.2, XY(axis(Axes.ABS_X), raxis(Axes.ABS_Y)))" 56 | }, 57 | "trigger_left": { 58 | "action": "axis(Axes.ABS_Z)" 59 | }, 60 | "trigger_right": { 61 | "action": "axis(Axes.ABS_RZ)" 62 | }, 63 | "version": 1.4 64 | } -------------------------------------------------------------------------------- /default_profiles/XBox Controller.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "_": "", 3 | "buttons": { 4 | "A": { 5 | "action": "button(Keys.BTN_GAMEPAD)" 6 | }, 7 | "B": { 8 | "action": "button(Keys.BTN_EAST)" 9 | }, 10 | "BACK": { 11 | "action": "button(Keys.BTN_SELECT)" 12 | }, 13 | "C": { 14 | "action": "hold(menu('Default.menu'), menu('Default.menu'))" 15 | }, 16 | "LB": { 17 | "action": "button(Keys.BTN_TL)" 18 | }, 19 | "LGRIP": { 20 | "action": "button(Keys.BTN_GAMEPAD)" 21 | }, 22 | "RB": { 23 | "action": "button(Keys.BTN_TR)" 24 | }, 25 | "RGRIP": { 26 | "action": "button(Keys.BTN_NORTH)" 27 | }, 28 | "RPAD": { 29 | "action": "button(Keys.BTN_THUMBR)" 30 | }, 31 | "START": { 32 | "action": "button(Keys.BTN_START)" 33 | }, 34 | "STICKPRESS": { 35 | "action": "button(Keys.BTN_THUMBL)" 36 | }, 37 | "X": { 38 | "action": "button(Keys.BTN_NORTH)" 39 | }, 40 | "Y": { 41 | "action": "button(Keys.BTN_WEST)" 42 | } 43 | }, 44 | "cpad": {}, 45 | "gyro": { 46 | "action": "cemuhook" 47 | }, 48 | "is_template": false, 49 | "menus": {}, 50 | "pad_left": { 51 | "action": "click(dpad(hatup(Axes.ABS_HAT0Y), hatdown(Axes.ABS_HAT0Y), hatleft(Axes.ABS_HAT0X), hatright(Axes.ABS_HAT0X)))" 52 | }, 53 | "pad_right": { 54 | "action": "feedback(RIGHT, 256, XY(axis(Axes.ABS_RX), raxis(Axes.ABS_RY)))" 55 | }, 56 | "stick": { 57 | "action": "sens(1.2, 1.2, XY(axis(Axes.ABS_X), raxis(Axes.ABS_Y)))" 58 | }, 59 | "trigger_left": { 60 | "action": "axis(Axes.ABS_Z)" 61 | }, 62 | "trigger_right": { 63 | "action": "axis(Axes.ABS_RZ)" 64 | }, 65 | "version": 1.4 66 | } 67 | -------------------------------------------------------------------------------- /docs/menu-file.md: -------------------------------------------------------------------------------- 1 | SC-Controller menu file specification 2 | ---------------------------------------- 3 | 4 | Menu file contains json-encoded list with menu items (actions), submenus, 5 | separators and menu generators. 6 | 7 | ### Menu Items 8 | 9 | Every menu item is defined by action, in same way as action would be defined 10 | [in profile file](profile-file.md#Action_definition) with one additional 11 | `id` key. `id` specifies action ID and can be anything, but each menu item 12 | should have unique ID. 13 | 14 | `name` key is still optional, but highly recommended as used as menu item title 15 | displayed on screen. If `name` is not specified, title is auto-generated. 16 | 17 | Example: 18 | 19 | [{ 20 | "id": "item1", 21 | "action": "profile('Desktop')", 22 | "name": "Switch to Desktop profile", 23 | }, { 24 | "id": "item2", 25 | "action": "turnoff()", 26 | "name": "Turn controller OFF", 27 | }] 28 | 29 | specifies menu with two items. 30 | 31 | ### Submenus 32 | 33 | Submenu is reference to another menu file (submenu cannot be defined in same 34 | file or profile file). When selected, another menu is loaded and drawn over 35 | original menu. 36 | Submenu is dict with `submenu` key, value of key is filename relative to 37 | `~/.config/scc/menus` or `/usr/share/scc/default_menus/`, whichever exists, in 38 | that order. 39 | `name` key may be defined. 40 | 41 | Example: 42 | 43 | [{ 44 | "submenu": "profiles.menu", 45 | "name": "All Profiles" 46 | }] 47 | 48 | specifies menu with sumbmenu called "All Profiles" defined in *profiles.menu* 49 | 50 | ### Separators 51 | 52 | Separator is empty space that splits menu into two or more logical blocks. 53 | Name, if set, is displayed in different way from menu items. Separator is 54 | defined by dict with `separator` key set to True. 55 | 56 | ### Menu Generators 57 | 58 | Generator is something that generates menu items automatically. It is defined 59 | by dict with `generator` key, where value is type of generator to use. 60 | 61 | Example: 62 | 63 | [{ 64 | "generator": "profiles" 65 | }, { 66 | "id": "item2", 67 | "action": "turnoff()", 68 | "name": "Turn controller OFF", 69 | }] 70 | 71 | specifies menu with list of all profiles, followed by one normal menu item. 72 | 73 | 74 | ### Available generators 75 | 76 | #### `profiles` 77 | Generates menu item for each available profile. Selecting item will switch to 78 | represented profile. 79 | 80 | #### `recent` 81 | Generates menu item for *X* recently selected profiles. *X* is 5 by default and 82 | can be set with additional `rows` key. 83 | -------------------------------------------------------------------------------- /docs/osd-keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/docs/osd-keyboard.png -------------------------------------------------------------------------------- /docs/screenshot1-tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/docs/screenshot1-tn.png -------------------------------------------------------------------------------- /docs/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/docs/screenshot1.png -------------------------------------------------------------------------------- /docs/screenshot2-tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/docs/screenshot2-tn.png -------------------------------------------------------------------------------- /docs/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/docs/screenshot2.png -------------------------------------------------------------------------------- /docs/screenshot3-tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/docs/screenshot3-tn.png -------------------------------------------------------------------------------- /docs/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/docs/screenshot3.png -------------------------------------------------------------------------------- /docs/screenshot4-tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/docs/screenshot4-tn.png -------------------------------------------------------------------------------- /docs/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/docs/screenshot4.png -------------------------------------------------------------------------------- /glade/ae/axis.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | True 7 | False 8 | 9 | 10 | True 11 | False 12 | 5 13 | 5 14 | click on axis or trigger 15 | 1 16 | 17 | 18 | 19 | 20 | 21 | 0 22 | 1 23 | 2 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /glade/ae/custom.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | True 10 | False 11 | vertical 12 | 13 | 14 | True 15 | False 16 | Custom Action 17 | 4 18 | 0 19 | 20 | 21 | 22 | 23 | 24 | False 25 | True 26 | 0 27 | 28 | 29 | 30 | 31 | 150 32 | True 33 | True 34 | 5 35 | in 36 | 37 | 38 | True 39 | True 40 | 5 41 | 5 42 | 5 43 | 5 44 | tbCustomAction 45 | 46 | 47 | 48 | 49 | True 50 | True 51 | 1 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /glade/ae/first_page.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | True 7 | False 8 | 9 | 10 | True 11 | False 12 | 20 13 | (markup goes here) 14 | True 15 | False 16 | 0 17 | 0 18 | 19 | 20 | 21 | 0 22 | 0 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /glade/ae/recent_list.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 2 7 | 10 8 | 2 9 | 1 10 | 10 11 | 12 | 13 | True 14 | False 15 | 20 16 | 20 17 | 10 18 | 10 19 | True 20 | True 21 | 22 | 23 | True 24 | False 25 | 10 26 | True 27 | Number of Profiles to Display 28 | 0 29 | 30 | 31 | 32 | 33 | 34 | 0 35 | 0 36 | 37 | 38 | 39 | 40 | 150 41 | True 42 | True 43 | adjNumOfProfiles 44 | 45 | 46 | 1 47 | 0 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /glade/simple_chooser.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | False 8 | center-on-parent 9 | dialog 10 | 11 | 12 | 13 | 14 | 15 | 16 | True 17 | False 18 | True 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /images/24x24/status/scc-alive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/24x24/status/scc-alive.png -------------------------------------------------------------------------------- /images/24x24/status/scc-dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/24x24/status/scc-dead.png -------------------------------------------------------------------------------- /images/24x24/status/scc-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/24x24/status/scc-error.png -------------------------------------------------------------------------------- /images/24x24/status/scc-unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/24x24/status/scc-unknown.png -------------------------------------------------------------------------------- /images/256x256/status/scc-alive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/256x256/status/scc-alive.png -------------------------------------------------------------------------------- /images/256x256/status/scc-alive.tmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/256x256/status/scc-alive.tmp.png -------------------------------------------------------------------------------- /images/256x256/status/scc-dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/256x256/status/scc-dead.png -------------------------------------------------------------------------------- /images/256x256/status/scc-dead.tmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/256x256/status/scc-dead.tmp.png -------------------------------------------------------------------------------- /images/256x256/status/scc-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/256x256/status/scc-error.png -------------------------------------------------------------------------------- /images/256x256/status/scc-error.tmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/256x256/status/scc-error.tmp.png -------------------------------------------------------------------------------- /images/256x256/status/scc-statusicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/256x256/status/scc-statusicon.png -------------------------------------------------------------------------------- /images/256x256/status/scc-unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/256x256/status/scc-unknown.png -------------------------------------------------------------------------------- /images/256x256/status/scc-unknown.tmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/256x256/status/scc-unknown.tmp.png -------------------------------------------------------------------------------- /images/C.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /images/CPAD.svg: -------------------------------------------------------------------------------- 1 | button-images/CPAD.svg -------------------------------------------------------------------------------- /images/DOUBLECLICK.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 62 | 65 | 2x 76 | 77 | 78 | -------------------------------------------------------------------------------- /images/GYRO.svg: -------------------------------------------------------------------------------- 1 | button-images/GYRO.svg -------------------------------------------------------------------------------- /images/LGRIP.svg: -------------------------------------------------------------------------------- 1 | button-images/LGRIP.svg -------------------------------------------------------------------------------- /images/LPAD.svg: -------------------------------------------------------------------------------- 1 | button-images/LPAD.svg -------------------------------------------------------------------------------- /images/LT.svg: -------------------------------------------------------------------------------- 1 | button-images/LT.svg -------------------------------------------------------------------------------- /images/RGRIP.svg: -------------------------------------------------------------------------------- 1 | button-images/RGRIP.svg -------------------------------------------------------------------------------- /images/RPAD.svg: -------------------------------------------------------------------------------- 1 | button-images/RPAD.svg -------------------------------------------------------------------------------- /images/RSTICK.svg: -------------------------------------------------------------------------------- 1 | button-images/RSTICK.bw.svg -------------------------------------------------------------------------------- /images/RT.svg: -------------------------------------------------------------------------------- 1 | button-images/RT.svg -------------------------------------------------------------------------------- /images/button-images/C.bw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /images/button-images/C.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 63 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /images/button-images/CPAD.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /images/button-images/ELIPSE.bw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 48 | 50 | 51 | 53 | image/svg+xml 54 | 56 | 57 | 58 | 59 | 63 | 65 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /images/button-images/ELIPSE.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 48 | 50 | 51 | 53 | image/svg+xml 54 | 56 | 57 | 58 | 59 | 63 | 66 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /images/button-images/RECTANGLE.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 46 | 48 | 49 | 51 | image/svg+xml 52 | 54 | 55 | 56 | 57 | 58 | 63 | 65 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /images/button-images/RPAD.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 62 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /images/button-images/RSTICK.bw.svg: -------------------------------------------------------------------------------- 1 | STICK.bw.svg -------------------------------------------------------------------------------- /images/button-images/TOUCHPAD.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 45 | 47 | 48 | 50 | image/svg+xml 51 | 53 | 54 | 55 | 56 | 62 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /images/button-images/ds4SHARE.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 46 | 48 | 49 | 51 | image/svg+xml 52 | 54 | 55 | 56 | 57 | 58 | 64 | 67 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /images/button-images/groups.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key" : "sc", 4 | "type" : "buttons", 5 | "buttons" : [ "A", "B", "X", "Y", "BACK", "C", "START", 6 | "LB", "RB", "LT", "RT", "LG", "RG" ] 7 | }, 8 | 9 | { 10 | "key" : "xo", 11 | "type" : "buttons", 12 | "buttons" : [ "CROSS", "CIRCLE", "SQUARE", "TRIANGLE", "RECTANGLE", "C", 13 | "RTRIANGLE", "LB", "RB", "LT", "RT", "LG", "RG" ] 14 | }, 15 | 16 | { 17 | "key" : "1234", 18 | "type" : "buttons", 19 | "buttons" : [ "1", "2", "3", "4", "BACK", "C", "START", 20 | "LB", "RB", "LT", "RT", "LG", "RG" ] 21 | }, 22 | 23 | { 24 | "key" : "snes", 25 | "type" : "buttons", 26 | "buttons" : [ "snesB", "snesA", "snesY", "snesX", "snesSTART", "C", 27 | "snesSTART", "LB", "RB", "LT", "RT", "LG", "RG" ] 28 | } 29 | 30 | ] -------------------------------------------------------------------------------- /images/button-images/snesSTART.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 62 | 64 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /images/controller-icons/ds4evdev-0.svg: -------------------------------------------------------------------------------- 1 | ds4-0.svg -------------------------------------------------------------------------------- /images/controller-icons/ds4evdev-1.svg: -------------------------------------------------------------------------------- 1 | ds4-1.svg -------------------------------------------------------------------------------- /images/controller-icons/ds4evdev-2.svg: -------------------------------------------------------------------------------- 1 | ds4-2.svg -------------------------------------------------------------------------------- /images/controller-icons/ds4evdev-3.svg: -------------------------------------------------------------------------------- 1 | ds4-3.svg -------------------------------------------------------------------------------- /images/controller-icons/ds4evdev-4.svg: -------------------------------------------------------------------------------- 1 | ds4-4.svg -------------------------------------------------------------------------------- /images/controller-icons/ds4evdev-5.svg: -------------------------------------------------------------------------------- 1 | ds4-5.svg -------------------------------------------------------------------------------- /images/controller-icons/ds4evdev-6.svg: -------------------------------------------------------------------------------- 1 | ds4-6.svg -------------------------------------------------------------------------------- /images/controller-icons/evdev-0.svg: -------------------------------------------------------------------------------- 1 | hid-0.svg -------------------------------------------------------------------------------- /images/controller-icons/evdev-1.svg: -------------------------------------------------------------------------------- 1 | hid-1.svg -------------------------------------------------------------------------------- /images/controller-icons/evdev-2.svg: -------------------------------------------------------------------------------- 1 | hid-2.svg -------------------------------------------------------------------------------- /images/controller-icons/evdev-3.svg: -------------------------------------------------------------------------------- 1 | hid-3.svg -------------------------------------------------------------------------------- /images/controller-icons/evdev-4.svg: -------------------------------------------------------------------------------- 1 | hid-4.svg -------------------------------------------------------------------------------- /images/controller-icons/evdev-5.svg: -------------------------------------------------------------------------------- 1 | hid-5.svg -------------------------------------------------------------------------------- /images/controller-icons/evdev-6.svg: -------------------------------------------------------------------------------- 1 | hid-6.svg -------------------------------------------------------------------------------- /images/deck.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui": { 3 | "background": "deck", 4 | "buttons": [ 5 | "A", "B", "X", "Y", "VIEW", "ELIPSE", "MENU", "LB", "RB", "LT", "RT", 6 | "STICK", "TOUCHPAD", "TOUCHPAD", "RGRIP", "LGRIP", "DOTS" 7 | ] 8 | }, 9 | "buttons": [ 10 | "DOTS", "RSTICKTOUCH", "LSTICKTOUCH", "RGRIP2", "LGRIP2", "RSTICKPRESS", 11 | "LSTICKPRESS", "RPADTOUCH", "LPADTOUCH", "RPADPRESS", "LPADPRESS", "C", 12 | "RGRIP", "LGRIP", "START", "BACK", "A", "X", "B", "Y", "LB", "RB" 13 | ], 14 | "axes": [ 15 | "stick_x", "stick_y", "rstick_x", "rstick_y", 16 | "lpad_x", "lpad_x", "rpad_y", "rpad_y", 17 | "dpad_x", "dpad_y", "ltrig", "rtrig" 18 | ], 19 | "gyros": true 20 | } 21 | 22 | -------------------------------------------------------------------------------- /images/ds4-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui": { 3 | "background": "ds4", 4 | "buttons": [ 5 | "CROSS", "CIRCLE", "SQUARE", "TRIANGLE", "ds4SHARE", 6 | "C", "ds4SHARE", "LB", "RB", "LT", "RT", 7 | "STICK", "LPAD", "RPAD", "LG", "RG" 8 | ] 9 | }, 10 | 11 | "_ " : "Buttons defined here are not actually loaded by driver, but gui", 12 | "__" : "uses list to determine what buttons and axes are available", 13 | 14 | "gyros": true, 15 | "buttons": { 16 | "288": "Y", 17 | "289": "B", 18 | "290": "A", 19 | "291": "X", 20 | "294": "RB", 21 | "295": "LB", 22 | "298": "C", 23 | "293": "CPAD", 24 | "296": "BACK", 25 | "297": "START" 26 | } 27 | } -------------------------------------------------------------------------------- /images/keyboard.svg.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : { 3 | "button1" : "162082", 4 | "button1_border" : "262b5e", 5 | "button2" : "162d44", 6 | "button2_border" : "27323e", 7 | "text" : "ffffff", 8 | "background" : "160c00" 9 | } 10 | } -------------------------------------------------------------------------------- /images/menu-icons/buttons/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/1.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/2.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/3.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/4.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/A.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/B.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/BACK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/BACK.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/CIRCLE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/CIRCLE.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/CROSS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/CROSS.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/LB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/LB.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/LICENCES: -------------------------------------------------------------------------------- 1 | All images in this specific directory are licensed under CC0, https://creativecommons.org/publicdomain/zero/1.0/ 2 | -------------------------------------------------------------------------------- /images/menu-icons/buttons/RB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/RB.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/SQUARE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/SQUARE.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/START.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/START.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/TRIANGLE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/TRIANGLE.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/X.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/Y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/Y.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/snesA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/snesA.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/snesB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/snesB.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/snesX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/snesX.png -------------------------------------------------------------------------------- /images/menu-icons/buttons/snesY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/buttons/snesY.png -------------------------------------------------------------------------------- /images/menu-icons/driving/LICENCES: -------------------------------------------------------------------------------- 1 | Icons provided on http://opengameart.org and http://game-icons.net/ under 2 | the Creative Commons 3.0 BY 3 | 4 | List of icons and contributors: 5 | 6 | steering-wheel.bw.png - Delapouite, http://delapouite.com 7 | pedal1.bw.png - Based on image by Looneybits, (CC 0) 8 | pedal2.bw.png - Based on image by Looneybits, (CC 0) 9 | autoshift-drive.bw.pn - Based on image by Looneybits, (CC 0) 10 | autoshift-reverse.bw.png - Based on image by Looneybits, (CC 0) 11 | gearbox-N.bw.png - Kozec, (CC 0) 12 | gearbox-1.bw.png - Kozec, (CC 0) 13 | gearbox-2.bw.png - Kozec, (CC 0) 14 | gearbox-3.bw.png - Kozec, (CC 0) 15 | gearbox-4.bw.png - Kozec, (CC 0) 16 | gearbox-5.bw.png - Kozec, (CC 0) 17 | gearbox-6.bw.png - Kozec, (CC 0) 18 | gearbox-R.bw.png - Kozec, (CC 0) 19 | -------------------------------------------------------------------------------- /images/menu-icons/driving/autoshift-drive.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/autoshift-drive.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/autoshift-reverse.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/autoshift-reverse.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/gearbox-1.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/gearbox-1.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/gearbox-2.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/gearbox-2.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/gearbox-3.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/gearbox-3.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/gearbox-4.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/gearbox-4.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/gearbox-5.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/gearbox-5.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/gearbox-6.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/gearbox-6.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/gearbox-N.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/gearbox-N.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/gearbox-R.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/gearbox-R.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/pedal1.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/pedal1.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/pedal2.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/pedal2.bw.png -------------------------------------------------------------------------------- /images/menu-icons/driving/steering-wheel.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/driving/steering-wheel.bw.png -------------------------------------------------------------------------------- /images/menu-icons/items/LICENCES: -------------------------------------------------------------------------------- 1 | Icons provided on http://opengameart.org and http://game-icons.net/ under 2 | the Creative Commons 3.0 BY 3 | 4 | List of icons and contributors: 5 | 6 | medpack.bw.png - sbed 7 | coffee.bw.png - Lorc, http://lorcblog.blogspot.com 8 | pill.bw.png - Lorc, http://lorcblog.blogspot.com 9 | syringe.bw.png - Lorc, http://lorcblog.blogspot.com 10 | apple.bw.png - Lorc, http://lorcblog.blogspot.com 11 | flask.bw.png - Lorc, http://lorcblog.blogspot.com 12 | potion.bw.png - Lorc, http://lorcblog.blogspot.com 13 | heart-bottle.bw.png - Lorc, http://lorcblog.blogspot.com 14 | bottle.bw.png - Delapouite, http://delapouite.com 15 | -------------------------------------------------------------------------------- /images/menu-icons/items/apple.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/items/apple.bw.png -------------------------------------------------------------------------------- /images/menu-icons/items/bottle.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/items/bottle.bw.png -------------------------------------------------------------------------------- /images/menu-icons/items/coffee.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/items/coffee.bw.png -------------------------------------------------------------------------------- /images/menu-icons/items/flask.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/items/flask.bw.png -------------------------------------------------------------------------------- /images/menu-icons/items/heart-bottle.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/items/heart-bottle.bw.png -------------------------------------------------------------------------------- /images/menu-icons/items/medpack.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/items/medpack.bw.png -------------------------------------------------------------------------------- /images/menu-icons/items/pill.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/items/pill.bw.png -------------------------------------------------------------------------------- /images/menu-icons/items/potion.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/items/potion.bw.png -------------------------------------------------------------------------------- /images/menu-icons/items/syringe.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/items/syringe.bw.png -------------------------------------------------------------------------------- /images/menu-icons/media/LICENCES: -------------------------------------------------------------------------------- 1 | Icons under the CC0, https://creativecommons.org/publicdomain/zero/1.0/ 2 | 3 | List of icons and contributors: 4 | 5 | mute.bw.png - Kozec, (CC 0) 6 | next.bw.png - Kozec, (CC 0) 7 | pause.bw.png - Kozec, (CC 0) 8 | play.bw.png - Kozec, (CC 0) 9 | prev.bw.png - Kozec, (CC 0) 10 | stop.bw.png - Kozec, (CC 0) 11 | volume-down.bw.png - Kozec, (CC 0) 12 | volume-up.bw.png - Kozec, (CC 0) 13 | -------------------------------------------------------------------------------- /images/menu-icons/media/mute.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/media/mute.bw.png -------------------------------------------------------------------------------- /images/menu-icons/media/next.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/media/next.bw.png -------------------------------------------------------------------------------- /images/menu-icons/media/pause.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/media/pause.bw.png -------------------------------------------------------------------------------- /images/menu-icons/media/play.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/media/play.bw.png -------------------------------------------------------------------------------- /images/menu-icons/media/prev.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/media/prev.bw.png -------------------------------------------------------------------------------- /images/menu-icons/media/stop.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/media/stop.bw.png -------------------------------------------------------------------------------- /images/menu-icons/media/volume-down.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/media/volume-down.bw.png -------------------------------------------------------------------------------- /images/menu-icons/media/volume-up.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/media/volume-up.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/LICENCES: -------------------------------------------------------------------------------- 1 | Icons provided on http://game-icons.net under the Creative Commons 3.0 BY 2 | 3 | List of icons and contributors: 4 | 5 | keyboard.bw.png - Delapouite, http://delapouite.com 6 | refresh.bw.png - Delapouite, http://delapouite.com 7 | expand.bw.png - Delapouite, http://delapouite.com 8 | mouse.bw.png - Delapouite, http://delapouite.com 9 | cog.bw.png - Lorc, http://lorcblog.blogspot.com 10 | controller.bw.png - Skoll 11 | sc-controller.png - Kozec, (CC 0) 12 | sc-controller.bw.png - Kozec, (CC 0) 13 | turn-off.png - Kozec, (CC 0) 14 | turn-off.bw.png - Kozec, (CC 0) 15 | autoswitch.bw.png - Kozec, (CC 0) 16 | profiles.bw.png - Kozec, (CC 0) 17 | turn-off.bw.png - Kozec, (CC 0) 18 | window.bw.png - Kozec, (CC 0) 19 | windowlist.bw.png - Kozec, (CC 0) 20 | -------------------------------------------------------------------------------- /images/menu-icons/system/autoswitch.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/autoswitch.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/binding-display.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/binding-display.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/cog.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/cog.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/controller.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/controller.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/edit.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/edit.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/expand.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/expand.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/keyboard.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/keyboard.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/mouse.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/mouse.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/profiles.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/profiles.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/profiles.bw.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/profiles.bw.xcf -------------------------------------------------------------------------------- /images/menu-icons/system/refresh.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/refresh.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/sc-controller.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/sc-controller.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/sc-controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/sc-controller.png -------------------------------------------------------------------------------- /images/menu-icons/system/turn-off.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/turn-off.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/turn-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/turn-off.png -------------------------------------------------------------------------------- /images/menu-icons/system/window.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/window.bw.png -------------------------------------------------------------------------------- /images/menu-icons/system/windowlist.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/system/windowlist.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/LICENCES: -------------------------------------------------------------------------------- 1 | Icons provided on http://game-icons.net under the Creative Commons 3.0 BY 2 | 3 | List of icons and contributors: 4 | 5 | bow-arrow.bw.png - Delapouite, http://delapouite.com 6 | brass-knuckles.bw.png - Delapouite, http://delapouite.com 7 | crowbar.bw.png - Delapouite, http://delapouite.com 8 | mp5.bw.png - Delapouite, http://delapouite.com 9 | sawed-off-shotgun.bw.png - Delapouite, http://delapouite.com 10 | switch-weapon.bw.png - Delapouite, http://delapouite.com 11 | uzi.bw.png - Delapouite, http://delapouite.com 12 | katana.bw.png - Delapouite, http://delapouite.com 13 | baseball-bat.bw.png - Delapouite, http://delapouite.com 14 | pistol-gun.bw.png - John Colburn, http://ninmunanmu.com 15 | minigun.bw.png - Lorc, http://lorcblog.blogspot.com 16 | missile-swarm.bw.png - Lorc, http://lorcblog.blogspot.com 17 | reticule.bw.png - Lorc, http://lorcblog.blogspot.com 18 | grenade.bw.png - Lorc, http://lorcblog.blogspot.com 19 | stun-grenade.bw.png - Lorc, http://lorcblog.blogspot.com 20 | gladius.bw.png - Skoll 21 | steyr-aug.bw.png - Skoll 22 | -------------------------------------------------------------------------------- /images/menu-icons/weapons/baseball-bat.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/baseball-bat.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/bow-arrow.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/bow-arrow.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/brass-knuckles.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/brass-knuckles.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/crowbar.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/crowbar.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/gladius.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/gladius.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/grenade.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/grenade.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/katana.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/katana.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/minigun.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/minigun.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/missile-swarm.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/missile-swarm.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/mp5.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/mp5.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/pistol-gun.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/pistol-gun.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/reticule.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/reticule.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/sawed-off-shotgun.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/sawed-off-shotgun.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/steyr-aug.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/steyr-aug.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/stun-grenade.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/stun-grenade.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/switch-weapon.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/switch-weapon.bw.png -------------------------------------------------------------------------------- /images/menu-icons/weapons/uzi.bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kozec/sc-controller/2e2fe855afff693950827a45637cea3290eae782/images/menu-icons/weapons/uzi.bw.png -------------------------------------------------------------------------------- /images/radial-menu.svg.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : { 3 | "text": "00ff00", 4 | "border" : "00ff00", 5 | "background" : "000093", 6 | "menuitem_border": "00005a", 7 | "menuitem_hilight_border": "010101" 8 | } 9 | } -------------------------------------------------------------------------------- /images/remotepad.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui": { 3 | "background": "remotepad", 4 | "no_buttons_in_gui": true, 5 | "buttons": [ 6 | "A", "B", "X", "Y", "RECTANGLE", 7 | "C", "RTRIANGLE", "LB", "RB", "LT", "RT", 8 | "STICK", "LPAD", "RPAD", "LG", "RG" 9 | ] 10 | }, 11 | 12 | "_ " : "Buttons defined here are not actually loaded by driver, but gui", 13 | "__" : "uses list to determine what buttons and axes are available", 14 | 15 | "gyros": true, 16 | "buttons": { 17 | "288": "Y", 18 | "289": "B", 19 | "290": "A", 20 | "291": "X", 21 | "294": "RB", 22 | "295": "LB", 23 | "298": "C", 24 | "296": "BACK", 25 | "297": "START" 26 | } 27 | } -------------------------------------------------------------------------------- /images/sc-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "_ " : "Currently, this file is used only when changing controller image", 3 | "__" : "manually from context menu", 4 | 5 | "gui": { 6 | "background": "sc", 7 | "buttons": [ 8 | "A", "B", "X", "Y", "BACK", 9 | "C", "START", "LB", "RB", "LT", "RT", 10 | "STICK", "LPAD", "RPAD", "LG", "RG" 11 | ] 12 | }, 13 | 14 | "gyros": true, 15 | "buttons": { 16 | "1": "A", 17 | "2": "B", 18 | "3": "X", 19 | "4": "Y", 20 | "5": "RB", 21 | "6": "LB", 22 | "7": "C", 23 | "9": "BACK", 24 | "10": "START" 25 | } 26 | } -------------------------------------------------------------------------------- /images/scc-statusicon-error.svg: -------------------------------------------------------------------------------- 1 | scc-error.svg -------------------------------------------------------------------------------- /images/x360-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "_ " : "Currently, this file is used only when changing controller image", 3 | "__" : "manually from context menu", 4 | 5 | "gui": { 6 | "background": "x360", 7 | "buttons": [ 8 | "A", "B", "X", "Y", "BACK", 9 | "C", "START", "LB", "RB", "LT", "RT", 10 | "STICK", "LPAD", "RPAD", "LG", "RG" 11 | ] 12 | }, 13 | 14 | "gyros": false, 15 | "buttons": { 16 | "1": "A", 17 | "2": "B", 18 | "3": "X", 19 | "4": "Y", 20 | "5": "RB", 21 | "6": "LB", 22 | "7": "C", 23 | "9": "BACK", 24 | "10": "START" 25 | } 26 | } -------------------------------------------------------------------------------- /osd-styles/Blue.colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "###" : "Colors used by OSD", 3 | "osd_colors": { 4 | "background": "101010", 5 | "border": "0000DF", 6 | "text": "4060FF", 7 | "menuitem_border": "000040", 8 | "menuitem_hilight": "000050", 9 | "menuitem_hilight_text": "FFFFFF", 10 | "menuitem_hilight_border": "0000FF", 11 | "menuseparator": "505090" 12 | }, 13 | 14 | "###" : "Colors used by on-screen keyboard", 15 | "osk_colors": { 16 | "hilight" : "00688D", 17 | "pressed" : "1A9485", 18 | "button1" : "162082", 19 | "button1_border" : "262b5e", 20 | "button2" : "162d44", 21 | "button2_border" : "27323e", 22 | "text" : "ffffff" 23 | } 24 | } -------------------------------------------------------------------------------- /osd-styles/Cyan.colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "###" : "Colors used by OSD", 3 | "osd_colors": { 4 | "background": "101010", 5 | "border": "00DFDF", 6 | "text": "40F0FF", 7 | "menuitem_border": "004040", 8 | "menuitem_hilight": "005050", 9 | "menuitem_hilight_text": "FFFFFF", 10 | "menuitem_hilight_border": "00FFFF", 11 | "menuseparator": "509090" 12 | }, 13 | 14 | "###" : "Colors used by on-screen keyboard", 15 | "osk_colors": { 16 | "hilight" : "00688D", 17 | "pressed" : "1A9485", 18 | "button1" : "162082", 19 | "button1_border" : "262b5e", 20 | "button2" : "162d44", 21 | "button2_border" : "27323e", 22 | "text" : "ffffff" 23 | } 24 | } -------------------------------------------------------------------------------- /osd-styles/Green.colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "####" : "This is default color scheme", 3 | 4 | "###" : "Colors used by OSD", 5 | "osd_colors": { 6 | "background": "101010", 7 | "border": "00FF00", 8 | "text": "16BF24", 9 | "menuitem_border": "004000", 10 | "menuitem_hilight": "000070", 11 | "menuitem_hilight_text": "16FF26", 12 | "menuitem_hilight_border": "00FF00", 13 | "menuseparator": "109010" 14 | }, 15 | 16 | "###" : "Colors used by on-screen keyboard", 17 | "osk_colors": { 18 | "hilight" : "00688D", 19 | "pressed" : "1A9485", 20 | "button1" : "162082", 21 | "button1_border" : "262b5e", 22 | "button2" : "162d44", 23 | "button2_border" : "27323e", 24 | "text" : "ffffff" 25 | } 26 | } -------------------------------------------------------------------------------- /osd-styles/Red.colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "###" : "Colors used by OSD", 3 | "osd_colors": { 4 | "background": "101010", 5 | "border": "DF0000", 6 | "text": "FF2020", 7 | "menuitem_border": "400000", 8 | "menuitem_hilight": "600000", 9 | "menuitem_hilight_text": "FFFFFF", 10 | "menuitem_hilight_border": "FF0000", 11 | "menuseparator": "905050" 12 | }, 13 | 14 | "###" : "Colors used by on-screen keyboard", 15 | "osk_colors": { 16 | "hilight" : "00688D", 17 | "pressed" : "1A9485", 18 | "button1" : "162082", 19 | "button1_border" : "262b5e", 20 | "button2" : "162d44", 21 | "button2_border" : "27323e", 22 | "text" : "ffffff" 23 | } 24 | } -------------------------------------------------------------------------------- /osd-styles/Yellow.colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "###" : "Colors used by OSD", 3 | "osd_colors": { 4 | "background": "101010", 5 | "border": "FFFF00", 6 | "text": "BFBF24", 7 | "menuitem_border": "404000", 8 | "menuitem_hilight": "FFFF00", 9 | "menuitem_hilight_text": "000000", 10 | "menuitem_hilight_border": "FFFF00", 11 | "menuseparator": "A5A5A5" 12 | }, 13 | 14 | "###" : "Colors used by on-screen keyboard", 15 | "osk_colors": { 16 | "hilight" : "00688D", 17 | "pressed" : "1A9485", 18 | "button1" : "162082", 19 | "button1_border" : "262b5e", 20 | "button2" : "162d44", 21 | "button2_border" : "27323e", 22 | "text" : "ffffff" 23 | } 24 | } -------------------------------------------------------------------------------- /profile_examples/DiRT Rally.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "_": ["For best results Steering Linearity, Steering Deadzone, Throttle Deadzone and", "Brake Deadzone should be set to 0 in Advanced Gamepad Settings. Steering Sensitivity,", "Steering Saturation, Throttle Saturation and Brake Saturation should be set to 100."], 3 | "buttons": { 4 | "A": { 5 | "action": "button(Keys.BTN_GAMEPAD)", 6 | "name": "Gear Up" 7 | }, 8 | "B": { 9 | "action": "button(Keys.BTN_EAST)", 10 | "name": "Handbreak" 11 | }, 12 | "BACK": { 13 | "action": "resetgyro()", 14 | "name": "Reset Gyro" 15 | }, 16 | "C": { 17 | "action": "hold(menu('Default.menu'), menu('Default.menu'))" 18 | }, 19 | "LB": { 20 | "action": "button(Keys.BTN_TL)", 21 | "name": "Clutch" 22 | }, 23 | "LGRIP": { 24 | "action": "button(Keys.BTN_NORTH)", 25 | "name": "Gear Down" 26 | }, 27 | "LPAD": { 28 | "action": "button(Keys.BTN_EAST)", 29 | "name": "Handbreak" 30 | }, 31 | "RB": { 32 | "action": "button(Keys.BTN_TR)", 33 | "name": "Camera" 34 | }, 35 | "RGRIP": { 36 | "action": "button(Keys.BTN_GAMEPAD)", 37 | "name": "Gear Up" 38 | }, 39 | "RPAD": { 40 | "action": "button(Keys.BTN_THUMBR)", 41 | "name": "Look Back" 42 | }, 43 | "START": { 44 | "action": "button(Keys.BTN_START)", 45 | "name": "Pause" 46 | }, 47 | "X": { 48 | "action": "button(Keys.BTN_NORTH)", 49 | "name": "Gear Down" 50 | }, 51 | "Y": { 52 | "action": "button(Keys.BTN_WEST)", 53 | "name": "Recover Vehicle" 54 | } 55 | }, 56 | "cpad": {}, 57 | "gyro": { 58 | "action": "feedback(BOTH, gyroabs(None, Axes.ABS_X))", 59 | "name": "Steering" 60 | }, 61 | "is_template": false, 62 | "menus": {}, 63 | "pad_left": {}, 64 | "pad_right": {}, 65 | "stick": { 66 | "action": "dpad(hatup(Axes.ABS_HAT0Y), hatdown(Axes.ABS_HAT0Y), hatleft(Axes.ABS_HAT0X), hatright(Axes.ABS_HAT0X))", 67 | "name": "Menu Navigation" 68 | }, 69 | "trigger_left": { 70 | "action": "axis(Axes.ABS_Z)", 71 | "name": "Break/Reverse" 72 | }, 73 | "trigger_right": { 74 | "action": "axis(Axes.ABS_RZ)", 75 | "name": "Accelerate" 76 | }, 77 | "version": 1.4 78 | } -------------------------------------------------------------------------------- /profile_examples/Kodi.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "_": "", 3 | "buttons": { 4 | "A": { 5 | "action": "button(Keys.KEY_ENTER)" 6 | }, 7 | "B": { 8 | "action": "hold(mode(RT, button(Keys.KEY_B), None), mode(RT, name('Add bookmark', button(Keys.KEY_B)), button(Keys.KEY_BACKSPACE)))" 9 | }, 10 | "BACK": { 11 | "action": "button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_LEFTALT) and button(Keys.KEY_LEFT)" 12 | }, 13 | "C": { 14 | "action": "hold(menu('Default.menu'), mode(RT, shell('sc-controller'), menu('Default.menu')))" 15 | }, 16 | "LB": { 17 | "action": "mode(RT, button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_S), button(Keys.KEY_X))" 18 | }, 19 | "LGRIP": { 20 | "action": "mode(RT, button(Keys.KEY_LEFT), button(Keys.KEY_UP))" 21 | }, 22 | "LPAD": { 23 | "action": "button(Keys.KEY_M)" 24 | }, 25 | "RB": { 26 | "action": "button(Keys.KEY_BACKSLASH)" 27 | }, 28 | "RGRIP": { 29 | "action": "mode(RT, button(Keys.KEY_RIGHT), button(Keys.KEY_DOWN))" 30 | }, 31 | "RPAD": { 32 | "action": "button(Keys.BTN_LEFT)" 33 | }, 34 | "START": { 35 | "action": "button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_LEFTALT) and button(Keys.KEY_RIGHT)" 36 | }, 37 | "STICKPRESS": { 38 | "action": "keyboard()" 39 | }, 40 | "X": { 41 | "action": "button(Keys.KEY_SPACE)" 42 | }, 43 | "Y": { 44 | "action": "button(Keys.KEY_C)" 45 | } 46 | }, 47 | "cpad": {}, 48 | "gyro": {}, 49 | "is_template": false, 50 | "menus": {}, 51 | "pad_left": { 52 | "action": "circular(mouse(Rels.REL_WHEEL))" 53 | }, 54 | "pad_right": { 55 | "action": "feedback(RIGHT, 256, ball(mouse(None, 1.0)))" 56 | }, 57 | "stick": { 58 | "action": "dpad(button(Keys.KEY_UP), button(Keys.KEY_DOWN), button(Keys.KEY_LEFT), button(Keys.KEY_RIGHT))" 59 | }, 60 | "trigger_left": { 61 | "action": "trigger(50, 50, button(Keys.BTN_RIGHT))" 62 | }, 63 | "trigger_right": { 64 | "action": "trigger(50, 50, button(Keys.BTN_LEFT))" 65 | }, 66 | "version": 1.4 67 | } -------------------------------------------------------------------------------- /profile_examples/No Man Sky.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "_": "", 3 | "buttons": { 4 | "A": { 5 | "action": "button(Keys.KEY_SPACE)", 6 | "name": "Jump / Swim Up" 7 | }, 8 | "B": { 9 | "action": "button(Keys.KEY_X)", 10 | "name": "Weapon Mode" 11 | }, 12 | "BACK": { 13 | "action": "button(Keys.KEY_TAB)", 14 | "name": "[TAB] Inventory" 15 | }, 16 | "C": { 17 | "action": "menu('Default.menu')" 18 | }, 19 | "LB": { 20 | "action": "button(Keys.BTN_MIDDLE)", 21 | "name": "Grenade" 22 | }, 23 | "LGRIP": { 24 | "action": "button(Keys.KEY_LEFTSHIFT)", 25 | "name": "Sprint" 26 | }, 27 | "LPAD": { 28 | "action": "button(Keys.KEY_M)", 29 | "name": "Map" 30 | }, 31 | "RB": { 32 | "action": "button(Keys.KEY_Q)", 33 | "name": "Melle" 34 | }, 35 | "RGRIP": { 36 | "action": "button(Keys.KEY_Q)", 37 | "name": "Swim Down" 38 | }, 39 | "RPAD": { 40 | "action": "button(Keys.KEY_T)", 41 | "name": "Torch" 42 | }, 43 | "START": { 44 | "action": "button(Keys.KEY_ESC)", 45 | "name": "Menu" 46 | }, 47 | "STICKPRESS": { 48 | "action": "button(Keys.KEY_C)", 49 | "name": "Scan" 50 | }, 51 | "X": { 52 | "action": "button(Keys.KEY_E)", 53 | "name": "(E) Interact" 54 | }, 55 | "Y": { 56 | "action": "button(Keys.KEY_R)", 57 | "name": "Reload" 58 | } 59 | }, 60 | "cpad": {}, 61 | "gyro": {}, 62 | "is_template": false, 63 | "menus": {}, 64 | "pad_left": { 65 | "action": "dpad(button(Keys.KEY_UP), button(Keys.KEY_DOWN), button(Keys.KEY_LEFT), button(Keys.KEY_RIGHT))", 66 | "name": "Move Map" 67 | }, 68 | "pad_right": { 69 | "action": "feedback(RIGHT, 256, sens(0.5, 0.5, ball(mouse(None, 1.0))))", 70 | "name": "Mouse" 71 | }, 72 | "stick": { 73 | "action": "dpad(button(Keys.KEY_W), button(Keys.KEY_S), button(Keys.KEY_A), button(Keys.KEY_D))" 74 | }, 75 | "trigger_left": { 76 | "action": "trigger(50, 50, button(Keys.BTN_RIGHT))", 77 | "name": "(RClick) Zoom" 78 | }, 79 | "trigger_right": { 80 | "action": "trigger(50, 50, button(Keys.BTN_LEFT))", 81 | "name": "(LClick) Fire" 82 | }, 83 | "version": 1.4 84 | } -------------------------------------------------------------------------------- /profile_examples/Portal 2 coop.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "_": "", 3 | "buttons": { 4 | "A": { 5 | "action": "mode(RT, button(Keys.KEY_KPMINUS), button(Keys.KEY_ENTER))" 6 | }, 7 | "B": { 8 | "action": "button(Keys.KEY_E)" 9 | }, 10 | "BACK": { 11 | "action": "mode(RT, button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_LEFTSHIFT) and button(Keys.KEY_R), button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_LEFTALT) and button(Keys.KEY_LEFT))" 12 | }, 13 | "C": { 14 | "action": "hold(menu('Default.menu'), menu('Default.menu'))" 15 | }, 16 | "LB": { 17 | "action": "button(Keys.KEY_Q)" 18 | }, 19 | "LGRIP": { 20 | "action": "mode(RT, button(Keys.KEY_LEFT), button(Keys.KEY_LEFTCTRL))" 21 | }, 22 | "LPAD": { 23 | "action": "button(Keys.KEY_LEFTSHIFT) and button(Keys.KEY_TAB)" 24 | }, 25 | "RB": { 26 | "action": "button(Keys.KEY_F)" 27 | }, 28 | "RGRIP": { 29 | "action": "mode(RT, button(Keys.KEY_RIGHT), button(Keys.KEY_SPACE))" 30 | }, 31 | "START": { 32 | "action": "mode(RT, button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_R), button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_LEFTALT) and button(Keys.KEY_RIGHT))" 33 | }, 34 | "X": { 35 | "action": "button(Keys.KEY_TAB)" 36 | }, 37 | "Y": { 38 | "action": "button(Keys.KEY_ESC)" 39 | } 40 | }, 41 | "cpad": {}, 42 | "gyro": { 43 | "action": "mode(RPADTOUCH, mouse(ROLL), None)" 44 | }, 45 | "is_template": false, 46 | "menus": {}, 47 | "pad_left": { 48 | "action": "circular(mouse(Rels.REL_WHEEL))" 49 | }, 50 | "pad_right": { 51 | "action": "feedback(RIGHT, 256, ball(mouse(None, 1.0)))" 52 | }, 53 | "stick": { 54 | "action": "dpad(button(Keys.KEY_W), button(Keys.KEY_S), button(Keys.KEY_A), button(Keys.KEY_D))" 55 | }, 56 | "trigger_left": { 57 | "action": "trigger(50, 50, button(Keys.BTN_RIGHT))" 58 | }, 59 | "trigger_right": { 60 | "action": "trigger(50, 50, button(Keys.BTN_LEFT))" 61 | }, 62 | "version": 1.4 63 | } -------------------------------------------------------------------------------- /profile_examples/README.md: -------------------------------------------------------------------------------- 1 | Drag and drop link on main window to import profile. -------------------------------------------------------------------------------- /profile_examples/Undertale.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "_": "", 3 | "buttons": { 4 | "A": { 5 | "action": "button(Keys.KEY_ENTER)", 6 | "name": "Confirm" 7 | }, 8 | "B": { 9 | "action": "button(Keys.KEY_LEFTSHIFT)", 10 | "name": "Cancel" 11 | }, 12 | "BACK": { 13 | "action": "button(Keys.KEY_ESC)", 14 | "name": "Quit" 15 | }, 16 | "C": { 17 | "action": "menu('Default.menu')" 18 | }, 19 | "START": { 20 | "action": "button(Keys.KEY_F4)", 21 | "name": "Fullscreen" 22 | }, 23 | "Y": { 24 | "action": "button(Keys.KEY_LEFTCTRL)", 25 | "name": "In-Game Menu" 26 | } 27 | }, 28 | "cpad": {}, 29 | "gyro": {}, 30 | "is_template": false, 31 | "menus": {}, 32 | "pad_left": {}, 33 | "pad_right": { 34 | "action": "feedback(RIGHT, 256, ball(mouse(None, 1.0)))" 35 | }, 36 | "stick": { 37 | "action": "dpad(button(Keys.KEY_UP), button(Keys.KEY_DOWN), button(Keys.KEY_LEFT), button(Keys.KEY_RIGHT))", 38 | "name": "Movement" 39 | }, 40 | "trigger_left": {}, 41 | "trigger_right": {}, 42 | "version": 1.4 43 | } -------------------------------------------------------------------------------- /profile_examples/Vanguard Princess.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "_": "", 3 | "buttons": { 4 | "A": { 5 | "action": "button(Keys.BTN_GAMEPAD)", 6 | "name": "Weak Attack" 7 | }, 8 | "B": { 9 | "action": "button(Keys.BTN_EAST)", 10 | "name": "Normal Attack" 11 | }, 12 | "C": { 13 | "action": "hold(position(10, 10, menu('Default.menu')), position(20, 20, menu('VP')))" 14 | }, 15 | "LB": { 16 | "action": "button(Keys.BTN_TL)", 17 | "name": "Parry" 18 | }, 19 | "LGRIP": { 20 | "action": "button(Keys.BTN_GAMEPAD)" 21 | }, 22 | "LPAD": { 23 | "action": "button(Keys.BTN_THUMBL)" 24 | }, 25 | "RB": { 26 | "action": "button(Keys.BTN_TR)", 27 | "name": "2nd player" 28 | }, 29 | "RGRIP": { 30 | "action": "button(Keys.BTN_NORTH)" 31 | }, 32 | "RPAD": { 33 | "action": "button(Keys.BTN_THUMBR)" 34 | }, 35 | "START": { 36 | "action": "button(Keys.BTN_START)", 37 | "name": "Pause" 38 | }, 39 | "STICKPRESS": { 40 | "action": "button(Keys.BTN_THUMBL)" 41 | }, 42 | "X": { 43 | "action": "button(Keys.BTN_NORTH)", 44 | "name": "Strong Attack" 45 | }, 46 | "Y": { 47 | "action": "button(Keys.BTN_WEST)", 48 | "name": "Friend Assist" 49 | } 50 | }, 51 | "cpad": {}, 52 | "gyro": {}, 53 | "is_template": false, 54 | "menus": { 55 | "VP": [{ 56 | "action": "button(Keys.KEY_F4)", 57 | "id": "item1", 58 | "name": "Toggle Fullscreen" 59 | }, { 60 | "action": "button(Keys.BTN_START) and button(Keys.BTN_GAMEPAD) and button(Keys.BTN_EAST) and button(Keys.BTN_NORTH) and button(Keys.BTN_WEST)", 61 | "id": "item2", 62 | "name": "Exit Match" 63 | }, { 64 | "separator": true 65 | }, { 66 | "action": "button(Keys.KEY_LEFTALT) and button(Keys.KEY_F4)", 67 | "id": "item4", 68 | "name": "Exit Game" 69 | }] 70 | }, 71 | "pad_left": { 72 | "action": "dpad(axis(Axes.ABS_Y, 0, -32767), axis(Axes.ABS_Y, 0, 32767), axis(Axes.ABS_X, 0, -32767), axis(Axes.ABS_X, 0, 32767))", 73 | "name": "Move, Jump" 74 | }, 75 | "pad_right": {}, 76 | "stick": { 77 | "action": "XY(axis(Axes.ABS_X), raxis(Axes.ABS_Y))", 78 | "name": "Move, Jump" 79 | }, 80 | "trigger_left": {}, 81 | "trigger_right": {}, 82 | "version": 1.4 83 | } -------------------------------------------------------------------------------- /profile_examples/firefox.sccprofile: -------------------------------------------------------------------------------- 1 | { 2 | "_": "", 3 | "buttons": { 4 | "A": { 5 | "action": "button(Keys.KEY_ENTER)" 6 | }, 7 | "B": { 8 | "action": "button(Keys.KEY_BACKSPACE)" 9 | }, 10 | "BACK": { 11 | "action": "mode(RT, button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_LEFTSHIFT) and button(Keys.KEY_R), button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_LEFTALT) and button(Keys.KEY_LEFT))" 12 | }, 13 | "C": { 14 | "action": "hold(menu('Default.menu'), menu('Default.menu'))" 15 | }, 16 | "LB": { 17 | "action": "button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_LEFTSHIFT) and button(Keys.KEY_TAB)" 18 | }, 19 | "LGRIP": { 20 | "action": "doubleclick(button(Keys.KEY_END), mode(RT, button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_KPMINUS), button(Keys.KEY_PAGEDOWN)))" 21 | }, 22 | "RB": { 23 | "action": "button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_TAB)" 24 | }, 25 | "RGRIP": { 26 | "action": "doubleclick(button(Keys.KEY_HOME), mode(RT, button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_KPPLUS), button(Keys.KEY_PAGEUP)))" 27 | }, 28 | "RPAD": { 29 | "action": "button(Keys.BTN_LEFT)" 30 | }, 31 | "START": { 32 | "action": "mode(RT, button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_R), button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_LEFTALT) and button(Keys.KEY_RIGHT))" 33 | }, 34 | "STICKPRESS": { 35 | "action": "keyboard()" 36 | }, 37 | "X": { 38 | "action": "button(Keys.KEY_SPACE)" 39 | }, 40 | "Y": { 41 | "action": "button(Keys.KEY_LEFTCTRL) and button(Keys.KEY_W)" 42 | } 43 | }, 44 | "cpad": {}, 45 | "gyro": { 46 | "action": "mode(RPADTOUCH, mouse(ROLL), None)" 47 | }, 48 | "is_template": false, 49 | "menus": {}, 50 | "pad_left": { 51 | "action": "circular(mouse(Rels.REL_WHEEL))" 52 | }, 53 | "pad_right": { 54 | "action": "feedback(RIGHT, 256, ball(mouse(None, 1.0)))" 55 | }, 56 | "stick": { 57 | "action": "dpad(button(Keys.KEY_UP), button(Keys.KEY_DOWN), button(Keys.KEY_LEFT), button(Keys.KEY_RIGHT))" 58 | }, 59 | "trigger_left": { 60 | "action": "trigger(50, 50, button(Keys.BTN_RIGHT))" 61 | }, 62 | "trigger_right": { 63 | "action": "trigger(50, 50, button(Keys.BTN_LEFT))" 64 | }, 65 | "version": 1.4 66 | } -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | C_MODULES=(uinput hiddrv sc_by_bt remotepad cemuhook) 3 | C_VERSION_uinput=9 4 | C_VERSION_hiddrv=5 5 | C_VERSION_sc_by_bt=3 6 | C_VERSION_remotepad=1 7 | C_VERSION_cemuhook=1 8 | 9 | function rebuild_c_modules() { 10 | echo "lib$1.so is outdated or missing, building one" 11 | echo "Please wait, this should be done only once." 12 | echo "" 13 | 14 | # Next line generates string like 'lib.linux-x86_64-2.7', directory where libuinput.so was just generated 15 | LIB=$( python2 -c 'import platform ; print "lib.linux-%s-%s.%s" % ((platform.machine(),) + platform.python_version_tuple()[0:2])' ) 16 | 17 | for cmod in ${C_MODULES[@]}; do 18 | if [ -e build/$LIB/lib${cmod}.so ] ; then 19 | rm build/$LIB/lib${cmod}.so || exit 1 20 | fi 21 | done 22 | 23 | python2 setup.py build || exit 1 24 | echo "" 25 | 26 | for cmod in ${C_MODULES[@]}; do 27 | if [ ! -e lib${cmod}.so ] ; then 28 | ln -s build/$LIB/lib${cmod}.so ./lib${cmod}.so || exit 1 29 | echo Symlinked ./lib${cmod}.so '->' build/$LIB/lib${cmod}.so 30 | fi 31 | done 32 | echo "" 33 | } 34 | 35 | 36 | # Ensure correct cwd 37 | cd "$(dirname "$0")" 38 | 39 | # Check if c modules are compiled and actual 40 | for cmod in ${C_MODULES[@]}; do 41 | eval expected_version=\$C_VERSION_${cmod} 42 | reported_version=$(PYTHONPATH="." python2 -c 'import os, ctypes; lib=ctypes.CDLL("./'lib${cmod}'.so"); print lib.'${cmod}'_module_version()') 43 | if [ x"$reported_version" != x"$expected_version" ] ; then 44 | rebuild_c_modules ${cmod} 45 | fi 46 | done 47 | 48 | # Set PATH 49 | SCRIPTS="$(pwd)/scripts" 50 | export PATH="$SCRIPTS":"$PATH" 51 | export PYTHONPATH=".":"$PYTHONPATH" 52 | export SCC_SHARED="$(pwd)" 53 | 54 | # Execute 55 | python2 'scripts/sc-controller' $@ 56 | -------------------------------------------------------------------------------- /scc-mime-types.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Steam Controller Database Profile 5 | 6 | 7 | 8 | 9 | 10 | SC Controller Profile 11 | 12 | 13 | 14 | 15 | 16 | SC Controller Profile Package 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /scc/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller 4 | Copyright (C) 2018 Kozec 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License version 2 as published by 8 | the Free Software Foundation 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | """ 19 | 20 | pass 21 | -------------------------------------------------------------------------------- /scc/aliases.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Aliases 4 | 5 | This module generates Keys.BTN_x and Axes.AXIS_x aliases when imported 6 | """ 7 | 8 | from scc.uinput import Axes, Keys 9 | 10 | ALL_BUTTONS = ( Keys.BTN_START, Keys.BTN_MODE, Keys.BTN_SELECT, Keys.BTN_A, 11 | Keys.BTN_B, Keys.BTN_X, Keys.BTN_Y, Keys.BTN_TL, Keys.BTN_TR, 12 | Keys.BTN_THUMBL, Keys.BTN_THUMBR, Keys.BTN_WHEEL, Keys.BTN_GEAR_DOWN, 13 | Keys.BTN_GEAR_UP, Keys.KEY_OK, Keys.KEY_SELECT, Keys.KEY_GOTO, 14 | Keys.KEY_CLEAR, Keys.KEY_OPTION, Keys.KEY_INFO, Keys.KEY_TIME, 15 | Keys.KEY_VENDOR, Keys.KEY_ARCHIVE, Keys.KEY_PROGRAM, Keys.KEY_CHANNEL, 16 | Keys.KEY_FAVORITES, Keys.KEY_EPG ) 17 | 18 | ALL_AXES = ( Axes.ABS_X, Axes.ABS_Y, Axes.ABS_RX, Axes.ABS_RY, Axes.ABS_Z, 19 | Axes.ABS_RZ, Axes.ABS_HAT0X, Axes.ABS_HAT0Y, Axes.ABS_HAT1X, Axes.ABS_HAT1Y, 20 | Axes.ABS_HAT2X, Axes.ABS_HAT2Y, Axes.ABS_HAT3X, Axes.ABS_HAT3Y, 21 | Axes.ABS_PRESSURE, Axes.ABS_DISTANCE, Axes.ABS_TILT_X, Axes.ABS_TILT_Y, 22 | Axes.ABS_TOOL_WIDTH, Axes.ABS_VOLUME, Axes.ABS_MISC ) 23 | 24 | for i in xrange(0, len(ALL_BUTTONS)): 25 | setattr(Keys, "BTN%i" % (i,), ALL_BUTTONS[i]) 26 | 27 | for i in xrange(0, len(ALL_AXES)): 28 | setattr(Axes, "ABS%i" % (i,), ALL_AXES[i]) 29 | -------------------------------------------------------------------------------- /scc/c_branch.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Glue between code from future and current stuff in python 3 | */ 4 | #pragma once 5 | #include "Python.h" 6 | #include 7 | #include 8 | 9 | #define LERROR(fmt, ...) do { fprintf(stderr, "E " LOG_TAG " " fmt, ##__VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } while(0) 10 | #define WARN(fmt, ...) do { fprintf(stderr, "W " LOG_TAG " " fmt, ##__VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } while(0) 11 | #define DEBUG(fmt, ...) do { fprintf(stdout, "D " LOG_TAG " " fmt, ##__VA_ARGS__); fprintf(stdout, "\n"); fflush(stdout); } while(0) 12 | #define LOG(fmt, ...) do { fprintf(stdout, "L " LOG_TAG " " fmt, ##__VA_ARGS__); fprintf(stdout, "\n"); fflush(stdout); } while(0) 13 | 14 | typedef uint64_t monotime_t; 15 | 16 | /** Returns current value of CLOCK_MONOTONIC converted to number of milliseconds */ 17 | inline static uint64_t mono_time_ms() { 18 | static struct timespec t; 19 | clock_gettime(CLOCK_MONOTONIC, &t); 20 | return t.tv_sec * 1000 + (t.tv_nsec / 10e5); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /scc/cemuhook_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Daemon - CemuHookUDP motion provider 4 | 5 | Accepts all connections from clients and sends data captured 6 | by 'cemuhook' actions to them. 7 | """ 8 | from __future__ import unicode_literals 9 | from scc.tools import find_library 10 | from scc.lib.enum import IntEnum 11 | from ctypes import c_uint32, c_int, c_bool, c_char_p, c_size_t, c_float 12 | from ctypes import create_string_buffer 13 | import logging, os, socket 14 | log = logging.getLogger("CemuHook") 15 | 16 | BUFFER_SIZE = 1024 17 | PORT = 26760 18 | 19 | 20 | class MessageType(IntEnum): 21 | DSUC_VERSIONREQ = 0x100000 22 | DSUS_VERSIONRSP = 0x100000 23 | DSUC_LISTPORTS = 0x100001 24 | DSUS_PORTINFO = 0x100001 25 | DSUC_PADDATAREQ = 0x100002 26 | DSUS_PADDATARSP = 0x100002 27 | 28 | 29 | class CemuhookServer: 30 | C_DATA_T = c_float * 6 31 | 32 | def __init__(self, daemon): 33 | self._lib = find_library('libcemuhook') 34 | self._lib.cemuhook_data_recieved.argtypes = [ c_int, c_int, c_char_p, c_size_t ] 35 | self._lib.cemuhook_data_recieved.restype = None 36 | self._lib.cemuhook_feed.argtypes = [ c_int, c_int, CemuhookServer.C_DATA_T ] 37 | self._lib.cemuhook_feed.restype = None 38 | self._lib.cemuhook_socket_enable.argtypes = [] 39 | self._lib.cemuhook_socket_enable.restype = c_bool 40 | 41 | if not self._lib.cemuhook_socket_enable(): 42 | raise OSError("cemuhook_socket_enable failed") 43 | 44 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 45 | self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 46 | 47 | poller = daemon.get_poller() 48 | daemon.poller.register(self.socket.fileno(), poller.POLLIN, self.on_data_recieved) 49 | 50 | server_port = os.getenv('SCC_SERVER_PORT') or PORT; 51 | self.socket.bind(('127.0.0.1', server_port)) 52 | log.info("Created CemuHookUDP Motion Provider") 53 | 54 | 55 | def on_data_recieved(self, fd, event_type): 56 | if fd != self.socket.fileno(): return 57 | message, (ip, port) = self.socket.recvfrom(BUFFER_SIZE) 58 | buffer = create_string_buffer(BUFFER_SIZE) 59 | self._lib.cemuhook_data_recieved(fd, port, message, len(message), buffer) 60 | 61 | 62 | def feed(self, data): 63 | c_data = CemuhookServer.C_DATA_T() 64 | c_data[3:6] = data[0:3] 65 | self._lib.cemuhook_feed(self.socket.fileno(), 0, c_data) 66 | 67 | 68 | -------------------------------------------------------------------------------- /scc/custom.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Custom module loader 4 | 5 | Loads ~/.config/scc/custom.py, if present. This allows injecting custom action 6 | classes by user and breaking everything in very creative ways. 7 | 8 | load_custom_module function needs to be called by daemon and GUI, so it exists 9 | in separate module. 10 | """ 11 | 12 | from scc.paths import get_config_path 13 | import os 14 | 15 | def load_custom_module(log, who_calls="daemon"): 16 | """ 17 | Loads and imports ~/.config/scc/custom.py, if it is present and displays 18 | big, fat warning in such case. 19 | 20 | Returns True if file exists. 21 | """ 22 | 23 | filename = os.path.join(get_config_path(), "custom.py") 24 | if os.path.exists(filename): 25 | log.warning("=" * 60) 26 | log.warning("Loading %s" % (filename, )) 27 | log.warning("If you don't know what this means or you haven't " 28 | "created it, stop daemon right now and remove this file.") 29 | log.warning("") 30 | log.warning("Also try removing it if %s crashes " 31 | "shortly after this message." % (who_calls,)) 32 | 33 | import imp 34 | imp.load_source("custom", filename) 35 | log.warning("=" * 60) 36 | return True 37 | return False 38 | -------------------------------------------------------------------------------- /scc/drivers/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller goes through all modules in scc.drivers package and calls 4 | init(daemon) methods from every module that defines it. 5 | 6 | Drivers then can use daemon.add_mainloop() method to add code that should 7 | be run in every mainloop iteration and daemon.add_controller() to add and 8 | daemon.remove_controller() to remove Controller instances. 9 | 10 | Additionaly, start(daemon) method is called from each module that defines it 11 | just before daemon startup is complete. 12 | 13 | Assigning Mapper to Controller is handled by daemon. 14 | """ 15 | 16 | MOD_INIT_ORDER = ( 17 | # Modules mentioned here are initialized before everything else, in this exact order. 18 | "scc.drivers.usb", 19 | "scc.drivers.evdevdrv", 20 | "scc.drivers.hiddrv" 21 | ) 22 | -------------------------------------------------------------------------------- /scc/drivers/fake.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC Controller - Fake controller driver 4 | 5 | This driver does nothing by default, unless SCC_FAKES environment variable is 6 | set. If it is, creates as many fake controller devices as integer stored in 7 | SCC_FAKES says. 8 | 9 | Created controllers are completely useless. For debuging purposes only. 10 | """ 11 | 12 | from scc.controller import Controller 13 | import os, logging 14 | 15 | ENV_VAR = "SCC_FAKES" 16 | 17 | if ENV_VAR in os.environ: 18 | log = logging.getLogger("FakeDrv") 19 | 20 | 21 | def init(daemon, config): 22 | return True 23 | 24 | 25 | def start(daemon): 26 | num = int(os.environ[ENV_VAR]) 27 | log.debug("Creating %s fake controllers", num) 28 | for x in xrange(0, num): 29 | daemon.add_controller(FakeController(x)) 30 | 31 | 32 | class FakeController(Controller): 33 | def __init__(self, number): 34 | Controller.__init__(self) 35 | self._number = number 36 | self._id = "fake%s" % (self._number,) 37 | 38 | 39 | def get_type(self): 40 | return "fake" 41 | 42 | 43 | def set_led_level(self, level): 44 | log.debug("FakeController %s led level set to %s", self.get_id(), level) 45 | 46 | 47 | def __repr__(self): 48 | return "" % (self.get_id(),) 49 | -------------------------------------------------------------------------------- /scc/drivers/remotepad.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SC Controller - remotepad driver 3 | * 4 | * This is implementation or protocol used by Retroarch's Remote RetroPad core. 5 | * 6 | * Based on https://github.com/libretro/RetroArch/blob/master/cores/libretro-net-retropad. 7 | */ 8 | #include "scc_future.h" 9 | 10 | /** MAX_DESC_LEN has to fit "" */ 11 | #define MAX_DESC_LEN 32 12 | #define MAX_ID_LEN 24 13 | 14 | struct remote_joypad_message { 15 | int port; 16 | int device; 17 | int index; 18 | int id; 19 | uint16_t state; 20 | }; 21 | 22 | typedef struct RemotePad { 23 | Mapper* mapper; 24 | ControllerInput input; 25 | } RemotePad; 26 | 27 | 28 | void remotepad_input(RemotePad* pad, struct remote_joypad_message* msg); 29 | 30 | ////// Following are declarations from libretro ////// 31 | 32 | // Buttons for the RetroPad (JOYPAD). 33 | // The placement of these is equivalent to placements on the Super Nintendo controller. 34 | // L2/R2/L3/R3 buttons correspond to the PS1 DualShock. 35 | #define RETRO_DEVICE_ID_JOYPAD_B 0 36 | #define RETRO_DEVICE_ID_JOYPAD_Y 1 37 | #define RETRO_DEVICE_ID_JOYPAD_SELECT 2 38 | #define RETRO_DEVICE_ID_JOYPAD_START 3 39 | #define RETRO_DEVICE_ID_JOYPAD_UP 4 40 | #define RETRO_DEVICE_ID_JOYPAD_DOWN 5 41 | #define RETRO_DEVICE_ID_JOYPAD_LEFT 6 42 | #define RETRO_DEVICE_ID_JOYPAD_RIGHT 7 43 | #define RETRO_DEVICE_ID_JOYPAD_A 8 44 | #define RETRO_DEVICE_ID_JOYPAD_X 9 45 | #define RETRO_DEVICE_ID_JOYPAD_L 10 46 | #define RETRO_DEVICE_ID_JOYPAD_R 11 47 | #define RETRO_DEVICE_ID_JOYPAD_L2 12 48 | #define RETRO_DEVICE_ID_JOYPAD_R2 13 49 | #define RETRO_DEVICE_ID_JOYPAD_L3 14 50 | #define RETRO_DEVICE_ID_JOYPAD_R3 15 51 | 52 | #define RETRO_DEVICE_JOYPAD 1 53 | #define RETRO_DEVICE_ANALOG 5 54 | #define RETRO_DEVICE_INDEX_ANALOG_LEFT 0 55 | #define RETRO_DEVICE_INDEX_ANALOG_RIGHT 1 56 | #define RETRO_DEVICE_ID_ANALOG_X 0 57 | #define RETRO_DEVICE_ID_ANALOG_Y 1 58 | -------------------------------------------------------------------------------- /scc/foreign/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | pass -------------------------------------------------------------------------------- /scc/foreign/vdffz.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | Imports VDFFZ profile and converts it to Profile object. 4 | VDFFZ is just VDF encapsulated in json, so this just gets one value and calls 5 | VDFProfile to decode rest. 6 | """ 7 | from vdf import VDFProfile 8 | from scc.lib.vdf import parse_vdf 9 | 10 | import json, logging 11 | log = logging.getLogger("import.vdffz") 12 | 13 | class VDFFZProfile(VDFProfile): 14 | def load(self, filename): 15 | try: 16 | data = json.loads(open(filename, "r").read()) 17 | except Exception, e: 18 | raise ValueError("Failed to parse JSON") 19 | if 'ConfigData' not in data: 20 | raise ValueError("ConfigData missing in JSON") 21 | self.load_data(parse_vdf(data['ConfigData'].encode('utf-8'))) 22 | -------------------------------------------------------------------------------- /scc/gui/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | from scc.constants import SCButtons 3 | 4 | BUTTON_ORDER = ( 5 | SCButtons.A, SCButtons.B, SCButtons.X, SCButtons.Y, 6 | SCButtons.BACK, SCButtons.C, SCButtons.START, 7 | SCButtons.LB, SCButtons.RB, SCButtons.LT, SCButtons.RT, 8 | SCButtons.STICKPRESS, SCButtons.RPAD, SCButtons.LPAD, 9 | SCButtons.RGRIP, SCButtons.LGRIP 10 | ) 11 | -------------------------------------------------------------------------------- /scc/gui/aboutdialog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - About dialog 4 | """ 5 | from __future__ import unicode_literals 6 | from scc.tools import _ 7 | 8 | from gi.repository import Gtk 9 | from scc.gui.editor import Editor 10 | import os, sys 11 | 12 | class AboutDialog(Editor): 13 | """ Standard looking about dialog """ 14 | GLADE = "about.glade" 15 | 16 | def __init__(self, app): 17 | self.app = app 18 | self.setup_widgets() 19 | 20 | 21 | def setup_widgets(self): 22 | Editor.setup_widgets(self) 23 | 24 | # Get app version 25 | app_ver = "(unknown version)" 26 | try: 27 | import pkg_resources, scc 28 | if scc.__file__.startswith(pkg_resources.require("sccontroller")[0].location): 29 | app_ver = "v" + pkg_resources.require("sccontroller")[0].version 30 | except: 31 | # pkg_resources is not available or __version__ file missing 32 | # There is no reason to crash on this. 33 | pass 34 | # Display version in UI 35 | self.builder.get_object("lblVersion").set_label(app_ver) 36 | 37 | 38 | def show(self, modal_for): 39 | if modal_for: 40 | self.window.set_transient_for(modal_for) 41 | self.window.set_modal(True) 42 | self.window.show() 43 | 44 | 45 | def on_dialog_response(self, *a): 46 | self.close() 47 | -------------------------------------------------------------------------------- /scc/gui/ae/axis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Action Editor - Axis Component 4 | 5 | Handles specific XYActions 6 | """ 7 | from __future__ import unicode_literals 8 | from scc.tools import _ 9 | 10 | from gi.repository import Gtk, Gdk, GLib 11 | from scc.actions import ButtonAction, MultiAction, NoAction 12 | from scc.actions import Action, AxisAction, MouseAction 13 | from scc.gui.ae import AEComponent, describe_action 14 | from scc.gui.area_to_action import action_to_area 15 | from scc.gui.simple_chooser import SimpleChooser 16 | from scc.gui.chooser import Chooser 17 | 18 | import os, logging 19 | log = logging.getLogger("AE.Axis") 20 | 21 | __all__ = [ 'AxisComponent' ] 22 | 23 | 24 | class AxisComponent(AEComponent, Chooser): 25 | GLADE = "ae/axis.glade" 26 | NAME = "axis" 27 | IMAGES = { "axis" : "axistrigger.svg" } 28 | CTXS = 0 29 | 30 | def __init__(self, app, editor): 31 | AEComponent.__init__(self, app, editor) 32 | Chooser.__init__(self, app) 33 | self.full = None 34 | 35 | 36 | def load(self): 37 | if not self.loaded: 38 | AEComponent.load(self) 39 | self.setup_image(grid_columns=2) 40 | 41 | 42 | def area_action_selected(self, area, action): 43 | if area: 44 | self.set_active_area(area) 45 | if self.full: 46 | action = MultiAction(ButtonAction(None, self.full), action) 47 | self.editor.set_action(action) 48 | 49 | 50 | def set_action(self, mode, action): 51 | if self.handles(mode, action): 52 | if isinstance(action, MultiAction) and len(action.actions) == 2: 53 | # axis + button on fully pressed trigger 54 | self.full = action.actions[0].button2 55 | self.builder.get_object("lblFullPressed").set_label(describe_action(Action.AC_BUTTON, ButtonAction, self.full)) 56 | action = action.actions[1] 57 | area = action_to_area(action) 58 | if area is not None: 59 | self.set_active_area(area) 60 | return 61 | self.set_active_area(None) 62 | 63 | 64 | def get_button_title(self): 65 | return _("Trigger or Axis") 66 | 67 | 68 | def handles(self, mode, action): 69 | if isinstance(action, MultiAction) and len(action.actions) == 2: 70 | # Handles combination of axis + button on fully pressed trigger 71 | if not isinstance(action.actions[0], ButtonAction): 72 | return False 73 | action = action.actions[1] 74 | return isinstance(action, (AxisAction, MouseAction)) 75 | -------------------------------------------------------------------------------- /scc/gui/ae/custom.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Action Editor - Custom action 4 | 5 | Custom Action page in Action Editor window 6 | """ 7 | from __future__ import unicode_literals 8 | from scc.tools import _ 9 | 10 | from gi.repository import Gtk, Gdk, GLib 11 | from scc.gui.parser import GuiActionParser, InvalidAction 12 | from scc.gui.ae import AEComponent 13 | from scc.actions import Action 14 | 15 | import os, logging 16 | log = logging.getLogger("AE.Custom") 17 | 18 | __all__ = [ 'CustomActionComponent' ] 19 | 20 | class CustomActionComponent(AEComponent): 21 | GLADE = "ae/custom.glade" 22 | NAME = "custom" 23 | PRIORITY = -1 24 | CTXS = Action.AC_ALL 25 | 26 | def __init__(self, app, editor): 27 | AEComponent.__init__(self, app, editor) 28 | self.parser = GuiActionParser() 29 | 30 | 31 | def handles(self, mode, action): 32 | # Custom Action Editor handles all actions 33 | return isinstance(action, Action) 34 | 35 | 36 | def get_button_title(self): 37 | return _("Custom Action") 38 | 39 | 40 | def load(self): 41 | if self.loaded : return 42 | AEComponent.load(self) 43 | try: 44 | txCustomAction = self.builder.get_object("txCustomAction") 45 | txCustomAction.set_monospace(True) 46 | except: pass 47 | 48 | 49 | def set_action(self, mode, action): 50 | action = self.editor.generate_modifiers(action, from_custom=True) 51 | tbCustomAction = self.builder.get_object("tbCustomAction") 52 | tbCustomAction.set_text(action.to_string(True)) 53 | 54 | 55 | def on_tbCustomAction_changed(self, tbCustomAction, *a): 56 | """ 57 | Converts text from Custom Action text area into action instance and 58 | sends that instance back to editor. 59 | """ 60 | txCustomAction = self.builder.get_object("txCustomAction") 61 | txt = tbCustomAction.get_text(tbCustomAction.get_start_iter(), tbCustomAction.get_end_iter(), True) 62 | if len(txt.strip(" \t\r\n")) > 0: 63 | action = self.parser.restart(txt).parse() 64 | self.editor.set_action(action, from_custom=True) 65 | 66 | 67 | def shown(self): 68 | self.editor.set_modifiers_enabled(False) 69 | 70 | 71 | def hidden(self): 72 | self.editor.set_modifiers_enabled(True) 73 | -------------------------------------------------------------------------------- /scc/gui/ae/menu_only.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Action Editor - Menu Only Component 4 | 5 | Displays page that can edito only MenuAction 6 | """ 7 | from __future__ import unicode_literals 8 | from scc.tools import _ 9 | 10 | from scc.special_actions import MenuAction, PositionModifier 11 | from scc.actions import Action 12 | from scc.gui.userdata_manager import UserDataManager 13 | from scc.gui.ae.menu_action import MenuActionCofC 14 | from scc.gui.ae import AEComponent 15 | 16 | import os, logging 17 | log = logging.getLogger("AE.SA") 18 | 19 | __all__ = [ 'MenuOnlyComponent' ] 20 | 21 | 22 | class MenuOnlyComponent(AEComponent, MenuActionCofC): 23 | GLADE = "ae/menu_only.glade" 24 | NAME = "menu_only" 25 | CTXS = Action.AC_MENU 26 | PRIORITY = 0 27 | 28 | def __init__(self, app, editor): 29 | AEComponent.__init__(self, app, editor) 30 | MenuActionCofC.__init__(self) 31 | self._userdata_load_started = False 32 | self._recursing = False 33 | 34 | 35 | def shown(self): 36 | if not self._userdata_load_started: 37 | self._userdata_load_started = True 38 | self.load_menu_list() 39 | 40 | 41 | def set_action(self, mode, action): 42 | if isinstance(action, PositionModifier): 43 | action = action.action 44 | if isinstance(action, MenuAction): 45 | self._current_menu = action.menu_id 46 | 47 | 48 | def get_button_title(self): 49 | return _("Menu") 50 | 51 | 52 | def handles(self, mode, action): 53 | """ Not visible by default """ 54 | return False 55 | -------------------------------------------------------------------------------- /scc/gui/ae/osk_action.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Action Editor - On Screen Keyboard Action Component 4 | 5 | Assigns actions from scc.osd.osk_actions 6 | """ 7 | from __future__ import unicode_literals 8 | from scc.tools import _ 9 | 10 | from gi.repository import Gtk, Gdk, GLib 11 | from scc.actions import Action, NoAction, ButtonAction 12 | from scc.constants import LEFT, RIGHT 13 | from scc.uinput import Keys 14 | from scc.gui.ae import AEComponent 15 | from scc.gui.parser import GuiActionParser 16 | from scc.osd.osk_actions import OSKAction, CloseOSKAction, OSKCursorAction 17 | from scc.osd.osk_actions import MoveOSKAction, OSKPressAction 18 | 19 | import os, logging 20 | log = logging.getLogger("AE.SA") 21 | 22 | __all__ = [ 'OSKActionComponent' ] 23 | 24 | 25 | class OSKActionComponent(AEComponent): 26 | GLADE = "ae/osk_action.glade" 27 | NAME = "osk_action" 28 | CTXS = Action.AC_OSK 29 | PRIORITY = 2 30 | 31 | def __init__(self, app, editor): 32 | AEComponent.__init__(self, app, editor) 33 | self._recursing = False 34 | 35 | 36 | def set_action(self, mode, action): 37 | cb = self.builder.get_object("cbActionType") 38 | if isinstance(action, CloseOSKAction): 39 | self.set_cb(cb, "OSK.close()") 40 | elif isinstance(action, OSKCursorAction) and action.side == LEFT: 41 | self.set_cb(cb, "OSK.cursor(LEFT)") 42 | elif isinstance(action, OSKCursorAction): # and action.side == RIGHT: 43 | self.set_cb(cb, "OSK.cursor(RIGHT)") 44 | elif isinstance(action, OSKPressAction) and action.side == LEFT: 45 | self.set_cb(cb, "OSK.press(LEFT)") 46 | elif isinstance(action, OSKPressAction): # and action.side == RIGHT: 47 | self.set_cb(cb, "OSK.press(RIGHT)") 48 | elif isinstance(action, MoveOSKAction): 49 | self.set_cb(cb, "OSK.move()") 50 | if isinstance(action, ButtonAction): 51 | if action.button == Keys.BTN_LEFT: 52 | self.set_cb(cb, "button(Keys.BTN_LEFT)") 53 | elif action.button == Keys.BTN_RIGHT: 54 | self.set_cb(cb, "button(Keys.BTN_RIGHT)") 55 | else: 56 | self.set_cb(cb, "None") 57 | 58 | 59 | def get_button_title(self): 60 | return _("On-Screen Keyboard") 61 | 62 | 63 | def handles(self, mode, action): 64 | if isinstance(action, ButtonAction): 65 | return action.button in ( Keys.BTN_LEFT, Keys.BTN_RIGHT ) 66 | return isinstance(action, (NoAction, OSKAction, OSKCursorAction)) 67 | 68 | 69 | def on_cbActionType_changed(self, *a): 70 | cbActionType = self.builder.get_object("cbActionType") 71 | key = cbActionType.get_model().get_value(cbActionType.get_active_iter(), 0) 72 | self.editor.set_action(GuiActionParser().restart(key).parse()) 73 | -------------------------------------------------------------------------------- /scc/gui/ae/osk_buttons.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Action Editor - OSK Button Component 4 | 5 | Binds controller buttons on on on on on... screen keyboard. 6 | Retuses ButtonsComponent, but hides image, so user can't select mouse or gamepad 7 | button. 8 | """ 9 | from __future__ import unicode_literals 10 | from scc.tools import _ 11 | 12 | from gi.repository import Gtk, Gdk, GLib 13 | from scc.actions import Action, ButtonAction, MultiAction, NoAction 14 | from scc.gui.area_to_action import action_to_area 15 | from scc.gui.ae.buttons import ButtonsComponent 16 | from scc.gui.key_grabber import KeyGrabber 17 | from scc.gui.parser import InvalidAction 18 | from scc.gui.chooser import Chooser 19 | from scc.gui.ae import AEComponent 20 | 21 | import os, logging 22 | log = logging.getLogger("AE.Buttons") 23 | 24 | __all__ = [ 'OSKButtonsComponent' ] 25 | 26 | 27 | class OSKButtonsComponent(ButtonsComponent): 28 | CTXS = Action.AC_OSK 29 | PRIORITY = 1 30 | IMAGES = { } 31 | 32 | 33 | def get_button_title(self): 34 | return _("Key") 35 | 36 | 37 | def load(self): 38 | if not self.loaded: 39 | AEComponent.load(self) 40 | self.builder.get_object("lblClickAnyButton").set_visible(False) 41 | -------------------------------------------------------------------------------- /scc/gui/ae/per_axis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Action Editor - Per-Axis Component 4 | 5 | Handles all XYActions 6 | """ 7 | from __future__ import unicode_literals 8 | from scc.tools import _ 9 | 10 | from gi.repository import Gtk, Gdk, GLib 11 | from scc.actions import Action, NoAction, XYAction 12 | from scc.gui.ae import AEComponent, describe_action 13 | from scc.gui.area_to_action import action_to_area 14 | from scc.gui.simple_chooser import SimpleChooser 15 | from scc.gui.parser import GuiActionParser 16 | 17 | import os, logging 18 | log = logging.getLogger("AE.PerAxis") 19 | 20 | __all__ = [ 'PerAxisComponent' ] 21 | 22 | 23 | class PerAxisComponent(AEComponent): 24 | GLADE = "ae/per_axis.glade" 25 | NAME = "per_axis" 26 | CTXS = Action.AC_STICK | Action.AC_PAD 27 | PRIORITY = 1 28 | 29 | def __init__(self, app, editor): 30 | AEComponent.__init__(self, app, editor) 31 | self.x = self.y = NoAction() 32 | 33 | 34 | def set_action(self, mode, action): 35 | if isinstance(action, XYAction): 36 | self.x = action.x 37 | self.y = action.y 38 | self.update() 39 | 40 | 41 | def get_button_title(self): 42 | return _("Per Axis") 43 | 44 | 45 | def handles(self, mode, action): 46 | return isinstance(action, XYAction) 47 | 48 | 49 | def update(self): 50 | self.builder.get_object("lblAxisX").set_label(describe_action(Action.AC_STICK, None, self.x)) 51 | self.builder.get_object("lblAxisY").set_label(describe_action(Action.AC_STICK, None, self.y)) 52 | 53 | 54 | def send(self): 55 | self.editor.set_action(XYAction(self.x, self.y)) 56 | 57 | 58 | def on_btAxisX_clicked(self, *a): 59 | """ 'Select X Axis Action' handler """ 60 | def cb(action): 61 | self.x = action 62 | self.update() 63 | self.send() 64 | self.grab_action(self.x, cb) 65 | 66 | 67 | def on_btAxisY_clicked(self, *a): 68 | """ 'Select Y Axis Action' handler """ 69 | def cb(action): 70 | self.y = action 71 | self.update() 72 | self.send() 73 | self.grab_action(self.y, cb) 74 | 75 | 76 | def grab_action(self, action, cb): 77 | b = SimpleChooser(self.app, "axis", cb) 78 | b.set_title(_("Select Axis")) 79 | area = action_to_area(action) 80 | b.display_action(Action.AC_STICK, area) 81 | b.show(self.editor.window) 82 | -------------------------------------------------------------------------------- /scc/gui/ae/recent_list.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Action Editor - Recent List Component 4 | 5 | Displays page that can edit settings for RecentListMenuGenerator 6 | """ 7 | from __future__ import unicode_literals 8 | from scc.tools import _ 9 | 10 | from scc.gui.ae import AEComponent 11 | 12 | import os, logging 13 | log = logging.getLogger("AE.SA") 14 | 15 | __all__ = [ 'RecentListGenComponent' ] 16 | 17 | 18 | class RecentListGenComponent(AEComponent): 19 | GLADE = "ae/recent_list.glade" 20 | NAME = "recent_list" 21 | CTXS = 0 22 | PRIORITY = 0 23 | 24 | def __init__(self, app, editor): 25 | AEComponent.__init__(self, app, editor) 26 | 27 | 28 | def set_action(self, mode, action): 29 | pass 30 | 31 | 32 | def get_button_title(self): 33 | return _("Recent List") 34 | 35 | 36 | def handles(self, mode, action): 37 | """ Not visible by default """ 38 | return False 39 | 40 | 41 | def set_row_count(self, count): 42 | self.builder.get_object("sclNumOfProfiles").set_value(count) 43 | 44 | 45 | def get_row_count(self): 46 | return int(self.builder.get_object("sclNumOfProfiles").get_value()) 47 | -------------------------------------------------------------------------------- /scc/gui/ae/tilt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Action Editor - Tilt 4 | 5 | Setups DPAD emulation or menu display 6 | """ 7 | from __future__ import unicode_literals 8 | from scc.tools import _ 9 | 10 | from gi.repository import Gtk, Gdk, GLib 11 | from scc.actions import Action, NoAction, TiltAction, ButtonAction 12 | from scc.special_actions import MenuAction 13 | from scc.modifiers import NameModifier 14 | from scc.uinput import Keys 15 | from scc.gui.ae import AEComponent, describe_action 16 | from scc.gui.ae.menu_action import MenuActionCofC 17 | from scc.gui.binding_editor import BindingEditor 18 | from scc.gui.action_editor import ActionEditor 19 | 20 | 21 | import os, logging 22 | log = logging.getLogger("AE.Tilt") 23 | 24 | __all__ = [ 'TiltComponent' ] 25 | 26 | 27 | class TiltComponent(AEComponent, BindingEditor): 28 | GLADE = "ae/tilt.glade" 29 | NAME = "tilt" 30 | CTXS = Action.AC_GYRO 31 | PRIORITY = 2 32 | 33 | def __init__(self, app, editor): 34 | AEComponent.__init__(self, app, editor) 35 | BindingEditor.__init__(self, app) 36 | self._recursing = False 37 | self.actions = [ NoAction() ] * 6 38 | 39 | 40 | def set_action(self, mode, action): 41 | if isinstance(action, TiltAction): 42 | self.actions = list(action.actions) 43 | while len(self.actions) < 6: 44 | self.actions.append(NoAction()) 45 | self.update_button_desc(action) 46 | 47 | 48 | def update_button_desc(self, action): 49 | for i in xrange(0, len(action.actions)): 50 | self.actions[i] = action.actions[i] 51 | for i in xrange(0, 6): 52 | self.set_button_desc(i) 53 | 54 | 55 | def set_button_desc(self, i): 56 | desc = describe_action(Action.AC_BUTTON, None, self.actions[i]) 57 | l = self.builder.get_object("lblTilt%s" % (i,)) 58 | if l is None: 59 | l = self.builder.get_object("btTilt%s" % (i,)).get_children()[0] 60 | l.set_markup(desc) 61 | 62 | 63 | def get_button_title(self): 64 | return _("Tilt") 65 | 66 | 67 | def handles(self, mode, action): 68 | return isinstance(action, TiltAction) 69 | 70 | 71 | def update(self): 72 | self.editor.set_action(TiltAction(*self.actions)) 73 | 74 | 75 | def on_action_chosen(self, i, action, mark_changed=True): 76 | self.actions[i] = action 77 | self.set_button_desc(i) 78 | self.update() 79 | 80 | 81 | def on_btTilt_clicked(self, b): 82 | """ 'Select Tilt Action' handler """ 83 | i = int(b.get_name()) 84 | action = self.actions[i] 85 | ae = self.choose_editor(action, "") 86 | ae.set_title(_("Select Tilt Action")) 87 | ae.set_input(i, action, mode = Action.AC_BUTTON) 88 | ae.show(self.editor.window) 89 | -------------------------------------------------------------------------------- /scc/gui/creg/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | -------------------------------------------------------------------------------- /scc/gui/creg/constants.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Controller Registration Constants 4 | 5 | Just huge chunk of constants put aside to make impotant code more readable 6 | """ 7 | from __future__ import unicode_literals 8 | 9 | from scc.constants import SCButtons, STICK, LEFT, RIGHT 10 | from scc.gui import BUTTON_ORDER 11 | 12 | X = 0 13 | Y = 1 14 | 15 | AXIS_ORDER = ( 16 | ("stick_x", X), ("stick_y", Y), 17 | ("rpad_x", X), ("rpad_y", Y), 18 | ("lpad_x", X), ("lpad_y", Y), 19 | ("ltrig", X), # index 6 20 | ("rtrig", X), 21 | ) 22 | 23 | STICK_PAD_AREAS = { 24 | # Numbers here are indexes to AXIS_ORDER tuple 25 | "STICK": (STICK, (0, 1)), 26 | "RPAD": (RIGHT, (2, 3)), 27 | "LPAD": (LEFT, (4, 5)), 28 | } 29 | 30 | TRIGGER_AREAS = { 31 | # Numbers here are indexes to AXIS_ORDER tuple 32 | "LT": 6, 33 | "RT": 7 34 | } 35 | 36 | AXIS_TO_BUTTON = { 37 | # Maps stick and dpad axes to their respective "pressed" button 38 | "stick_x": SCButtons.STICKPRESS, 39 | "stick_y": SCButtons.STICKPRESS, 40 | "rpad_x": SCButtons.RPAD, 41 | "rpad_y": SCButtons.RPAD, 42 | "lpad_x": SCButtons.LPAD, 43 | "lpad_y": SCButtons.LPAD, 44 | } 45 | 46 | SDL_TO_SCC_NAMES = { 47 | 'guide': 'C', 48 | 'leftstick': 'STICKPRESS', 49 | 'rightstick': 'RPAD', 50 | 'leftshoulder': 'LB', 51 | 'rightshoulder': 'RB', 52 | } 53 | 54 | SDL_AXES = ( 55 | # This tuple has to use same order as AXIS_ORDER 56 | 'leftx', 'lefty', 57 | 'rightx', 'righty', 58 | "dpadx", "dpady", 59 | 'lefttrigger', 60 | 'righttrigger' 61 | ) 62 | 63 | 64 | SDL_DPAD = { 65 | # Numbers here are indexes to AXIS_ORDER tuple 66 | # Booleans here are True for positive movements (down/right) and 67 | # False for negative (up/left) 68 | 'dpdown': (5, True), 69 | 'dpleft': (4, False), 70 | 'dpright': (4, True), 71 | 'dpup': (5, False), 72 | } 73 | -------------------------------------------------------------------------------- /scc/gui/creg/data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Controller Registration data 4 | 5 | Dummy container classes 6 | """ 7 | from __future__ import unicode_literals 8 | 9 | from scc.constants import STICK_PAD_MAX, STICK_PAD_MIN 10 | from scc.gui.creg.constants import AXIS_TO_BUTTON 11 | 12 | import logging 13 | log = logging.getLogger("CReg.data") 14 | 15 | 16 | class AxisData(object): 17 | """ 18 | (Almost) dumb container. 19 | Stores position, center and limits for single axis. 20 | """ 21 | 22 | def __init__(self, name, xy, min=STICK_PAD_MAX, max=STICK_PAD_MIN): 23 | self.name = name 24 | self.area = name.split("_")[0].upper() 25 | if self.area.endswith("TRIG"): self.area = self.area[0:-3] 26 | self.xy = xy 27 | self.pos = 0 28 | self.center = 0 29 | self.min = min 30 | self.max = max 31 | self.invert = False 32 | self.cursor = None 33 | 34 | 35 | def reset(self): 36 | """ 37 | Resets min and max value so axis can (has to be) recalibrated again 38 | """ 39 | self.min = STICK_PAD_MAX 40 | self.max = STICK_PAD_MIN 41 | 42 | 43 | def __repr__(self): 44 | return "" % (self.name, ) 45 | 46 | 47 | def set_position(self, value): 48 | """ 49 | Returns (changed, x), value determining if axis limits were changed and 50 | current position position. 51 | translated to range of (STICK_PAD_MIN, STICK_PAD_MAX) 52 | """ 53 | changed = False 54 | if value < self.min: 55 | self.min = value 56 | changed = True 57 | if value > self.max: 58 | self.max = value 59 | changed = True 60 | self.pos = value 61 | try: 62 | r = (STICK_PAD_MAX - STICK_PAD_MIN) / (self.max - self.min) 63 | v = (self.pos - self.min) * r 64 | if self.invert: 65 | return changed, STICK_PAD_MAX - v 66 | else: 67 | return changed, v + STICK_PAD_MIN 68 | except ZeroDivisionError: 69 | return changed, 0 70 | 71 | 72 | class DPadEmuData(object): 73 | """ 74 | Dumb container that stores dpad emulation data. 75 | DPAd emulation is used, for example, on PS3 controller, where dpad does not 76 | inputs as 2 axes, but as 4 buttons. 77 | 78 | This class stores mapping of one button to one half of axis. 79 | """ 80 | 81 | def __init__(self, axis_data, positive): 82 | self.axis_data = axis_data 83 | self.positive = positive 84 | self.button = AXIS_TO_BUTTON[axis_data.name] 85 | -------------------------------------------------------------------------------- /scc/gui/dwsnc.py: -------------------------------------------------------------------------------- 1 | """ 2 | DWSNC - Doing Weird Things in Name of Compatibility 3 | 4 | This module, when imported, applies various fixes and monkey-patching to allow 5 | application to run with older versions of GLib and/or GTK. 6 | """ 7 | from __future__ import unicode_literals 8 | from gi.repository import Gtk, GObject 9 | import os 10 | 11 | 12 | def fix_label_missing_set_XYalign_methods(): 13 | """ 14 | Fix Gtk.Label missing set_xalign and set_yalign methods with older 15 | versions of Gtk. 16 | 17 | Prevents crashing, but alings are ignored. 18 | """ 19 | Gtk.Label.set_xalign = Gtk.Label.set_yalign = lambda *a : None 20 | 21 | def child_get_property(parent, child, propname): 22 | """ 23 | Wrapper for child_get_property, which pygobject doesn't properly 24 | introspect 25 | """ 26 | value = GObject.Value() 27 | value.init(GObject.TYPE_INT) 28 | parent.child_get_property(child, propname, value) 29 | return value.get_int() 30 | 31 | 32 | def headerbar(bar): 33 | """ 34 | Moves all buttons from left to right (and vice versa) if user's desktop 35 | environment is identified as Unity. 36 | 37 | Removes 'icon' button otherwise 38 | """ 39 | bar.set_decoration_layout(":minimize,close") 40 | pass # Not outside of Unity 41 | 42 | IS_UNITY = False 43 | IS_GNOME = False 44 | IS_KDE = False 45 | 46 | if "XDG_CURRENT_DESKTOP" in os.environ: 47 | if "GNOME" in os.environ["XDG_CURRENT_DESKTOP"].split(":"): 48 | IS_GNOME = True 49 | 50 | if "KDE" in os.environ["XDG_CURRENT_DESKTOP"].split(":"): 51 | IS_KDE = True 52 | 53 | if "Unity" in os.environ["XDG_CURRENT_DESKTOP"].split(":"): 54 | # User runs Unity 55 | IS_UNITY = True 56 | 57 | def _headerbar(bar): 58 | children = [] + bar.get_children() 59 | pack_start = [] 60 | pack_end = [] 61 | for c in children: 62 | if child_get_property(bar, c, 'pack-type') == Gtk.PackType.END: 63 | bar.remove(c) 64 | pack_start.append(c) 65 | else: 66 | bar.remove(c) 67 | pack_end.append(c) 68 | if len(pack_end) > 1: 69 | c, pack_end = pack_end[0], pack_end[1:] 70 | pack_end.append(c) 71 | if (Gtk.get_major_version(), Gtk.get_minor_version()) > (3, 10): 72 | # Old ubuntu has this in order, new Ubuntu has it reversed 73 | pack_end = reversed(pack_end) 74 | for c in pack_start: bar.pack_start(c) 75 | for c in pack_end: bar.pack_end(c) 76 | headerbar = _headerbar 77 | 78 | if not hasattr(Gtk.Label, "set_xalign"): 79 | # GTK is old enough 80 | fix_label_missing_set_XYalign_methods() 81 | 82 | -------------------------------------------------------------------------------- /scc/gui/importexport/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | -------------------------------------------------------------------------------- /scc/gui/keycode_to_key.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - KEYCODE_TO_KEY 4 | 5 | Similar to GDK_TO_KEY, maps X11 keycodes to Keys.KEY_* constants. 6 | Used by OSD keyboard 7 | """ 8 | from __future__ import unicode_literals 9 | 10 | from scc.uinput import Keys 11 | from gdk_to_key import KEYCODE_TO_KEY 12 | 13 | KEY_TO_KEYCODE = { KEYCODE_TO_KEY[a] : a for a in KEYCODE_TO_KEY } 14 | -------------------------------------------------------------------------------- /scc/gui/osk_binding_editor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - On Screen Keyboard Binding Editor 4 | 5 | Edits '.scc-osd.keyboard.sccprofile', profile used by on screen keyboard 6 | """ 7 | from __future__ import unicode_literals 8 | from scc.tools import _ 9 | 10 | from gi.repository import Gdk 11 | from scc.constants import SCButtons, STICK 12 | from scc.paths import get_profiles_path 13 | from scc.tools import find_profile 14 | from scc.profile import Profile 15 | from scc.actions import Action 16 | from scc.gui.binding_editor import BindingEditor 17 | from scc.gui.controller_widget import TRIGGERS, STICKS 18 | from scc.gui.parser import GuiActionParser 19 | from scc.gui.editor import Editor 20 | from scc.osd.keyboard import Keyboard as OSDKeyboard 21 | 22 | import os, logging 23 | log = logging.getLogger("OSKEdit") 24 | 25 | 26 | class OSKBindingEditor(Editor, BindingEditor): 27 | GLADE = "osk_binding_editor.glade" 28 | 29 | def __init__(self, app): 30 | BindingEditor.__init__(self, app) 31 | self.app = app 32 | self.gladepath = app.gladepath 33 | self.imagepath = app.imagepath 34 | self.current = Profile(GuiActionParser()) 35 | self.current.load(find_profile(OSDKeyboard.OSK_PROF_NAME)) 36 | self.setup_widgets() 37 | 38 | 39 | def setup_widgets(self): 40 | Editor.setup_widgets(self) 41 | self.create_binding_buttons(use_icons=False, enable_press=False) 42 | 43 | 44 | def show_editor(self, id): 45 | if id in STICKS: 46 | ae = self.choose_editor(self.current.stick, 47 | _("Stick")) 48 | ae.set_input(STICK, self.current.stick, mode=Action.AC_OSK) 49 | ae.show(self.window) 50 | elif id in SCButtons: 51 | title = _("%s Button") % (id.name,) 52 | ae = self.choose_editor(self.current.buttons[id], title) 53 | ae.set_input(id, self.current.buttons[id], mode=Action.AC_OSK) 54 | ae.show(self.window) 55 | elif id in TRIGGERS: 56 | ae = self.choose_editor(self.current.triggers[id], 57 | _("%s Trigger") % (id,)) 58 | ae.set_input(id, self.current.triggers[id], mode=Action.AC_OSK) 59 | ae.show(self.window) 60 | 61 | 62 | def on_action_chosen(self, id, action, mark_changed=True): 63 | self.set_action(self.current, id, action) 64 | self.save_profile() 65 | 66 | 67 | def save_profile(self, *a): 68 | """ 69 | Saves osk profile from 'profile' object into 'giofile'. 70 | Calls on_profile_saved when done 71 | """ 72 | self.current.save(os.path.join(get_profiles_path(), 73 | OSDKeyboard.OSK_PROF_NAME + ".sccprofile")) 74 | # OSK reloads profile when daemon reports configuration change 75 | self.app.dm.reconfigure() 76 | -------------------------------------------------------------------------------- /scc/gui/parser.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from scc.parser import ActionParser, ParseError 3 | from scc.actions import Action 4 | from scc.tools import _ 5 | 6 | import logging 7 | log = logging.getLogger("gui.parse") 8 | 9 | class InvalidAction(Action): 10 | def __init__(self, string, error): 11 | self.string = string 12 | self.error = error 13 | self.name = None 14 | 15 | 16 | def __str__(self): 17 | return "" % (self.string,) 18 | 19 | __repr__ = __str__ 20 | 21 | 22 | def to_string(self, *a): 23 | return self.string 24 | 25 | 26 | def describe(self, *a): 27 | return _("(invalid)") 28 | 29 | 30 | class GuiActionParser(ActionParser): 31 | """ 32 | ActionParser that stores original string and 33 | returns InvalidAction instance when parsing fails 34 | """ 35 | 36 | def restart(self, string): 37 | self.string = string 38 | return ActionParser.restart(self, string) 39 | 40 | 41 | def parse(self): 42 | """ 43 | Returns parsed action or None if action cannot be parsed. 44 | """ 45 | try: 46 | a = ActionParser.parse(self) 47 | a.string = self.string 48 | return a 49 | except ParseError, e: 50 | log.error("Failed to parse '%s'", self.string) 51 | log.error(e) 52 | return InvalidAction(self.string, e) 53 | -------------------------------------------------------------------------------- /scc/gui/simple_chooser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Simple Chooser 4 | 5 | Used by Action Editor to display window with just one Component 6 | """ 7 | from __future__ import unicode_literals 8 | from scc.tools import _ 9 | 10 | from gi.repository import Gtk, Gdk, GLib 11 | from scc.gui.dwsnc import headerbar 12 | from scc.gui.ae import AEComponent 13 | from scc.gui.editor import Editor 14 | import logging, os, types, importlib 15 | log = logging.getLogger("SimpleChooser") 16 | 17 | class SimpleChooser(Editor): 18 | GLADE = "simple_chooser.glade" 19 | 20 | def __init__(self, app, component_name, callback): 21 | self.app = app 22 | self._action = None 23 | self.component = None 24 | self.callback = callback 25 | self.setup_widgets() 26 | self.load_component(component_name) 27 | 28 | 29 | def setup_widgets(self): 30 | Editor.setup_widgets(self) 31 | headerbar(self.builder.get_object("header")) 32 | 33 | 34 | def load_component(self, component_name): 35 | mod = importlib.import_module("scc.gui.ae.%s" % (component_name,)) 36 | for x in dir(mod): 37 | cls = getattr(mod, x) 38 | if isinstance(cls, (type, types.ClassType)) and issubclass(cls, AEComponent): 39 | if cls.NAME == component_name: 40 | self.component = cls(self.app, self) 41 | break 42 | if self.component is None: 43 | raise ValueError("Unknown component '%s'" % (component_name,)) 44 | self.component.load() 45 | if component_name == "buttons": 46 | self.component.hide_toggle() 47 | self.window.add(self.component.get_widget()) 48 | 49 | 50 | def display_action(self, mode, action): 51 | self._action = action 52 | self.component.set_action(mode, action) 53 | 54 | 55 | def set_action(self, action): 56 | self.callback(action) 57 | self.close() 58 | self.window.destroy() 59 | 60 | 61 | def hide_axes(self): 62 | """ Prevents user from selecting axes """ 63 | self.component.hide_axes() 64 | 65 | 66 | def hide_mouse(self): 67 | """ Prevents user from selecting mouse-related stuff """ 68 | self.component.hide_mouse() 69 | -------------------------------------------------------------------------------- /scc/lib/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from enum import Enum, IntEnum, unique 4 | -------------------------------------------------------------------------------- /scc/lib/ioctl_opt.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pythonified linux asm-generic/ioctl.h . 3 | 4 | "type" parameters expect ctypes-based types (ctypes.Structure subclasses, ...). 5 | 6 | https://github.com/vpelletier/python-ioctl-opt 7 | Licensed under GPL 2.0 8 | """ 9 | 10 | import ctypes 11 | 12 | _IOC_NRBITS = 8 13 | _IOC_TYPEBITS = 8 14 | _IOC_SIZEBITS = 14 15 | _IOC_DIRBITS = 2 16 | 17 | _IOC_NRMASK = (1 << _IOC_NRBITS) - 1 18 | _IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1 19 | _IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1 20 | _IOC_DIRMASK = (1 << _IOC_DIRBITS) - 1 21 | 22 | _IOC_NRSHIFT = 0 23 | _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS 24 | _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS 25 | _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS 26 | 27 | IOC_NONE = 0 28 | IOC_WRITE = 1 29 | IOC_READ = 2 30 | 31 | def IOC(dir, type, nr, size): 32 | assert dir <= _IOC_DIRMASK, dir 33 | assert type <= _IOC_TYPEMASK, type 34 | assert nr <= _IOC_NRMASK, nr 35 | assert size <= _IOC_SIZEMASK, size 36 | return (dir << _IOC_DIRSHIFT) | (type << _IOC_TYPESHIFT) | (nr << _IOC_NRSHIFT) | (size << _IOC_SIZESHIFT) 37 | 38 | def IOC_TYPECHECK(t): 39 | result = ctypes.sizeof(t) 40 | assert result <= _IOC_SIZEMASK, result 41 | return result 42 | 43 | def IO(type, nr): 44 | return IOC(IOC_NONE, type, nr, 0) 45 | 46 | def IOR(type, nr, size): 47 | return IOC(IOC_READ, type, nr, IOC_TYPECHECK(size)) 48 | 49 | def IOW(type, nr, size): 50 | return IOC(IOC_WRITE, type, nr, IOC_TYPECHECK(size)) 51 | 52 | def IORW(type, nr, size): 53 | return IOC(IOC_READ | IOC_WRITE, type, nr, IOC_TYPECHECK(size)) 54 | 55 | def IOC_DIR(nr): 56 | return (nr >> _IOC_DIRSHIFT) & _IOC_DIRMASK 57 | 58 | def IOC_TYPE(nr): 59 | return (nr >> _IOC_TYPESHIFT) & _IOC_TYPEMASK 60 | 61 | def IOC_NR(nr): 62 | return (nr >> _IOC_NRSHIFT) & _IOC_NRMASK 63 | 64 | def IOC_SIZE(nr): 65 | return (nr >> _IOC_SIZESHIFT) & _IOC_SIZEMASK 66 | 67 | IOC_IN = IOC_WRITE << _IOC_DIRSHIFT 68 | IOC_OUT = IOC_READ << _IOC_DIRSHIFT 69 | IOC_INOUT = (IOC_WRITE | IOC_READ) << _IOC_DIRSHIFT 70 | IOCSIZE_MASK = _IOC_SIZEMASK << _IOC_SIZESHIFT 71 | IOCSIZE_SHIFT = _IOC_SIZESHIFT 72 | -------------------------------------------------------------------------------- /scc/lib/vdf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | VDF file reader 4 | Copyright (C) 2017 Kozec 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License version 2 as published by 8 | the Free Software Foundation 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | """ 19 | import shlex 20 | 21 | 22 | def parse_vdf(fileobj): 23 | """ 24 | Converts VDF file or file-like object into python dict 25 | 26 | Throws ValueError if profile cannot be parsed. 27 | """ 28 | rv = {} 29 | stack = [ rv ] 30 | lexer = shlex.shlex(fileobj) 31 | key = None 32 | 33 | t = lexer.get_token() 34 | while t: 35 | if t == "{": 36 | # Set value to dict and add it on top of stack 37 | if key is None: 38 | raise ValueError("Dict without key") 39 | value = {} 40 | if key in stack[-1]: 41 | lst = ensure_list(stack[-1][key]) 42 | lst.append(value) 43 | stack[-1][key] = lst 44 | else: 45 | stack[-1][key] = value 46 | 47 | stack.append(value) 48 | key = None 49 | elif t == "}": 50 | # Pop last dict from stack 51 | if len(stack) < 2: 52 | raise ValueError("'}' without '{'") 53 | stack = stack[0:-1] 54 | elif key is None: 55 | key = t.strip('"').lower() 56 | elif key in stack[-1]: 57 | lst = ensure_list(stack[-1][key]) 58 | lst.append(t.strip('"')) 59 | stack[-1][key] = lst 60 | key = None 61 | else: 62 | stack[-1][key] = t.strip('"') 63 | key = None 64 | 65 | t = lexer.get_token() 66 | 67 | if len(stack) > 1: 68 | raise ValueError("'{' without '}'") 69 | 70 | return rv 71 | 72 | 73 | def ensure_list(value): 74 | """ 75 | If value is list, returns same value. 76 | Otherwise, returns [ value ] 77 | """ 78 | return value if type(value) == list else [ value ] 79 | 80 | 81 | if __name__ == "__main__": 82 | print parse_vdf(file('app_generic.vdf', "r")) 83 | 84 | -------------------------------------------------------------------------------- /scc/lib/xinput.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | """ 4 | XInput tools 5 | 6 | Interfaces with XInput by calling `xinput` command. 7 | Currently allows only querying list of xinput devices and floating them. 8 | 9 | Copyright (C) 2017 Kozec 10 | 11 | This program is free software; you can redistribute it and/or modify 12 | it under the terms of the GNU General Public License version 2 as published by 13 | the Free Software Foundation 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License along 21 | with this program; if not, write to the Free Software Foundation, Inc., 22 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 | """ 24 | from __future__ import unicode_literals 25 | 26 | import logging, re, subprocess 27 | log = logging.getLogger("XI") 28 | 29 | RE_DEVICE = re.compile(r"[ \t⎜]+↳ (.*)\tid=([0-9]+)[ \t]+\[([a-z ]+)") 30 | 31 | def get_devices(): 32 | """ 33 | Returns list of devices reported by xinput. 34 | """ 35 | rv = [] 36 | try: 37 | lst = (subprocess.Popen([ "xinput" ], stdout=subprocess.PIPE, stdin=None) 38 | .communicate()[0] 39 | .decode("utf-8")) 40 | except: 41 | # calling xinput failed, return empty list 42 | return rv 43 | 44 | for line in lst.split("\n"): 45 | match = RE_DEVICE.match(line) 46 | if match: 47 | name, id, type = match.groups() 48 | name = name.strip(" \t") 49 | while " " in type: 50 | type = type.replace(" ", " ") 51 | id = int(id) 52 | rv.append(XIDevice(id, name, type)) 53 | return rv 54 | 55 | 56 | class XIDevice(object): 57 | def __init__(self, id, name, type): 58 | self._id = id 59 | self._name = name 60 | self._type = type 61 | 62 | 63 | def float(self): 64 | """ Removes slave device from its current master """ 65 | subprocess.Popen([ "xinput", "float", str(self._id) ]) 66 | log.info("Deatached device %s from its master", self._id) 67 | 68 | 69 | def get_name(self): 70 | return self._name 71 | 72 | 73 | def is_pointer(self): 74 | """ Returns True if device is pointer, ie can controll mouse """ 75 | return "pointer" in self._type 76 | 77 | 78 | def is_slave(self): 79 | """ Returns True if device is slave pointer or slave keyboard """ 80 | return "slave" in self._type 81 | 82 | 83 | def __str__(self): 84 | return "" % (self._id, self._name, self._type) 85 | 86 | __repr__ = __str__ 87 | -------------------------------------------------------------------------------- /scc/osd/grid_menu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Grid OSD Menu 4 | 5 | Works as OSD menu, but displays item in (as rectangluar as possible - and 6 | that's usually not very much) grid. 7 | """ 8 | from __future__ import unicode_literals 9 | from scc.tools import _, set_logging_level 10 | 11 | from gi.repository import Gtk 12 | from scc.menu_data import Separator, Submenu 13 | from scc.osd.menu import Menu, MenuIcon 14 | from scc.osd import OSDWindow 15 | from scc.tools import find_icon 16 | 17 | import math, logging 18 | log = logging.getLogger("osd.gridmenu") 19 | 20 | 21 | class GridMenu(Menu): 22 | PREFER_BW_ICONS = True 23 | 24 | def __init__(self, cls="osd-menu"): 25 | Menu.__init__(self, cls) 26 | self.ipr = 1 # items per row 27 | 28 | 29 | def create_parent(self): 30 | g = Gtk.Grid() 31 | g.set_name("osd-menu") 32 | return g 33 | 34 | 35 | def pack_items(self, parent, items): 36 | if self._size > 0: 37 | self.ipr = self._size 38 | else: 39 | self.ipr = int(math.sqrt(max(1, len(items)-1))+1) 40 | if len(items) == 6 : self.ipr = 3 # Special (common) cases 41 | if len(items) == 8 : self.ipr = 4 # Special (common) cases 42 | x, y = 0, 0 43 | for item in items: 44 | parent.attach(item.widget, x, y, 1, 1) 45 | x += 1 46 | if x >= self.ipr: 47 | x = 0 48 | y += 1 49 | 50 | 51 | def on_stick_direction(self, trash, x, y): 52 | if x != 0: 53 | self.next_item(-x) 54 | elif y != 0: 55 | for i in xrange(0, self.ipr): 56 | self.next_item(y) 57 | 58 | 59 | def generate_widget(self, item): 60 | if isinstance(item, Separator): 61 | # Ignored here 62 | return None 63 | elif item.id is None: 64 | # Dummies are ignored as well 65 | return None 66 | else: 67 | icon_file, has_colors = find_icon(item.icon, False) 68 | if icon_file: 69 | # Gridmenu hides label when icon is displayed 70 | widget = Gtk.Button() 71 | widget.set_relief(Gtk.ReliefStyle.NONE) 72 | widget.set_name("osd-menu-item-big-icon") 73 | if isinstance(item, Submenu): 74 | item.callback = self.show_submenu 75 | icon = MenuIcon(icon_file, has_colors) 76 | widget.add(icon) 77 | return widget 78 | else: 79 | return Menu.generate_widget(self, item) 80 | -------------------------------------------------------------------------------- /scc/osd/hmenu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Horisontal OSD Menu 4 | 5 | Works as OSD menu, but displays all items in one row. 6 | 7 | Designed mainly as RPG numeric pad display and looks 8 | awfull with larger number of items. 9 | """ 10 | from __future__ import unicode_literals 11 | from scc.tools import _, set_logging_level 12 | 13 | from gi.repository import Gtk 14 | from scc.menu_data import Separator, Submenu 15 | from scc.constants import STICK_PAD_MIN 16 | from scc.osd.grid_menu import GridMenu 17 | from scc.osd.menu import MenuIcon 18 | 19 | import logging 20 | log = logging.getLogger("osd.hmenu") 21 | 22 | 23 | class HorizontalMenu(GridMenu): 24 | def __init__(self, cls="osd-menu"): 25 | GridMenu.__init__(self, cls) 26 | 27 | 28 | def create_parent(self): 29 | g = Gtk.Grid() 30 | g.set_name("osd-menu") 31 | return g 32 | 33 | 34 | def generate_widget(self, item): 35 | """ 36 | Generates gtk widget for specified menutitem 37 | Ignores Submenus and Separators but applies icon size 38 | """ 39 | if isinstance(item, (Separator, Submenu)) or item.id is None: 40 | return None 41 | else: 42 | widget = GridMenu.generate_widget(self, item) 43 | icon = widget.get_children()[-1] 44 | if self._size > 1 and isinstance(icon, MenuIcon): 45 | widget.set_size_request(-1, 32 + self._size * 3) 46 | return widget 47 | 48 | 49 | def pack_items(self, parent, items): 50 | x = 0 51 | for item in items: 52 | parent.attach(item.widget, x, 0, 1, 1) 53 | x += 1 54 | 55 | 56 | def on_stick_direction(self, trash, x, y): 57 | if x != 0: 58 | self.next_item(-x) 59 | 60 | 61 | def on_event(self, daemon, what, data): 62 | # Restricts Y axis to dead center, as nothing 63 | # else makes sense in this kind of menu 64 | if self._submenu: 65 | return self._submenu.on_event(daemon, what, data) 66 | if what == self._control_with and self._use_cursor: 67 | data = data[0], STICK_PAD_MIN 68 | GridMenu.on_event(self, daemon, what, data) 69 | -------------------------------------------------------------------------------- /scc/osd/message.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - OSD Message 4 | 5 | Display message that just sits there 6 | """ 7 | from __future__ import unicode_literals 8 | from scc.tools import _, set_logging_level 9 | 10 | from gi.repository import Gtk, GLib 11 | from scc.special_actions import OSDAction 12 | from scc.osd import OSDWindow 13 | 14 | import os, sys, logging 15 | log = logging.getLogger("osd.message") 16 | 17 | 18 | class Message(OSDWindow): 19 | 20 | def __init__(self): 21 | OSDWindow.__init__(self, "osd-message") 22 | 23 | self.timeout = OSDAction.DEFAULT_TIMEOUT 24 | self.size = OSDAction.DEFAULT_SIZE 25 | self.text = "text" 26 | self._timeout_id = None 27 | 28 | 29 | def show(self): 30 | self.l = Gtk.Label() 31 | self.l.set_name("osd-label-%s" % (self.size, )) 32 | self.l.set_label(self.text) 33 | 34 | self.add(self.l) 35 | 36 | if self.size < 2: 37 | self.set_name("osd-message-1") 38 | OSDWindow.show(self) 39 | if self.timeout > 0: 40 | self._timeout_id = GLib.timeout_add_seconds(self.timeout, self.quit) 41 | 42 | 43 | def extend(self): 44 | self.set_state(Gtk.StateType.ACTIVE) 45 | self.l.set_state(Gtk.StateType.ACTIVE) 46 | GLib.timeout_add_seconds(0.5, self.cancel_active_state) 47 | if self._timeout_id: 48 | GLib.source_remove(self._timeout_id) 49 | self._timeout_id = GLib.timeout_add_seconds(self.timeout, self.quit) 50 | 51 | 52 | def cancel_active_state(self): 53 | self.set_state(Gtk.StateType.NORMAL) 54 | self.l.set_state(Gtk.StateType.NORMAL) 55 | 56 | 57 | def hash(self): 58 | return hash(self.text) + self.timeout - (self.size * 5) 59 | 60 | 61 | def _add_arguments(self): 62 | OSDWindow._add_arguments(self) 63 | self.argparser.add_argument('-t', type=float, metavar="seconds", 64 | default=5, help="time before message is hidden (default: 5; 0 means forever)") 65 | self.argparser.add_argument('-s', type=int, metavar="size", 66 | default=3, help="font size, in range 1 to 3 (default: 3)") 67 | self.argparser.add_argument('text', type=str, help="text to display") 68 | 69 | 70 | def parse_argumets(self, argv): 71 | if not OSDWindow.parse_argumets(self, argv): 72 | return False 73 | self.text = self.args.text 74 | self.timeout = self.args.t 75 | self.size = self.args.s 76 | return True 77 | -------------------------------------------------------------------------------- /scc/osd/timermanager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Timer manager 4 | 5 | Simple abstract class for named, cancelable timers 6 | """ 7 | 8 | from __future__ import unicode_literals 9 | from gi.repository import GLib 10 | 11 | class TimerManager(object): 12 | def __init__(self): 13 | self._timers = {} 14 | 15 | def timer(self, name, delay, callback, *data, **kwdata): 16 | """ 17 | Runs callback after specified number of seconds. Uses 18 | GLib.timeout_add_seconds with small wrapping to allow named 19 | timers to be canceled by reset() call 20 | """ 21 | method = GLib.timeout_add_seconds 22 | if delay < 1 and delay > 0: 23 | method = GLib.timeout_add 24 | delay = delay * 1000.0 25 | if name is None: 26 | # No wrapping is needed, call GLib directly 27 | method(delay, callback, *data, **kwdata) 28 | else: 29 | if name in self._timers: 30 | # Cancel old timer 31 | GLib.source_remove(self._timers[name]) 32 | # Create new one 33 | self._timers[name] = method(delay, self._callback, name, callback, *data, **kwdata) 34 | 35 | def timer_active(self, name): 36 | """ Returns True if named timer is active """ 37 | return (name in self._timers) 38 | 39 | def cancel_timer(self, name): 40 | """ 41 | Cancels named timer. Returns True on success, False if there is no such timer. 42 | """ 43 | if name in self._timers: 44 | GLib.source_remove(self._timers[name]) 45 | del self._timers[name] 46 | return True 47 | return False 48 | 49 | def cancel_all(self): 50 | """ Cancels all active timers """ 51 | for x in self._timers: 52 | GLib.source_remove(self._timers[x]) 53 | self._timers = {} 54 | 55 | def _callback(self, name, callback, *data, **kwdata): 56 | """ 57 | Removes name from list of active timers and calls real callback. 58 | """ 59 | del self._timers[name] 60 | callback(*data, **kwdata) 61 | return False 62 | 63 | -------------------------------------------------------------------------------- /scc/poller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Poller 4 | 5 | Uses select to pool for file descriptors. Driver classes can use 6 | daemon.get_poller().register and .unregister to add file descriptors and 7 | register callbacks to be called when data is available in them. 8 | 9 | Callback is called as callback(fd, event) where event is one of select.POLL* 10 | """ 11 | import select, logging 12 | log = logging.getLogger("Poller") 13 | 14 | 15 | DO_NOTHING = lambda *a: False 16 | 17 | class Poller(object): 18 | POLLIN = select.POLLIN 19 | POLLOUT = select.POLLOUT 20 | POLLPRI = select.POLLPRI 21 | 22 | def __init__(self): 23 | self._events = {} 24 | self._callbacks = {} 25 | self._pool_in = () 26 | self._pool_out = () 27 | self._pool_pri = () 28 | 29 | 30 | def register(self, fd, events, callback): 31 | if fd < 0: 32 | raise ValueError("Invalid file descriptor") 33 | self._events[fd] = events 34 | self._callbacks[fd] = callback 35 | self._generate_lists() 36 | 37 | 38 | def unregister(self, fd): 39 | if fd in self._events: del self._events[fd] 40 | if fd in self._callbacks: del self._callbacks[fd] 41 | self._generate_lists() 42 | 43 | 44 | def _generate_lists(self): 45 | self._pool_in = [ fd for fd, events in self._events.iteritems() if events & Poller.POLLIN ] 46 | self._pool_out = [ fd for fd, events in self._events.iteritems() if events & Poller.POLLOUT ] 47 | self._pool_pri = [ fd for fd, events in self._events.iteritems() if events & Poller.POLLPRI ] 48 | 49 | 50 | def poll(self, timeout=0.01): 51 | inn, out, pri = select.select( self._pool_in, self._pool_out, self._pool_pri, timeout ) 52 | 53 | for fd in inn: 54 | self._callbacks.get(fd, DO_NOTHING)(fd, Poller.POLLIN) 55 | for fd in out: 56 | self._callbacks.get(fd, DO_NOTHING)(fd, Poller.POLLOUT) 57 | for fd in pri: 58 | self._callbacks.get(fd, DO_NOTHING)(fd, Poller.POLLPRI) 59 | -------------------------------------------------------------------------------- /scc/scheduler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Scheduler 4 | 5 | Centralized scheduler that should be used everywhere. 6 | Runs in SCCDaemon's (single-threaded) mainloop. That means all callbacks are 7 | also called on main thread. 8 | 9 | Use schedule(delay, callback, *data) to register one-time task. 10 | """ 11 | import time, Queue, logging 12 | log = logging.getLogger("Scheduler") 13 | 14 | # TODO: Maybe create actual thread for this? Use poler? Scrap everything and rewrite it in GO? 15 | 16 | class Scheduler(object): 17 | 18 | def __init__(self): 19 | self._scheduled = Queue.PriorityQueue() 20 | self._next = None 21 | self._now = time.time() 22 | 23 | 24 | def schedule(self, delay, callback, *data): 25 | """ 26 | Schedules one-time task to be executed no sooner than after 'delay' of 27 | seconds. Delay may be float number. 28 | 'callback' is called as callback(*data). 29 | 30 | Returned Task instance can be used to cancel task once scheduled. 31 | """ 32 | task = Task(self._now + delay, callback, data) 33 | if self._next is None or task.time < self._next.time: 34 | if self._next: 35 | self._scheduled.put(self._next) 36 | self._next = task 37 | else: 38 | self._scheduled.put(task) 39 | return task 40 | 41 | 42 | def cancel_task(self, task): 43 | """ 44 | Returns True if task was sucessfully removed or False if task was 45 | already executed or not known at all. 46 | 47 | Note that this is slow as hell and completly thread-unsafe, 48 | so it _has_ to be called on main thread. 49 | """ 50 | if task == self._next: 51 | self._next = None if self._scheduled.empty() else self._scheduled.get() 52 | return True 53 | # Fun part: All tasks are removed from PriorityQueue 54 | # until correct is found. Then everything is put back 55 | tasks, found = [], False 56 | while not self._scheduled.empty(): 57 | t = self._scheduled.get() 58 | if t == task: 59 | found = True 60 | break 61 | tasks.append(t) 62 | for t in tasks: 63 | self._scheduled.put(t) 64 | return found 65 | 66 | 67 | def run(self): 68 | self._now = time.time() 69 | while self._next and self._now >= self._next.time: 70 | callback, data = self._next.callback, self._next.data 71 | self._next = None if self._scheduled.empty() else self._scheduled.get() 72 | callback(*data) 73 | 74 | 75 | class Task(object): 76 | 77 | def __init__(self, time, callback, data): 78 | self.time = time 79 | self.callback = callback 80 | self.data = data 81 | 82 | 83 | def cancel(self): 84 | """ Marks task as canceled, without actually removing it from scheduler """ 85 | self.callback = lambda *a, **b: False 86 | self.data = () 87 | 88 | -------------------------------------------------------------------------------- /scc/x11/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - X11 4 | 5 | Daemon-related stuff that really needs X server to work. 6 | """ 7 | 8 | # there is also absolutely nothing usefull in this file... -------------------------------------------------------------------------------- /scc/x11/scc-autoswitch-daemon.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Autoswitch Daemon 4 | 5 | Observes active window and commands scc-daemon to change profiles as needed. 6 | """ 7 | from __future__ import unicode_literals 8 | 9 | from scc.x11.autoswitcher import AutoSwitcher 10 | from scc.lib import xwrappers as X 11 | from scc.tools import set_logging_level, find_profile 12 | from scc.paths import get_daemon_socket 13 | from scc.config import Config 14 | 15 | import os, sys, time, socket, threading, signal, logging 16 | log = logging.getLogger("AS-Daemon") 17 | 18 | 19 | if __name__ == "__main__": 20 | from scc.tools import init_logging, set_logging_level 21 | from scc.paths import get_share_path 22 | init_logging(suffix=" AS ") 23 | set_logging_level('debug' in sys.argv, 'debug' in sys.argv) 24 | 25 | if "DISPLAY" not in os.environ: 26 | log.error("DISPLAY env variable not set.") 27 | sys.exit(1) 28 | 29 | d = AutoSwitcher() 30 | signal.signal(signal.SIGINT, d.sigint) 31 | d.run() 32 | sys.exit(d.exit_code) 33 | -------------------------------------------------------------------------------- /scc/x11/scc_autoswitch_daemon.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | SC-Controller - Autoswitch Daemon 4 | 5 | Observes active window and commands scc-daemon to change profiles as needed. 6 | """ 7 | from __future__ import unicode_literals 8 | 9 | from scc.x11.autoswitcher import AutoSwitcher 10 | import sys, os 11 | 12 | if __name__ == "__main__": 13 | from scc.tools import init_logging, set_logging_level 14 | from scc.paths import get_share_path 15 | init_logging(suffix=" AutoSwitcher") 16 | set_logging_level('debug' in sys.argv, 'debug' in sys.argv) 17 | 18 | if "DISPLAY" not in os.environ: 19 | log.error("DISPLAY env variable not set.") 20 | sys.exit(1) 21 | 22 | d = AutoSwitcher() 23 | d.run() 24 | sys.exit(d.exit_code) 25 | -------------------------------------------------------------------------------- /scripts/69-sc-controller.rules: -------------------------------------------------------------------------------- 1 | # This file allows SC-Controller application and daemon to access Steam Controller or its USB dongle. 2 | # This is done by allowing read/write access to all users. You may want to change this to something like 3 | # MODE="0660", GROUP="games" to allow r/w access only to members of that group. 4 | 5 | # Valve USB devices 6 | SUBSYSTEM=="usb", ATTRS{idVendor}=="28de", MODE="0666" 7 | # Valve HID devices over bluetooth hidraw 8 | KERNEL=="hidraw*", KERNELS=="*28DE:*", MODE="0666", TAG+="uaccess" 9 | # Sony USB devices 10 | SUBSYSTEM=="usb", ATTRS{idVendor}=="054c", MODE="0666" 11 | # Sony input devices over bluetooth 12 | SUBSYSTEM=="input", KERNELS=="*054C:09CC*", MODE="0666", TAG+="uaccess" 13 | # uinput kernel module write access (allows keyboard, mouse and gamepad emulation) 14 | KERNEL=="uinput", SUBSYSTEM=="misc", TAG+="uaccess", OPTIONS+="static_node=uinput", MODE="0666" 15 | -------------------------------------------------------------------------------- /scripts/appimage-AppRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export PATH=${APPDIR}:${APPDIR}/usr/bin:$PATH 3 | export LD_LIBRARY_PATH=${APPDIR}/usr/lib:$LD_LIBRARY_PATH 4 | export LD_LIBRARY_PATH=${APPDIR}/usr/lib64:$LD_LIBRARY_PATH 5 | export GI_TYPELIB_PATH=${APPDIR}/usr/lib/girepository-1.0:/usr/lib/girepository-1.0 6 | export GDK_PIXBUF_MODULEDIR=${APPDIR}/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders 7 | export PYTHONPATH=${APPDIR}/usr/lib/python2.7/site-packages 8 | export PYTHONPATH=${APPDIR}/usr/lib64/python2.7/site-packages 9 | export PYTHONPATH=$PYTHONPATH:${APPDIR}/usr/lib64/python2.7 10 | export PYTHONPATH=$PYTHONPATH:${APPDIR}/usr/lib64/python2.7/plat-linux2 11 | export PYTHONPATH=$PYTHONPATH:${APPDIR}/usr/lib64/python2.7/lib-dynload 12 | export SCC_SHARED=${APPDIR}/usr/share/scc 13 | export PYTHON=${APPDIR}/usr/bin/python2 14 | 15 | 16 | function dependency_check_failed() { 17 | # This checks 4 different ways to open error message in addition to 18 | # throwing it to screen directly 19 | >&2 cat /tmp/scc.depcheck.$$.txt 20 | 21 | [ -e /usr/bin/zenity ] && run_and_die /usr/bin/zenity --error --no-wrap --text "$(cat /tmp/scc.depcheck.$$.txt)" 22 | [ -e /usr/bin/yad ] && run_and_die /usr/bin/yad --error --text "$(cat /tmp/scc.depcheck.$$.txt)" 23 | [ -e /usr/bin/Xdialog ] && run_and_die /usr/bin/Xdialog --textbox "/tmp/scc.depcheck.$$.txt" 10 100 24 | [ -e /usr/bin/xdg ] && run_and_die /usr/bin/xdg-open "/tmp/scc.depcheck.$$.txt" 25 | exit 1 26 | } 27 | 28 | function run_and_die() { 29 | "$@" 30 | exit 1 31 | } 32 | 33 | # Check dependencies 1st 34 | ${PYTHON2} ${APPDIR}/usr/bin/scc dependency-check \ 35 | &>/tmp/scc.depcheck.$$.txt \ 36 | || dependency_check_failed 37 | rm /tmp/scc.depcheck.$$.txt || true 38 | 39 | # Pre-parse arguments 40 | ARG1=$1 41 | if [ "x$ARG1" == "x" ] ; then 42 | # Start gui if no arguments are passed 43 | ARG1="gui" 44 | elif [ "x$ARG1" == "xbash" ] ; then 45 | bash 46 | exit $? 47 | else 48 | shift 49 | fi 50 | 51 | # Start 52 | export GDK_PIXBUF_MODULE_FILE=${APPDIR}/../$$-gdk-pixbuf-loaders.cache 53 | gdk-pixbuf-query-loaders >"$GDK_PIXBUF_MODULE_FILE" 54 | ${PYTHON2} ${APPDIR}/usr/bin/scc $ARG1 $@ 55 | rm "$GDK_PIXBUF_MODULE_FILE" &>/dev/null 56 | 57 | -------------------------------------------------------------------------------- /scripts/sc-controller: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import os, sys, signal 3 | 4 | def sigint(*a): 5 | print("\n*break*") 6 | sys.exit(0) 7 | 8 | if __name__ == "__main__": 9 | signal.signal(signal.SIGINT, sigint) 10 | 11 | import gi 12 | gi.require_version('Gtk', '3.0') 13 | gi.require_version('GdkX11', '3.0') 14 | gi.require_version('Rsvg', '2.0') 15 | 16 | from scc.tools import init_logging 17 | from scc.paths import get_share_path 18 | init_logging() 19 | 20 | from gi.repository import Gtk, GObject 21 | glades = os.path.join(get_share_path(), "glade") 22 | images = os.path.join(get_share_path(), "images") 23 | if Gtk.IconTheme.get_default(): 24 | Gtk.IconTheme.get_default().append_search_path(images) 25 | GObject.threads_init() 26 | 27 | from scc.gui.app import App 28 | App(glades, images).run(sys.argv) 29 | -------------------------------------------------------------------------------- /scripts/sc-controller.appdata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | sc-controller.desktop 4 | SC Controller 5 | Edits controller mappings 6 | kozec 7 | 8 |

9 | User-mode driver and GTK3 based GUI for Steam Controller. 10 |

11 |
12 | CC0-1.0 13 | GPL-2.0 14 | https://github.com/kozec/sc-controller/issues 15 | https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=77DQD3L9K8RPU&lc=SK&item_name=kozec&item_number=scc&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted 16 | https://github.com/kozec/sc-controller/wiki 17 | https://github.com/kozec/sc-controller/ 18 | 19 | 20 | Main Application Window 21 | https://raw.githubusercontent.com/kozec/sc-controller/master/docs/screenshot1.png 22 | 23 | 24 | SC-Controller in action 25 | https://raw.githubusercontent.com/kozec/sc-controller/master/docs/screenshot2.png 26 | 27 | 28 |
29 | -------------------------------------------------------------------------------- /scripts/sc-controller.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=SC Controller 3 | GenericName=SC Controller 4 | Comment=Edits controller mappings 5 | Comment[ru]=Настройка контроллера 6 | Exec=sc-controller 7 | Type=Application 8 | Icon=sc-controller 9 | Categories=GTK;Settings;HardwareSettings; 10 | MimeType=application/x-steamcontrollerdb-profile;application/x-scc-profile;application/x-scc-profile-package; 11 | StartupWMClass=SC Controller 12 | -------------------------------------------------------------------------------- /scripts/scc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | """ 3 | Script that integrates everything SCC can do in one executable. 4 | Created so scc-* stuff doesn't polute /usr/bin. 5 | """ 6 | from scc.scripts import main 7 | 8 | if __name__ == '__main__': 9 | main() 10 | -------------------------------------------------------------------------------- /scripts/scc-daemon: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | from scc.sccdaemon import SCCDaemon 3 | from scc.paths import get_pid_file, get_daemon_socket 4 | from scc.tools import init_logging 5 | 6 | import os, argparse 7 | 8 | def main(): 9 | init_logging() 10 | parser = argparse.ArgumentParser() 11 | parser.add_argument('profile', type=str, nargs='*') 12 | parser.add_argument('command', type=str, choices=['start', 'stop', 'restart', 'debug']) 13 | parser.add_argument('--alone', action='store_true', help="prevent scc-daemon from launching osd-daemon and autoswitch-daemon") 14 | parser.add_argument('--once', action='store_true', help="use with 'stop' to send single SIGTERM without waiting for daemon to exit") 15 | daemon = SCCDaemon(get_pid_file(), get_daemon_socket()) 16 | args = parser.parse_args() 17 | daemon.alone = args.alone 18 | 19 | profile = " ".join(args.profile) 20 | if profile: 21 | daemon.set_default_profile(profile) 22 | # If no default_profile is set, daemon will try to load last used 23 | # from config 24 | 25 | if 'start' == args.command: 26 | daemon.start() 27 | elif 'stop' == args.command: 28 | daemon.stop(once = args.once) 29 | elif 'restart' == args.command: 30 | daemon.restart() 31 | elif 'debug' == args.command: 32 | daemon.debug() 33 | 34 | 35 | if __name__ == '__main__': 36 | main() 37 | -------------------------------------------------------------------------------- /scripts/scc-osd-dialog: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import os, sys, signal 3 | 4 | def sigint(*a): 5 | print("\n*break*") 6 | sys.exit(-1) 7 | 8 | def main(): 9 | signal.signal(signal.SIGINT, sigint) 10 | 11 | import gi 12 | gi.require_version('Gtk', '3.0') 13 | gi.require_version('Rsvg', '2.0') 14 | gi.require_version('GdkX11', '3.0') 15 | 16 | from scc.tools import init_logging 17 | from scc.paths import get_share_path 18 | init_logging() 19 | 20 | from scc.osd.dialog import Dialog 21 | m = Dialog() 22 | if not m.parse_argumets(sys.argv): 23 | sys.exit(1) 24 | m.run() 25 | if m.get_exit_code() == 0: 26 | print m.get_selected_item_id() 27 | sys.exit(m.get_exit_code()) 28 | 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /scripts/scc-osd-keyboard: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import os, sys, signal, argparse 3 | 4 | def sigint(*a): 5 | print("\n*break*") 6 | sys.exit(0) 7 | 8 | if __name__ == "__main__": 9 | signal.signal(signal.SIGINT, sigint) 10 | 11 | import gi 12 | gi.require_version('Gtk', '3.0') 13 | gi.require_version('Rsvg', '2.0') 14 | gi.require_version('GdkX11', '3.0') 15 | 16 | from scc.tools import init_logging 17 | from scc.paths import get_share_path 18 | init_logging() 19 | 20 | from scc.osd.keyboard import Keyboard 21 | k = Keyboard() 22 | if not k.parse_argumets(sys.argv): 23 | sys.exit(1) 24 | k.run() 25 | sys.exit(k.get_exit_code()) 26 | -------------------------------------------------------------------------------- /scripts/scc-osd-launcher: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import os, sys, signal 3 | 4 | def sigint(*a): 5 | print("\n*break*") 6 | sys.exit(-1) 7 | 8 | def main(): 9 | signal.signal(signal.SIGINT, sigint) 10 | 11 | import gi 12 | gi.require_version('Gtk', '3.0') 13 | gi.require_version('Rsvg', '2.0') 14 | gi.require_version('GdkX11', '3.0') 15 | 16 | from scc.tools import init_logging 17 | from scc.paths import get_share_path 18 | init_logging() 19 | 20 | from scc.osd.launcher import Launcher 21 | m = Launcher() 22 | if not m.parse_argumets(sys.argv): 23 | sys.exit(1) 24 | m.run() 25 | sys.exit(m.get_exit_code()) 26 | 27 | 28 | if __name__ == "__main__": 29 | main() 30 | -------------------------------------------------------------------------------- /scripts/scc-osd-menu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import os, sys, signal 3 | 4 | def sigint(*a): 5 | print("\n*break*") 6 | sys.exit(-1) 7 | 8 | def main(): 9 | signal.signal(signal.SIGINT, sigint) 10 | 11 | import gi 12 | gi.require_version('Gtk', '3.0') 13 | gi.require_version('Rsvg', '2.0') 14 | gi.require_version('GdkX11', '3.0') 15 | 16 | from scc.tools import init_logging 17 | from scc.paths import get_share_path 18 | init_logging() 19 | 20 | from scc.osd.menu import Menu 21 | m = Menu() 22 | if not m.parse_argumets(sys.argv): 23 | sys.exit(1) 24 | m.run() 25 | if m.get_exit_code() == 0: 26 | print m.get_selected_item_id() 27 | sys.exit(m.get_exit_code()) 28 | 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /scripts/scc-osd-message: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import os, sys, signal, argparse 3 | 4 | def sigint(*a): 5 | print("\n*break*") 6 | sys.exit(0) 7 | 8 | if __name__ == "__main__": 9 | signal.signal(signal.SIGINT, sigint) 10 | 11 | import gi 12 | gi.require_version('Gtk', '3.0') 13 | gi.require_version('Rsvg', '2.0') 14 | gi.require_version('GdkX11', '3.0') 15 | 16 | from scc.tools import init_logging 17 | from scc.paths import get_share_path 18 | init_logging() 19 | 20 | from scc.osd.message import Message 21 | m = Message() 22 | if not m.parse_argumets(sys.argv): 23 | sys.exit(1) 24 | m.run() 25 | sys.exit(m.get_exit_code()) 26 | -------------------------------------------------------------------------------- /scripts/scc-osd-radial-menu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import os, sys, signal, argparse 3 | 4 | def sigint(*a): 5 | print("\n*break*") 6 | sys.exit(-1) 7 | 8 | def main(): 9 | signal.signal(signal.SIGINT, sigint) 10 | 11 | import gi 12 | gi.require_version('Gtk', '3.0') 13 | gi.require_version('Rsvg', '2.0') 14 | gi.require_version('GdkX11', '3.0') 15 | 16 | from scc.tools import init_logging 17 | from scc.paths import get_share_path 18 | init_logging() 19 | 20 | from scc.osd.radial_menu import RadialMenu 21 | m = RadialMenu() 22 | if not m.parse_argumets(sys.argv): 23 | sys.exit(1) 24 | m.run() 25 | if m.get_exit_code() == 0: 26 | print m.get_selected_item_id() 27 | sys.exit(m.get_exit_code()) 28 | 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /scripts/scc-osd-show-bindings: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import os, sys, signal 3 | 4 | def sigint(*a): 5 | print("\n*break*") 6 | sys.exit(-1) 7 | 8 | def main(): 9 | signal.signal(signal.SIGINT, sigint) 10 | 11 | import gi 12 | gi.require_version('Gtk', '3.0') 13 | gi.require_version('Rsvg', '2.0') 14 | gi.require_version('GdkX11', '3.0') 15 | 16 | from scc.tools import init_logging 17 | from scc.paths import get_share_path 18 | init_logging() 19 | 20 | from scc.osd.binding_display import BindingDisplay 21 | d = BindingDisplay() 22 | if not d.parse_argumets(sys.argv): 23 | sys.exit(1) 24 | d.run() 25 | sys.exit(d.get_exit_code()) 26 | 27 | 28 | if __name__ == "__main__": 29 | main() 30 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | ## TESTS 2 | 3 | Tests are using [pytest framework](https://docs.pytest.org/en/latest/). 4 | 5 | To run all of them, navigate to directory above and do 6 | `$ PYTHONPATH=. py.test2 tests` 7 | -------------------------------------------------------------------------------- /tests/test_boolean.py: -------------------------------------------------------------------------------- 1 | from scc.actions import Action, NoAction 2 | 3 | class TestBoolean(object): 4 | 5 | def test_noaction_is_false(self): 6 | """ 7 | Tests if None can be used as False boolean value. 8 | """ 9 | assert not NoAction() 10 | if NoAction(): 11 | raise Exception("NoAction is True :(") 12 | 13 | 14 | def test_action_is_true(self): 15 | """ 16 | Tests if random action works as True boolean value. 17 | """ 18 | a = Action() 19 | assert a 20 | if a: 21 | return 22 | raise Exception("Action is False :(") 23 | 24 | -------------------------------------------------------------------------------- /tests/test_docs.py: -------------------------------------------------------------------------------- 1 | from scc.actions import Action 2 | 3 | import os 4 | 5 | class TestDocs(object): 6 | """ 7 | Tests every glade file in glade/ directory (and subdirectories) for known 8 | problems that may cause GUI to crash in some environments. 9 | 10 | (one case on one environment so far) 11 | """ 12 | 13 | def test_every_action_has_docs(self): 14 | """ 15 | Tests if every known Action is documentated in docs/actions.md 16 | """ 17 | # Read docs first 18 | actions_md = file("docs/actions.md", "r").read() 19 | profile_md = file("docs/profile-file.md", "r").read() 20 | 21 | # Do stupid fulltext search, because currently it's simply fast enough 22 | for command in Action.ALL: 23 | if command in (None, 'None', 'exit'): 24 | # Woo for special cases 25 | continue 26 | anchor = '' % (command,) 27 | assert anchor in actions_md, "Action '%s' is not documented in actions.md" % (command,) 28 | 29 | for key in Action.PKEYS: 30 | anchor = '#### `%s`' % (key,) 31 | assert key in profile_md, "Key '%s' is not documented in profile-file.md" % (key,) 32 | -------------------------------------------------------------------------------- /tests/test_glade.py: -------------------------------------------------------------------------------- 1 | import xml.etree.cElementTree as ET 2 | import os 3 | 4 | def _get_files(): 5 | """ 6 | Generates list of all glade files in glade/ directory. 7 | """ 8 | # TODO: Caching, when there is more than one test using this 9 | rv = [] 10 | def recursive(path): 11 | for f in os.listdir(path): 12 | filename = os.path.join(path, f) 13 | if os.path.isdir(filename): 14 | recursive(filename) 15 | elif filename.endswith(".glade"): 16 | rv.append(filename) 17 | 18 | recursive("glade/") 19 | return rv 20 | 21 | 22 | def _check_ids(el, filename, parent_id): 23 | """ Recursively walks through tree and check if every object has ID """ 24 | for child in el: 25 | if child.tag == "object": 26 | msg = "Widget has no ID in %s; class %s; Parent id: %s" % ( 27 | filename, 28 | child.attrib['class'], 29 | parent_id 30 | ) 31 | assert 'id' in child.attrib and child.attrib['id'], msg 32 | for subel in child: 33 | if subel.tag == "child": 34 | _check_ids(subel, filename, child.attrib['id']) 35 | 36 | class TestGlade(object): 37 | """ 38 | Tests every glade file in glade/ directory (and subdirectories) for known 39 | problems that may cause GUI to crash in some environments. 40 | 41 | (one case on one environment so far) 42 | """ 43 | 44 | def test_every_widget_has_id(self): 45 | """ 46 | Tests if every defined widget has ID. 47 | Dummy widgets without ID are OK, in theory, but Ubuntu version 48 | of libglade crashes witht them :( 49 | """ 50 | for filename in _get_files(): 51 | root = ET.parse(filename).getroot() 52 | _check_ids(root, filename, "") 53 | -------------------------------------------------------------------------------- /tests/test_parser/__init__.py: -------------------------------------------------------------------------------- 1 | from scc.parser import ActionParser 2 | 3 | parser = ActionParser() 4 | 5 | def _parses_as_itself(action): 6 | """ 7 | Tests if provided action can be converted to string and 8 | parsed back to same action. 9 | """ 10 | # Simple 11 | a_str = action.to_string() 12 | assert parser.restart(a_str).parse().to_string() == a_str 13 | # Multiline 14 | m_str = action.to_string(True) 15 | assert parser.restart(m_str).parse().to_string() == a_str 16 | return True 17 | 18 | def _parse_compressed(a_str): 19 | return parser.restart(a_str).parse().compress() 20 | -------------------------------------------------------------------------------- /tests/test_parser/test_macros.py: -------------------------------------------------------------------------------- 1 | from scc.uinput import Keys 2 | from scc.actions import ButtonAction, AxisAction 3 | from scc.macros import * 4 | from . import _parses_as_itself, parser 5 | import inspect 6 | 7 | class TestMacros(object): 8 | 9 | def test_tests(self): 10 | """ 11 | Tests if this class has test for each known macro-related action defined. 12 | """ 13 | for cls in Action.ALL.values(): 14 | if "/macros.py" in inspect.getfile(cls): 15 | method_name = "test_%s" % (cls.COMMAND,) 16 | assert hasattr(self, method_name), \ 17 | "There is no test for %s" % (cls.COMMAND) 18 | 19 | 20 | def test_macro(self): 21 | """ 22 | Tests if Macro can be converted to string and parsed back to 23 | same action. 24 | """ 25 | assert _parses_as_itself(Macro( 26 | ButtonAction(Keys.BTN_LEFT), 27 | ButtonAction(Keys.BTN_RIGHT), 28 | ButtonAction(Keys.BTN_MIDDLE) 29 | )) 30 | 31 | 32 | def test_type(self): 33 | """ 34 | Tests if Type macro can be converted to string and parsed back to 35 | same action. 36 | """ 37 | assert _parses_as_itself(Type("ilovecandy")) 38 | 39 | 40 | def test_cycle(self): 41 | """ 42 | Tests if Cycle can be converted to string and parsed back to 43 | same action. 44 | """ 45 | assert _parses_as_itself(Cycle( 46 | ButtonAction(Keys.BTN_LEFT), 47 | ButtonAction(Keys.BTN_RIGHT), 48 | ButtonAction(Keys.BTN_MIDDLE) 49 | )) 50 | 51 | 52 | def test_repeat(self): 53 | """ 54 | Tests if Repeat can be converted to string and parsed back to 55 | same action. 56 | """ 57 | assert _parses_as_itself(Repeat(ButtonAction(Keys.BTN_LEFT))) 58 | assert _parses_as_itself(Repeat(Macro( 59 | ButtonAction(Keys.BTN_LEFT), 60 | ButtonAction(Keys.BTN_RIGHT), 61 | ButtonAction(Keys.BTN_MIDDLE) 62 | ))) 63 | 64 | 65 | def test_sleep(self): 66 | """ 67 | Tests if SleepAction can be converted to string and parsed back to 68 | same action. 69 | """ 70 | assert _parses_as_itself(SleepAction(1.5)) 71 | 72 | 73 | def test_press(self): 74 | """ 75 | Tests if PressAction can be converted to string and 76 | parsed back to same action. 77 | """ 78 | assert _parses_as_itself(PressAction(Keys.BTN_LEFT)) 79 | 80 | 81 | def test_release(self): 82 | """ 83 | Tests if ReleaseAction can be converted to string 84 | and parsed back to same action. 85 | """ 86 | assert _parses_as_itself(ReleaseAction(Keys.BTN_LEFT)) 87 | 88 | 89 | def test_tap(self): 90 | """ 91 | Tests if TapAction can be converted to string 92 | and parsed back to same action. 93 | """ 94 | assert _parses_as_itself(TapAction(Keys.BTN_LEFT)) 95 | -------------------------------------------------------------------------------- /tests/test_profile/__init__.py: -------------------------------------------------------------------------------- 1 | from scc.parser import ActionParser 2 | 3 | parser = ActionParser() 4 | -------------------------------------------------------------------------------- /tests/test_profile/test_modeshift.py: -------------------------------------------------------------------------------- 1 | from scc.uinput import Keys, Axes, Rels 2 | from scc.actions import ButtonAction, AxisAction, GyroAction 3 | from scc.constants import SCButtons, HapticPos 4 | from scc.modifiers import * 5 | from . import parser 6 | import inspect 7 | 8 | class TestModeshift(object): 9 | """ 10 | Tests various combinations of modeshift and modifiers. 11 | Most are based on stuff that was failing in past. 12 | """ 13 | 14 | def test_146_1(self): 15 | """ 16 | https://github.com/kozec/sc-controller/issues/146 17 | """ 18 | STR = "mode(LB, dpad(button(Keys.KEY_UP)), rotate(3.8, sens(2.0, 2.0, ball(0.552, mouse()))))" 19 | a = parser.from_json_data({ 20 | "action": "mouse()", 21 | "ball": [ 0.552 ], 22 | "rotate": 3.8, 23 | "sensitivity": [2.0, 2.0, 1.0], 24 | "modes": { 25 | "LB": { 26 | "dpad": [{ 27 | "action": "button(Keys.KEY_UP)" 28 | }] 29 | } 30 | } 31 | }) 32 | 33 | assert a.to_string() == STR 34 | assert isinstance(a, ModeModifier) 35 | assert isinstance(a.default, RotateInputModifier) 36 | sens = a.default.action 37 | assert isinstance(sens, SensitivityModifier) 38 | assert tuple(sens.speeds) == (2.0, 2.0, 1.0) 39 | ball = sens.action 40 | assert isinstance(ball, BallModifier) 41 | assert ball.friction == 0.552 42 | 43 | 44 | def test_146_2(self): 45 | """ 46 | https://github.com/kozec/sc-controller/issues/146 47 | """ 48 | STR = "mode(LGRIP, ball(XY(mouse(Rels.REL_HWHEEL), mouse(Rels.REL_WHEEL))), rotate(3.8, sens(2.0, 2.0, mouse())))" 49 | a = parser.from_json_data({ 50 | "action": "mouse()", 51 | "rotate": 3.8, 52 | "sensitivity": [2.0, 2.0, 1.0], 53 | "modes": { 54 | "LGRIP": { 55 | "X": { "action": "mouse(Rels.REL_HWHEEL)" }, 56 | "Y": { "action": "mouse(Rels.REL_WHEEL)" }, 57 | "ball": [] 58 | } 59 | }, 60 | }) 61 | 62 | assert a.to_string() == STR 63 | assert isinstance(a, ModeModifier) 64 | assert isinstance(a.default, RotateInputModifier) 65 | sens = a.default.action 66 | assert isinstance(sens, SensitivityModifier) 67 | assert tuple(sens.speeds) == (2.0, 2.0, 1.0) 68 | lgrip = a.mods[SCButtons.LGRIP] 69 | assert isinstance(lgrip, BallModifier) 70 | assert isinstance(lgrip.action, XYAction) 71 | -------------------------------------------------------------------------------- /tests/test_setup.py: -------------------------------------------------------------------------------- 1 | import scc 2 | import pkgutil 3 | 4 | class TestSetup(object): 5 | """ 6 | Tests if SCC should be installable. 7 | """ 8 | 9 | def test_packages(self): 10 | """ 11 | Tests if every known Action is documentated in docs/actions.md 12 | """ 13 | try: 14 | import gi 15 | gi.require_version('Gtk', '3.0') 16 | gi.require_version('GdkX11', '3.0') 17 | gi.require_version('Rsvg', '2.0') 18 | except ImportError: 19 | pass 20 | 21 | from setup import packages 22 | for importer, modname, ispkg in pkgutil.walk_packages(path=scc.__path__, prefix="scc.", onerror=lambda x: None): 23 | if ispkg: 24 | assert modname in packages, "Package '%s' is not being installed by setup.py" % (modname,) 25 | -------------------------------------------------------------------------------- /tests/test_strings/__init__.py: -------------------------------------------------------------------------------- 1 | from scc.parser import ActionParser 2 | from scc.actions import Action 3 | import sys 4 | 5 | parser = ActionParser() 6 | 7 | def _parses_as(a_str, action): 8 | """ 9 | Tests if action parsed from string equals specified action. 10 | 11 | Done by parsing string to Action and comparing it using _same_action() 12 | """ 13 | parsed = parser.restart(a_str).parse() 14 | assert _same_action(parsed, action) 15 | return True 16 | 17 | 18 | def _same_action(a1, a2): 19 | """ 20 | Tests if two actions are the same. 21 | Done by comparing .parameters list and .to_string() output. 22 | """ 23 | assert len(a1.parameters) == len(a2.parameters) 24 | for i in xrange(0, len(a1.parameters)): 25 | if isinstance(a1.parameters[i], Action): 26 | assert isinstance(a2.parameters[i], Action), "Parameter missmatch" 27 | assert _same_action(a1.parameters[i], a2.parameters[i]) 28 | else: 29 | assert a1.parameters[i] == a2.parameters[i], "Parameter missmatch" 30 | assert a1.to_string() == a2.to_string() 31 | return True 32 | -------------------------------------------------------------------------------- /tests/test_strings/test_modifiers.py: -------------------------------------------------------------------------------- 1 | from scc.actions import Action, ButtonAction, AxisAction, MouseAction 2 | from scc.constants import SCButtons, STICK, HapticPos 3 | from scc.uinput import Keys, Axes, Rels 4 | from scc.modifiers import * 5 | from . import _parses_as, parser 6 | import inspect 7 | 8 | class TestModifiers(object): 9 | 10 | # TODO: Much more tests 11 | # TODO: test_tests 12 | 13 | def test_ball(self): 14 | """ 15 | Tests if BallModifier can be converted from string 16 | """ 17 | # All options 18 | assert _parses_as( 19 | "ball(15, 40, 15, 0.1, 3265, 4, axis(ABS_X))", 20 | BallModifier(15, 40, 15, 0.1, 3265, 4, AxisAction(Axes.ABS_X)) 21 | ) 22 | -------------------------------------------------------------------------------- /tests/test_vdf.py: -------------------------------------------------------------------------------- 1 | from scc.lib.vdf import parse_vdf 2 | from scc.foreign.vdf import VDFProfile 3 | from cStringIO import StringIO 4 | import os, pytest 5 | 6 | class TestVDF(object): 7 | """ Tests VDF parser """ 8 | 9 | def test_parsing(self): 10 | """ Tests if VDF parser parses VDF """ 11 | sio = StringIO(""" 12 | "data" 13 | { 14 | "version" "3" 15 | "more data" { 16 | "version" "7" 17 | } 18 | } 19 | """) 20 | parsed = parse_vdf(sio) 21 | assert type(parsed["data"]) == dict 22 | assert parsed["data"]["version"] == "3" 23 | assert parsed["data"]["more data"]["version"] == "7" 24 | 25 | 26 | def test_dict_without_key(self): 27 | """ 28 | Tests if VDF parser throws exception when there is dict with key missing 29 | """ 30 | sio = StringIO(""" 31 | "data" 32 | { 33 | "version" "3" 34 | { 35 | "version" "7" 36 | } 37 | } 38 | """) 39 | with pytest.raises(ValueError) as excinfo: 40 | parsed = parse_vdf(sio) 41 | 42 | 43 | def test_unclosed_bracket(self): 44 | """ 45 | Tests if VDF parser throws exception when there is unclosed { 46 | """ 47 | sio = StringIO(""" 48 | "data" 49 | { 50 | "version" "3" 51 | "more data" { 52 | "version" "7" 53 | } 54 | """) 55 | with pytest.raises(ValueError) as excinfo: 56 | parsed = parse_vdf(sio) 57 | 58 | 59 | def test_too_many_brackets(self): 60 | """ 61 | Tests if VDF parser throws exception when there is } wihtout matching { 62 | """ 63 | sio = StringIO(""" 64 | "data" 65 | { 66 | "version" "3" 67 | "more data" { 68 | "version" "7" 69 | } 70 | } 71 | } 72 | """) 73 | with pytest.raises(ValueError) as excinfo: 74 | parsed = parse_vdf(sio) 75 | 76 | 77 | def test_import(self): 78 | """ 79 | Tests if every *.vdf file in tests/vdfs can be imported. 80 | """ 81 | path = "tests/vdfs" 82 | for f in os.listdir(path): 83 | filename = os.path.join(path, f) 84 | print "Testing import of '%s'" % (filename,) 85 | VDFProfile().load(filename) 86 | -------------------------------------------------------------------------------- /update-wiki.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import sys, os, subprocess 3 | 4 | def try_run(cmd): 5 | if os.system(cmd) != 0: 6 | sys.exit(1) 7 | 8 | 9 | def merge(f1, f2, from_, to): 10 | """ 11 | Merges lines from line containting 'from_' to line containing 'to' 12 | from f1 to f2 13 | """ 14 | lines1, inside = [], False 15 | for line in open(f1, "r").readlines(): 16 | if from_ in line.strip("\r\n\t "): 17 | inside = True 18 | elif to in line.strip("\r\n\t "): 19 | inside = False 20 | if inside: 21 | lines1.append(line) 22 | 23 | lines2, inside = [], False 24 | for line in open(f2, "r").readlines(): 25 | if from_ in line.strip("\r\n\t "): 26 | inside = True 27 | lines2 += lines1 28 | elif to in line.strip("\r\n\t "): 29 | inside = False 30 | elif not inside: 31 | lines2.append(line) 32 | 33 | open(f2, "w").write("".join(lines2)) 34 | 35 | 36 | def main(): 37 | 38 | if not os.path.exists("sc-controller.wiki/.git"): 39 | try_run("git clone 'https://github.com/kozec/sc-controller.wiki.git'") 40 | 41 | os.chdir("sc-controller.wiki") 42 | try_run("git pull") 43 | try_run("git reset master") 44 | 45 | merge( 46 | '../docs/actions.md', 47 | 'Custom-Action-Examples-and-Explanations.md', 48 | '# ', 49 | '# ' 50 | ) 51 | 52 | try_run("git commit -a -m \"Updated wiki from docs\"") 53 | 54 | 55 | if __name__ == "__main__": 56 | main() --------------------------------------------------------------------------------