Merge branch 'keymap2'
Conflicts: common/keyboard.c
This commit is contained in:
commit
6c990fa9a7
620
README.md
620
README.md
@ -7,12 +7,17 @@ Source code is available here: <http://github.com/tmk/tmk_keyboard>
|
||||
|
||||
Features
|
||||
--------
|
||||
* Mouse key - Mouse control by keyboard.
|
||||
* System Control Key - Power Down, Sleep, Wake Up and USB Remote Wake up.
|
||||
* Media Control Key - Volume Down/Up, Mute, Next/Prev track, Play, Stop and etc.
|
||||
* USB NKRO - Can send 120 keys(+ 8 modifiers) simultaneously.
|
||||
* PS/2 mouse support - integrate PS/2 mouse(TrackPoint) into keyboard as composite device.
|
||||
* keyboard protocols - PS/2, ADB and old keyboard protocols.
|
||||
* Multi-layer keymap - Multiple keyboard layouts with layer switching.
|
||||
* Mouse key - Mouse control with keyboard
|
||||
* System Control Key - Power Down, Sleep, Wake Up and USB Remote Wake up
|
||||
* Media Control Key - Volume Down/Up, Mute, Next/Prev track, Play, Stop and etc
|
||||
* USB NKRO - 120 keys(+ 8 modifiers) simultaneously
|
||||
* PS/2 mouse support - PS/2 mouse(TrackPoint) as composite device
|
||||
* Keyboard protocols - PS/2, ADB, M0110, Sun and other old keyboard protocols
|
||||
* User Function - Customizable function of key with writing code
|
||||
* Macro - Very primitive at this time
|
||||
* Keyboard Tricks - Oneshot modifier and modifier with tapping feature
|
||||
* Debug Console - Messages for debug and interaction with firmware
|
||||
|
||||
|
||||
Projects
|
||||
@ -24,11 +29,17 @@ Projects
|
||||
* terminal_usb - [IBM Model M terminal keyboard(PS/2 scancode set3) to USB][GH_terminal]
|
||||
* news_usb - [Sony NEWS keyboard to USB][GH_news]
|
||||
* x68k_usb - [Sharp X68000 keyboard to USB][GH_x68k]
|
||||
* sun_usb - Sun to USB(type4, 5 and 3?)
|
||||
* usb_usb - USB to USB(experimental)
|
||||
|
||||
### keyboard
|
||||
* hhkb - [Happy Hacking Keyboard professional][GH_hhkb]
|
||||
* macway - [Compact keyboard mod][GH_macway]
|
||||
* hbkb - [Happy Buckling sprint keyboard(IBM Model M mod)][GH_hbkb]
|
||||
* hhkb - [Happy Hacking Keyboard professional][GH_hhkb]
|
||||
* macway - [Compact keyboard mod][GH_macway]
|
||||
* hbkb - [Happy Buckling sprint keyboard(IBM Model M mod)][GH_hbkb]
|
||||
* IIgs_Standard - Apple IIGS keyboard mod(by JeffreySung)
|
||||
* hid_liber - [HID liberation controller][HID_liber](by alaricljs)
|
||||
* phantom - [Phantom keyboard][PHANTOM] (by Tranquilite)
|
||||
* gh60 - [GH60 keyboard][GH60]
|
||||
|
||||
[GH_macway]: http://geekhack.org/showwiki.php?title=Island:11930
|
||||
[GH_hhkb]: http://geekhack.org/showwiki.php?title=Island:12047
|
||||
@ -40,52 +51,99 @@ Projects
|
||||
[GH_terminal]: http://geekhack.org/showwiki.php?title=Island:27272
|
||||
[GH_x68k]: http://geekhack.org/showwiki.php?title=Island:29060
|
||||
[GH_hbkb]: http://geekhack.org/showwiki.php?title=Island:29483
|
||||
[HID_liber]: http://deskthority.net/wiki/HID_Liberation_Device_-_DIY_Instructions
|
||||
[PHANTOM]: http://geekhack.org/index.php?topic=26742
|
||||
[GH60]: http://geekhack.org/index.php?topic=34959
|
||||
|
||||
|
||||
|
||||
Files & Directories
|
||||
-------------------
|
||||
### Top
|
||||
* common/ - common codes
|
||||
* protocol/ - keyboard protocol support
|
||||
* keyboard/ - keyboard projects
|
||||
* converter/ - protocol converter projects
|
||||
* doc/ - documents
|
||||
* common.mk - Makefile for common
|
||||
* protoco.mk - Makefile for protocol
|
||||
* rules.mk - Makefile for build rules
|
||||
|
||||
### Keyboard Protocols
|
||||
* pjrc/ - PJRC USB stack
|
||||
* vusb/ - Objective Development V-USB
|
||||
* iwrap/ - Bluetooth HID for Bluegiga iWRAP
|
||||
* ps2.c - PS/2 protocol
|
||||
* adb.c - Apple Desktop Bus protocol
|
||||
* m0110.c - Macintosh 128K/512K/Plus keyboard protocol
|
||||
* news.c - Sony NEWS keyboard protocol
|
||||
* x68k.c - Sharp X68000 keyboard protocol
|
||||
|
||||
|
||||
Build & Program
|
||||
---------------
|
||||
### Install Tools
|
||||
First, you need tools to build firmware and program your controller. I assume you are on Windows here.
|
||||
|
||||
1. Install [WinAVR][winavr]. This is old but works well for this purpose. `WinAVR` is a tool set to build firmware including C compiler(gcc) and make commands. You can use [CrossPack][crosspack] instead if you are on Mac.
|
||||
|
||||
2. Install [Atmel FLIP][flip]. `FLIP` is a tool to program(load) firmware into AVR controller(ATMega32u4) via DFU bootloader. ATMega32u4 has DFU bootloader by factory default. You can use [dfu-programmer][dfu-prog] instead if you are on Mac.
|
||||
|
||||
3. Install driver for DFU bootloader. At first time you start DFU bootloader on Chip 'Found New Hardware Wizard' will come up on Windows. If you install device driver properly you can find chip name like 'ATmega32U4' under 'LibUSB-Win32 Devices' tree on 'Device Manager'. If not you shall need to update its driver on 'Device Manager'. You will find the driver in `FLIP` install directory like: C:\Program Files (x86)\Atmel\Flip 3.4.5\usb\. If you use `dfu-programmer` install its driver.
|
||||
|
||||
If you use PJRC Teensy you don't need step 2 and 3, just get [Teensy loader][teensy-loader].
|
||||
|
||||
|
||||
### Download source
|
||||
You can find firmware source at github: https://github.com/tmk/tmk_keyboard
|
||||
|
||||
If you are familiar with `Git` tools you are recommended to use it.
|
||||
If not you can download zip archive from: https://github.com/tmk/tmk_keyboard/archive/master.zip
|
||||
|
||||
|
||||
### Build firmware
|
||||
To compile you need `AVR GCC`, `AVR Libc` and `GNU make`.
|
||||
You can use [WinAVR][winavr] on Windows and [CrossPack][crosspack] on Mac.
|
||||
1. Open terminal window to get access to commands. You can use `cmd` in Windows or `Terminal.app` on Mac OSX. In Windows press `Windows` key and `R` then enter `cmd` in Run command dialog showing up.
|
||||
|
||||
$ cd <project>
|
||||
$ make
|
||||
2. Move to project directory in the firmware source.
|
||||
|
||||
cd tmk_keyboard/{keyboard or converter}/<project>
|
||||
|
||||
3. Build firmware using GNU `make` command. You'll see <project>_<variant>.hex file in that directory unless something unexpected occurs in build process.
|
||||
|
||||
mkae -f Makefile.<variant> clean
|
||||
make -f Makefile.<variant>
|
||||
|
||||
|
||||
|
||||
|
||||
### Program Controller
|
||||
Sock AVR USB chip including ATmega32U4 has DFU bootloader by factory default, you can use DFU tools for this purpose. `FLIP` is a DFU tool on Windows offered by Atmel. Open source command line DFU tool `dfu-programmer` also supports AVR chips, which run on Linux, Mac OSX and even Windows. If you have a PJRC Teensy you should use `Teensy Loader`.
|
||||
|
||||
#### DFU bootloader
|
||||
To program AVR chip with DFU bootloader use `FLIP` or `dfu-programmer`.
|
||||
If you have a proper program command in `Makefile` just type this.
|
||||
|
||||
`FLIP` has two version of tool, GUI app and command line program. If you want GUI see tutorial section below.
|
||||
To use command line tool run this command. Note that you need to set PATH variable properly.
|
||||
|
||||
$ make -f Makefile.<variant> flip
|
||||
|
||||
To program with `dfu-programmer` run:
|
||||
|
||||
$ make -f Makefile.<variant> dfu
|
||||
|
||||
#### Teensy
|
||||
If you have PJRC Teensy see instruction of `Teensy Loader` at: <http://www.pjrc.com/teensy/loader.html>
|
||||
Or use this command if you have command line version of Teensy Loader installed.
|
||||
|
||||
$ make -f Makefile.<variant> teensy
|
||||
|
||||
|
||||
#### Other programmer
|
||||
You may want to use other you favorite programmer like `avrdude` with AVRISPmkII, Aruduino or USBasp. In that case you can still use make target `program` for build with configuring PROGRAM_CMD in Makefile.
|
||||
|
||||
$ make -f Makefile.<variant> program
|
||||
|
||||
|
||||
|
||||
#### FLIP GUI tutorial
|
||||
1. On menu bar click Device -> Select, then. `ATmega32u4`.
|
||||
2. On menu bar click Settings -> Communication -> USB, then click 'Open' button on 'USB Port Connection' dialog.
|
||||
At this point you'll see greyouted widgets on the app get colored and ready.
|
||||
|
||||
3. On menu bar click File -> Load HEX File, then select your firmware hex file on File Selector dialog.
|
||||
4. On 'Operations Flow' panel click 'Run' button to load the firmware binary to the chip. Note that you should keep 'Erase', 'Blank Check', 'Program' and 'Verify' check boxes selected.
|
||||
5. Re-plug USB cord or click 'Start Application' button to restart your controller.
|
||||
Done.
|
||||
|
||||
See also these instaructions if you need.
|
||||
|
||||
- <http://code.google.com/p/micropendous/wiki/LoadingFirmwareWithFLIP>
|
||||
- <http://www.atmel.com/Images/doc7769.pdf>
|
||||
|
||||
The firmware will be compiled as a file `tmk_<project>.hex`.
|
||||
|
||||
[winavr]: http://winavr.sourceforge.net/
|
||||
[crosspack]: http://www.obdev.at/products/crosspack/index.html
|
||||
|
||||
### Program Controller
|
||||
If you have a proper program command in `Makefile` just type this.
|
||||
|
||||
$ make program
|
||||
|
||||
As for `Teensy` you can use `PJRC's loader` to program hex file. <http://www.pjrc.com/teensy/loader.html>
|
||||
[flip]: http://www.atmel.com/tools/FLIP.aspx
|
||||
[dfu-prog]: http://dfu-programmer.sourceforge.net/
|
||||
[teensy-loader]:http://www.pjrc.com/teensy/loader.html
|
||||
|
||||
|
||||
|
||||
@ -106,20 +164,22 @@ Note that ***comment out*** to disable them.
|
||||
NKRO_ENABLE = yes # USB Nkey Rollover
|
||||
|
||||
### 3. Programmer
|
||||
Set proper command for your controller, bootloader and programmer.
|
||||
Optional. Set proper command for your controller, bootloader and programmer.
|
||||
|
||||
# for PJRC Teensy
|
||||
PROGRAM_CMD = teensy_loader_cli -mmcu=$(MCU) -w -v $(TARGET).hex
|
||||
|
||||
# for Atmel AT90USBKEY
|
||||
# for Atmel chip with DFU bootloader
|
||||
PROGRAM_CMD = dfu-programmer $(MCU) flash $(TARGET).hex
|
||||
|
||||
# avrdude
|
||||
# avrdude with other methods
|
||||
PROGRAM_CMD = avrdude -p $(MCU) -c avrispmkII -P USB -U flash:w:$(TARGET).hex
|
||||
PROGRAM_CMD = avrdude -p $(MCU) -c usbasp -U flash:w:$(TARGET).hex
|
||||
PROGRAM_CMD = avrdude -p $(MCU) -c arduino -P COM1 -b 57600 -U flash:w:$(TARGET).hex
|
||||
|
||||
config.h Options
|
||||
|
||||
|
||||
Config.h Options
|
||||
----------------
|
||||
### 1. USB vendor/product ID and device description
|
||||
#define VENDOR_ID 0xFEED
|
||||
@ -145,42 +205,458 @@ config.h Options
|
||||
|
||||
Keymap
|
||||
------
|
||||
Many of existent projects offer keymap framework to define your own keymap easily. The following will explain how you can define keymap using this framework.
|
||||
Instead, you can also implement your own `keymap_get_action()` to return action code for each key if you want.
|
||||
|
||||
This is keymap example for [HHKB](http://en.wikipedia.org/wiki/Happy_Hacking_Keyboard) keyboard. Keymap is defined in `keymaps[]` array.
|
||||
|
||||
static const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
|
||||
/* Layer 0: Default Layer
|
||||
* ,-----------------------------------------------------------.
|
||||
* |Esc| 1| 2| 3| 4| 5| 6| 7| 8| 9| 0| -| =| \| `|
|
||||
* |-----------------------------------------------------------|
|
||||
* |Tab | Q| W| E| R| T| Y| U| I| O| P| [| ]|Backs|
|
||||
* |-----------------------------------------------------------|
|
||||
* |Contro| A| S| D| F| G| H| J| K| L| ;| '|Enter |
|
||||
* |-----------------------------------------------------------|
|
||||
* |Shift | Z| X| C| V| B| N| M| ,| .| /|Shift |Fn1|
|
||||
* `-----------------------------------------------------------'
|
||||
* |Gui|Alt |Space |Alt |Fn2|
|
||||
* `-------------------------------------------'
|
||||
*/
|
||||
KEYMAP(ESC, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, MINS,EQL, BSLS,GRV, \
|
||||
TAB, Q, W, E, R, T, Y, U, I, O, P, LBRC,RBRC,BSPC, \
|
||||
LCTL,A, S, D, F, G, H, J, K, L, FN2, QUOT,ENT, \
|
||||
LSFT,Z, X, C, V, B, N, M, COMM,DOT, SLSH,RSFT,FN1, \
|
||||
LGUI,LALT, SPC, RALT,FN3),
|
||||
|
||||
/* Layer 1: HHKB mode (HHKB Fn)
|
||||
* ,-----------------------------------------------------------.
|
||||
* |Pwr| F1| F2| F3| F4| F5| F6| F7| F8| F9|F10|F11|F12|Ins|Del|
|
||||
* |-----------------------------------------------------------|
|
||||
* |Caps | | | | | | | |Psc|Slk|Pus|Up | |Backs|
|
||||
* |-----------------------------------------------------------|
|
||||
* |Contro|VoD|VoU|Mut| | | *| /|Hom|PgU|Lef|Rig|Enter |
|
||||
* |-----------------------------------------------------------|
|
||||
* |Shift | | | | | | +| -|End|PgD|Dow|Shift |Fn1|
|
||||
* `-----------------------------------------------------------'
|
||||
* |Gui |Alt |Space |Alt |Gui|
|
||||
* `--------------------------------------------'
|
||||
*/
|
||||
KEYMAP(PWR, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, INS, DEL, \
|
||||
CAPS,NO, NO, NO, NO, NO, NO, NO, PSCR,SLCK,PAUS,UP, NO, BSPC, \
|
||||
LCTL,VOLD,VOLU,MUTE,NO, NO, PAST,PSLS,HOME,PGUP,LEFT,RGHT,ENT, \
|
||||
LSFT,NO, NO, NO, NO, NO, PPLS,PMNS,END, PGDN,DOWN,RSFT,FN0, \
|
||||
LGUI,LALT, SPC, RALT,RGUI),
|
||||
/* Layer 2: Mouse mode (Semicolon)
|
||||
* ,-----------------------------------------------------------.
|
||||
* |Esc| F1| F2| F3| F4| F5| F6| F7| F8| F9|F10|F11|F12|Ins|Del|
|
||||
* |-----------------------------------------------------------|
|
||||
* |Tab | | | | | |MwL|MwD|MwU|MwR| | | |Backs|
|
||||
* |-----------------------------------------------------------|
|
||||
* |Contro| | | | | |McL|McD|McU|McR|Fn0| |Return |
|
||||
* |-----------------------------------------------------------|
|
||||
* |Shift | | | | |Mb3|Mb2|Mb1|Mb4|Mb5| |Shift | |
|
||||
* `-----------------------------------------------------------'
|
||||
* |Gui |Alt |Mb1 |Alt |Fn0|
|
||||
* `--------------------------------------------'
|
||||
* Mc: Mouse Cursor / Mb: Mouse Button / Mw: Mouse Wheel
|
||||
*/
|
||||
KEYMAP(ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, INS, DEL, \
|
||||
TAB, NO, NO, NO, NO, NO, WH_L,WH_D,WH_U,WH_R,NO, NO, NO, BSPC, \
|
||||
LCTL,NO, ACL0,ACL1,ACL2,NO, MS_L,MS_D,MS_U,MS_R,FN0, QUOT,ENT, \
|
||||
LSFT,NO, NO, NO, NO, BTN3,BTN2,BTN1,BTN4,BTN5,SLSH,RSFT,NO, \
|
||||
LGUI,LALT, BTN1, RALT,FN0),
|
||||
};
|
||||
|
||||
static const uint16_t PROGMEM fn_actions[] = {
|
||||
ACTION_LAYER_DEFAULT, // FN0
|
||||
ACTION_LAYER_SET(1), // FN1
|
||||
ACTION_LAYER_SET_TAP_KEY(2, KC_SCLN), // FN2
|
||||
ACTION_LAYER_BIT_TOGGLE(2), // FN3
|
||||
};
|
||||
|
||||
|
||||
Build your own firmware
|
||||
-----------------------
|
||||
|
||||
### 1. Keycode
|
||||
See `common/keycode.h`. Keycode is 8bit internal code to inidicate action performed on key in keymap. Keycode has `KC_` prefixed symbol respectively. Most of keycodes like `KC_A` have simple action register key on press and unregister on release, on the other some of keycodes has some special actions like Fn keys, Media contorl keys, System control keys and Mouse keys.
|
||||
|
||||
***In `KEYMAP` definition you need to omit prefix part `KC_` of keycode to keep keymap compact.*** For example, just use `A` instead you place `KC_A` in `KEYMAP`. Some keycodes has 4-letter short name in addition to descriptive name, you'll prefer short one in `KEYMAP`.
|
||||
|
||||
#### 1.1 Normal key
|
||||
- `KC_NO` for no aciton
|
||||
- `KC_A` to `KC_Z`, `KC_1` to `KC_0` for alpha numeric key
|
||||
- `KC_MINS`, `KC_EQL`, `KC_GRV`, `KC_RBRC`, `KC_LBRC`, `KC_COMM`, `KC_DOT`, `KC_BSLS`, `KC_SLSH`, `KC_SCLN`, `KC_QUOT`
|
||||
- `KC_ESC`, `KC_TAB`, `KC_SPC`, `KC_BSPC`, `KC_ENT`, `KC_DEL`, `KC_INS`
|
||||
- `KC_UP`, `KC_DOWN`, `KC_RGHT`, `KC_LEFT`, `KC_PGUP`, `KC_PGDN`, `KC_HOME`, `KC_END`
|
||||
- `KC_CAPS`, `KC_NLCK`, `KC_SLCK`, `KC_PSCR`, `KC_PAUS`, `KC_APP`, `KC_F1` to `KC_F24`
|
||||
- `KC_P1` to `KC_P0`, `KC_PDOT`, `KC_PCMM`, `KC_PSLS`, `KC_PAST`, `KC_PMNS`, `KC_PPLS`, `KC_PEQL`, `KC_PENT` for keypad.
|
||||
|
||||
#### 1.2 Modifier
|
||||
There are 8 modifiers which has discrimination between left and right.
|
||||
|
||||
- `KC_LCTL` and `KC_RCTL` for Control
|
||||
- `KC_LSFT` and `KC_RSFT` for Shift
|
||||
- `KC_LALT` and `KC_RALT` for Alt
|
||||
- `KC_LGUI` and `KC_RGUI` for Windows key or Command key in Mac
|
||||
|
||||
#### 1.3 Fn key
|
||||
**`KC_FNnn`** are `Fn` keys which not given any action at the beginning unlike most of keycodes has its own action. To use these keys in `KEYMAP` you need to assign action you want at first. Action of `Fn` is defined in `fn_actions[]` and index of the array is identical with number part of `KC_FNnn`. Thus `KC_FN0` designates action defined in first element of the array. ***32 `Fn` keys can be defined at most.***
|
||||
|
||||
#### 1.4 Mousekey
|
||||
- `KC_MS_U`, `KC_MS_D`, `KC_MS_L`, `KC_MS_R` for mouse cursor
|
||||
- `KC_WH_U`, `KC_WH_D`, `KC_WH_L`, `KC_WH_R` for mouse wheel
|
||||
- `KC_BTN1`, `KC_BTN2`, `KC_BTN3`, `KC_BTN4`, `KC_BTN5` for mouse buttons
|
||||
|
||||
#### 1.5 System & Media key
|
||||
- `KC_PWR`, `KC_SLEP`, `KC_WAKE` for Power, Sleep, Wake
|
||||
- `KC_MUTE`, `KC_VOLU`, `KC_VOLD` for audio volume control
|
||||
- `KC_MNXT`, `KC_MPRV`, `KC_MSTP`, `KC_MPLY`, `KC_MSEL` for media control
|
||||
- `KC_MAIL`, `KC_CALC`, `KC_MYCM` for application launch
|
||||
- `KC_WSCH`, `KC_WHOM`, `KC_WBAK`, `KC_WFWD`, `KC_WSTP`, `KC_WREF`, `KC_WFAV` for web browser operation
|
||||
|
||||
#### Keycode Table
|
||||
See [keycode table](doc/keycode.txt) in `doc/keycode.txt` or `common/keycode.h` for the detail or other keycodes.
|
||||
|
||||
In regard to implementation side most of keycodes are identical with [HID usage] sent to host for real and some virtual keycodes are defined to support special actions.
|
||||
[HID usage]: http://www.usb.org/developers/devclass_docs/Hut1_11.pdf
|
||||
|
||||
|
||||
|
||||
### 2. Action
|
||||
See `common/action.h`. Action is a 16bit code and defines function to perform on events of a key like press, release, hold and tap. You can define various actions to use various action codes.
|
||||
|
||||
Most of keys just register 8bit keycode as HID usage(or scan code) to host, but to support other complex features needs 16bit extended action codes internally. But using 16bit action codes in keymap results in double size in memory against keycodes. To avoid this waste 8bit keycodes are used in `KEYMAP` to define instead of action codes. ***Keycodes can be considered as subset of action codes.*** Like `KC_A`(0x04) is equal to a `Key` action(0x0004) that transmit keycode of *'A'*.
|
||||
|
||||
#### 2.1 Key action
|
||||
Key is simple action that registers keycode on press of key and unregister on release.
|
||||
You can define `Key` action on *'A'* key with:
|
||||
|
||||
ACTION_KEY(KC_A)
|
||||
|
||||
But you don't need to use this expression directly because you can just put symbol `A` in `KEYMAP` definition.
|
||||
|
||||
Say you want to assign a key to `Shift + 1` to get charactor *'!'* or `Alt + Tab` to switch windows.
|
||||
|
||||
ACTION_MOD_KEY(KC_LSHIFT, KC_1)
|
||||
ACTION_MOD_KEY(KC_LALT, KC_TAB)
|
||||
|
||||
Or `Alt,Shift + Tab` can be defined.
|
||||
|
||||
ACTION_MODS_KEY((MOD_BIT(KC_LALT) | MOD_BIT(KC_LSHIFT)), KC_TAB)
|
||||
|
||||
These actions are comprised of strokes of modifiers and a key. `Macro` action is needed if you want more complex key strokes.
|
||||
|
||||
#### 2.2 Layer Actions
|
||||
This sets `default layer` into `current layer`. With this action you can return to `default layer`.
|
||||
|
||||
ACTION_LAYER_DEFAULT
|
||||
|
||||
`Layer Set` action sets given layer argument to `current layer`. `Layer Set` action can take 0 to 15 as argument.
|
||||
|
||||
ACTION_LAYER_SET(layer)
|
||||
ACTION_LAYER_SET_TOGGLE(layer)
|
||||
ACTION_LAYER_SET_TAP_KEY(layer, key)
|
||||
ACTION_LAYER_SET_TAP_TOGGLE(layer)
|
||||
|
||||
`Layer Bit` action XOR bits with `current layer`. `Layer Bit` action can take 0 to 8 as argument.
|
||||
|
||||
ACTION_LAYER_BIT(bits)
|
||||
ACTION_LAYER_BIT_TOGGLE(bits)
|
||||
ACTION_LAYER_BIT_TAP_KEY(bits, key)
|
||||
ACTION_LAYER_BIT_TAP_TOGGLE(bits)
|
||||
|
||||
These acitons change `default layer`.
|
||||
ACTION_LAYER_SET_DEFAULT(layer)
|
||||
ACTION_LAYER_BIT_DEFAULT(bits)
|
||||
|
||||
|
||||
#### 2.3 Macro action
|
||||
***NOT FIXED***
|
||||
`Macro` action indicates complex key strokes.
|
||||
|
||||
MACRO( MD(LSHIFT), D(D), END )
|
||||
MACRO( U(D), MU(LSHIFT), END )
|
||||
MACRO( I(255), T(H), T(E), T(L), T(L), W(255), T(O), END )
|
||||
|
||||
##### 2.3.1 Normal mode
|
||||
- **I()** change interavl of stroke.
|
||||
- **D()** press key
|
||||
- **U()** release key
|
||||
- **T()** type key(press and release)
|
||||
- **W()** wait
|
||||
- **MD()** modifier down
|
||||
- **MU()** modifier up
|
||||
- **END** end mark
|
||||
|
||||
##### 2.3.2 Extended mode
|
||||
|
||||
***TODO: sample impl***
|
||||
See `keyboard/hhkb/keymap.c` for sample.
|
||||
|
||||
|
||||
#### 2.4 Function action
|
||||
***NOT FIXED***
|
||||
There are two type of action, normal `Function` and tappable `Function`.
|
||||
These actions call user defined function with `id`, `opt`, and key event information as arguments.
|
||||
|
||||
##### 2.4.1 Function
|
||||
To define normal `Function` action in keymap use this.
|
||||
|
||||
ACTION_FUNCTION(id, opt)
|
||||
|
||||
##### 2.4.2 Function with tap
|
||||
To define tappable `Function` action in keymap use this.
|
||||
|
||||
ACTION_FUNCTION_TAP(id, opt)
|
||||
|
||||
##### 2.4.3 Implement user function
|
||||
`Function` actions can be defined freely with C by user in callback function:
|
||||
|
||||
void keymap_call_function(keyrecord_t *event, uint8_t id, uint8_t opt)
|
||||
|
||||
This C function is called every time key is operated, argument `id` selects action to be performed and `opt` can be used for option. Functon `id` can be 0-255 and `opt` can be 0-15.
|
||||
|
||||
`keyrecord_t` is comprised of key event and tap count. `keyevent_t` indicates which and when key is pressed or released. From `tap_count` you can know tap state, 0 means no tap. These information will be used in user function to decide how action of key is performed.
|
||||
|
||||
typedef struct {
|
||||
keyevent_t event;
|
||||
uint8_t tap_count;
|
||||
} keyrecord_t;
|
||||
|
||||
typedef struct {
|
||||
key_t key;
|
||||
bool pressed;
|
||||
uint16_t time;
|
||||
} keyevent_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t col;
|
||||
uint8_t row;
|
||||
} key_t;
|
||||
|
||||
***TODO: sample impl***
|
||||
See `keyboard/hhkb/keymap.c` for sample.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 3. Layer
|
||||
Layer is key-action map to assign action to every physical key. You can define multiple layers in keymap and select a layer out of keymap during operation at will.
|
||||
|
||||
First layer is indexed by `Layer 0` which usually become **`default layer`** and active in initial state. **`current layer`** is active layer at that time and can be changed with user interaction. You can define **16 layers** at most in default keymap framework.
|
||||
|
||||
you can define a layer with placing keycode symbols separated with `comma` in `KEYMAP`, which is formed with resemblance to physical keyboard layout so as you can easily put keycode on place you want to map. ***You can define most of keys with just using keycodes*** except for `Fn` key serving special actions.
|
||||
|
||||
|
||||
|
||||
### 4. Layer switching
|
||||
You can have some ways to switch layer with these actions.
|
||||
There are two kind of layer switch action `Layer Set` and `Layer Bit` and two type of switching behaviour **Momentary** and **Toggle**.
|
||||
|
||||
#### 4.1 Momentary switching
|
||||
Momentary switching changes layer only while holding Fn key.
|
||||
|
||||
##### 4.1.1 Momentary Set
|
||||
This `Layer Set` action sets new layer `Layer 1` to `current layer` on key press event.
|
||||
|
||||
ACTION_LAYER_SET(1)
|
||||
|
||||
It switches to destination layer immediately when key is pressed, after that actions on keymap of destination layer is perfomed. ***Thus you shall need to place action to come back on destination layer***, or you will be stuck in destination layer without way to get back. To get back to `default layer` you can use this action.
|
||||
|
||||
ACTION_LAYER_DEFAULT
|
||||
|
||||
##### 4.1.2 Momentary Bit
|
||||
This `Layer Bit` action performs XOR `1` with `current layer` on both press and release event. If you are on `Layer 0` now next layer to switch will be `Layer 1`. To come back to previous layer you need to place same action on destination layer.
|
||||
|
||||
ACTION_LAYER_BIT(1)
|
||||
|
||||
#### 4.2 Toggle switching
|
||||
Toggle switching changes layer after press then release. You keep being on the layer until you press key to return.
|
||||
|
||||
##### 4.2.1 Toggle Set
|
||||
This `Layer Set Toggle` action is to set `Layer 1` to `current layer` on release and do none on press.
|
||||
|
||||
ACTION_LAYER_SET_TOGGLE(1)
|
||||
|
||||
To get back to `default layer` you can use this action.
|
||||
|
||||
ACTION_LAYER_DEFAULT
|
||||
|
||||
##### 4.2.2 Toggle Bit
|
||||
This `Layer Bit Toggle` action is to XOR `1` with `current layer` on release and do none on press. If you are on `Layer 2` you'll switch to `Layer 3` on press. To come back to previous layer you need to place same action on destination layer.
|
||||
|
||||
ACTION_LAYER_BIT_TOGGLE(1)
|
||||
|
||||
|
||||
#### 4.3 Momentary switching with Tap key
|
||||
These actions switch to layer only while holding `Fn` key and register key on tap. **Tap** means to press and release key quickly.
|
||||
|
||||
ACTION_LAYER_SET_TAP_KEY(2, KC_SCLN)
|
||||
ACTION_LAYER_SET_BIT_KEY(2, KC_SCLN)
|
||||
|
||||
With these you can place layer switching function on normal alphabet key like `;` without losing its original register function.
|
||||
|
||||
#### 4.4 Momentary switching with Tap Toggle
|
||||
This changes layer only while holding `Fn` key and toggle layer after several taps. **Tap** means to press and release key quickly.
|
||||
|
||||
ACTION_LAYER_SET_TAP_TOGGLE(layer)
|
||||
ACTION_LAYER_BIT_TAP_TOGGLE(layer)
|
||||
|
||||
Number of taps can be defined with `TAPPING_TOGGLE` in `config.h`, `5` by default.
|
||||
|
||||
|
||||
|
||||
|
||||
Legacy Keymap
|
||||
-------------
|
||||
This was used in prior version and still works due to legacy support code in `common/keymap.c`. Legacy keymap doesn't support many of features that new keymap offers.
|
||||
|
||||
In comparison with new keymap how to define Fn key is different. It uses two arrays `fn_layer[]` and `fn_keycode[]`. The index of arrays corresponds with postfix number of `Fn` key. Array `fn_layer[]` indicates destination layer to switch and `fn_keycode[]` has keycodes to send when tapping `Fn` key.
|
||||
|
||||
In following setting example, `Fn0`, `Fn1` and `Fn2` switch layer to 1, 2 and 2 respectively. `Fn2` registers `Space` key when tap while `Fn0` and `Fn1` doesn't send any key.
|
||||
|
||||
static const uint8_t PROGMEM fn_layer[] = {
|
||||
1, // Fn0
|
||||
2, // Fn1
|
||||
2, // Fn2
|
||||
};
|
||||
|
||||
static const uint8_t PROGMEM fn_keycode[] = {
|
||||
KC_NO, // Fn0
|
||||
KC_NO, // Fn1
|
||||
KC_SPC, // Fn2
|
||||
};
|
||||
|
||||
|
||||
|
||||
Debuging
|
||||
--------
|
||||
Use PJRC's `hid_listen` to see debug messages and press `<COMMAND> + H` to debug menu.
|
||||
See `config.h` for definition of `<COMMAND>` key combination.
|
||||
Use PJRC's `hid_listen` to see debug messages. You can use the tool for debug even if firmware use LUFA stack.
|
||||
|
||||
You will see output from firmware like this.
|
||||
|
||||
r/c 01234567
|
||||
00: 00000000
|
||||
01: 00000000
|
||||
02: 00000000
|
||||
03: 00000000
|
||||
04: 00000000
|
||||
05: 00000000
|
||||
06: 00000000
|
||||
07: 00000000
|
||||
|
||||
---- action_exec: start -----
|
||||
EVENT: 0307u(22511)
|
||||
Tapping: Tap release(2)
|
||||
ACTION: ACT_LAYER[5:2C]
|
||||
LAYER_PRESSED: Tap: unregister_code
|
||||
TAPPING_KEY=0307u(22511):2
|
||||
processed: 0307u(22511):2
|
||||
|
||||
Tapping: End(Timeout after releasing last tap): FFFFu(22715)
|
||||
TAPPING_KEY=0000u(0):0
|
||||
|
||||
|
||||
Other Keyboard Projects
|
||||
|
||||
|
||||
Magic Comannds
|
||||
--------------
|
||||
To see help press `Magic` + `H`.
|
||||
|
||||
`Magic` key bind may be `LShift` + `RShift` in many project, but `Power` key on ADB converter. `Magic` keybind can be vary on each project, check `config.h` in project directory.
|
||||
|
||||
Following commands can be also executed with `Magic` + key. In console mode `Magic` keybind is not needed.
|
||||
|
||||
----- Command Help -----
|
||||
c: enter console mode
|
||||
d: toggle debug enable
|
||||
x: toggle matrix debug
|
||||
k: toggle keyboard debug
|
||||
m: toggle mouse debug
|
||||
p: toggle print enable
|
||||
v: print device version & info
|
||||
t: print timer count
|
||||
s: print status
|
||||
0/F10: switch to Layer0
|
||||
1/F1: switch to Layer1
|
||||
2/F2: switch to Layer2
|
||||
3/F3: switch to Layer3
|
||||
4/F4: switch to Layer4
|
||||
PScr: power down/remote wake-up
|
||||
Caps: Lock Keyboard(Child Proof)
|
||||
Paus: jump to bootloader
|
||||
|
||||
### Boot Magic
|
||||
Magic commands are executed when boot time. Press `Magic` command key then pulgin.
|
||||
|
||||
Define these macros in config.h.
|
||||
|
||||
IS_BOOTMAGIC_DEBUG
|
||||
IS_BOOTMAGIC_BOOTLOADER
|
||||
|
||||
***TODO: sample impl***
|
||||
See `keyboard/hhkb/config.h` for sample.
|
||||
|
||||
|
||||
|
||||
Start Your Own Project
|
||||
-----------------------
|
||||
### PJRC USB Keyboard/Mouse Example
|
||||
- <http://www.pjrc.com/teensy/usb_keyboard.html>
|
||||
- <http://www.pjrc.com/teensy/usb_mouse.html>
|
||||
|
||||
### kbupgrade
|
||||
- <http://github.com/rhomann/kbupgrade>
|
||||
- <http://geekhack.org/showwiki.php?title=Island:8406>
|
||||
|
||||
### c64key
|
||||
- <http://symlink.dk/projects/c64key/>
|
||||
|
||||
### rump
|
||||
- <http://mg8.org/rump/>
|
||||
- <http://github.com/clee/rump>
|
||||
Files & Directories
|
||||
-------------------
|
||||
### Top
|
||||
* common/ - common codes
|
||||
* protocol/ - keyboard protocol support
|
||||
* keyboard/ - keyboard projects
|
||||
* converter/ - protocol converter projects
|
||||
* doc/ - documents
|
||||
* common.mk - Makefile for common
|
||||
* protoco.mk - Makefile for protocol
|
||||
* rules.mk - Makefile for build rules
|
||||
|
||||
### dulcimer
|
||||
- <http://www.schatenseite.de/dulcimer.html>
|
||||
### Common
|
||||
* action.[ch]
|
||||
* action_macro.[ch]
|
||||
* bootloader.[ch]
|
||||
* command.[ch]
|
||||
* controller_teensy.h
|
||||
* debug.[ch]
|
||||
* host.[ch]
|
||||
* host_driver.h
|
||||
* keyboard.[ch]
|
||||
* keycode.h
|
||||
* keymap.[ch]
|
||||
* led.h
|
||||
* matrix.h
|
||||
* mousekey.[ch]
|
||||
* print.[ch]
|
||||
* report.h
|
||||
* sendchar.h
|
||||
* sendchar_null.c
|
||||
* sendchar_uart.c
|
||||
* timer.[ch]
|
||||
* uart.[ch]
|
||||
* util.[ch]
|
||||
|
||||
### humblehacker-keyboard
|
||||
- <http://github.com/humblehacker>
|
||||
- <http://www.humblehacker.com/keyboard/>
|
||||
- <http://geekhack.org/showwiki.php?title=Island:6292>
|
||||
### Keyboard Protocols
|
||||
* lufa/ - LUFA USB stack
|
||||
* pjrc/ - PJRC USB stack
|
||||
* vusb/ - Objective Development V-USB
|
||||
* iwrap/ - Bluetooth HID for Bluegiga iWRAP
|
||||
* ps2.c - PS/2 protocol
|
||||
* adb.c - Apple Desktop Bus protocol
|
||||
* m0110.c - Macintosh 128K/512K/Plus keyboard protocol
|
||||
* news.c - Sony NEWS keyboard protocol
|
||||
* x68k.c - Sharp X68000 keyboard protocol
|
||||
* serial_soft.c - Asynchronous Serial protocol implemented by software
|
||||
|
||||
### ps2avr
|
||||
- <http://sourceforge.net/projects/ps2avr/>
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
Under `GPL` 2 or later. Some protocol files are under `Modified BSD License`.
|
||||
PJRC stack has its own license.
|
||||
|
@ -1,6 +1,9 @@
|
||||
COMMON_DIR = common
|
||||
SRC += $(COMMON_DIR)/host.c \
|
||||
$(COMMON_DIR)/keyboard.c \
|
||||
$(COMMON_DIR)/action.c \
|
||||
$(COMMON_DIR)/action_macro.c \
|
||||
$(COMMON_DIR)/keymap.c \
|
||||
$(COMMON_DIR)/command.c \
|
||||
$(COMMON_DIR)/timer.c \
|
||||
$(COMMON_DIR)/print.c \
|
||||
|
883
common/action.c
Normal file
883
common/action.c
Normal file
@ -0,0 +1,883 @@
|
||||
/*
|
||||
Copyright 2012,2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "host.h"
|
||||
#include "timer.h"
|
||||
#include "keymap.h"
|
||||
#include "keycode.h"
|
||||
#include "keyboard.h"
|
||||
#include "mousekey.h"
|
||||
#include "command.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "action.h"
|
||||
|
||||
|
||||
static void process_action(keyrecord_t *record);
|
||||
static bool process_tapping(keyrecord_t *record);
|
||||
static void waiting_buffer_scan_tap(void);
|
||||
|
||||
static void debug_event(keyevent_t event);
|
||||
static void debug_record(keyrecord_t record);
|
||||
static void debug_action(action_t action);
|
||||
static void debug_tapping_key(void);
|
||||
static void debug_waiting_buffer(void);
|
||||
|
||||
|
||||
/*
|
||||
* Tapping
|
||||
*/
|
||||
/* period of tapping(ms) */
|
||||
#ifndef TAPPING_TERM
|
||||
#define TAPPING_TERM 200
|
||||
#endif
|
||||
|
||||
/* tap count needed for toggling a feature */
|
||||
#ifndef TAPPING_TOGGLE
|
||||
#define TAPPING_TOGGLE 5
|
||||
#endif
|
||||
|
||||
/* stores a key event of current tap. */
|
||||
static keyrecord_t tapping_key = {};
|
||||
|
||||
#define IS_TAPPING() !IS_NOEVENT(tapping_key.event)
|
||||
#define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed)
|
||||
#define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed)
|
||||
#define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k)))
|
||||
#define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM)
|
||||
|
||||
|
||||
/*
|
||||
* Waiting buffer
|
||||
*
|
||||
* stores key events waiting for settling current tap.
|
||||
*/
|
||||
#define WAITING_BUFFER_SIZE 8
|
||||
static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
|
||||
|
||||
/* point to empty cell to enq */
|
||||
static uint8_t waiting_buffer_head = 0;
|
||||
|
||||
/* point to the oldest data cell to deq */
|
||||
static uint8_t waiting_buffer_tail = 0;
|
||||
|
||||
static bool waiting_buffer_enq(keyrecord_t record)
|
||||
{
|
||||
if (IS_NOEVENT(record.event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) {
|
||||
debug("waiting_buffer_enq: Over flow.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
waiting_buffer[waiting_buffer_head] = record;
|
||||
waiting_buffer_head = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE;
|
||||
|
||||
debug("waiting_buffer_enq: "); debug_waiting_buffer();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void waiting_buffer_clear(void)
|
||||
{
|
||||
waiting_buffer_head = 0;
|
||||
waiting_buffer_tail = 0;
|
||||
}
|
||||
|
||||
#if TAPPING_TERM >= 500
|
||||
static bool waiting_buffer_typed(keyevent_t event)
|
||||
{
|
||||
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
||||
if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool waiting_buffer_has_anykey_pressed(void)
|
||||
{
|
||||
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
||||
if (waiting_buffer[i].event.pressed) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Oneshot modifier
|
||||
*
|
||||
* Problem: Want to capitalize like 'The' but the result tends to be 'THe'.
|
||||
* Solution: Oneshot modifier have its effect on only one key coming next.
|
||||
* Tap Shift, then type 't', 'h' and 'e'. Not need to hold Shift key.
|
||||
*
|
||||
* Hold: works as normal modifier.
|
||||
* Tap: one shot modifier.
|
||||
* 2 Tap: cancel one shot modifier.
|
||||
* 5-Tap: toggles enable/disable oneshot feature.
|
||||
*/
|
||||
static struct {
|
||||
uint8_t mods;
|
||||
uint8_t time;
|
||||
bool ready;
|
||||
bool disabled;
|
||||
} oneshot_state;
|
||||
|
||||
static void oneshot_start(uint8_t mods, uint16_t time)
|
||||
{
|
||||
oneshot_state.mods = mods;
|
||||
oneshot_state.time = time;
|
||||
oneshot_state.ready = true;
|
||||
}
|
||||
|
||||
static void oneshot_cancel(void)
|
||||
{
|
||||
oneshot_state.mods = 0;
|
||||
oneshot_state.time = 0;
|
||||
oneshot_state.ready = false;
|
||||
}
|
||||
|
||||
static void oneshot_toggle(void)
|
||||
{
|
||||
oneshot_state.disabled = !oneshot_state.disabled;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void action_exec(keyevent_t event)
|
||||
{
|
||||
if (!IS_NOEVENT(event)) {
|
||||
debug("\n---- action_exec: start -----\n");
|
||||
debug("EVENT: "); debug_event(event); debug("\n");
|
||||
}
|
||||
|
||||
keyrecord_t record = { .event = event };
|
||||
|
||||
// pre-process on tapping
|
||||
if (process_tapping(&record)) {
|
||||
if (!IS_NOEVENT(record.event)) {
|
||||
debug("processed: "); debug_record(record); debug("\n");
|
||||
}
|
||||
} else {
|
||||
// enqueue
|
||||
if (!waiting_buffer_enq(record)) {
|
||||
// clear all in case of overflow.
|
||||
debug("OVERFLOW: CLEAR ALL STATES\n");
|
||||
clear_keyboard();
|
||||
waiting_buffer_clear();
|
||||
tapping_key = (keyrecord_t){};
|
||||
}
|
||||
}
|
||||
|
||||
// process waiting_buffer
|
||||
if (!IS_NOEVENT(event) && waiting_buffer_head != waiting_buffer_tail) {
|
||||
debug("---- action_exec: process waiting_buffer -----\n");
|
||||
}
|
||||
|
||||
for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {
|
||||
if (process_tapping(&waiting_buffer[waiting_buffer_tail])) {
|
||||
debug("processed: waiting_buffer["); debug_dec(waiting_buffer_tail); debug("] = ");
|
||||
debug_record(waiting_buffer[waiting_buffer_tail]); debug("\n\n");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!IS_NOEVENT(event)) {
|
||||
debug("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void process_action(keyrecord_t *record)
|
||||
{
|
||||
keyevent_t event = record->event;
|
||||
uint8_t tap_count = record->tap_count;
|
||||
|
||||
if (IS_NOEVENT(event)) { return; }
|
||||
|
||||
action_t action = keymap_get_action(current_layer, event.key.pos.row, event.key.pos.col);
|
||||
//debug("action: "); debug_hex16(action.code); if (event.pressed) debug("d\n"); else debug("u\n");
|
||||
debug("ACTION: "); debug_action(action); debug("\n");
|
||||
|
||||
switch (action.kind.id) {
|
||||
/* Key and Mods */
|
||||
case ACT_LMODS:
|
||||
case ACT_RMODS:
|
||||
{
|
||||
uint8_t mods = (action.kind.id == ACT_LMODS) ? action.key.mods :
|
||||
action.key.mods<<4;
|
||||
if (event.pressed) {
|
||||
uint8_t tmp_mods = host_get_mods();
|
||||
if (mods) {
|
||||
host_add_mods(mods);
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
register_code(action.key.code);
|
||||
if (mods && action.key.code) {
|
||||
host_set_mods(tmp_mods);
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
} else {
|
||||
if (mods && !action.key.code) {
|
||||
host_del_mods(mods);
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
unregister_code(action.key.code);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACT_LMODS_TAP:
|
||||
case ACT_RMODS_TAP:
|
||||
{
|
||||
uint8_t mods = (action.kind.id == ACT_LMODS_TAP) ? action.key.mods :
|
||||
action.key.mods<<4;
|
||||
switch (action.layer.code) {
|
||||
case 0x00:
|
||||
// Oneshot modifier
|
||||
if (event.pressed) {
|
||||
if (tap_count == 0) {
|
||||
debug("MODS_TAP: Oneshot: add_mods\n");
|
||||
add_mods(mods);
|
||||
}
|
||||
else if (tap_count == 1) {
|
||||
debug("MODS_TAP: Oneshot: start\n");
|
||||
oneshot_start(mods, event.time);
|
||||
}
|
||||
else if (tap_count == TAPPING_TOGGLE) {
|
||||
debug("MODS_TAP: Oneshot: toggle\n");
|
||||
oneshot_toggle();
|
||||
}
|
||||
else {
|
||||
debug("MODS_TAP: Oneshot: cancel&add_mods\n");
|
||||
// double tap cancels oneshot and works as normal modifier.
|
||||
oneshot_cancel();
|
||||
add_mods(mods);
|
||||
}
|
||||
} else {
|
||||
if (tap_count == 0) {
|
||||
debug("MODS_TAP: Oneshot: cancel/del_mods\n");
|
||||
// cancel oneshot by holding.
|
||||
oneshot_cancel();
|
||||
del_mods(mods);
|
||||
}
|
||||
else if (tap_count == 1) {
|
||||
debug("MODS_TAP: Oneshot: del_mods\n");
|
||||
// retain Oneshot
|
||||
del_mods(mods);
|
||||
}
|
||||
else {
|
||||
debug("MODS_TAP: Oneshot: del_mods\n");
|
||||
// cancel Mods
|
||||
del_mods(mods);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (event.pressed) {
|
||||
if (tap_count > 0) {
|
||||
if (waiting_buffer_has_anykey_pressed()) {
|
||||
debug("MODS_TAP: Tap: Cancel: add_mods\n");
|
||||
// ad hoc: set 0 to cancel tap
|
||||
record->tap_count = 0;
|
||||
add_mods(mods);
|
||||
} else {
|
||||
debug("MODS_TAP: Tap: register_code\n");
|
||||
register_code(action.key.code);
|
||||
}
|
||||
} else {
|
||||
debug("MODS_TAP: No tap: add_mods\n");
|
||||
add_mods(mods);
|
||||
}
|
||||
} else {
|
||||
if (tap_count > 0) {
|
||||
debug("MODS_TAP: Tap: unregister_code\n");
|
||||
unregister_code(action.key.code);
|
||||
} else {
|
||||
debug("MODS_TAP: No tap: add_mods\n");
|
||||
del_mods(mods);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* other HID usage */
|
||||
case ACT_USAGE:
|
||||
#ifdef EXTRAKEY_ENABLE
|
||||
switch (action.usage.page) {
|
||||
case PAGE_SYSTEM:
|
||||
if (event.pressed) {
|
||||
host_system_send(action.usage.code);
|
||||
} else {
|
||||
host_system_send(0);
|
||||
}
|
||||
break;
|
||||
case PAGE_CONSUMER:
|
||||
if (event.pressed) {
|
||||
host_consumer_send(action.usage.code);
|
||||
} else {
|
||||
host_consumer_send(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
/* Mouse key */
|
||||
case ACT_MOUSEKEY:
|
||||
#ifdef MOUSEKEY_ENABLE
|
||||
if (event.pressed) {
|
||||
mousekey_on(action.key.code);
|
||||
mousekey_send();
|
||||
} else {
|
||||
mousekey_off(action.key.code);
|
||||
mousekey_send();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
/* Layer key */
|
||||
case ACT_LAYER:
|
||||
switch (action.layer.code) {
|
||||
case LAYER_MOMENTARY: /* momentary */
|
||||
if (event.pressed) {
|
||||
layer_switch(action.layer.val);
|
||||
}
|
||||
else {
|
||||
layer_switch(default_layer);
|
||||
}
|
||||
break;
|
||||
case LAYER_ON_PRESS:
|
||||
if (event.pressed) {
|
||||
layer_switch(action.layer.val);
|
||||
}
|
||||
break;
|
||||
case LAYER_ON_RELEASE:
|
||||
if (!event.pressed) {
|
||||
layer_switch(action.layer.val);
|
||||
}
|
||||
break;
|
||||
case LAYER_DEFAULT: /* default layer */
|
||||
switch (action.layer.val) {
|
||||
case DEFAULT_ON_BOTH:
|
||||
layer_switch(default_layer);
|
||||
break;
|
||||
case DEFAULT_ON_PRESS:
|
||||
if (event.pressed) {
|
||||
layer_switch(default_layer);
|
||||
}
|
||||
break;
|
||||
case DEFAULT_ON_RELEASE:
|
||||
if (!event.pressed) {
|
||||
layer_switch(default_layer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case LAYER_TAP_TOGGLE: /* switch on hold and toggle on several taps */
|
||||
if (event.pressed) {
|
||||
if (tap_count < TAPPING_TOGGLE) {
|
||||
layer_switch(action.layer.val);
|
||||
}
|
||||
} else {
|
||||
if (tap_count >= TAPPING_TOGGLE) {
|
||||
debug("LAYER_PRESSED: tap toggle.\n");
|
||||
layer_switch(action.layer.val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LAYER_CHANGE_DEFAULT: /* change default layer */
|
||||
if (event.pressed) {
|
||||
default_layer = action.layer.val;
|
||||
layer_switch(default_layer);
|
||||
}
|
||||
break;
|
||||
default: /* switch layer on hold and key on tap*/
|
||||
if (event.pressed) {
|
||||
if (tap_count > 0) {
|
||||
debug("LAYER_PRESSED: Tap: register_code\n");
|
||||
register_code(action.layer.code);
|
||||
} else {
|
||||
debug("LAYER_PRESSED: No tap: layer_switch\n");
|
||||
layer_switch(action.layer.val);
|
||||
}
|
||||
} else {
|
||||
if (tap_count > 0) {
|
||||
debug("LAYER_PRESSED: Tap: unregister_code\n");
|
||||
unregister_code(action.layer.code);
|
||||
} else {
|
||||
//debug("LAYER_PRESSED: No tap: NO ACTION\n");
|
||||
//TODO: this is ok?
|
||||
debug("LAYER_PRESSED: No tap: return to default layer\n");
|
||||
layer_switch(default_layer);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ACT_LAYER_BIT:
|
||||
switch (action.layer.code) {
|
||||
case LAYER_MOMENTARY: /* momentary */
|
||||
if (event.pressed) {
|
||||
layer_switch(current_layer ^ action.layer.val);
|
||||
} else {
|
||||
layer_switch(current_layer ^ action.layer.val);
|
||||
}
|
||||
break;
|
||||
case LAYER_ON_PRESS:
|
||||
if (event.pressed) {
|
||||
layer_switch(current_layer ^ action.layer.val);
|
||||
}
|
||||
break;
|
||||
case LAYER_ON_RELEASE:
|
||||
if (!event.pressed) {
|
||||
layer_switch(current_layer ^ action.layer.val);
|
||||
}
|
||||
break;
|
||||
case LAYER_TAP_TOGGLE: /* switch on hold and toggle on several taps */
|
||||
if (event.pressed) {
|
||||
if (tap_count < TAPPING_TOGGLE) {
|
||||
debug("LAYER_BIT: tap toggle(press).\n");
|
||||
layer_switch(current_layer ^ action.layer.val);
|
||||
}
|
||||
} else {
|
||||
if (tap_count <= TAPPING_TOGGLE) {
|
||||
debug("LAYER_BIT: tap toggle(release).\n");
|
||||
layer_switch(current_layer ^ action.layer.val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xFF:
|
||||
// change default layer
|
||||
if (event.pressed) {
|
||||
default_layer = current_layer ^ action.layer.val;
|
||||
layer_switch(default_layer);
|
||||
} else {
|
||||
default_layer = current_layer ^ action.layer.val;
|
||||
layer_switch(default_layer);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// with tap key
|
||||
if (event.pressed) {
|
||||
if (IS_TAPPING_KEY(event.key) && tap_count > 0) {
|
||||
debug("LAYER_BIT: Tap: register_code\n");
|
||||
register_code(action.layer.code);
|
||||
} else {
|
||||
debug("LAYER_BIT: No tap: layer_switch(bit on)\n");
|
||||
layer_switch(current_layer ^ action.layer.val);
|
||||
}
|
||||
} else {
|
||||
if (IS_TAPPING_KEY(event.key) && tap_count > 0) {
|
||||
debug("LAYER_BIT: Tap: unregister_code\n");
|
||||
unregister_code(action.layer.code);
|
||||
} else {
|
||||
debug("LAYER_BIT: No tap: layer_switch(bit off)\n");
|
||||
layer_switch(current_layer ^ action.layer.val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
/* Extentions */
|
||||
case ACT_MACRO:
|
||||
break;
|
||||
case ACT_COMMAND:
|
||||
break;
|
||||
case ACT_FUNCTION:
|
||||
// TODO
|
||||
keymap_call_function(record, action.func.id, action.func.opt);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tapping
|
||||
*
|
||||
* Rule: Tap key is typed(pressed and released) within TAPPING_TERM.
|
||||
* (without interfering by typing other key)
|
||||
*/
|
||||
/* return true when key event is processed or consumed. */
|
||||
static bool process_tapping(keyrecord_t *keyp)
|
||||
{
|
||||
keyevent_t event = keyp->event;
|
||||
|
||||
// if tapping
|
||||
if (IS_TAPPING_PRESSED()) {
|
||||
if (WITHIN_TAPPING_TERM(event)) {
|
||||
if (tapping_key.tap_count == 0) {
|
||||
if (IS_TAPPING_KEY(event.key) && !event.pressed) {
|
||||
// first tap!
|
||||
debug("Tapping: First tap(0->1).\n");
|
||||
tapping_key.tap_count = 1;
|
||||
debug_tapping_key();
|
||||
process_action(&tapping_key);
|
||||
|
||||
// enqueue
|
||||
keyp->tap_count = tapping_key.tap_count;
|
||||
return false;
|
||||
}
|
||||
#if TAPPING_TERM >= 500
|
||||
/* This can prevent from typing some tap keys in a row at a time. */
|
||||
else if (!event.pressed && waiting_buffer_typed(event)) {
|
||||
// other key typed. not tap.
|
||||
debug("Tapping: End. No tap. Interfered by typing key\n");
|
||||
process_action(&tapping_key);
|
||||
tapping_key = (keyrecord_t){};
|
||||
debug_tapping_key();
|
||||
|
||||
// enqueue
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
// other key events shall be enq'd till tapping state settles.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// tap_count > 0
|
||||
else {
|
||||
if (IS_TAPPING_KEY(event.key) && !event.pressed) {
|
||||
debug("Tapping: Tap release("); debug_dec(tapping_key.tap_count); debug(")\n");
|
||||
keyp->tap_count = tapping_key.tap_count;
|
||||
process_action(keyp);
|
||||
tapping_key = *keyp;
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
}
|
||||
else if (is_tap_key(keyp->event.key) && event.pressed) {
|
||||
if (tapping_key.tap_count > 1) {
|
||||
debug("Tapping: Start new tap with releasing last tap(>1).\n");
|
||||
// unregister key
|
||||
process_action(&(keyrecord_t){
|
||||
.tap_count = tapping_key.tap_count,
|
||||
.event.key = tapping_key.event.key,
|
||||
.event.time = event.time,
|
||||
.event.pressed = false
|
||||
});
|
||||
} else {
|
||||
debug("Tapping: Start while last tap(1).\n");
|
||||
}
|
||||
tapping_key = *keyp;
|
||||
waiting_buffer_scan_tap();
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (!IS_NOEVENT(keyp->event)) {
|
||||
debug("Tapping: key event while last tap(>0).\n");
|
||||
}
|
||||
process_action(keyp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// after TAPPING_TERM
|
||||
else {
|
||||
if (tapping_key.tap_count == 0) {
|
||||
debug("Tapping: End. Timeout. Not tap(0): ");
|
||||
debug_event(event); debug("\n");
|
||||
process_action(&tapping_key);
|
||||
tapping_key = (keyrecord_t){};
|
||||
debug_tapping_key();
|
||||
return false;
|
||||
} else {
|
||||
if (IS_TAPPING_KEY(event.key) && !event.pressed) {
|
||||
debug("Tapping: End. last timeout tap release(>0).");
|
||||
keyp->tap_count = tapping_key.tap_count;
|
||||
process_action(keyp);
|
||||
tapping_key = (keyrecord_t){};
|
||||
return true;
|
||||
}
|
||||
else if (is_tap_key(keyp->event.key) && event.pressed) {
|
||||
if (tapping_key.tap_count > 1) {
|
||||
debug("Tapping: Start new tap with releasing last timeout tap(>1).\n");
|
||||
// unregister key
|
||||
process_action(&(keyrecord_t){
|
||||
.tap_count = tapping_key.tap_count,
|
||||
.event.key = tapping_key.event.key,
|
||||
.event.time = event.time,
|
||||
.event.pressed = false
|
||||
});
|
||||
} else {
|
||||
debug("Tapping: Start while last timeout tap(1).\n");
|
||||
}
|
||||
tapping_key = *keyp;
|
||||
waiting_buffer_scan_tap();
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (!IS_NOEVENT(keyp->event)) {
|
||||
debug("Tapping: key event while last timeout tap(>0).\n");
|
||||
}
|
||||
process_action(keyp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (IS_TAPPING_RELEASED()) {
|
||||
if (WITHIN_TAPPING_TERM(event)) {
|
||||
if (tapping_key.tap_count > 0 && IS_TAPPING_KEY(event.key) && event.pressed) {
|
||||
// sequential tap.
|
||||
keyp->tap_count = tapping_key.tap_count + 1;
|
||||
debug("Tapping: Tap press("); debug_dec(keyp->tap_count); debug(")\n");
|
||||
process_action(keyp);
|
||||
tapping_key = *keyp;
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
} else if (event.pressed && is_tap_key(event.key)) {
|
||||
// Sequential tap can be interfered with other tap key.
|
||||
debug("Tapping: Start with interfering other tap.\n");
|
||||
tapping_key = *keyp;
|
||||
waiting_buffer_scan_tap();
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
} else {
|
||||
if (!IS_NOEVENT(keyp->event)) debug("Tapping: other key just after tap.\n");
|
||||
process_action(keyp);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// timeout. no sequential tap.
|
||||
debug("Tapping: End(Timeout after releasing last tap): ");
|
||||
debug_event(event); debug("\n");
|
||||
tapping_key = (keyrecord_t){};
|
||||
debug_tapping_key();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// not tapping satate
|
||||
else {
|
||||
if (event.pressed && is_tap_key(event.key)) {
|
||||
debug("Tapping: Start(Press tap key).\n");
|
||||
tapping_key = *keyp;
|
||||
waiting_buffer_scan_tap();
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
} else {
|
||||
process_action(keyp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* scan buffer for tapping */
|
||||
static void waiting_buffer_scan_tap(void)
|
||||
{
|
||||
// tapping already is settled
|
||||
if (tapping_key.tap_count > 0) return;
|
||||
// invalid state: tapping_key released && tap_count == 0
|
||||
if (!tapping_key.event.pressed) return;
|
||||
|
||||
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
||||
if (IS_TAPPING_KEY(waiting_buffer[i].event.key) &&
|
||||
!waiting_buffer[i].event.pressed &&
|
||||
WITHIN_TAPPING_TERM(waiting_buffer[i].event)) {
|
||||
tapping_key.tap_count = 1;
|
||||
waiting_buffer[i].tap_count = 1;
|
||||
process_action(&tapping_key);
|
||||
|
||||
debug("waiting_buffer_scan_tap: found at ["); debug_dec(i); debug("]\n");
|
||||
debug_waiting_buffer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Utilities for actions.
|
||||
*/
|
||||
void register_code(uint8_t code)
|
||||
{
|
||||
if (code == KC_NO) {
|
||||
return;
|
||||
}
|
||||
else if IS_KEY(code) {
|
||||
// TODO: should push command_proc out of this block?
|
||||
if (command_proc(code)) return;
|
||||
|
||||
if (oneshot_state.mods && oneshot_state.ready && !oneshot_state.disabled) {
|
||||
uint8_t tmp_mods = host_get_mods();
|
||||
host_add_mods(oneshot_state.mods);
|
||||
host_add_key(code);
|
||||
host_send_keyboard_report();
|
||||
|
||||
host_set_mods(tmp_mods);
|
||||
oneshot_state.ready = false;
|
||||
} else {
|
||||
host_add_key(code);
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
}
|
||||
else if IS_MOD(code) {
|
||||
host_add_mods(MOD_BIT(code));
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
}
|
||||
|
||||
void unregister_code(uint8_t code)
|
||||
{
|
||||
if IS_KEY(code) {
|
||||
host_del_key(code);
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
else if IS_MOD(code) {
|
||||
host_del_mods(MOD_BIT(code));
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
}
|
||||
|
||||
void add_mods(uint8_t mods)
|
||||
{
|
||||
if (mods) {
|
||||
host_add_mods(mods);
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
}
|
||||
|
||||
void del_mods(uint8_t mods)
|
||||
{
|
||||
if (mods) {
|
||||
host_del_mods(mods);
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
}
|
||||
|
||||
void set_mods(uint8_t mods)
|
||||
{
|
||||
host_set_mods(mods);
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
|
||||
void clear_keyboard(void)
|
||||
{
|
||||
host_clear_mods();
|
||||
clear_keyboard_but_mods();
|
||||
}
|
||||
|
||||
void clear_keyboard_but_mods(void)
|
||||
{
|
||||
host_clear_keys();
|
||||
host_send_keyboard_report();
|
||||
#ifdef MOUSEKEY_ENABLE
|
||||
mousekey_clear();
|
||||
mousekey_send();
|
||||
#endif
|
||||
#ifdef EXTRAKEY_ENABLE
|
||||
host_system_send(0);
|
||||
host_consumer_send(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool sending_anykey(void)
|
||||
{
|
||||
return (host_has_anykey() || host_mouse_in_use() ||
|
||||
host_last_sysytem_report() || host_last_consumer_report());
|
||||
}
|
||||
|
||||
void layer_switch(uint8_t new_layer)
|
||||
{
|
||||
if (current_layer != new_layer) {
|
||||
debug("Layer Switch: "); debug_hex(current_layer);
|
||||
debug(" -> "); debug_hex(new_layer); debug("\n");
|
||||
|
||||
current_layer = new_layer;
|
||||
clear_keyboard_but_mods(); // To avoid stuck keys
|
||||
// NOTE: update mods with full scan of matrix? if modifier changes between layers
|
||||
}
|
||||
}
|
||||
|
||||
bool is_tap_key(key_t key)
|
||||
{
|
||||
action_t action = keymap_get_action(current_layer, key.pos.row, key.pos.col);
|
||||
switch (action.kind.id) {
|
||||
case ACT_LMODS_TAP:
|
||||
case ACT_RMODS_TAP:
|
||||
return true;
|
||||
case ACT_LAYER:
|
||||
case ACT_LAYER_BIT:
|
||||
switch (action.layer.code) {
|
||||
case LAYER_MOMENTARY:
|
||||
case LAYER_ON_PRESS:
|
||||
case LAYER_ON_RELEASE:
|
||||
case LAYER_DEFAULT:
|
||||
return false;
|
||||
case LAYER_TAP_TOGGLE:
|
||||
default: /* tap key */
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case ACT_FUNCTION:
|
||||
if (action.func.opt & FUNC_TAP) { return true; }
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* debug print
|
||||
*/
|
||||
static void debug_event(keyevent_t event)
|
||||
{
|
||||
debug_hex16(event.key.raw);
|
||||
if (event.pressed) debug("d("); else debug("u(");
|
||||
debug_dec(event.time); debug(")");
|
||||
}
|
||||
static void debug_record(keyrecord_t record)
|
||||
{
|
||||
debug_event(record.event); debug(":"); debug_dec(record.tap_count);
|
||||
}
|
||||
static void debug_action(action_t action)
|
||||
{
|
||||
switch (action.kind.id) {
|
||||
case ACT_LMODS: debug("ACT_LMODS"); break;
|
||||
case ACT_RMODS: debug("ACT_RMODS"); break;
|
||||
case ACT_LMODS_TAP: debug("ACT_LMODS_TAP"); break;
|
||||
case ACT_RMODS_TAP: debug("ACT_RMODS_TAP"); break;
|
||||
case ACT_USAGE: debug("ACT_USAGE"); break;
|
||||
case ACT_MOUSEKEY: debug("ACT_MOUSEKEY"); break;
|
||||
case ACT_LAYER: debug("ACT_LAYER"); break;
|
||||
case ACT_LAYER_BIT: debug("ACT_LAYER_BIT"); break;
|
||||
case ACT_MACRO: debug("ACT_MACRO"); break;
|
||||
case ACT_COMMAND: debug("ACT_COMMAND"); break;
|
||||
case ACT_FUNCTION: debug("ACT_FUNCTION"); break;
|
||||
default: debug("UNKNOWN"); break;
|
||||
}
|
||||
debug("[");
|
||||
debug_hex4(action.kind.param>>8);
|
||||
debug(":");
|
||||
debug_hex8(action.kind.param & 0xff);
|
||||
debug("]");
|
||||
}
|
||||
static void debug_tapping_key(void)
|
||||
{
|
||||
debug("TAPPING_KEY="); debug_record(tapping_key); debug("\n");
|
||||
}
|
||||
static void debug_waiting_buffer(void)
|
||||
{
|
||||
debug("{ ");
|
||||
for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
|
||||
debug("["); debug_dec(i); debug("]="); debug_record(waiting_buffer[i]); debug(" ");
|
||||
}
|
||||
debug("}\n");
|
||||
}
|
331
common/action.h
Normal file
331
common/action.h
Normal file
@ -0,0 +1,331 @@
|
||||
/*
|
||||
Copyright 2012,2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef ACTION_H
|
||||
#define ACTION_H
|
||||
|
||||
#include "keyboard.h"
|
||||
#include "keycode.h"
|
||||
|
||||
|
||||
/* Execute action per keyevent */
|
||||
void action_exec(keyevent_t event);
|
||||
|
||||
|
||||
/* Struct to record event and tap count */
|
||||
typedef struct {
|
||||
keyevent_t event;
|
||||
uint8_t tap_count;
|
||||
} keyrecord_t;
|
||||
|
||||
/* Action struct.
|
||||
*
|
||||
* In avr-gcc bit field seems to be assigned from LSB(bit0) to MSB(bit15).
|
||||
* AVR looks like a little endian in avr-gcc.
|
||||
*
|
||||
* NOTE: not portable across compiler/endianness?
|
||||
* Byte order and bit order of 0x1234:
|
||||
* Big endian: 15 ... 8 7 ... 210
|
||||
* | 0x12 | 0x34 |
|
||||
* 0001 0010 0011 0100
|
||||
* Little endian: 012 ... 7 8 ... 15
|
||||
* | 0x34 | 0x12 |
|
||||
* 0010 1100 0100 1000
|
||||
*/
|
||||
typedef union {
|
||||
uint16_t code;
|
||||
struct action_kind {
|
||||
uint16_t param :12;
|
||||
uint8_t id :4;
|
||||
} kind;
|
||||
struct action_key {
|
||||
uint8_t code :8;
|
||||
uint8_t mods :4;
|
||||
uint8_t kind :4;
|
||||
} key;
|
||||
struct action_layer {
|
||||
uint8_t code :8;
|
||||
uint8_t val :4;
|
||||
uint8_t kind :4;
|
||||
} layer;
|
||||
struct action_usage {
|
||||
uint16_t code :10;
|
||||
uint8_t page :2;
|
||||
uint8_t kind :4;
|
||||
} usage;
|
||||
struct action_command {
|
||||
uint8_t id :8;
|
||||
uint8_t opt :4;
|
||||
uint8_t kind :4;
|
||||
} command;
|
||||
struct action_function {
|
||||
uint8_t id :8;
|
||||
uint8_t opt :4;
|
||||
uint8_t kind :4;
|
||||
} func;
|
||||
} action_t;
|
||||
|
||||
|
||||
/*
|
||||
* Utilities for actions.
|
||||
*/
|
||||
void register_code(uint8_t code);
|
||||
void unregister_code(uint8_t code);
|
||||
void add_mods(uint8_t mods);
|
||||
void del_mods(uint8_t mods);
|
||||
void set_mods(uint8_t mods);
|
||||
void clear_keyboard(void);
|
||||
void clear_keyboard_but_mods(void);
|
||||
bool sending_anykey(void);
|
||||
void layer_switch(uint8_t new_layer);
|
||||
bool is_tap_key(key_t key);
|
||||
bool waiting_buffer_has_anykey_pressed(void);
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Action codes
|
||||
* ============
|
||||
* 16bit code: action_kind(4bit) + action_parameter(12bit)
|
||||
*
|
||||
Keyboard Keys
|
||||
-------------
|
||||
ACT_LMODS(0000):
|
||||
0000|0000|000000|00 No action
|
||||
0000|0000| keycode Key
|
||||
0000|mods|000000|00 Left mods
|
||||
0000|mods| keycode Key & Left mods
|
||||
|
||||
ACT_RMODS(0001):
|
||||
0001|0000|000000|00 No action
|
||||
0001|0000| keycode Key(no used)
|
||||
0001|mods|000000|00 Right mods
|
||||
0001|mods| keycode Key & Right mods
|
||||
|
||||
ACT_LMODS_TAP(0010):
|
||||
0010|mods|000000|00 Left mods OneShot
|
||||
0010|mods|000000|01 (reserved)
|
||||
0010|mods|000000|10 (reserved)
|
||||
0010|mods|000000|11 (reserved)
|
||||
0010|mods| keycode Left mods + tap Key
|
||||
|
||||
ACT_RMODS_TAP(0011):
|
||||
0011|mods|000000|00 Right mods OneShot
|
||||
0011|mods|000000|01 (reserved)
|
||||
0011|mods|000000|10 (reserved)
|
||||
0011|mods|000000|11 (reserved)
|
||||
0011|mods| keycode Right mods + tap Key
|
||||
|
||||
|
||||
Other HID Usage
|
||||
---------------
|
||||
This action handles other usages than keyboard.
|
||||
ACT_USAGE(0100):
|
||||
0100|00| usage(10) System control(0x80) - General Desktop page(0x01)
|
||||
0100|01| usage(10) Consumer control(0x01) - Consumer page(0x0C)
|
||||
0100|10| usage(10) (reserved)
|
||||
0100|11| usage(10) (reserved)
|
||||
|
||||
|
||||
Mouse Keys
|
||||
----------
|
||||
TODO: can be combined with 'Other HID Usage'? to save action kind id.
|
||||
ACT_MOUSEKEY(0110):
|
||||
0101|XXXX| keycode Mouse key
|
||||
|
||||
|
||||
Layer Actions
|
||||
-------------
|
||||
ACT_LAYER(1000): Set layer
|
||||
ACT_LAYER_BIT(1001): Bit-op layer
|
||||
|
||||
1000|LLLL|0000 0000 set L to layer on press and set default on release(momentary)
|
||||
1000|LLLL|0000 0001 set L to layer on press
|
||||
1000|LLLL|0000 0010 set L to layer on release
|
||||
1000|----|0000 0011 set default to layer on both(return to default layer)
|
||||
1000|LLLL|xxxx xxxx set L to layer while hold and send key on tap
|
||||
1000|LLLL|1111 0000 set L to layer while hold and toggle on several taps
|
||||
1000|LLLL|1111 1111 set L to default and layer(on press)
|
||||
|
||||
1001|BBBB|0000 0000 (not used)
|
||||
1001|BBBB|0000 0001 bit-xor layer with B on press
|
||||
1001|BBBB|0000 0010 bit-xor layer with B on release
|
||||
1001|BBBB|0000 0011 bit-xor layer with B on both(momentary)
|
||||
1001|BBBB|xxxx xxxx bit-xor layer with B while hold and send key on tap
|
||||
1001|BBBB|1111 0000 bit-xor layer with B while hold and toggle on several taps
|
||||
1001|BBBB|1111 1111 bit-xor default with B and set layer(on press)
|
||||
|
||||
|
||||
|
||||
Extensions(11XX)
|
||||
----------------
|
||||
NOTE: NOT FIXED
|
||||
|
||||
ACT_MACRO(1100):
|
||||
1100|opt | id(8) Macro play?
|
||||
1100|1111| id(8) Macro record?
|
||||
|
||||
ACT_COMMAND(1110):
|
||||
1110|opt | id(8) Built-in Command exec
|
||||
|
||||
ACT_FUNCTION(1111):
|
||||
1111| address(12) Function?
|
||||
1111|opt | id(8) Function?
|
||||
|
||||
*/
|
||||
enum action_kind_id {
|
||||
ACT_LMODS = 0b0000,
|
||||
ACT_RMODS = 0b0001,
|
||||
ACT_LMODS_TAP = 0b0010,
|
||||
ACT_RMODS_TAP = 0b0011,
|
||||
|
||||
ACT_USAGE = 0b0100,
|
||||
ACT_MOUSEKEY = 0b0101,
|
||||
|
||||
ACT_LAYER = 0b1000,
|
||||
ACT_LAYER_BIT = 0b1001,
|
||||
|
||||
ACT_MACRO = 0b1100,
|
||||
ACT_COMMAND = 0b1110,
|
||||
ACT_FUNCTION = 0b1111
|
||||
};
|
||||
|
||||
|
||||
/* action utility */
|
||||
#define ACTION_NO 0
|
||||
#define ACTION(kind, param) ((kind)<<12 | (param))
|
||||
#define MODS4(mods) (((mods)>>4 | (mods)) & 0x0F)
|
||||
|
||||
/* Key */
|
||||
#define ACTION_KEY(key) ACTION(ACT_LMODS, key)
|
||||
/* Mods & key */
|
||||
#define ACTION_LMODS(mods) ACTION(ACT_LMODS, (mods)<<8 | 0x00)
|
||||
#define ACTION_LMODS_KEY(mods, key) ACTION(ACT_LMODS, (mods)<<8 | (key))
|
||||
#define ACTION_RMODS(mods) ACTION(ACT_RMODS, (mods)<<8 | 0x00)
|
||||
#define ACTION_RMODS_KEY(mods, key) ACTION(ACT_RMODS, (mods)<<8 | (key))
|
||||
/* Mod & key */
|
||||
#define ACTION_LMOD(mod) ACTION(ACT_LMODS, MODS4(MOD_BIT(mod))<<8 | 0x00)
|
||||
#define ACTION_LMOD_KEY(mod, key) ACTION(ACT_LMODS, MODS4(MOD_BIT(mod))<<8 | (key))
|
||||
#define ACTION_RMOD(mod) ACTION(ACT_RMODS, MODS4(MOD_BIT(mod))<<8 | 0x00)
|
||||
#define ACTION_RMOD_KEY(mod, key) ACTION(ACT_RMODS, MODS4(MOD_BIT(mod))<<8 | (key))
|
||||
|
||||
/* Mods + Tap key */
|
||||
enum mods_codes {
|
||||
MODS_ONESHOT = 0x00,
|
||||
};
|
||||
#define ACTION_LMODS_TAP_KEY(mods, key) ACTION(ACT_LMODS_TAP, MODS4(mods)<<8 | (key))
|
||||
#define ACTION_LMODS_ONESHOT(mods) ACTION(ACT_LMODS_TAP, MODS4(mods)<<8 | MODS_ONESHOT)
|
||||
#define ACTION_RMODS_TAP_KEY(mods, key) ACTION(ACT_RMODS_TAP, MODS4(mods)<<8 | (key))
|
||||
#define ACTION_RMODS_ONESHOT(mods) ACTION(ACT_RMODS_TAP, MODS4(mods)<<8 | MODS_ONESHOT)
|
||||
/* Mod + Tap key */
|
||||
#define ACTION_LMOD_TAP_KEY(mod, key) ACTION(ACT_LMODS_TAP, MODS4(MOD_BIT(mod))<<8 | (key))
|
||||
#define ACTION_LMOD_ONESHOT(mod) ACTION(ACT_LMODS_TAP, MODS4(MOD_BIT(mod))<<8 | MODS_ONESHOT)
|
||||
#define ACTION_RMOD_TAP_KEY(mod, key) ACTION(ACT_RMODS_TAP, MODS4(MOD_BIT(mod))<<8 | (key))
|
||||
#define ACTION_RMOD_ONESHOT(mod) ACTION(ACT_RMODS_TAP, MODS4(MOD_BIT(mod))<<8 | MODS_ONESHOT)
|
||||
|
||||
|
||||
/*
|
||||
* Switch layer
|
||||
*/
|
||||
enum layer_codes {
|
||||
LAYER_MOMENTARY = 0,
|
||||
LAYER_ON_PRESS = 1,
|
||||
LAYER_ON_RELEASE = 2,
|
||||
LAYER_DEFAULT =3,
|
||||
LAYER_TAP_TOGGLE = 0xF0,
|
||||
LAYER_CHANGE_DEFAULT = 0xFF
|
||||
};
|
||||
enum layer_vals_default {
|
||||
DEFAULT_ON_PRESS = 1,
|
||||
DEFAULT_ON_RELEASE = 2,
|
||||
DEFAULT_ON_BOTH = 3,
|
||||
};
|
||||
|
||||
/*
|
||||
* return to default layer
|
||||
*/
|
||||
#define ACTION_LAYER_DEFAULT ACTION_LAYER_DEFAULT_R
|
||||
/* set default layer on press */
|
||||
#define ACTION_LAYER_DEFAULT_P ACTION(ACT_LAYER, DEFAULT_ON_PRESS<<8 | LAYER_DEFAULT)
|
||||
/* set default layer on release */
|
||||
#define ACTION_LAYER_DEFAULT_R ACTION(ACT_LAYER, DEFAULT_ON_RELEASE<<8 | LAYER_DEFAULT)
|
||||
/* change default layer and set layer */
|
||||
|
||||
/*
|
||||
* Set layer
|
||||
*/
|
||||
/* set layer on press and set default on release */
|
||||
#define ACTION_LAYER_SET(layer) ACTION_LAYER_SET_MOMENTARY(layer)
|
||||
#define ACTION_LAYER_SET_MOMENTARY(layer) ACTION(ACT_LAYER, (layer)<<8 | LAYER_MOMENTARY)
|
||||
/* set layer on press and none on release */
|
||||
#define ACTION_LAYER_SET_TOGGLE(layer) ACTION_LAYER_SET_R(layer)
|
||||
/* set layer while hold and send key on tap */
|
||||
#define ACTION_LAYER_SET_TAP_KEY(layer, key) ACTION(ACT_LAYER, (layer)<<8 | (key))
|
||||
/* set layer on press */
|
||||
#define ACTION_LAYER_SET_P(layer) ACTION(ACT_LAYER, (layer)<<8 | LAYER_ON_PRESS)
|
||||
/* set layer on release */
|
||||
#define ACTION_LAYER_SET_R(layer) ACTION(ACT_LAYER, (layer)<<8 | LAYER_ON_RELEASE)
|
||||
/* set layer on hold and toggle on several taps */
|
||||
#define ACTION_LAYER_SET_TAP_TOGGLE(layer) ACTION(ACT_LAYER, (layer)<<8 | LAYER_TAP_TOGGLE)
|
||||
/* set default layer on both press and release */
|
||||
#define ACTION_LAYER_SET_DEFAULT(layer) ACTION(ACT_LAYER, (layer)<<8 | LAYER_CHANGE_DEFAULT)
|
||||
|
||||
/*
|
||||
* Bit-op layer
|
||||
*/
|
||||
/* bit-xor on both press and release */
|
||||
#define ACTION_LAYER_BIT(bits) ACTION_LAYER_BIT_MOMENTARY(bits)
|
||||
#define ACTION_LAYER_BIT_MOMENTARY(bits) ACTION(ACT_LAYER_BIT, (bits)<<8 | LAYER_MOMENTARY)
|
||||
/* bit-xor on press */
|
||||
#define ACTION_LAYER_BIT_TOGGLE(bits) ACTION_LAYER_BIT_R(bits)
|
||||
/* bit-xor while hold and send key on tap */
|
||||
#define ACTION_LAYER_BIT_TAP_KEY(bits, key) ACTION(ACT_LAYER_BIT, (bits)<<8 | (key))
|
||||
/* bit-xor on press */
|
||||
#define ACTION_LAYER_BIT_P(bits) ACTION(ACT_LAYER_BIT, (bits)<<8 | LAYER_ON_PRESS)
|
||||
/* bit-xor on release */
|
||||
#define ACTION_LAYER_BIT_R(bits) ACTION(ACT_LAYER_BIT, (bits)<<8 | LAYER_ON_RELEASE)
|
||||
/* bit-xor while hold and toggle on several taps */
|
||||
#define ACTION_LAYER_BIT_TAP_TOGGLE(bits) ACTION(ACT_LAYER_BIT, (bits)<<8 | LAYER_TAP_TOGGLE)
|
||||
/* bit-xor default layer and set layer */
|
||||
#define ACTION_LAYER_BIT_DEFAULT(bits) ACTION(ACT_LAYER, (bits)<<8 | LAYER_CHANGE_DEFAULT)
|
||||
|
||||
|
||||
/* HID Usage */
|
||||
enum usage_pages {
|
||||
PAGE_SYSTEM,
|
||||
PAGE_CONSUMER
|
||||
};
|
||||
#define ACTION_USAGE_SYSTEM(id) ACTION(ACT_USAGE, PAGE_SYSTEM<<10 | (id))
|
||||
#define ACTION_USAGE_CONSUMER(id) ACTION(ACT_USAGE, PAGE_CONSUMER<<10 | (id))
|
||||
|
||||
/* Mousekey */
|
||||
#define ACTION_MOUSEKEY(key) ACTION(ACT_MOUSEKEY, key)
|
||||
|
||||
/* Macro */
|
||||
#define ACTION_MACRO(opt, id) ACTION(ACT_FUNCTION, (opt)<<8 | (addr))
|
||||
|
||||
/* Command */
|
||||
#define ACTION_COMMAND(opt, id) ACTION(ACT_COMMAND, (opt)<<8 | (addr))
|
||||
|
||||
/* Function */
|
||||
enum function_opts {
|
||||
FUNC_TAP = 0x8, /* indciates function is tappable */
|
||||
};
|
||||
#define ACTION_FUNCTION(id, opt) ACTION(ACT_FUNCTION, (opt)<<8 | id)
|
||||
#define ACTION_FUNCTION_TAP(id) ACTION(ACT_FUNCTION, FUNC_TAP<<8 | id)
|
||||
|
||||
#endif /* ACTION_H */
|
67
common/action_macro.c
Normal file
67
common/action_macro.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <util/delay.h>
|
||||
#include "debug.h"
|
||||
#include "action.h"
|
||||
#include "action_macro.h"
|
||||
|
||||
|
||||
#define MACRO_READ() (macro = pgm_read_byte(macro_p++))
|
||||
void action_macro_play(const prog_macro_t *macro_p)
|
||||
{
|
||||
macro_t macro = END;
|
||||
uint8_t interval = 0;
|
||||
|
||||
if (!macro_p) return;
|
||||
while (true) {
|
||||
switch (MACRO_READ()) {
|
||||
case INTERVAL:
|
||||
interval = MACRO_READ();
|
||||
debug("INTERVAL("); debug_dec(interval); debug(")\n");
|
||||
break;
|
||||
case WAIT:
|
||||
MACRO_READ();
|
||||
debug("WAIT("); debug_dec(macro); debug(")\n");
|
||||
{ uint8_t ms = macro; while (ms--) _delay_ms(1); }
|
||||
break;
|
||||
case MODS_DOWN:
|
||||
MACRO_READ();
|
||||
debug("MODS_DOWN("); debug_hex(macro); debug(")\n");
|
||||
debug("MODS_UP("); debug_hex(macro); debug(")\n");
|
||||
add_mods(macro);
|
||||
break;
|
||||
case MODS_UP:
|
||||
MACRO_READ();
|
||||
debug("MODS_UP("); debug_hex(macro); debug(")\n");
|
||||
del_mods(macro);
|
||||
break;
|
||||
case 0x04 ... 0x73:
|
||||
debug("DOWN("); debug_hex(macro); debug(")\n");
|
||||
register_code(macro);
|
||||
break;
|
||||
case 0x84 ... 0xF3:
|
||||
debug("UP("); debug_hex(macro); debug(")\n");
|
||||
unregister_code(macro&0x7F);
|
||||
break;
|
||||
case END:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
// interval
|
||||
{ uint8_t ms = interval; while (ms--) _delay_ms(1); }
|
||||
}
|
||||
}
|
107
common/action_macro.h
Normal file
107
common/action_macro.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef ACTION_MACRO_H
|
||||
#define ACTION_MACRO_H
|
||||
#include <stdint.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
|
||||
typedef uint8_t macro_t;
|
||||
typedef macro_t prog_macro_t PROGMEM;
|
||||
|
||||
|
||||
void action_macro_play(const prog_macro_t *macro);
|
||||
|
||||
|
||||
|
||||
/* TODO: NOT FINISHED
|
||||
normal mode command:
|
||||
key(down): 0x04-7f/73(F24)
|
||||
key(up): 0x84-ff
|
||||
command: 0x00-03, 0x80-83(0x74-7f, 0xf4-ff)
|
||||
mods down 0x00
|
||||
mods up 0x01
|
||||
wait 0x02
|
||||
interval 0x03
|
||||
extkey down 0x80
|
||||
extkey up 0x81
|
||||
ext commad 0x82
|
||||
ext mode 0x83
|
||||
end 0xff
|
||||
|
||||
extension mode command: NOT IMPLEMENTED
|
||||
key down 0x00
|
||||
key up 0x01
|
||||
key down + wait
|
||||
key up + wait
|
||||
mods push
|
||||
mods pop
|
||||
wait
|
||||
interval
|
||||
if
|
||||
loop
|
||||
push
|
||||
pop
|
||||
all up
|
||||
end
|
||||
*/
|
||||
enum macro_command_id{
|
||||
/* 0x00 - 0x03 */
|
||||
END = 0x00,
|
||||
MODS_DOWN = 0x01,
|
||||
MODS_UP = 0x02,
|
||||
MODS_SET,
|
||||
MODS_PUSH,
|
||||
MODS_POP,
|
||||
|
||||
WAIT = 0x74,
|
||||
INTERVAL,
|
||||
/* 0x74 - 0x7f */
|
||||
/* 0x80 - 0x84 */
|
||||
|
||||
EXT_DOWN,
|
||||
EXT_UP,
|
||||
EXT_WAIT,
|
||||
EXT_INTERVAL,
|
||||
COMPRESSION_MODE,
|
||||
|
||||
EXTENSION_MODE = 0xff,
|
||||
};
|
||||
|
||||
|
||||
/* normal mode */
|
||||
#define DOWN(key) (key)
|
||||
#define UP(key) ((key) | 0x80)
|
||||
#define TYPE(key) (key), (key | 0x80)
|
||||
#define MODS_DOWN(mods) MODS_DOWN, (mods)
|
||||
#define MODS_UP(mods) MODS_UP, (mods)
|
||||
#define WAIT(ms) WAIT, (ms)
|
||||
#define INTERVAL(ms) INTERVAL, (ms)
|
||||
|
||||
#define D(key) DOWN(KC_##key)
|
||||
#define U(key) UP(KC_##key)
|
||||
#define T(key) TYPE(KC_##key)
|
||||
#define MD(key) MODS_DOWN(MOD_BIT(KC_##key))
|
||||
#define MU(key) MODS_UP(MOD_BIT(KC_##key))
|
||||
#define W(ms) WAIT(ms)
|
||||
#define I(ms) INTERVAL(ms)
|
||||
|
||||
|
||||
/* extension mode */
|
||||
|
||||
|
||||
#endif /* ACTION_MACRO_H */
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include <util/delay.h>
|
||||
#include "keycode.h"
|
||||
#include "host.h"
|
||||
#include "keymap.h"
|
||||
#include "print.h"
|
||||
#include "debug.h"
|
||||
#include "util.h"
|
||||
@ -53,7 +54,6 @@ static void mousekey_console_help(void);
|
||||
|
||||
static uint8_t numkey2num(uint8_t code);
|
||||
static void switch_layer(uint8_t layer);
|
||||
static void clear_keyboard(void);
|
||||
|
||||
|
||||
typedef enum { ONESHOT, CONSOLE, MOUSEKEY } cmdstate_t;
|
||||
@ -556,18 +556,3 @@ static void switch_layer(uint8_t layer)
|
||||
default_layer = layer;
|
||||
print("switch to "); print_val_hex8(layer);
|
||||
}
|
||||
|
||||
static void clear_keyboard(void)
|
||||
{
|
||||
host_clear_keys();
|
||||
host_clear_mods();
|
||||
host_send_keyboard_report();
|
||||
|
||||
host_system_send(0);
|
||||
host_consumer_send(0);
|
||||
|
||||
#ifdef MOUSEKEY_ENABLE
|
||||
mousekey_clear();
|
||||
mousekey_send();
|
||||
#endif
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#define debug_dec(data) do { if (debug_enable) print_dec(data); } while (0)
|
||||
#define debug_decs(data) do { if (debug_enable) print_decs(data); } while (0)
|
||||
#define debug_hex4(data) do { if (debug_enable) print_hex4(data); } while (0)
|
||||
#define debug_hex8(data) do { if (debug_enable) print_hex8(data); } while (0)
|
||||
#define debug_hex16(data) do { if (debug_enable) print_hex16(data); } while (0)
|
||||
#define debug_hex32(data) do { if (debug_enable) print_hex32(data); } while (0)
|
||||
|
@ -127,14 +127,19 @@ void host_clear_keys(void)
|
||||
}
|
||||
}
|
||||
|
||||
void host_add_mod_bit(uint8_t mod)
|
||||
uint8_t host_get_mods(void)
|
||||
{
|
||||
keyboard_report->mods |= mod;
|
||||
return keyboard_report->mods;
|
||||
}
|
||||
|
||||
void host_del_mod_bit(uint8_t mod)
|
||||
void host_add_mods(uint8_t mods)
|
||||
{
|
||||
keyboard_report->mods &= ~mod;
|
||||
keyboard_report->mods |= mods;
|
||||
}
|
||||
|
||||
void host_del_mods(uint8_t mods)
|
||||
{
|
||||
keyboard_report->mods &= ~mods;
|
||||
}
|
||||
|
||||
void host_set_mods(uint8_t mods)
|
||||
|
@ -52,10 +52,13 @@ void host_consumer_send(uint16_t data);
|
||||
void host_add_key(uint8_t key);
|
||||
void host_del_key(uint8_t key);
|
||||
void host_clear_keys(void);
|
||||
void host_add_mod_bit(uint8_t mod);
|
||||
void host_del_mod_bit(uint8_t mod);
|
||||
|
||||
uint8_t host_get_mods(void);
|
||||
void host_add_mods(uint8_t mods);
|
||||
void host_del_mods(uint8_t mods);
|
||||
void host_set_mods(uint8_t mods);
|
||||
void host_clear_mods(void);
|
||||
|
||||
uint8_t host_has_anykey(void);
|
||||
uint8_t host_has_anymod(void);
|
||||
uint8_t host_get_first_key(void);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2011,2012 Jun Wako <wakojun@gmail.com>
|
||||
Copyright 2011,2012,2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -26,536 +26,39 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "command.h"
|
||||
#include "util.h"
|
||||
#include "sendchar.h"
|
||||
#include "bootloader.h"
|
||||
#ifdef MOUSEKEY_ENABLE
|
||||
#include "mousekey.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define Kdebug(s) do { if (debug_keyboard) debug(s); } while(0)
|
||||
#define Kdebug_P(s) do { if (debug_keyboard) debug_P(s); } while(0)
|
||||
#define Kdebug_hex(s) do { if (debug_keyboard) debug_hex(s); } while(0)
|
||||
|
||||
#define LAYER_DELAY 250
|
||||
|
||||
typedef enum keykind {
|
||||
NONE,
|
||||
FN_DOWN, FN_UP,
|
||||
FNK_DOWN, FNK_UP,
|
||||
KEY_DOWN, KEY_UP,
|
||||
MOD_DOWN, MOD_UP,
|
||||
} keykind_t;
|
||||
|
||||
typedef enum { IDLE, DELAYING, WAITING, PRESSING } kbdstate_t;
|
||||
|
||||
|
||||
#ifdef KEYMAP_DEFAULT_LAYER
|
||||
uint8_t default_layer = KEYMAP_DEFAULT_LAYER;
|
||||
uint8_t current_layer = KEYMAP_DEFAULT_LAYER;
|
||||
#else
|
||||
uint8_t default_layer = 0;
|
||||
uint8_t current_layer = 0;
|
||||
#endif
|
||||
|
||||
/* keyboard internal states */
|
||||
static kbdstate_t kbdstate = IDLE;
|
||||
static uint8_t fn_state_bits = 0;
|
||||
static keyrecord_t delayed_fn;
|
||||
static keyrecord_t waiting_key;
|
||||
|
||||
|
||||
static const char *state_str(kbdstate_t state)
|
||||
{
|
||||
if (state == IDLE) return PSTR("IDLE");
|
||||
if (state == DELAYING) return PSTR("DELAYING");
|
||||
if (state == WAITING) return PSTR("WAITING");
|
||||
if (state == PRESSING) return PSTR("PRESSING");
|
||||
return PSTR("UNKNOWN");
|
||||
}
|
||||
|
||||
static inline keykind_t get_keykind(uint8_t code, bool pressed)
|
||||
{
|
||||
if IS_KEY(code) return (pressed ? KEY_DOWN : KEY_UP);
|
||||
if IS_MOD(code) return (pressed ? MOD_DOWN : MOD_UP);
|
||||
if IS_FN(code) {
|
||||
if (keymap_fn_keycode(FN_INDEX(code)))
|
||||
return (pressed ? FNK_DOWN : FNK_UP);
|
||||
else
|
||||
return (pressed ? FN_DOWN : FN_UP);
|
||||
}
|
||||
if IS_MOUSEKEY(code) return (pressed ? KEY_DOWN : KEY_UP);
|
||||
if IS_SYSTEM(code) return (pressed ? KEY_DOWN : KEY_UP);
|
||||
if IS_CONSUMER(code) return (pressed ? KEY_DOWN : KEY_UP);
|
||||
return NONE;
|
||||
}
|
||||
|
||||
static void clear_keyboard(void)
|
||||
{
|
||||
host_clear_keys();
|
||||
host_clear_mods();
|
||||
host_send_keyboard_report();
|
||||
|
||||
host_system_send(0);
|
||||
host_consumer_send(0);
|
||||
|
||||
#ifdef MOUSEKEY_ENABLE
|
||||
mousekey_clear();
|
||||
mousekey_send();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void clear_keyboard_but_mods(void)
|
||||
{
|
||||
host_clear_keys();
|
||||
host_send_keyboard_report();
|
||||
|
||||
host_system_send(0);
|
||||
host_consumer_send(0);
|
||||
|
||||
#ifdef MOUSEKEY_ENABLE
|
||||
mousekey_clear();
|
||||
mousekey_send();
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool anykey_sent_to_host(void)
|
||||
{
|
||||
return (host_has_anykey() || host_mouse_in_use() ||
|
||||
host_last_sysytem_report() || host_last_consumer_report());
|
||||
}
|
||||
|
||||
static void layer_switch_on(uint8_t code)
|
||||
{
|
||||
if (!IS_FN(code)) return;
|
||||
fn_state_bits |= FN_BIT(code);
|
||||
uint8_t new_layer = (fn_state_bits ? keymap_fn_layer(biton(fn_state_bits)) : default_layer);
|
||||
if (current_layer != new_layer) {
|
||||
Kdebug("Layer Switch(on): "); Kdebug_hex(current_layer);
|
||||
Kdebug(" -> "); Kdebug_hex(new_layer); Kdebug("\n");
|
||||
|
||||
clear_keyboard_but_mods();
|
||||
current_layer = new_layer;
|
||||
}
|
||||
}
|
||||
|
||||
static bool layer_switch_off(uint8_t code)
|
||||
{
|
||||
if (!IS_FN(code)) return false;
|
||||
fn_state_bits &= ~FN_BIT(code);
|
||||
uint8_t new_layer = (fn_state_bits ? keymap_fn_layer(biton(fn_state_bits)) : default_layer);
|
||||
if (current_layer != new_layer) {
|
||||
Kdebug("Layer Switch(off): "); Kdebug_hex(current_layer);
|
||||
Kdebug(" -> "); Kdebug_hex(new_layer); Kdebug("\n");
|
||||
|
||||
clear_keyboard_but_mods();
|
||||
current_layer = new_layer;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void register_code(uint8_t code)
|
||||
{
|
||||
if IS_KEY(code) {
|
||||
if (!command_proc(code)) {
|
||||
host_add_key(code);
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
}
|
||||
else if IS_MOD(code) {
|
||||
host_add_mod_bit(MOD_BIT(code));
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
else if IS_FN(code) {
|
||||
if (!command_proc(keymap_fn_keycode(FN_INDEX(code)))) {
|
||||
host_add_key(keymap_fn_keycode(FN_INDEX(code)));
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
}
|
||||
else if IS_MOUSEKEY(code) {
|
||||
#ifdef MOUSEKEY_ENABLE
|
||||
mousekey_on(code);
|
||||
mousekey_send();
|
||||
#endif
|
||||
}
|
||||
else if IS_CONSUMER(code) {
|
||||
uint16_t usage = 0;
|
||||
switch (code) {
|
||||
case KC_AUDIO_MUTE:
|
||||
usage = AUDIO_MUTE;
|
||||
break;
|
||||
case KC_AUDIO_VOL_UP:
|
||||
usage = AUDIO_VOL_UP;
|
||||
break;
|
||||
case KC_AUDIO_VOL_DOWN:
|
||||
usage = AUDIO_VOL_DOWN;
|
||||
break;
|
||||
case KC_MEDIA_NEXT_TRACK:
|
||||
usage = TRANSPORT_NEXT_TRACK;
|
||||
break;
|
||||
case KC_MEDIA_PREV_TRACK:
|
||||
usage = TRANSPORT_PREV_TRACK;
|
||||
break;
|
||||
case KC_MEDIA_STOP:
|
||||
usage = TRANSPORT_STOP;
|
||||
break;
|
||||
case KC_MEDIA_PLAY_PAUSE:
|
||||
usage = TRANSPORT_PLAY_PAUSE;
|
||||
break;
|
||||
case KC_MEDIA_SELECT:
|
||||
usage = AL_CC_CONFIG;
|
||||
break;
|
||||
case KC_MAIL:
|
||||
usage = AL_EMAIL;
|
||||
break;
|
||||
case KC_CALCULATOR:
|
||||
usage = AL_CALCULATOR;
|
||||
break;
|
||||
case KC_MY_COMPUTER:
|
||||
usage = AL_LOCAL_BROWSER;
|
||||
break;
|
||||
case KC_WWW_SEARCH:
|
||||
usage = AC_SEARCH;
|
||||
break;
|
||||
case KC_WWW_HOME:
|
||||
usage = AC_HOME;
|
||||
break;
|
||||
case KC_WWW_BACK:
|
||||
usage = AC_BACK;
|
||||
break;
|
||||
case KC_WWW_FORWARD:
|
||||
usage = AC_FORWARD;
|
||||
break;
|
||||
case KC_WWW_STOP:
|
||||
usage = AC_STOP;
|
||||
break;
|
||||
case KC_WWW_REFRESH:
|
||||
usage = AC_REFRESH;
|
||||
break;
|
||||
case KC_WWW_FAVORITES:
|
||||
usage = AC_BOOKMARKS;
|
||||
break;
|
||||
}
|
||||
host_consumer_send(usage);
|
||||
}
|
||||
else if IS_SYSTEM(code) {
|
||||
uint16_t usage = 0;
|
||||
switch (code) {
|
||||
case KC_SYSTEM_POWER:
|
||||
usage = SYSTEM_POWER_DOWN;
|
||||
break;
|
||||
case KC_SYSTEM_SLEEP:
|
||||
usage = SYSTEM_SLEEP;
|
||||
break;
|
||||
case KC_SYSTEM_WAKE:
|
||||
usage = SYSTEM_WAKE_UP;
|
||||
break;
|
||||
}
|
||||
host_system_send(usage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void unregister_code(uint8_t code)
|
||||
{
|
||||
if IS_KEY(code) {
|
||||
host_del_key(code);
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
else if IS_MOD(code) {
|
||||
host_del_mod_bit(MOD_BIT(code));
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
else if IS_FN(code) {
|
||||
host_del_key(keymap_fn_keycode(FN_INDEX(code)));
|
||||
host_send_keyboard_report();
|
||||
}
|
||||
else if IS_MOUSEKEY(code) {
|
||||
#ifdef MOUSEKEY_ENABLE
|
||||
mousekey_off(code);
|
||||
mousekey_send();
|
||||
#endif
|
||||
}
|
||||
else if IS_CONSUMER(code) {
|
||||
host_consumer_send(0x0000);
|
||||
}
|
||||
else if IS_SYSTEM(code) {
|
||||
host_system_send(0x0000);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Event/State|IDLE PRESSING DELAYING[f] WAITING[f,k]
|
||||
* -----------+------------------------------------------------------------------
|
||||
* Fn Down |(L+) -*1 WAITING(Sk) IDLE(Rf,Ps)*7
|
||||
* Up |(L-) IDLE(L-)*8 IDLE(L-)*8 IDLE(L-)*8
|
||||
* Fnk Down |DELAYING(Sf)* (Rf) WAITING(Sk) IDLE(Rf,Ps,Rf)
|
||||
* Up |(L-) IDLE(L-/Uf)*8 IDLE(Rf,Uf/L-)*3 IDLE(Rf,Ps,Uf/L-)*3
|
||||
* Key Down |PRESSING(Rk) (Rk) WAITING(Sk) IDLE(Rf,Ps,Rk)
|
||||
* Up |(Uk) IDLE(Uk)*4 (Uk) IDLE(L+,Ps,Pk)/(Uk)*a
|
||||
* |
|
||||
* Delay |- - IDLE(L+) IDLE(L+,Ps)
|
||||
* Magic Key |COMMAND*5
|
||||
*
|
||||
* *1: ignore Fn if other key is down.
|
||||
* *2: register Fnk if any key is pressing
|
||||
* *3: register/unregister delayed Fnk and move to IDLE if code == delayed Fnk, else *8
|
||||
* *4: if no keys registered to host
|
||||
* *5: unregister all keys
|
||||
* *6: only if no keys down
|
||||
* *7: ignore Fn because Fnk key and stored key are down.
|
||||
* *8: move to IDLE if layer switch(off) occurs, else stay at current state
|
||||
* *9: repeat key if pressing Fnk twice quickly(move to PRESSING)
|
||||
* *a: layer switch and process waiting key and code if code == wainting key, else unregister key
|
||||
*
|
||||
* States:
|
||||
* IDLE: No key is down except modifiers
|
||||
* DELAYING: delay layer switch after pressing Fn with alt keycode
|
||||
* WAITING: key is pressed during DELAYING
|
||||
*
|
||||
* Events:
|
||||
* Fn: Fn key without alternative keycode
|
||||
* Fnk: Fn key with alternative keycode
|
||||
* -: ignore
|
||||
* Delay: layer switch delay term is elapsed
|
||||
*
|
||||
* Actions:
|
||||
* Rk: register key
|
||||
* Uk: unregister key
|
||||
* Rf: register Fn(alt keycode)
|
||||
* Uf: unregister Fn(alt keycode)
|
||||
* Rs: register stored key
|
||||
* Us: unregister stored key
|
||||
* Sk: Store key(waiting Key)
|
||||
* Sf: Store Fn(delayed Fn)
|
||||
* Ps: Process stored key
|
||||
* Ps: Process key
|
||||
* Is: Interpret stored keys in current layer
|
||||
* L+: Switch to new layer(*unregister* all keys but modifiers)
|
||||
* L-: Switch back to last layer(*unregister* all keys but modifiers)
|
||||
* Ld: Switch back to default layer(*unregister* all keys but modifiers)
|
||||
*/
|
||||
#define NEXT(state) do { \
|
||||
Kdebug("NEXT: "); Kdebug_P(state_str(kbdstate)); \
|
||||
kbdstate = state; \
|
||||
Kdebug(" -> "); Kdebug_P(state_str(kbdstate)); Kdebug("\n"); \
|
||||
} while (0)
|
||||
|
||||
static inline void process_key(keyevent_t event)
|
||||
{
|
||||
uint8_t code = keymap_get_keycode(current_layer, event.key.row, event.key.col);
|
||||
keykind_t kind = get_keykind(code, event.pressed);
|
||||
|
||||
uint8_t tmp_mods;
|
||||
|
||||
Kdebug("state: "); Kdebug_P(state_str(kbdstate));
|
||||
Kdebug(" kind: "); Kdebug_hex(kind);
|
||||
Kdebug(" code: "); Kdebug_hex(code);
|
||||
if (event.pressed) { Kdebug("d"); } else { Kdebug("u"); }
|
||||
Kdebug("\n");
|
||||
|
||||
switch (kbdstate) {
|
||||
case IDLE:
|
||||
switch (kind) {
|
||||
case FN_DOWN:
|
||||
layer_switch_on(code);
|
||||
break;
|
||||
case FN_UP:
|
||||
layer_switch_off(code);
|
||||
break;
|
||||
case FNK_DOWN:
|
||||
// repeat Fn alt key when press Fn key down, up then down again quickly
|
||||
if (KEYEQ(delayed_fn.event.key, event.key) &&
|
||||
timer_elapsed(delayed_fn.time) < LAYER_DELAY) {
|
||||
register_code(code);
|
||||
NEXT(PRESSING);
|
||||
} else {
|
||||
delayed_fn = (keyrecord_t) {
|
||||
.event = event,
|
||||
.code = code,
|
||||
.mods = keyboard_report->mods,
|
||||
.time = timer_read()
|
||||
};
|
||||
NEXT(DELAYING);
|
||||
}
|
||||
break;
|
||||
case FNK_UP:
|
||||
layer_switch_off(code);
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
register_code(code);
|
||||
NEXT(PRESSING);
|
||||
break;
|
||||
case MOD_DOWN:
|
||||
register_code(code);
|
||||
break;
|
||||
case KEY_UP:
|
||||
case MOD_UP:
|
||||
unregister_code(code);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PRESSING:
|
||||
switch (kind) {
|
||||
case FN_DOWN:
|
||||
// ignored when any key is pressed
|
||||
break;
|
||||
case FN_UP:
|
||||
if (layer_switch_off(code))
|
||||
NEXT(IDLE);
|
||||
break;
|
||||
case FNK_DOWN:
|
||||
register_code(code);
|
||||
break;
|
||||
case FNK_UP:
|
||||
if (layer_switch_off(code)) {
|
||||
NEXT(IDLE);
|
||||
} else {
|
||||
unregister_code(code);
|
||||
if (!anykey_sent_to_host())
|
||||
NEXT(IDLE);
|
||||
}
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
case MOD_DOWN:
|
||||
register_code(code);
|
||||
break;
|
||||
case KEY_UP:
|
||||
case MOD_UP:
|
||||
unregister_code(code);
|
||||
if (!anykey_sent_to_host())
|
||||
NEXT(IDLE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DELAYING:
|
||||
switch (kind) {
|
||||
case FN_DOWN:
|
||||
case FNK_DOWN:
|
||||
case KEY_DOWN:
|
||||
waiting_key = (keyrecord_t) {
|
||||
.event = event,
|
||||
.code = code,
|
||||
.mods = keyboard_report->mods,
|
||||
.time = timer_read()
|
||||
};
|
||||
NEXT(WAITING);
|
||||
break;
|
||||
case MOD_DOWN:
|
||||
register_code(code);
|
||||
break;
|
||||
case FN_UP:
|
||||
if (layer_switch_off(code))
|
||||
NEXT(IDLE);
|
||||
break;
|
||||
case FNK_UP:
|
||||
if (code == delayed_fn.code) {
|
||||
// type Fn with alt keycode
|
||||
// restore the mod status at the time of pressing Fn key
|
||||
tmp_mods = keyboard_report->mods;
|
||||
host_set_mods(delayed_fn.mods);
|
||||
register_code(delayed_fn.code);
|
||||
unregister_code(delayed_fn.code);
|
||||
host_set_mods(tmp_mods);
|
||||
NEXT(IDLE);
|
||||
} else {
|
||||
if (layer_switch_off(code))
|
||||
NEXT(IDLE);
|
||||
}
|
||||
break;
|
||||
case KEY_UP:
|
||||
case MOD_UP:
|
||||
unregister_code(code);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WAITING:
|
||||
switch (kind) {
|
||||
case FN_DOWN:
|
||||
case FNK_DOWN:
|
||||
case KEY_DOWN:
|
||||
tmp_mods = keyboard_report->mods;
|
||||
host_set_mods(delayed_fn.mods);
|
||||
register_code(delayed_fn.code);
|
||||
host_set_mods(waiting_key.mods);
|
||||
register_code(waiting_key.code);
|
||||
host_set_mods(tmp_mods);
|
||||
if (kind == FN_DOWN) {
|
||||
// ignore Fn
|
||||
} else if (kind == FNK_DOWN) {
|
||||
register_code(code);
|
||||
} else if (kind == KEY_DOWN) {
|
||||
register_code(code);
|
||||
}
|
||||
NEXT(IDLE);
|
||||
break;
|
||||
case MOD_DOWN:
|
||||
register_code(code);
|
||||
break;
|
||||
case FN_UP:
|
||||
if (layer_switch_off(code))
|
||||
NEXT(IDLE);
|
||||
break;
|
||||
case FNK_UP:
|
||||
if (code == delayed_fn.code) {
|
||||
// alt down, key down, alt up
|
||||
tmp_mods = keyboard_report->mods;
|
||||
host_set_mods(delayed_fn.mods);
|
||||
register_code(delayed_fn.code);
|
||||
host_set_mods(waiting_key.mods);
|
||||
register_code(waiting_key.code);
|
||||
unregister_code(delayed_fn.code);
|
||||
host_set_mods(tmp_mods);
|
||||
NEXT(IDLE);
|
||||
} else {
|
||||
if (layer_switch_off(code))
|
||||
NEXT(IDLE);
|
||||
}
|
||||
break;
|
||||
case KEY_UP:
|
||||
if (code == waiting_key.code) {
|
||||
layer_switch_on(delayed_fn.code);
|
||||
NEXT(IDLE);
|
||||
// process waiting_key
|
||||
tmp_mods = keyboard_report->mods;
|
||||
host_set_mods(waiting_key.mods);
|
||||
process_key(waiting_key.event);
|
||||
host_set_mods(tmp_mods);
|
||||
process_key(event);
|
||||
} else {
|
||||
unregister_code(code);
|
||||
}
|
||||
break;
|
||||
case MOD_UP:
|
||||
unregister_code(code);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void keyboard_init(void)
|
||||
{
|
||||
// TODO: to enable debug print magic key bind on boot time
|
||||
|
||||
// TODO: configuration of sendchar impl
|
||||
print_sendchar_func = sendchar;
|
||||
|
||||
timer_init();
|
||||
matrix_init();
|
||||
|
||||
/* boot magic keys goes here */
|
||||
matrix_scan();
|
||||
#ifdef IS_BOOTMAGIC_BOOTLOADER
|
||||
/* kick up bootloader */
|
||||
if (IS_BOOTMAGIC_BOOTLOADER()) bootloader_jump();
|
||||
#endif
|
||||
#ifdef IS_BOOTMAGIC_DEBUG
|
||||
if (IS_BOOTMAGIC_DEBUG()) debug_enable = true;
|
||||
#endif
|
||||
|
||||
#ifdef PS2_MOUSE_ENABLE
|
||||
ps2_mouse_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Do keyboard routine jobs: scan mantrix, light LEDs, ...
|
||||
* This is repeatedly called as fast as possible.
|
||||
*/
|
||||
void keyboard_task(void)
|
||||
{
|
||||
static matrix_row_t matrix_prev[MATRIX_ROWS];
|
||||
@ -572,9 +75,10 @@ void keyboard_task(void)
|
||||
|
||||
for (uint8_t c = 0; c < MATRIX_COLS; c++) {
|
||||
if (matrix_change & ((matrix_row_t)1<<c)) {
|
||||
process_key((keyevent_t){
|
||||
.key = (key_t){ .row = r, .col = c },
|
||||
.pressed = (matrix_row & ((matrix_row_t)1<<c))
|
||||
action_exec((keyevent_t){
|
||||
.key.pos = (keypos_t){ .row = r, .col = c },
|
||||
.pressed = (matrix_row & (1<<c)),
|
||||
.time = (timer_read() | 1) /* time should not be 0 */
|
||||
});
|
||||
// record a processed key
|
||||
matrix_prev[r] ^= ((matrix_row_t)1<<c);
|
||||
@ -584,55 +88,19 @@ void keyboard_task(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
MATRIX_LOOP_END:
|
||||
|
||||
// layer switch when delay term elapses
|
||||
if (kbdstate == DELAYING || kbdstate == WAITING) {
|
||||
if (timer_elapsed(delayed_fn.time) > LAYER_DELAY) {
|
||||
if (kbdstate == DELAYING) {
|
||||
layer_switch_on(delayed_fn.code);
|
||||
NEXT(IDLE);
|
||||
}
|
||||
if (kbdstate == WAITING) {
|
||||
layer_switch_on(delayed_fn.code);
|
||||
NEXT(IDLE);
|
||||
uint8_t tmp_mods = keyboard_report->mods;
|
||||
host_set_mods(waiting_key.mods);
|
||||
process_key(waiting_key.event);
|
||||
host_set_mods(tmp_mods);
|
||||
}
|
||||
}
|
||||
}
|
||||
// call with pseudo tick event when no real key event.
|
||||
action_exec(TICK);
|
||||
|
||||
MATRIX_LOOP_END:
|
||||
#ifdef MOUSEKEY_ENABLE
|
||||
// mousekey repeat & acceleration
|
||||
mousekey_task();
|
||||
#endif
|
||||
|
||||
// FAIL SAFE: clear all key if no key down
|
||||
if (matrix_change) {
|
||||
matrix_row_t is_matrix_on = 0;
|
||||
for (int r = 0; r < MATRIX_ROWS; r++) {
|
||||
is_matrix_on |= matrix_get_row(r);
|
||||
}
|
||||
if (!is_matrix_on) {
|
||||
Kdebug("FAIL SAFE: clear all keys(default layer).\n");
|
||||
clear_keyboard();
|
||||
current_layer = default_layer;
|
||||
fn_state_bits = 0;
|
||||
delayed_fn = (keyrecord_t){};
|
||||
waiting_key = (keyrecord_t){};
|
||||
NEXT(IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
// update LED
|
||||
if (led_status != host_keyboard_leds()) {
|
||||
led_status = host_keyboard_leds();
|
||||
keyboard_set_leds(led_status);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void keyboard_set_leds(uint8_t leds)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
Copyright 2011,2012,2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -26,28 +26,44 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* key matrix position */
|
||||
typedef struct {
|
||||
uint8_t row;
|
||||
uint8_t col;
|
||||
uint8_t row;
|
||||
} keypos_t;
|
||||
|
||||
// TODO: need raw? keypos_t -> key_t?
|
||||
typedef union {
|
||||
uint16_t raw;
|
||||
keypos_t pos;
|
||||
} key_t;
|
||||
|
||||
/* key event */
|
||||
typedef struct {
|
||||
key_t key;
|
||||
bool pressed;
|
||||
uint16_t time;
|
||||
} keyevent_t;
|
||||
|
||||
typedef struct {
|
||||
keyevent_t event;
|
||||
uint8_t code;
|
||||
uint8_t mods;
|
||||
uint16_t time;
|
||||
} keyrecord_t;
|
||||
/* equivalent test of key_t */
|
||||
#define KEYEQ(keya, keyb) ((keya).raw == (keyb).raw)
|
||||
|
||||
#define KEYEQ(keya, keyb) (keya.row == keyb.row && keya.col == keyb.col)
|
||||
/* (time == 0) means no event and assumes matrix has no 255 line. */
|
||||
#define IS_NOEVENT(event) ((event).time == 0 || ((event).key.pos.row == 255 && (event).key.pos.col == 255))
|
||||
|
||||
#define NOEVENT (keyevent_t){ \
|
||||
.key.pos = (keypos_t){ .row = 255, .col = 255 }, \
|
||||
.pressed = false, \
|
||||
.time = 0 \
|
||||
}
|
||||
|
||||
/* tick event */
|
||||
#define TICK (keyevent_t){ \
|
||||
.key.pos = (keypos_t){ .row = 255, .col = 255 }, \
|
||||
.pressed = false, \
|
||||
.time = (timer_read() | 1) \
|
||||
}
|
||||
|
||||
extern uint8_t current_layer;
|
||||
extern uint8_t default_layer;
|
||||
|
||||
void keyboard_init(void);
|
||||
void keyboard_task(void);
|
||||
|
@ -28,14 +28,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL)
|
||||
#define IS_MOD(code) (KC_LCTRL <= (code) && (code) <= KC_RGUI)
|
||||
|
||||
#define IS_FN(code) (KC_FN0 <= (code) && (code) <= KC_FN7)
|
||||
#define IS_FN(code) (KC_FN0 <= (code) && (code) <= KC_FN31)
|
||||
#define IS_MOUSEKEY(code) (KC_MS_UP <= (code) && (code) <= KC_MS_ACCEL2)
|
||||
#define IS_MOUSEKEY_MOVE(code) (KC_MS_UP <= (code) && (code) <= KC_MS_RIGHT)
|
||||
#define IS_MOUSEKEY_BUTTON(code) (KC_MS_BTN1 <= (code) && (code) <= KC_MS_BTN5)
|
||||
#define IS_MOUSEKEY_WHEEL(code) (KC_MS_WH_UP <= (code) && (code) <= KC_MS_WH_RIGHT)
|
||||
#define IS_MOUSEKEY_ACCEL(code) (KC_MS_ACCEL0 <= (code) && (code) <= KC_MS_ACCEL2)
|
||||
|
||||
#define IS_SPECIAL(code) ((0xB0 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF))
|
||||
#define IS_SPECIAL(code) ((0xA5 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF))
|
||||
#define IS_CONSUMER(code) (KC_MUTE <= (code) && (code) <= KC_WFAV)
|
||||
#define IS_SYSTEM(code) (KC_POWER <= (code) && (code) <= KC_WAKE)
|
||||
|
||||
@ -43,6 +43,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define MOD_INDEX(code) ((code) & 0x07)
|
||||
#define FN_BIT(code) (1<<FN_INDEX(code))
|
||||
#define FN_INDEX(code) ((code) - KC_FN0)
|
||||
#define FN_MIN KC_FN0
|
||||
#define FN_MAX KC_FN31
|
||||
|
||||
|
||||
/*
|
||||
@ -388,11 +390,10 @@ enum internal_special_keycodes {
|
||||
/* System Control */
|
||||
KC_SYSTEM_POWER = 0xA5,
|
||||
KC_SYSTEM_SLEEP,
|
||||
KC_SYSTEM_WAKE, /* 0xA7 */
|
||||
/* 0xA8-AF */
|
||||
KC_SYSTEM_WAKE,
|
||||
|
||||
/* Consumer Page */
|
||||
KC_AUDIO_MUTE = 0xB0,
|
||||
/* Media Control */
|
||||
KC_AUDIO_MUTE,
|
||||
KC_AUDIO_VOL_UP,
|
||||
KC_AUDIO_VOL_DOWN,
|
||||
KC_MEDIA_NEXT_TRACK,
|
||||
@ -408,21 +409,47 @@ enum internal_special_keycodes {
|
||||
KC_WWW_BACK,
|
||||
KC_WWW_FORWARD,
|
||||
KC_WWW_STOP,
|
||||
KC_WWW_REFRESH, /* 0xC0 */
|
||||
KC_WWW_FAVORITES, /* 0xC1 */
|
||||
/* 0xC2-DF vacant for future use */
|
||||
KC_WWW_REFRESH,
|
||||
KC_WWW_FAVORITES, /* 0xB9 */
|
||||
|
||||
/* 0xE0-E7 for Modifiers. DO NOT USE. */
|
||||
|
||||
/* Layer Switching */
|
||||
KC_FN0 = 0xE8,
|
||||
/* Fn key */
|
||||
KC_FN0 = 0xC0,
|
||||
KC_FN1,
|
||||
KC_FN2,
|
||||
KC_FN3,
|
||||
KC_FN4,
|
||||
KC_FN5,
|
||||
KC_FN6,
|
||||
KC_FN7, /* 0xEF */
|
||||
KC_FN7,
|
||||
KC_FN8,
|
||||
KC_FN9,
|
||||
KC_FN10,
|
||||
KC_FN11,
|
||||
KC_FN12,
|
||||
KC_FN13,
|
||||
KC_FN14,
|
||||
KC_FN15,
|
||||
|
||||
KC_FN16 = 0xD0,
|
||||
KC_FN17,
|
||||
KC_FN18,
|
||||
KC_FN19,
|
||||
KC_FN20,
|
||||
KC_FN21,
|
||||
KC_FN22,
|
||||
KC_FN23,
|
||||
KC_FN24,
|
||||
KC_FN25,
|
||||
KC_FN26,
|
||||
KC_FN27,
|
||||
KC_FN28,
|
||||
KC_FN29,
|
||||
KC_FN30,
|
||||
KC_FN31, /* 0xDF */
|
||||
|
||||
/**************************************/
|
||||
/* 0xE0-E7 for Modifiers. DO NOT USE. */
|
||||
/**************************************/
|
||||
|
||||
/* Mousekey */
|
||||
KC_MS_UP = 0xF0,
|
||||
|
73
common/keymap.c
Normal file
73
common/keymap.c
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "keymap.h"
|
||||
#include "report.h"
|
||||
#include "keycode.h"
|
||||
|
||||
|
||||
/* layer */
|
||||
uint8_t default_layer = 0;
|
||||
uint8_t current_layer = 0;
|
||||
|
||||
|
||||
#ifndef NO_LEGACY_KEYMAP_SUPPORT
|
||||
/* legacy support with weak reference */
|
||||
__attribute__ ((weak))
|
||||
action_t keymap_get_action(uint8_t layer, uint8_t row, uint8_t col)
|
||||
{
|
||||
/* convert from legacy keycode to action */
|
||||
uint8_t key = keymap_get_keycode(layer, row, col);
|
||||
action_t action;
|
||||
switch (key) {
|
||||
case KC_A ... KC_EXSEL:
|
||||
action.code = ACTION_KEY(key);
|
||||
break;
|
||||
case KC_LCTRL ... KC_LGUI:
|
||||
action.code = ACTION_LMOD(key);
|
||||
break;
|
||||
case KC_RCTRL ... KC_RGUI:
|
||||
action.code = ACTION_RMOD(key);
|
||||
break;
|
||||
case KC_SYSTEM_POWER ... KC_SYSTEM_WAKE:
|
||||
action.code = ACTION_USAGE_SYSTEM(KEYCODE2SYSTEM(key));
|
||||
break;
|
||||
case KC_AUDIO_MUTE ... KC_WWW_FAVORITES:
|
||||
action.code = ACTION_USAGE_CONSUMER(KEYCODE2CONSUMER(key));
|
||||
break;
|
||||
case KC_MS_UP ... KC_MS_ACCEL2:
|
||||
action.code = ACTION_MOUSEKEY(key);
|
||||
break;
|
||||
case KC_FN0 ... KC_FN31:
|
||||
{
|
||||
uint8_t layer = keymap_fn_layer(FN_INDEX(key));
|
||||
uint8_t code = keymap_fn_keycode(FN_INDEX(key));
|
||||
action.code = ACTION_LAYER_SET_TAP_KEY(layer, code);
|
||||
}
|
||||
break;
|
||||
case KC_NO ... KC_UNDEFINED:
|
||||
default:
|
||||
action.code = ACTION_NO;
|
||||
break;
|
||||
}
|
||||
return action;
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__ ((weak))
|
||||
void keymap_call_function(keyrecord_t *event, uint8_t id, uint8_t opt)
|
||||
{
|
||||
}
|
@ -20,9 +20,26 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "action.h"
|
||||
|
||||
|
||||
/* keycode in specific layer */
|
||||
// TODO: move to action.h?
|
||||
/* layer used currently */
|
||||
extern uint8_t current_layer;
|
||||
/* layer to return or start with */
|
||||
extern uint8_t default_layer;
|
||||
|
||||
|
||||
/* action for key */
|
||||
// TODO: should use struct key_t?
|
||||
action_t keymap_get_action(uint8_t layer, uint8_t row, uint8_t col);
|
||||
|
||||
/* user defined special function */
|
||||
void keymap_call_function(keyrecord_t *record, uint8_t id, uint8_t opt);
|
||||
|
||||
|
||||
#ifndef NO_LEGACY_KEYMAP_SUPPORT
|
||||
/* keycode of key */
|
||||
uint8_t keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t col);
|
||||
|
||||
/* layer to move during press Fn key */
|
||||
@ -30,5 +47,6 @@ uint8_t keymap_fn_layer(uint8_t fn_bits);
|
||||
|
||||
/* keycode to send when release Fn key without using */
|
||||
uint8_t keymap_fn_keycode(uint8_t fn_bits);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -113,7 +113,6 @@ void print_decs(int16_t data)
|
||||
}
|
||||
|
||||
|
||||
static inline
|
||||
void print_hex4(uint8_t data)
|
||||
{
|
||||
sendchar(data + ((data < 10) ? '0' : 'A' - 10));
|
||||
@ -137,6 +136,12 @@ void print_hex32(uint32_t data)
|
||||
print_hex16(data);
|
||||
}
|
||||
|
||||
void print_bin4(uint8_t data)
|
||||
{
|
||||
for (int i = 4; i >= 0; i--) {
|
||||
sendchar((data & (1<<i)) ? '1' : '0');
|
||||
}
|
||||
}
|
||||
|
||||
void print_bin8(uint8_t data)
|
||||
{
|
||||
|
@ -87,11 +87,13 @@ void print_dec(uint16_t data);
|
||||
void print_decs(int16_t data);
|
||||
|
||||
/* hex */
|
||||
void print_hex4(uint8_t data);
|
||||
void print_hex8(uint8_t data);
|
||||
void print_hex16(uint16_t data);
|
||||
void print_hex32(uint32_t data);
|
||||
|
||||
/* binary */
|
||||
void print_bin4(uint8_t data);
|
||||
void print_bin8(uint8_t data);
|
||||
void print_bin16(uint16_t data);
|
||||
void print_bin32(uint32_t data);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
Copyright 2011,2012 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define REPORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "keycode.h"
|
||||
|
||||
|
||||
/* report id */
|
||||
@ -33,8 +34,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define MOUSE_BTN4 (1<<3)
|
||||
#define MOUSE_BTN5 (1<<4)
|
||||
|
||||
// Consumer Page(0x0C)
|
||||
// following are supported by Windows: http://msdn.microsoft.com/en-us/windows/hardware/gg463372.aspx
|
||||
/* Consumer Page(0x0C)
|
||||
* following are supported by Windows: http://msdn.microsoft.com/en-us/windows/hardware/gg463372.aspx
|
||||
*/
|
||||
#define AUDIO_MUTE 0x00E2
|
||||
#define AUDIO_VOL_UP 0x00E9
|
||||
#define AUDIO_VOL_DOWN 0x00EA
|
||||
@ -42,10 +44,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define TRANSPORT_PREV_TRACK 0x00B6
|
||||
#define TRANSPORT_STOP 0x00B7
|
||||
#define TRANSPORT_PLAY_PAUSE 0x00CD
|
||||
/* application launch */
|
||||
#define AL_CC_CONFIG 0x0183
|
||||
#define AL_EMAIL 0x018A
|
||||
#define AL_CALCULATOR 0x0192
|
||||
#define AL_LOCAL_BROWSER 0x0194
|
||||
/* application control */
|
||||
#define AC_SEARCH 0x0221
|
||||
#define AC_HOME 0x0223
|
||||
#define AC_BACK 0x0224
|
||||
@ -53,20 +57,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define AC_STOP 0x0226
|
||||
#define AC_REFRESH 0x0227
|
||||
#define AC_BOOKMARKS 0x022A
|
||||
// supplement for Bluegiga iWRAP HID(not supported by Windows?)
|
||||
/* supplement for Bluegiga iWRAP HID(not supported by Windows?) */
|
||||
#define AL_LOCK 0x019E
|
||||
#define TRANSPORT_RECORD 0x00B2
|
||||
#define TRANSPORT_REWIND 0x00B4
|
||||
#define TRANSPORT_EJECT 0x00B8
|
||||
#define AC_MINIMIZE 0x0206
|
||||
|
||||
// Generic Desktop Page(0x01)
|
||||
/* Generic Desktop Page(0x01) - system power control */
|
||||
#define SYSTEM_POWER_DOWN 0x0081
|
||||
#define SYSTEM_SLEEP 0x0082
|
||||
#define SYSTEM_WAKE_UP 0x0083
|
||||
|
||||
|
||||
// key report size(NKRO or boot mode)
|
||||
/* key report size(NKRO or boot mode) */
|
||||
#if defined(HOST_PJRC)
|
||||
# include "usb.h"
|
||||
# if defined(KBD2_REPORT_KEYS) && KBD2_REPORT_KEYS > KBD_REPORT_KEYS
|
||||
@ -97,6 +101,34 @@ typedef struct {
|
||||
int8_t h;
|
||||
} __attribute__ ((packed)) report_mouse_t;
|
||||
|
||||
|
||||
/* keycode to system usage */
|
||||
#define KEYCODE2SYSTEM(key) \
|
||||
(key == KC_SYSTEM_POWER ? SYSTEM_POWER_DOWN : \
|
||||
(key == KC_SYSTEM_SLEEP ? SYSTEM_SLEEP : \
|
||||
(key == KC_SYSTEM_WAKE ? SYSTEM_WAKE_UP : 0)))
|
||||
|
||||
/* keycode to consumer usage */
|
||||
#define KEYCODE2CONSUMER(key) \
|
||||
(key == KC_AUDIO_MUTE ? AUDIO_MUTE : \
|
||||
(key == KC_AUDIO_VOL_UP ? AUDIO_VOL_UP : \
|
||||
(key == KC_AUDIO_VOL_DOWN ? AUDIO_VOL_DOWN : \
|
||||
(key == KC_MEDIA_NEXT_TRACK ? TRANSPORT_NEXT_TRACK : \
|
||||
(key == KC_MEDIA_PREV_TRACK ? TRANSPORT_PREV_TRACK : \
|
||||
(key == KC_MEDIA_STOP ? TRANSPORT_STOP : \
|
||||
(key == KC_MEDIA_PLAY_PAUSE ? TRANSPORT_PLAY_PAUSE : \
|
||||
(key == KC_MEDIA_SELECT ? AL_CC_CONFIG : \
|
||||
(key == KC_MAIL ? AL_EMAIL : \
|
||||
(key == KC_CALCULATOR ? AL_CALCULATOR : \
|
||||
(key == KC_MY_COMPUTER ? AL_LOCAL_BROWSER : \
|
||||
(key == KC_WWW_SEARCH ? AC_SEARCH : \
|
||||
(key == KC_WWW_HOME ? AC_HOME : \
|
||||
(key == KC_WWW_BACK ? AC_BACK : \
|
||||
(key == KC_WWW_FORWARD ? AC_FORWARD : \
|
||||
(key == KC_WWW_STOP ? AC_STOP : \
|
||||
(key == KC_WWW_REFRESH ? AC_REFRESH : \
|
||||
(key == KC_WWW_FAVORITES ? AC_BOOKMARKS : 0))))))))))))))))))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
261
doc/keycode.txt
Normal file
261
doc/keycode.txt
Normal file
@ -0,0 +1,261 @@
|
||||
Keycode Symbol Table
|
||||
====================
|
||||
Keycodes are defined in `common/keycode.h`.
|
||||
Range of 00-A4 and E0-E7 are identical with HID Usage:
|
||||
<http://www.usb.org/developers/devclass_docs/Hut1_11.pdf>
|
||||
Virtual keycodes are defined out of above range to support special actions.
|
||||
|
||||
|
||||
Keycode Symbol Short name Description
|
||||
--------------------------------------------------------------------------------
|
||||
KC_NO 00 Reserved (no event indicated)
|
||||
KC_ROLL_OVER 01 Keyboard ErrorRollOver
|
||||
KC_POST_FAIL 02 Keyboard POSTFail
|
||||
KC_UNDEFINED 03 Keyboard ErrorUndefined
|
||||
KC_A 04 Keyboard a and A
|
||||
KC_B 05 Keyboard b and B
|
||||
KC_C 06 Keyboard c and C
|
||||
KC_D 07 Keyboard d and D
|
||||
KC_E 08 Keyboard e and E
|
||||
KC_F 09 Keyboard f and F
|
||||
KC_G 0A Keyboard g and G
|
||||
KC_H 0B Keyboard h and H
|
||||
KC_I 0C Keyboard i and I
|
||||
KC_J 0D Keyboard j and J
|
||||
KC_K 0E Keyboard k and K
|
||||
KC_L 0F Keyboard l and L
|
||||
KC_M 10 Keyboard m and M
|
||||
KC_N 11 Keyboard n and N
|
||||
KC_O 12 Keyboard o and O
|
||||
KC_P 13 Keyboard p and P
|
||||
KC_Q 14 Keyboard q and Q
|
||||
KC_R 15 Keyboard r and R
|
||||
KC_S 16 Keyboard s and S
|
||||
KC_T 17 Keyboard t and T
|
||||
KC_U 18 Keyboard u and U
|
||||
KC_V 19 Keyboard v and V
|
||||
KC_W 1A Keyboard w and W
|
||||
KC_X 1B Keyboard x and X
|
||||
KC_Y 1C Keyboard y and Y
|
||||
KC_Z 1D Keyboard z and Z
|
||||
KC_1 1E Keyboard 1 and !
|
||||
KC_2 1F Keyboard 2 and @
|
||||
KC_3 20 Keyboard 3 and #
|
||||
KC_4 21 Keyboard 4 and $
|
||||
KC_5 22 Keyboard 5 and %
|
||||
KC_6 23 Keyboard 6 and ^
|
||||
KC_7 24 Keyboard 7 and &
|
||||
KC_8 25 Keyboard 8 and *
|
||||
KC_9 26 Keyboard 9 and (
|
||||
KC_0 27 Keyboard 0 and )
|
||||
KC_ENTER KC_ENT 28 Keyboard Return (ENTER)
|
||||
KC_ESCAPE KC_ESC 29 Keyboard ESCAPE
|
||||
KC_BSPACE KC_BSPC 2A Keyboard DELETE (Backspace)
|
||||
KC_TAB 2B Keyboard Tab
|
||||
KC_SPACE KC_SPC 2C Keyboard Spacebar
|
||||
KC_MINUS KC_MINS 2D Keyboard - and (underscore)
|
||||
KC_EQUAL KC_EQL 2E Keyboard = and +
|
||||
KC_LBRACKET KC_LBRC 2F Keyboard [ and {
|
||||
KC_RBRACKET KC_RBRC 30 Keyboard ] and }
|
||||
KC_BSLASH KC_BSLS 31 Keyboard \ and |
|
||||
KC_NONUS_HASH KC_NUHS 32 Keyboard Non-US # and ~
|
||||
KC_SCOLON KC_SCLN 33 Keyboard ; and :
|
||||
KC_QUOTE KC_QUOT 34 Keyboard ‘ and “
|
||||
KC_GRAVE KC_GRV 35 Keyboard Grave Accent and Tilde
|
||||
KC_COMMA KC_COMM 36 Keyboard, and <
|
||||
KC_DOT 37 Keyboard . and >
|
||||
KC_SLASH KC_SLSH 38 Keyboard / and ?
|
||||
KC_CAPSLOCK KC_CAPS 39 Keyboard Caps Lock
|
||||
KC_F1 3A Keyboard F1
|
||||
KC_F2 3B Keyboard F2
|
||||
KC_F3 3C Keyboard F3
|
||||
KC_F4 3D Keyboard F4
|
||||
KC_F5 3E Keyboard F5
|
||||
KC_F6 3F Keyboard F6
|
||||
KC_F7 40 Keyboard F7
|
||||
KC_F8 41 Keyboard F8
|
||||
KC_F9 42 Keyboard F9
|
||||
KC_F10 43 Keyboard F10
|
||||
KC_F11 44 Keyboard F11
|
||||
KC_F12 45 Keyboard F12
|
||||
KC_PSCREEN KC_PSCR 46 Keyboard PrintScreen1
|
||||
KC_SCKLOCK KC_SLCK 47 Keyboard Scroll Lock11
|
||||
KC_PAUSE KC_PAUS 48 Keyboard Pause1
|
||||
KC_INSERT KC_INT 49 Keyboard Insert1
|
||||
KC_HOME 4A Keyboard Home1
|
||||
KC_PGUP 4B Keyboard PageUp1
|
||||
KC_DELETE KC_DELETE 4C Keyboard Delete Forward
|
||||
KC_END 4D Keyboard End1
|
||||
KC_PGDOWN KC_PGDN 4E Keyboard PageDown1
|
||||
KC_RIGHT KC_RGHT 4F Keyboard RightArrow1
|
||||
KC_LEFT 50 Keyboard LeftArrow1
|
||||
KC_DOWN 51 Keyboard DownArrow1
|
||||
KC_UP 52 Keyboard UpArrow1
|
||||
KC_NUMLOCK KC_NLCK 53 Keypad Num Lock and Clear11
|
||||
KC_KP_SLASH KC_PSLS 54 Keypad /1
|
||||
KC_KP_ASTERISK KC_PAST 55 Keypad *
|
||||
KC_KP_MINUS KC_PMNS 56 Keypad -
|
||||
KC_KP_PLUS KC_PPLS 57 Keypad +
|
||||
KC_KP_ENTER KC_PENT 58 Keypad ENTER5
|
||||
KC_KP_1 KC_P1 59 Keypad 1 and End
|
||||
KC_KP_2 KC_P2 5A Keypad 2 and Down Arrow
|
||||
KC_KP_3 KC_P3 5B Keypad 3 and PageDn
|
||||
KC_KP_4 KC_P4 5C Keypad 4 and Left Arrow
|
||||
KC_KP_5 KC_P5 5D Keypad 5
|
||||
KC_KP_6 KC_P6 5E Keypad 6 and Right Arrow
|
||||
KC_KP_7 KC_P7 5F Keypad 7 and Home
|
||||
KC_KP_8 KC_P8 60 Keypad 8 and Up Arrow
|
||||
KC_KP_9 KC_P9 61 Keypad 9 and PageUp
|
||||
KC_KP_0 KC_P0 62 Keypad 0 and Insert
|
||||
KC_KP_DOT KC_PDOT 63 Keypad . and Delete
|
||||
KC_NONUS_BSLASH KC_NUBS 64 Keyboard Non-US \ and |
|
||||
KC_APPLICATION KC_APP 65 Keyboard Application10
|
||||
KC_POWER 66 Keyboard Power9
|
||||
KC_KP_EQUAL KC_PEQL 67 Keypad =
|
||||
KC_F13 68 Keyboard F13
|
||||
KC_F14 69 Keyboard F14
|
||||
KC_F15 6A Keyboard F15
|
||||
KC_F16 6B Keyboard F16
|
||||
KC_F17 6C Keyboard F17
|
||||
KC_F18 6D Keyboard F18
|
||||
KC_F19 6E Keyboard F19
|
||||
KC_F20 6F Keyboard F20
|
||||
KC_F21 70 Keyboard F21
|
||||
KC_F22 71 Keyboard F22
|
||||
KC_F23 72 Keyboard F23
|
||||
KC_F24 73 Keyboard F24
|
||||
KC_EXECUTE 74 Keyboard Execute
|
||||
KC_HELP 75 Keyboard Help
|
||||
KC_MENU 76 Keyboard Menu
|
||||
KC_SELECT 77 Keyboard Select
|
||||
KC_STOP 78 Keyboard Stop
|
||||
KC_AGAIN 79 Keyboard Again
|
||||
KC_UNDO 7A Keyboard Undo
|
||||
KC_CUT 7B Keyboard Cut
|
||||
KC_COPY 7C Keyboard Copy
|
||||
KC_PASTE 7D Keyboard Paste
|
||||
KC_FIND 7E Keyboard Find
|
||||
KC__MUTE 7F Keyboard Mute
|
||||
KC__VOLUP 80 Keyboard Volume Up
|
||||
KC__VOLDOWN 81 Keyboard Volume Down
|
||||
KC_LOCKING_CAPS 82 Keyboard Locking Caps Lock12
|
||||
KC_LOCKING_NUM 83 Keyboard Locking Num Lock12
|
||||
KC_LOCKING_SCROLL 84 Keyboard Locking Scroll Lock12
|
||||
KC_KP_COMMA KC_PCMM 85 Keypad Comma27
|
||||
KC_KP_EQUAL_AS400 86 Keypad Equal Sign29
|
||||
KC_INT1 KC_RO 87 Keyboard International115,28
|
||||
KC_INT2 KC_KANA 88 Keyboard International216
|
||||
KC_INT3 KC_JYEN 89 Keyboard International317
|
||||
KC_INT4 KC_HENK 8A Keyboard International418
|
||||
KC_INT5 KC_MHEN 8B Keyboard International519
|
||||
KC_INT6 8C Keyboard International620
|
||||
KC_INT7 8D Keyboard International721
|
||||
KC_INT8 8E Keyboard International822
|
||||
KC_INT9 8F Keyboard International922
|
||||
KC_LANG1 90 Keyboard LANG125
|
||||
KC_LANG2 91 Keyboard LANG226
|
||||
KC_LANG3 92 Keyboard LANG330
|
||||
KC_LANG4 93 Keyboard LANG431
|
||||
KC_LANG5 94 Keyboard LANG532
|
||||
KC_LANG6 95 Keyboard LANG68
|
||||
KC_LANG7 96 Keyboard LANG78
|
||||
KC_LANG8 97 Keyboard LANG88
|
||||
KC_LANG9 98 Keyboard LANG98
|
||||
KC_ALT_ERASE 99 Keyboard Alternate Erase7
|
||||
KC_SYSREQ 9A Keyboard SysReq/Attention1
|
||||
KC_CANCEL 9B Keyboard Cancel
|
||||
KC_CLEAR 9C Keyboard Clear
|
||||
KC_PRIOR 9D Keyboard Prior
|
||||
KC_RETURN 9E Keyboard Return
|
||||
KC_SEPARATOR 9F Keyboard Separator
|
||||
KC_OUT A0 Keyboard Out
|
||||
KC_OPER A1 Keyboard Oper
|
||||
KC_CLEAR_AGAIN A2 Keyboard Clear/Again
|
||||
KC_CRSEL A3 Keyboard CrSel/Props
|
||||
KC_EXSEL A4 Keyboard ExSel
|
||||
/* Modifiers */
|
||||
KC_LCTRL KC_LCTRL E0 Keyboard LeftControl
|
||||
KC_LSHIFT KC_LSFT E1 Keyboard LeftShift
|
||||
KC_LALT E2 Keyboard LeftAlt
|
||||
KC_LGUI E3 Keyboard Left GUI(Windows/Apple/Meta key)
|
||||
KC_RCTRL KC_RCTL E4 Keyboard RightControl
|
||||
KC_RSHIFT KC_RSFT E5 Keyboard RightShift
|
||||
KC_RALT E6 Keyboard RightAlt
|
||||
KC_RGUI E7 Keyboard Right GUI(Windows/Apple/Meta key)
|
||||
|
||||
/*
|
||||
* Virtual keycodes
|
||||
*/
|
||||
/* System Control */
|
||||
KC_SYSTEM_POWER KC_PWR System Power Down
|
||||
KC_SYSTEM_SLEEP KC_SLEP System Sleep
|
||||
KC_SYSTEM_WAKE KC_WAKE System Wake
|
||||
/* Consumer Page */
|
||||
KC_AUDIO_MUTE KC_MUTE
|
||||
KC_AUDIO_VOL_UP KC_VOLU
|
||||
KC_AUDIO_VOL_DOWN KC_VOLD
|
||||
KC_MEDIA_NEXT_TRACK KC_MNXT
|
||||
KC_MEDIA_PREV_TRACK KC_MPRV
|
||||
KC_MEDIA_STOP KC_MSTP
|
||||
KC_MEDIA_PLAY_PAUSE KC_MPLY
|
||||
KC_MEDIA_SELECT KC_MSEL
|
||||
KC_MAIL KC_MAIL
|
||||
KC_CALCULATOR KC_CALC
|
||||
KC_MY_COMPUTER KC_MYCM
|
||||
KC_WWW_SEARCH KC_WSCH
|
||||
KC_WWW_HOME KC_WHOM
|
||||
KC_WWW_BACK KC_WBAK
|
||||
KC_WWW_FORWARD KC_WFWD
|
||||
KC_WWW_STOP KC_WSTP
|
||||
KC_WWW_REFRESH KC_WREF
|
||||
KC_WWW_FAVORITES KC_WFAV
|
||||
/* Mousekey */
|
||||
KC_MS_UP KC_MS_U Mouse Cursor Up
|
||||
KC_MS_DOWN KC_MS_D Mouse Cursor Down
|
||||
KC_MS_LEFT KC_MS_L Mouse Cursor Left
|
||||
KC_MS_RIGHT KC_MS_R Mouse Cursor Right
|
||||
KC_MS_BTN1 KC_BTN1 Mouse Button 1
|
||||
KC_MS_BTN2 KC_BTN2 Mouse Button 2
|
||||
KC_MS_BTN3 KC_BTN3 Mouse Button 3
|
||||
KC_MS_BTN4 KC_BTN4 Mouse Button 4
|
||||
KC_MS_BTN5 KC_BTN5 Mouse Button 5
|
||||
KC_MS_WH_UP KC_WH_U Mouse Wheel Up
|
||||
KC_MS_WH_DOWN KC_WH_D Mouse Wheel Down
|
||||
KC_MS_WH_LEFT KC_WH_L Mouse Wheel Left
|
||||
KC_MS_WH_RIGHT KC_WH_R Mouse Wheel Right
|
||||
KC_MS_ACCEL0 KC_ACL0 Mouse Acceleration 0
|
||||
KC_MS_ACCEL1 KC_ACL1 Mouse Acceleration 1
|
||||
KC_MS_ACCEL2 KC_ACL2 Mouse Acceleration 2
|
||||
/* Fn key */
|
||||
KC_FN0
|
||||
KC_FN1
|
||||
KC_FN2
|
||||
KC_FN3
|
||||
KC_FN4
|
||||
KC_FN5
|
||||
KC_FN6
|
||||
KC_FN7
|
||||
KC_FN8
|
||||
KC_FN9
|
||||
KC_FN10
|
||||
KC_FN11
|
||||
KC_FN12
|
||||
KC_FN13
|
||||
KC_FN14
|
||||
KC_FN15
|
||||
KC_FN16
|
||||
KC_FN17
|
||||
KC_FN18
|
||||
KC_FN19
|
||||
KC_FN20
|
||||
KC_FN21
|
||||
KC_FN22
|
||||
KC_FN23
|
||||
KC_FN24
|
||||
KC_FN25
|
||||
KC_FN26
|
||||
KC_FN27
|
||||
KC_FN28
|
||||
KC_FN29
|
||||
KC_FN30
|
||||
KC_FN31
|
Reference in New Issue
Block a user