├── .gitignore ├── ApexCtl.cabal ├── apexctl-profile ├── apexctl-resethub ├── config ├── 90-apex.hwdb ├── 90-apexctl.rules └── Xmodmap ├── keys.txt ├── license.txt ├── makefile ├── readme.md ├── scancodes.txt └── src └── Main.hs /.gitignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | *~ 3 | apexctl 4 | -------------------------------------------------------------------------------- /ApexCtl.cabal: -------------------------------------------------------------------------------- 1 | name: ApexCtl 2 | version: 0.0.2 3 | cabal-version: -any 4 | build-type: Simple 5 | license: AllRightsReserved 6 | build-depends: base -any, 7 | binary -any, 8 | bytestring -any, 9 | cmdargs -any, 10 | vector -any, 11 | usb >= 1.3.0.2 12 | description: . 13 | author: Audrius Šaikūnas 14 | executable: apexctl 15 | main-is: Main.hs 16 | buildable: True 17 | hs-source-dirs: src 18 | ghc-options: -threaded 19 | -------------------------------------------------------------------------------- /apexctl-profile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #profile application 4 | #apexctl colors \ 5 | # -s bb33bb:6 \ 6 | # -n ee1111:8 \ 7 | # -e 33aa33:6 \ 8 | # -w 88ee88:7 \ 9 | # -l aa7777:6 10 | #profile application 11 | sudo apexctl colors \ 12 | -s bb33bb:8 \ 13 | -n ee1111:8 \ 14 | -e 33aa33:8 \ 15 | -w 88ee88:8 \ 16 | -l aa7777:8 17 | #profile application 18 | #apexctl colors \ 19 | # -s ee2222:8 \ 20 | # -n ffffff:1 \ 21 | # -e 33aa33:6 \ 22 | # -w 88ee88:7 \ 23 | # -l aa7777:6 24 | -------------------------------------------------------------------------------- /apexctl-resethub: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #help 4 | if [ $# -eq 0 ] || [[ $1 =~ "-h" ]];then 5 | echo -e "apexct-resethub :: A script for resetting usb hubs" 6 | echo -e "Useful in the case that you mess something up with your keyboard or something. You must run it as root." 7 | echo 8 | echo -e "Usage:" 9 | echo -e "apexct-resethub " 10 | echo -e "\tReset the given hub" 11 | echo -e "\tExample: apexctl-resethub" 12 | echo -e "apexct-resethub < --all | -a >" 13 | echo -e "\tReset every usb hub on the machine" 14 | echo -e "apexct-resethub [ --help | -h ]" 15 | echo -e "\tShow this help" 16 | exit 0 17 | fi 18 | 19 | #root check 20 | if [[ `whoami` != 'root' ]]; then 21 | echo -e "You must run this as root" 22 | exit 1 23 | fi 24 | 25 | #get list of hubs to reset 26 | if [[ $1 =~ "-a" ]]; then 27 | echo -e "Resetting all hubs" 28 | exit 0 29 | list=$(find /sys/bus/pci/drivers/ \ 30 | -wholename *hci*/*:*:* -printf "%f\n") 31 | else 32 | list=$(find /sys/bus/pci/drivers/ \ 33 | -wholename *hci*/$1 -printf "%f") 34 | #check that hub exists 35 | echo -e "Resetting hub $1" 36 | fi 37 | 38 | #reset the hubs 39 | for hub in $list; do 40 | echo -e "Unbinding $hub" 41 | echo -n $hub > /sys/bus/pci/drivers/xhci_hcd/unbind 42 | echo -e "Binding $hub" 43 | echo -n $hub > /sys/bus/pci/drivers/xhci_hcd/bind 44 | done 45 | -------------------------------------------------------------------------------- /config/90-apex.hwdb: -------------------------------------------------------------------------------- 1 | # 1) Place this to /etc/udev/hwdb.d/90-apex.hwdb 2 | # 2) sudo udevadm hwdb --update 3 | # 3) sudo udevadm control --reload 4 | 5 | keyboard:usb:v1038p120[02]* 6 | KEYBOARD_KEY_0700a8=prog1 7 | KEYBOARD_KEY_0700a9=prog2 8 | KEYBOARD_KEY_0700aa=prog3 9 | KEYBOARD_KEY_0700ab=prog4 10 | KEYBOARD_KEY_0700e8=config 11 | KEYBOARD_KEY_0700e9=homepage 12 | KEYBOARD_KEY_0700ea=refresh 13 | KEYBOARD_KEY_0700eb=exit 14 | KEYBOARD_KEY_0700ec=move 15 | KEYBOARD_KEY_0700ed=edit 16 | KEYBOARD_KEY_0700ee=scrollup 17 | KEYBOARD_KEY_0700ef=scrolldown 18 | KEYBOARD_KEY_0700f0=kpleftparen 19 | KEYBOARD_KEY_0700f1=kprightparen 20 | KEYBOARD_KEY_0700f4=f13 21 | KEYBOARD_KEY_0700f5=f14 22 | KEYBOARD_KEY_0700f6=f15 23 | KEYBOARD_KEY_0700f7=f16 24 | KEYBOARD_KEY_0700f8=f17 25 | KEYBOARD_KEY_0700f9=f18 26 | KEYBOARD_KEY_0700fa=f19 27 | KEYBOARD_KEY_0700fb=f20 28 | KEYBOARD_KEY_0700fc=f21 29 | KEYBOARD_KEY_0700fd=f22 30 | KEYBOARD_KEY_0700fe=f23 31 | KEYBOARD_KEY_0700ff=f24 32 | KEYBOARD_KEY_0c00e2=mute 33 | KEYBOARD_KEY_0c00b6=previoussong 34 | KEYBOARD_KEY_0c00b5=nextsong 35 | KEYBOARD_KEY_0c00cd=playpause 36 | 37 | -------------------------------------------------------------------------------- /config/90-apexctl.rules: -------------------------------------------------------------------------------- 1 | # 1) Place this to /etc/udev/rules.d/90-apexctl.rules 2 | 3 | ACTION=="add", ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1200", \ 4 | RUN+="/usr/local/sbin/apexctl" 5 | ACTION=="add", ATTRS{idVendor}=="1038", ATTRS{idProduct}=="1202", \ 6 | RUN+="/usr/local/sbin/apexctl" 7 | 8 | -------------------------------------------------------------------------------- /config/Xmodmap: -------------------------------------------------------------------------------- 1 | 2 | !! begin ApexCtl 3 | 4 | ! MX keys 5 | keycode 179 = F25 6 | keycode 180 = F26 7 | keycode 181 = F27 8 | keycode 182 = F28 9 | keycode 183 = F29 10 | keycode 184 = F30 11 | keycode 185 = F31 12 | keycode 186 = F32 13 | keycode 187 = F33 14 | keycode 188 = F34 15 | 16 | ! M keys 17 | keycode 191 = F13 18 | keycode 192 = F14 19 | keycode 193 = F15 20 | keycode 194 = F16 21 | keycode 195 = F17 22 | keycode 196 = F18 23 | keycode 197 = F19 24 | keycode 198 = F20 25 | keycode 199 = F21 26 | keycode 200 = F22 27 | keycode 201 = F23 28 | keycode 202 = F24 29 | 30 | ! back/forward keys 31 | !keycode 000 = Back 32 | !keycode 000 = Forward 33 | 34 | !! end ApexCtl 35 | -------------------------------------------------------------------------------- /keys.txt: -------------------------------------------------------------------------------- 1 | # The map of the key codes and mappings 2 | 3 | ## Apex 4 | 5 | M1 144 = Find 6 | M2 185 = F31 7 | M3 186 = F32 8 | M4 184 = F30 9 | M5 xxx = Sleep 10 | M6 xxx = Lock 11 | M7 181 = F27 12 | M8 148 = XF86Calculator 13 | M9 248 = NoSymbol 14 | M10 248 = NoSymbol 15 | M11 248 = NoSymbol 16 | M12 248 = NoSymbol 17 | 18 | MX1 172 = XF86AudioPlay 19 | MX2 174 = XF86AudioStop 20 | MX3 173 = XF86AudioPrev 21 | MX4 171 = XF86AudioNext 22 | MX5 169 = XF86Eject 23 | MX6 123 = XF86AudioRaiseVolume 24 | MX7 122 = XF86AudioLowerVolume 25 | MX8 121 = XF86AudioMute 26 | MX9 158 = XF86WWW 27 | MX10 166 = XF86Back 28 | 29 | L1 248 = NoSymbol 30 | L2 248 = NoSymbol 31 | L3 248 = NoSymbol 32 | L4 248 = NoSymbol 33 | 34 | V+ 123 = XF86AudioRaiseVolume 35 | V- 122 = XF86AudioLowerVolume 36 | Vmute 121 = XF86AudioMute 37 | Mprev 173 = XF86AudioPrev 38 | Mnext 171 = XF86AudioNext 39 | Mpaus 172 = XF86AudioPlay 40 | 41 | 42 | ## Apex Raw 43 | 44 | MX1 172 = Play/Pause 45 | MX2 174 = 46 | MX3 173 = AudioPrev 47 | MX4 171 = AudioNext 48 | MX5 169 = 49 | 50 | M1 144 = 51 | M2 185 = 52 | M3 186 = 53 | M4 184 = 54 | 55 | M5 150 = 56 | M6 160 = 57 | M7 181 = 58 | M8 148 = 59 | 60 | M9 248 = NoSymbol 61 | M10 248 = NoSymbol 62 | M11 248 = NoSymbol 63 | M12 248 = NoSymbol 64 | 65 | L1 248 = NoSymbol 66 | L2 248 = NoSymbol -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # globals 2 | default: build 3 | freshen: clean build 4 | clean: 5 | rm -rf dist apexctl 6 | 7 | # vars 8 | SHELL := /bin/bash 9 | INSTALL_ROOT ?= / 10 | 11 | # lists 12 | hs_files = src/Main.hs 13 | config_files = \ 14 | config/90-apexctl.rules \ 15 | config/90-apex.hwdb \ 16 | config/Xmodmap 17 | binary = dist/build/apexctl/apexctl 18 | binary_install_dir = /usr/local/bin 19 | 20 | # build defs 21 | $(binary): $(hs_files) 22 | cabal configure 23 | cabal build 24 | apexctl: $(binary) 25 | cp $(binary) $@ 26 | 27 | # checks 28 | check-build: 29 | [ -f apexctl ] 30 | [ -f $(binary) ] 31 | check-root: 32 | [[ `whoami` = "root" ]] 33 | check-installed: 34 | [ -f $(INSTALL_ROOT)$(binary_install_dir)/apexctl ] 35 | [ -f $(INSTALL_ROOT)/etc/udev/hwdb.d/90-apex.hwdb ] 36 | [ -f $(INSTALL_ROOT)/etc/udev/rules.d/90-apexctl.rules ] 37 | [ -f $(INSTALL_ROOT)/etc/X11/Xmodmap.bak ] 38 | echo -en "ApexCtl is fully installed\n" 39 | 40 | # basic commands 41 | build: $(binary) apexctl 42 | 43 | enable: check-root 44 | # enable keys 45 | ./apexctl 46 | # reload udev rules 47 | udevadm hwdb --update 48 | udevadm control --reload 49 | # apply xmodmap 50 | xmodmap config/Xmodmap 51 | 52 | # global installation 53 | install: check-build 54 | # make dirs 55 | mkdir -p $(INSTALL_ROOT)/etc/udev/hwdb.d 56 | mkdir -p $(INSTALL_ROOT)/etc/udev/rules.d 57 | # install binary 58 | install -m 755 apexctl $(INSTALL_ROOT)$(binary_install_dir)/apexctl 59 | # install udev rules 60 | install config/90-apex.hwdb $(INSTALL_ROOT)/etc/udev/hwdb.d/ 61 | install config/90-apexctl.rules $(INSTALL_ROOT)/etc/udev/rules.d/ 62 | # install Xmodmap globally 63 | [[ -f $(INSTALL_ROOT)/etc/X11/Xmodmap ]] && \ 64 | cp $(INSTALL_ROOT)/etc/X11/Xmodmap $(INSTALL_ROOT)/etc/X11/Xmodmap.bak || : 65 | cat config/Xmodmap >> $(INSTALL_ROOT)/etc/X11/Xmodmap 66 | 67 | uninstall: 68 | # remove binary, scripts, and udev rules 69 | rm -f \ 70 | $(INSTALL_ROOT)$(binary_install_dir)/apexctl \ 71 | $(INSTALL_ROOT)/etc/udev/hwdb.d/90-apex.hwdb \ 72 | $(INSTALL_ROOT)/etc/udev/rules.d/90-apexctl.rules 73 | # unapply Xmodmap using backup made during install 74 | [[ -f $(INSTALL_ROOT)/etc/X11/Xmodmap.bak ]] && \ 75 | cp $(INSTALL_ROOT)/etc/X11/Xmodmap $(INSTALL_ROOT)/etc/X11/Xmodmap.bak2 && \ 76 | mv $(INSTALL_ROOT)/etc/X11/Xmodmap.bak $(INSTALL_ROOT)/etc/X11/Xmodmap || : 77 | # reload udev rules 78 | udevadm hwdb --update 79 | udevadm control --reload 80 | 81 | reinstall: \ 82 | check-build \ 83 | uninstall install 84 | 85 | # local installation 86 | local-install: check-build 87 | # make dirs 88 | mkdir -p ~/.local/bin 89 | # install binary 90 | install -m 755 apexctl ~/.local/bin/apexctl 91 | # install Xmodmap locally 92 | [[ -f ~/.Xmodmap ]] && \ 93 | cp ~/.Xmodmap ~/.Xmodmap.bak || : 94 | cat config/Xmodmap >> /etc/X11/Xmodmap 95 | install config/Xmodmap ~/.Xmodmap 96 | 97 | local-uninstall: 98 | # remove binary, scripts, and Xmodmap 99 | rm -f ~/.local/bin/apexctl ~/.Xmodmap 100 | 101 | local-reinstall: check-build \ 102 | local-uninstall local-install 103 | 104 | test: 105 | echo $(INSTALL_ROOT) 106 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ApexCtl # 2 | 3 | ### Steelseries Apex and Apex [Raw] support for Linux ### 4 | 5 | ========================================================= 6 | 7 | ### Contributors ### 8 | tuxmark5 : [github.com/tuxmark5](http://github.com/tuxmark5) 9 | Zimmux : [github.com/Zimmux](http://github.com/Zimmux) 10 | kiwistrongis : [github.com/kiwistrongis](http://github.com/kiwistrongis) 11 | 12 | ## Dependencies ## 13 | - udev >= 206 14 | - git, pkg-config 15 | - ghc 16 | - cabal, cabal-install 17 | - libusb 1.0.0 and headers 18 | - haskell usb (>= 1.3.0.2) and cmdargs libraries 19 | 20 | #### Debian: #### 21 | ```bash 22 | sudo aptitude install ghc libusb-1.0-0-dev cabal-install git pkg-config 23 | cabal update 24 | cabal install usb cmdargs 25 | ``` 26 | #### Fedora: #### 27 | ```bash 28 | sudo yum -y install ghc libusb libusb-devel cabal-install git pkgconfig 29 | cabal update 30 | cabal install usb cmdargs 31 | ``` 32 | #### Arch: #### 33 | ```bash 34 | sudo pacman -Syu ghc libusb cabal-install git pkg-config 35 | cabal update 36 | cabal install usb cmdargs 37 | ``` 38 | #### Other: #### 39 | Install GHC, libusb 1.0.0 headers, cabal. Then: 40 | ```bash 41 | cabal update 42 | cabal install usb cmdargs 43 | ``` 44 | 45 | ## Installation ## 46 | 47 | #### Global Install #### 48 | ```bash 49 | make && sudo make install 50 | ``` 51 | 52 | #### Local User Install #### 53 | You will have to run `~/.local/bin/apexctl` manually (as root) to enable the extra keys after every boot. 54 | ```bash 55 | make && make local-install 56 | ``` 57 | 58 | ## Usage ## 59 | 60 | #### Help #### 61 | ```bash 62 | apexctl --help 63 | ``` 64 | 65 | #### Enable Macro Keys #### 66 | ```bash 67 | sudo apexctl 68 | ``` 69 | 70 | #### Apex :: Set Colour Zones #### 71 | ```bash 72 | sudo apexctl colors \ 73 | -s RRGGBB:A \ 74 | -n RRGGBB:A \ 75 | -e RRGGBB:A \ 76 | -w RRGGBB:A \ 77 | -l RRGGBB:A 78 | ``` 79 | ```bash 80 | sudo apexctl colors \ 81 | --south=RRGGBB:A \ 82 | --north=RRGGBB:A \ 83 | --east=RRGGBB:A \ 84 | --west=RRGGBB:A \ 85 | --logo=RRGGBB:A 86 | ``` 87 | Where RR, GG, and BB are in the domain of [00,ff], and A is in the domain of [1,8]. 88 | 89 | Example: 90 | ```bash 91 | sudo apexctl colors \ 92 | -s bb33bb:6 \ 93 | -n ee1111:6 \ 94 | -e 33aa33:6 \ 95 | -w 88ee88:6 \ 96 | -l aa7777:6 97 | ``` 98 | 99 | #### Apex [Raw] :: Set Brightness #### 100 | ```bash 101 | sudo apexctl br (1..8) 102 | ``` 103 | Example: 104 | ```bash 105 | sudo apexctl br 6 106 | ``` 107 | 108 | ## Notes ## 109 | Some distros ( Fedora 19, for example ) do not have `/usr/local/sbin` in their `secure_path`. This means you cannot just run `sudo apexctl`, you will have to run `sudo -E apexctl` or `sudo /usr/local/sbin/apexctl`. To fix this, there are two options. 110 | 111 | Find the line that sets secure_path in `/etc/sudoers` and change it to the following ( or anything that includes `/usr/local/sbin` ): 112 | ``` 113 | Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin 114 | ``` 115 | 116 | Alternatively, before installation, change this line in the makefile: 117 | ``` 118 | binary_install_dir = /usr/local/sbin 119 | ``` 120 | to: 121 | ``` 122 | binary_install_dir = /usr/sbin 123 | ``` 124 | -------------------------------------------------------------------------------- /scancodes.txt: -------------------------------------------------------------------------------- 1 | M1 = 700f4 2 | M2 = 700f5 3 | M3 = 700f6 4 | M4 = 700f7 5 | M5 = 700f8 6 | M6 = 700f9 7 | M7 = 700fa 8 | M8 = 700fb 9 | M9 = 700fc 10 | M10 = 700fd 11 | M11 = 700fe 12 | M12 = 700ff 13 | 14 | MX1 = 700e8 15 | MX2 = 700e9 16 | MX3 = 700ea 17 | MX4 = 700eb 18 | MX5 = 700ec 19 | MX6 = 700ed 20 | MX7 = 700ee 21 | MX8 = 700ef 22 | MX9 = 700f0 23 | MX10 = 700f1 24 | 25 | L1 = 700a8 26 | L2 = 700a9 27 | L3 = 700aa 28 | L4 = 700ab 29 | 30 | V+ = c00e9 31 | V- = c00e9 32 | Vmute = c00e2 33 | Mprev = c00b6 34 | Mnext = c00b5 35 | Mpaus = c00cd -------------------------------------------------------------------------------- /src/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveDataTypeable #-} 2 | 3 | import Control.Concurrent 4 | import Control.Exception 5 | import Control.Monad 6 | import Data.Binary 7 | import Data.ByteString (ByteString) 8 | import qualified Data.ByteString as BS 9 | import qualified Data.ByteString.Lazy as BL 10 | import Data.Char (digitToInt) 11 | import qualified Data.Vector as V 12 | import Data.Word (Word16) 13 | import System.Console.CmdArgs.Implicit 14 | import System.USB 15 | import Text.Printf 16 | 17 | {- ########################################################################################## -} 18 | 19 | _SET_REPORT :: Request 20 | _SET_REPORT = 0x09 21 | 22 | {- ########################################################################################## -} 23 | 24 | data ApexCommand 25 | = CmdEnableExtraKeys 26 | -- 0..8 27 | | CmdSetBrightness Word8 28 | -- 0 -> 125 Hz 29 | -- 1 -> 250 Hz 30 | -- 2 -> 500 Hz 31 | -- 3 -> 1000 Hz 32 | | CmdSetPollFrequency Word8 33 | | CmdSetColorProfile 34 | { colorSouth :: Color 35 | , colorEast :: Color 36 | , colorNorth :: Color 37 | , colorWest :: Color 38 | , colorLogo :: Color 39 | } deriving (Show) 40 | 41 | instance Binary ApexCommand where 42 | get = undefined 43 | 44 | put (CmdEnableExtraKeys) = do 45 | put32 [0x02, 0x00, 0x02] 46 | 47 | put (CmdSetBrightness a) = do 48 | put32 [0x05, 0x01, a] 49 | 50 | put c@(CmdSetColorProfile {}) = do 51 | put (0x07 :: Word8) 52 | put (0x00 :: Word8) 53 | put $ colorSouth c 54 | put $ colorEast c 55 | put $ colorNorth c 56 | put $ colorWest c 57 | put $ colorLogo c 58 | replicateM_ 10 $ put (0x00 :: Word8) 59 | 60 | put (CmdSetPollFrequency rate) = do 61 | put32 [0x04, 0x00, rate] 62 | 63 | put32 :: [Word8] -> Put 64 | put32 words = do 65 | forM_ words $ put 66 | replicateM_ (32 - length words) $ put (0 :: Word8) 67 | 68 | {- ########################################################################################## -} 69 | 70 | data Color = Color Word8 Word8 Word8 Word8 71 | deriving (Data, Show, Typeable) 72 | 73 | instance Binary Color where 74 | get = undefined 75 | 76 | put (Color r g b a) = do 77 | put r 78 | put g 79 | put b 80 | put a 81 | 82 | instance Read Color where 83 | readsPrec _ = \str -> case str of 84 | [r0, r1, g0, g1, b0, b1, ':', a0] -> 85 | [(Color (toComp2 r0 r1) (toComp2 g0 g1) (toComp2 b0 b1) (toAlpha a0), [])] 86 | [r0, r1, g0, g1, b0, b1] -> 87 | [(Color (toComp2 r0 r1) (toComp2 g0 g1) (toComp2 b0 b1) 8, [])] 88 | [r0, g0, b0, ':', a0] -> 89 | [(Color (toComp1 r0) (toComp1 g0) (toComp1 b0) (toAlpha a0), [])] 90 | [r0, g0, b0] -> 91 | [(Color (toComp1 r0) (toComp1 g0) (toComp1 b0) 8, [])] 92 | 93 | toComp1 :: Char -> Word8 94 | toComp1 a = fromIntegral $ (digitToInt a) * 0x10 + 0xF 95 | 96 | toComp2 :: Char -> Char -> Word8 97 | toComp2 a b = fromIntegral $ (digitToInt a) * 0x10 + (digitToInt b) 98 | 99 | toAlpha :: Char -> Word8 100 | toAlpha alpha = case digitToInt alpha of 101 | a | a >= 0 && a <= 8 102 | -> fromIntegral a 103 | otherwise 104 | -> error "alpha must be between 0 and 8" 105 | 106 | toBrightness :: Int -> Word8 107 | toBrightness brightness = case brightness of 108 | b | b >= 1 && b <= 8 109 | -> fromIntegral b 110 | otherwise 111 | -> error "brightness must be between 1 and 8" 112 | 113 | toFrequency :: Int -> Word8 114 | toFrequency 125 = 0 115 | toFrequency 250 = 1 116 | toFrequency 500 = 2 117 | toFrequency 1000 = 3 118 | toFrequency _ = error "frequency must be 125, 250, 500 or 1000" 119 | 120 | {- ########################################################################################## -} 121 | 122 | colorCyan = Color 0x00 0xFF 0xFF 0x08 123 | colorGreen = Color 0x00 0xFF 0x00 0x08 124 | colorPurple = Color 0xFF 0x00 0xFF 0x08 125 | colorRed = Color 0xFF 0x00 0x00 0x08 126 | colorWhite = Color 0xFF 0xFF 0xFF 0x08 127 | 128 | cp0 = CmdSetColorProfile colorRed colorRed colorRed colorRed colorRed 129 | cp1 = CmdSetColorProfile colorCyan colorGreen colorPurple colorRed colorWhite 130 | cp2 c = CmdSetColorProfile c c c c c 131 | 132 | {- ########################################################################################## -} 133 | 134 | data ApexMode 135 | = ModeEnableExtraKeys 136 | | ModeSetBrightness 137 | { argBrightness :: Int } 138 | | ModeSetColorProfile 139 | { argSouth :: String 140 | , argEast :: String 141 | , argNorth :: String 142 | , argWest :: String 143 | , argLogo :: String 144 | } 145 | | ModeSetPollFrequency 146 | { argFrequency :: Int } 147 | deriving (Data, Show, Typeable) 148 | 149 | apexArgs :: ApexMode 150 | apexArgs = modes 151 | [ modeEnableExtraKeys 152 | , modeSetBrightness 153 | , modeSetColorProfile 154 | , modeSetPollFrequency 155 | ] 156 | &= program "apexctl" 157 | &= summary "An utility for managing Apex/Apex [RAW] keyboard settings" 158 | 159 | modeEnableExtraKeys :: ApexMode 160 | modeEnableExtraKeys = ModeEnableExtraKeys 161 | &= auto 162 | &= help "Enable macro keys" 163 | &= name "init" 164 | 165 | modeSetBrightness :: ApexMode 166 | modeSetBrightness = ModeSetBrightness 167 | { argBrightness = 8 &= argPos 0 &= typ "BRIGHTNESS" 168 | } 169 | &= help "Set brightness level (1..8)" 170 | &= name "br" 171 | 172 | colorAnnot :: String -> String -> String -> String 173 | {-# INLINE colorAnnot #-} 174 | colorAnnot name0 name1 desc = "FF0000:8" 175 | &= explicit 176 | &= name name0 177 | &= name name1 178 | &= help desc 179 | &= typ "COLOR" 180 | 181 | modeSetColorProfile :: ApexMode 182 | modeSetColorProfile = ModeSetColorProfile 183 | { argSouth = colorAnnot "s" "south" "Color for South zone" 184 | , argEast = colorAnnot "e" "east" "Color for East zone" 185 | , argNorth = colorAnnot "n" "north" "Color for North zone" 186 | , argWest = colorAnnot "w" "west" "Color for West zone" 187 | , argLogo = colorAnnot "l" "logo" "Color for Logo zone" 188 | } 189 | &= help (unlines 190 | [ "Change color profile." 191 | , "Colors should be specified for all color zones." 192 | , "Otherwise unspecified colors will default to red" ]) 193 | &= name "colors" 194 | 195 | modeSetPollFrequency :: ApexMode 196 | modeSetPollFrequency = ModeSetPollFrequency 197 | { argFrequency = 1000 198 | &= argPos 0 199 | &= typ "FREQUENCY" 200 | } 201 | &= help "Set polling frequency (125, 250, 500 or 1000)" 202 | &= name "freq" 203 | 204 | modeToCommand :: ApexMode -> ApexCommand 205 | 206 | modeToCommand m@ModeEnableExtraKeys {} = CmdEnableExtraKeys 207 | 208 | modeToCommand m@ModeSetBrightness {} = CmdSetBrightness (toBrightness $ argBrightness m) 209 | 210 | modeToCommand m@ModeSetColorProfile {} = CmdSetColorProfile 211 | { colorSouth = read $ argSouth m 212 | , colorEast = read $ argEast m 213 | , colorNorth = read $ argNorth m 214 | , colorWest = read $ argWest m 215 | , colorLogo = read $ argLogo m 216 | } 217 | 218 | modeToCommand m@ModeSetPollFrequency {} = CmdSetPollFrequency (toFrequency $ argFrequency m) 219 | 220 | {- ########################################################################################## -} 221 | 222 | controlSetup :: ControlSetup 223 | controlSetup = ControlSetup 224 | { controlSetupRequestType = Class 225 | , controlSetupRecipient = ToInterface 226 | , controlSetupRequest = _SET_REPORT 227 | , controlSetupValue = 0x0200 228 | , controlSetupIndex = 0 229 | } 230 | 231 | apexCtl :: DeviceHandle -> ByteString -> IO () 232 | apexCtl devHndl d = do 233 | putStrLn "WRITING SET_REPORT" 234 | writeControlExact devHndl controlSetup d noTimeout 235 | 236 | apexCommand :: Binary b => DeviceHandle -> b -> IO () 237 | apexCommand devHndl b = do 238 | apexCtl devHndl $ toStrict1 $ encode b 239 | threadDelay 10000 240 | 241 | apexRet :: DeviceHandle -> IO () 242 | apexRet devHndl = do 243 | (r, s) <- readControl devHndl controlSetup 32 noTimeout 244 | putStrLn $ hex r 245 | putStrLn $ show s 246 | 247 | deviceFilter :: Word16 -> [Word16] -> Device -> IO Bool 248 | deviceFilter venId prodIds dev = do 249 | devDesc <- getDeviceDesc dev 250 | return $ deviceVendorId devDesc == venId 251 | && any ((==) $ deviceProductId devDesc) prodIds 252 | 253 | hex :: BS.ByteString -> String 254 | hex = concatMap (printf "%02x") . BS.unpack 255 | 256 | toStrict1 :: BL.ByteString -> BS.ByteString 257 | toStrict1 = BS.concat . BL.toChunks 258 | 259 | withDevice :: Word16 -> [Word16] -> (Device -> IO a) -> IO () 260 | withDevice venId prodIds hnd = do 261 | ctx <- newCtx 262 | setDebug ctx PrintInfo 263 | devs <- getDevices ctx 264 | devs1 <- V.filterM (deviceFilter venId prodIds) devs 265 | if V.null devs1 266 | then return $ error "Device not found" 267 | else V.mapM_ hnd devs1 268 | 269 | {- ########################################################################################## -} 270 | 271 | main :: IO () 272 | main = do 273 | args <- cmdArgs apexArgs 274 | withDevice 0x1038 [0x1200, 0x1202, 0x1208] $ \dev -> do 275 | withDeviceHandle dev $ \devHndl -> 276 | withDetachedKernelDriver devHndl 0 $ 277 | withClaimedInterface devHndl 0 $ do 278 | res <- try $ do 279 | apexCommand devHndl $ modeToCommand args 280 | case res of 281 | Left (SomeException a) -> putStrLn $ show a 282 | Right _ -> putStrLn "OK" 283 | 284 | {- ########################################################################################## -} 285 | --------------------------------------------------------------------------------