Skip to content

Commit

Permalink
Re-implement sequences as macro in a module (#967)
Browse files Browse the repository at this point in the history
* Re-implement sequences as macro in a module

* Fix lint

* Add keys for uc mode selection to macros + missing docs
  • Loading branch information
xs5871 authored Jun 1, 2024
1 parent dfefe5a commit 3cc70ed
Show file tree
Hide file tree
Showing 8 changed files with 676 additions and 10 deletions.
2 changes: 1 addition & 1 deletion docs/en/Getting_Started.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Once you've got the gist of it:
- [International](international.md) extension adds keys for non US layouts and [Media Keys](media_keys.md) adds keys for ... media

And to go even further:
- [Sequences](sequences.md) are used for sending multiple keystrokes in a single action
- [Macros](macros.md) are used for sending multiple keystrokes in a single action
- [Layers](layers.md) can transform the whole way your keyboard is behaving with a single touch
- [HoldTap](holdtap.md) allow you to customize the way a key behaves whether it is tapped or hold, and [TapDance](tapdance.md) depending on the number of times it is pressed

Expand Down
1 change: 1 addition & 0 deletions docs/en/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Before you look further, you probably want to start with our [getting started gu
- [Combos](combos.md): Adds chords and sequences
- [Layers](layers.md): Adds layer support (Fn key) to allow many more keys to be put on your keyboard
- [HoldTap](holdtap.md): Adds support for augmented modifier keys to act as one key when tapped, and modifier when held.
- [Macros](macros.md): Adds macros.
- [Mouse keys](mouse_keys.md): Adds mouse keycodes
- [OneShot](oneshot.md): Adds support for oneshot/sticky keys.
- [Power](power.md): Power saving features. This is mostly useful when on battery power.
Expand Down
226 changes: 226 additions & 0 deletions docs/en/macros.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# Macros

Macros are used for sending multiple keystrokes in a single action, and can
be used for things like Unicode characters (even emojis! 🇨🇦), _Lorem ipsum_
generators, triggering side effects (think lighting, speakers,
microcontroller-optimized cryptocurrency miners, whatever).

## Setup

```python
from kmk.modules.macros import Macros

macros = Macros()
keyboard.modules.append(macros)
```

This will enable a new type of keycode: `KC.MACRO()`

## Keycodes

|Key |Description |
|-------------------|------------------------------------------|
|`KC.MACRO(macro)` |Create a key that will play back a macro. |
|`KC.UC_MODE_IBUS` |Switch Unicode mode to IBus. |
|`KC.UC_MODE_MACOS` |Switch Unicode mode to macOS. |
|`KC.UC_MODE_WINC` |Switch Unicode mode to Windows Compose. |

## Sending strings

The most basic sequence is an ASCII string. It can be used to send any standard
English alphabet character, and an assortment of other "standard" keyboard keys
(return, space, exclamation points, etc.).
Keep in mind that some characters from shifted keys are i18n dependent.

```python
WOW = KC.MACRO("Wow, KMK is awesome!")

keyboard.keymap = [<other keycodes>, WOW, <other keycodes>]
```

## Key sequences

If you need to add modifier keys to your sequence or you need more granular control.
You can use it to add things like copying/pasting, tabbing between fields, etc.

```python
from kmk.modules.macros import Press, Release, Tap

PASTE_WITH_COMMENTARY = KC.MACRO(
"look at this: ",
Press(KC.LCTL),
Tap(KC.V),
Release(KC.LCTL)
)

keyboard.keymap = [<other keycodes>, PASTE_WITH_COMMENTARY, <other keycodes>]
```

The above example will type out "look at this: " and then paste the contents of your
clipboard.


### Sleeping within a sequence

If you need to wait during a sequence, you can use `Delay(ms)` to wait a
length of time, in milliseconds.

```python
from kmk.modules.macros import Tap, Delay

COUNTDOWN_TO_PASTE = KC.MACRO(
Tap(KC.N3),
Tap(KC.ENTER),
Delay(1000),
Tap(KC.N2),
Tap(KC.ENTER),
Delay(1000),
Tap(KC.N1),
Tap(KC.ENTER),
Delay(1000),
Tap(KC.LCTL(KC.V)),
)

keyboard.keymap = [<other keycodes>, COUNTDOWN_TO_PASTE, <other keycodes>]
```

This example will type out the following, waiting one second (1000 ms) between numbers:

3
2
1

and then paste the contents of your clipboard.

### Alt Tab with delay

If alt tab isn't working because it requires a delay, adding a delay and triggering
down and up on ALT manually may fix the issue.

``` python
from kmk.modules.macros import Delay, Press, Release, Tap

NEXT = KC.MACRO(
Press(KC.LALT),
Delay(30),
Tap(KC.TAB),
Delay(30),
Release(KC.LALT),
)
```

## Unicode

### Unicode Modes

On Linux, Unicode uses `Ctrl-Shift-U`, which is supported by `ibus` and GTK+3.
`ibus` users will need to add `IBUS_ENABLE_CTRL_SHIFT_U=1` to their environment
(`~/profile`, `~/.bashrc`, `~/.zshrc`, or through your desktop environment's
configurator).

On Windows, [WinCompose](https://github.com/samhocevar/wincompose) is required.

- Linux : `UnicodeModeIBus`, the default
- MacOS: `UnicodeModeMacOS`
- Windows: `UnicodeModeWinC`

### Unicode Examples

Initialize `Macros` to use `UnicodeModeMac` and make a key to cycle between modes
at runtime.

```python
from kmk.keys import Key
from kmk.modules.macros import Macros, UnicodeModeIBus, UnicodeModeMacOS, UnicodeModeWinC

macros = Macros(unicode_mode=UnicodeModeMacOS)
keyboard.modules.append(macros)

def switch_um(keyboard):
if macros.unicode_mode == UnicodeModeIBus:
macros.unicode_mode = UnicodeModeMacOS
elif macros.unicode_mode == UnicodeModeMacOS:
macros.unicode_mode = UnicodeModeWinC
else:
macros.Unicode_mode = UnicodeModeIBus

UCCYCLE = Key(code=None, on_press=switch_um)

FLIP = KC.MACRO('(ノಠ痊ಠ)ノ彡┻━┻')

keyboard.keymap = [<other keycodes>, UCCYCLE, FLIP, <other keycodes>]
```

## Arbitrary Actions

As it happens, macros accept any callable object (even generators) as arguments.
The `KMKKeyboard` object is passed as argument to that callable.

### Example 1

Change the RGB animation mode to "SWIRL" for five seconds and print an ASCII
spinner

```python
# ... boilerplate omitted for brevity.

prev_animation = None

def start_spinning(keyboard):
global prev_animation
prev_animation = rgb.animation_mode
rgb.animation_mode = AnimationModes.SWIRL
rgb.effect_init = True

def stop_spinning(keyboard):
rgb.animation_mode = prev_animation
rgb.effect_init = True

DISCO = KC.MACRO(
"disco time!",
start_color_wheel,
"-",
DELAY(1000),
KC.BSPC,
"\\",
DELAY(1000),
KC.BSPC,
"|",
DELAY(1000),
KC.BSPC,
"/",
DELAY(1000),
KC.BSPC,
"-",
DELAY(1000),
KC.BSPC,
stop_color_wheel,
" disco time over.",
)
```

### Example 2

Here's a programmatic version of the earlier countdown-to-paste example, using a
generator.
Any return value that is not `None` is interpreted as a delay instruction in
milliseconds.

```python
def countdown(count, delay_ms):
def generator(keyboard):
for n in range(count, 0, -1):
KC[n].on_press(keyboard)
yield
KC[n].on_release(keyboard)
yield
KC.ENTER.on_press(keyboard)
yield
KC.ENTER.on_release(keyboard)
yield delay_ms
return generator

COUNTDOWN_TO_PASTE = KC.MACRO(
countdown(3, 1000),
Tap(KC.LCTL(KC.V)),
)
1 change: 1 addition & 0 deletions docs/en/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ modules are
put on your keyboard.
- [HoldTap](holdtap.md): Adds support for augmented modifier keys to act as one key
when tapped, and modifier when held.
- [Macros](macros.md): Adds macros.
- [Mouse keys](mouse_keys.md): Adds mouse keycodes.
- [OneShot](oneshot.md): Adds support for oneshot/sticky keys.
- [Power](power.md): Power saving features. This is mostly useful when on battery power.
Expand Down
16 changes: 8 additions & 8 deletions docs/en/porting_to_kmk.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ send its corresponding number. Use it after your pins and module definition
to define both `keyboard.coord_mapping` and `keyboard.keymap`.

```python
from kmk.handlers.sequences import simple_key_sequence
from kmk.modules.macros import Macros
from kmk.keys import KC

keyboard.modules.append(Macros())

# *2 for split keyboards, which will typically manage twice the number of keys
# of one side. Having this N too large will have no impact (maybe slower boot..)
N = len(keyboard.col_pins) * len(keyboard.row_pins) * 2
Expand All @@ -73,13 +75,11 @@ for i in range(N):
c, r = divmod(i, 100)
d, u = divmod(r, 10)
layer.append(
simple_key_sequence(
(
getattr(KC, 'N' + str(c)),
getattr(KC, 'N' + str(d)),
getattr(KC, 'N' + str(u)),
KC.SPC,
)
KC.MACRO(
getattr(KC, 'N' + str(c)),
getattr(KC, 'N' + str(d)),
getattr(KC, 'N' + str(u)),
KC.SPC,
)
)
keyboard.keymap = [layer]
Expand Down
Loading

0 comments on commit 3cc70ed

Please sign in to comment.