Skip to content

Commit

Permalink
Merge pull request #648 from kareltucek/implement_key_states
Browse files Browse the repository at this point in the history
Implement key states
  • Loading branch information
mondalaci authored Jul 25, 2023
2 parents 6ca6c60 + 9185ea4 commit 685f626
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 0 deletions.
12 changes: 12 additions & 0 deletions doc-dev/reference-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ Macro events allow hooking special behaviour, such as applying specific configur
$onKeymapChange {KEYMAPID|any}
$onLayerChange {LAYERID|any}
$onKeymapLayerChange KEYMAPID LAYERID
$onCapsLockStateChange
$onNumLockStateChange
$onScrollLockStateChange

Please note that:
- under Linux, scroll lock is by disabled by default. As a consequence, the macro event does not trigger.
- under MacOS, scroll lock dims the screen but does not toggle scroll lock state. As a consequence, the macro event does not trigger.

I.e., if you want to customize acceleration driver for your trackball module on keymap QWR, create macro named `$onKeymapChange QWR`, with content e.g.:

Expand Down Expand Up @@ -145,6 +152,7 @@ The following grammar is supported:
CONDITION = {ifPendingKeyReleased | ifNotPendingKeyReleased} <queue idx (NUMBER)>
CONDITION = {ifPlaytime | ifNotPlaytime} <timeout in ms (NUMBER)>
CONDITION = {ifShift | ifAlt | ifCtrl | ifGui | ifAnyMod | ifNotShift | ifNotAlt | ifNotCtrl | ifNotGui | ifNotAnyMod}
CONDITION = {ifCapsLockOn | ifNotCapsLockOn | ifScrollLockOn | ifNotScrollLockOn | ifNumLockOn | ifNotNumLockOn}
CONDITION = {ifRegEq | ifNotRegEq | ifRegGt | ifRegLt} <register index (NUMBER)> <value (NUMBER)>
CONDITION = {ifKeymap | ifNotKeymap} KEYMAPID
CONDITION = {ifLayer | ifNotLayer} LAYERID
Expand Down Expand Up @@ -375,6 +383,10 @@ Conditions are checked before processing the rest of the command. If the conditi
- `ifKeyDefined/ifNotKeyDefined KEYID` is true if the key in parameter has defined action on the current keymap && layer. If you wish to test keys from different layers/keymaps, you will have to toggle them manually first.
- `ifPlaytime/ifNotPlaytime <timeout in ms>` is true if at least `timeout` milliseconds passed since macro was started.
- `ifShift/ifAlt/ifCtrl/ifGui/ifAnyMod/ifNotShift/ifNotAlt/ifNotCtrl/ifNotGui/ifNotAnyMod` is true if either right or left modifier was held in the previous update cycle. This does not indicate modifiers which were triggered from macroes.
- `ifCapsLockOn/ifNotCapsLockOn/ifScrollLockOn/ifNotScrollLockOn/ifNumLockOn/ifNotNumLockOn` is true if corresponding caps lock / num lock / scroll lock is set to true by the host OS.
- Please note that:
- under Linux, scroll lock is by disabled by default. As a consequence, the macro event does not trigger.
- under MacOS, scroll lock dims the screen but does not toggle scroll lock state. As a consequence, the macro event does not trigger.
- `{ifRegEq|ifNotRegEq} <register inex> <value>` will test if the value in the register identified by first argument equals second argument.
- `{ifRegGt|ifRegLt} <register inex> <value>` will test if the value in the register identified by first argument is greater than/less than second argument.
- `{ifKeymap|ifNotKeymap|ifLayer|ifNotLayer} <value>` will test if the current Keymap/Layer are equals to the first argument (uses the same parsing rule as `switchKeymap` and `switchLayer`.
Expand Down
47 changes: 47 additions & 0 deletions right/src/macro_events.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,23 @@
static macro_index_t anyLayerChangeMacro = MacroIndex_None;
static macro_index_t layerChangeMacro[LayerId_Count];
static macro_index_t keymapLayerChangeMacro[LayerId_Count];
static macro_index_t capsLockChangeMacro = MacroIndex_None;
static macro_index_t scrollLockChangeMacro = MacroIndex_None;
static macro_index_t numLockChangeMacro = MacroIndex_None;

bool MacroEvent_CapsLockStateChanged = false;
bool MacroEvent_NumLockStateChanged = false;
bool MacroEvent_ScrollLockStateChanged = false;

/**
* Future possible extensions:
* - generalize change to always handle "in" and "out" events
* - add onKeymapLayerChange, onKeymapKeyPress, onKeyPress, onLayerChange events. These would be indexed when keymap is changed and kept at hand, so we could run them without any performance impact.
*/


static void registerKeyboardStates();

/**
* Macro events should be executed in order and wait for each other - first onInit, then `onKmeymapChange any`, finally other `onKeymapChange` ones.
*/
Expand All @@ -31,6 +41,8 @@ void MacroEvent_OnInit()
if (idx != 255) {
previousEventMacroSlot = Macros_StartMacro(idx, NULL, 255, false);
}

registerKeyboardStates();
}

static void startMacroInSlot(macro_index_t macroIndex, uint8_t* slotId) {
Expand Down Expand Up @@ -128,3 +140,38 @@ void MacroEvent_OnLayerChange(layer_id_t layerId)

previousEventMacroSlot = 255;
}

static void registerKeyboardStates()
{
for (int i = 0; i < AllMacrosCount; i++) {
const char *thisName, *thisNameEnd;
FindMacroName(&AllMacros[i], &thisName, &thisNameEnd);

if (TokenMatches(thisName, thisNameEnd, "$onCapsLockStateChange")) {
capsLockChangeMacro = i;
}
if (TokenMatches(thisName, thisNameEnd, "$onNumLockStateChange")) {
numLockChangeMacro = i;
}
if (TokenMatches(thisName, thisNameEnd, "$onScrollLockStateChange")) {
scrollLockChangeMacro = i;
}
}
}

void MacroEvent_ProcessStateKeyEvents()
{
if (MacroEvent_CapsLockStateChanged && capsLockChangeMacro != MacroIndex_None) {
MacroEvent_CapsLockStateChanged = false;
startMacroInSlot(capsLockChangeMacro, &previousEventMacroSlot);
}
if (MacroEvent_NumLockStateChanged && numLockChangeMacro != MacroIndex_None) {
MacroEvent_NumLockStateChanged = false;
startMacroInSlot(numLockChangeMacro, &previousEventMacroSlot);
}
if (MacroEvent_ScrollLockStateChanged && scrollLockChangeMacro != MacroIndex_None) {
MacroEvent_ScrollLockStateChanged = false;
startMacroInSlot(scrollLockChangeMacro, &previousEventMacroSlot);
}
previousEventMacroSlot = 255;
}
4 changes: 4 additions & 0 deletions right/src/macro_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@

// Variables:

extern bool MacroEvent_CapsLockStateChanged;
extern bool MacroEvent_NumLockStateChanged;
extern bool MacroEvent_ScrollLockStateChanged;

// Functions:

void MacroEvent_OnInit();
void MacroEvent_OnKeymapChange(uint8_t keymapIdx);
void MacroEvent_OnLayerChange(layer_id_t layerId);
void MacroEvent_RegisterLayerMacros();
void MacroEvent_ProcessStateKeyEvents();

#endif
35 changes: 35 additions & 0 deletions right/src/macros.c
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,11 @@ static bool processIfModifierCommand(bool negate, uint8_t modmask)
return ((InputModifiersPrevious & modmask) > 0) != negate;
}

static bool processIfStateKeyCommand(bool negate, bool *state)
{
return *state != negate;
}

static bool processIfRecordingCommand(bool negate)
{
return MacroRecorder_IsRecording() != negate;
Expand Down Expand Up @@ -2648,6 +2653,36 @@ static macro_result_t processCommand(const char* cmd, const char* cmdEnd)
return MacroResult_Finished;
}
}
else if (TokenMatches(cmd, cmdEnd, "ifCapsLockOn")) {
if (!processIfStateKeyCommand(false, &UsbBasicKeyboard_CapsLockOn) && !s->as.currentConditionPassed) {
return MacroResult_Finished;
}
}
else if (TokenMatches(cmd, cmdEnd, "ifNotCapsLockOn")) {
if (!processIfStateKeyCommand(true, &UsbBasicKeyboard_CapsLockOn) && !s->as.currentConditionPassed) {
return MacroResult_Finished;
}
}
else if (TokenMatches(cmd, cmdEnd, "ifNumLockOn")) {
if (!processIfStateKeyCommand(false, &UsbBasicKeyboard_NumLockOn) && !s->as.currentConditionPassed) {
return MacroResult_Finished;
}
}
else if (TokenMatches(cmd, cmdEnd, "ifNotNumLockOn")) {
if (!processIfStateKeyCommand(true, &UsbBasicKeyboard_NumLockOn) && !s->as.currentConditionPassed) {
return MacroResult_Finished;
}
}
else if (TokenMatches(cmd, cmdEnd, "ifScrollLockOn")) {
if (!processIfStateKeyCommand(false, &UsbBasicKeyboard_ScrollLockOn) && !s->as.currentConditionPassed) {
return MacroResult_Finished;
}
}
else if (TokenMatches(cmd, cmdEnd, "ifNotScrollLockOn")) {
if (!processIfStateKeyCommand(true, &UsbBasicKeyboard_ScrollLockOn) && !s->as.currentConditionPassed) {
return MacroResult_Finished;
}
}
else if (TokenMatches(cmd, cmdEnd, "ifRecording")) {
if (!processIfRecordingCommand(false) && !s->as.currentConditionPassed) {
return MacroResult_Finished;
Expand Down
3 changes: 3 additions & 0 deletions right/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ int main(void)
if (UsbMacroCommandWaitingForExecution) {
UsbMacroCommand_ExecuteSynchronously();
}
if (MacroEvent_ScrollLockStateChanged || MacroEvent_NumLockStateChanged || MacroEvent_CapsLockStateChanged) {
MacroEvent_ProcessStateKeyEvents();
}
__WFI();
}
}
Expand Down
19 changes: 19 additions & 0 deletions right/src/usb_interfaces/usb_interface_basic_keyboard.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include "led_display.h"
#include "lufa/HIDClassCommon.h"
#include "macro_events.h"
#include "usb_composite_device.h"
#include "usb_report_updater.h"

Expand All @@ -8,6 +10,10 @@ usb_hid_protocol_t usbBasicKeyboardProtocol;
uint32_t UsbBasicKeyboardActionCounter;
usb_basic_keyboard_report_t* ActiveUsbBasicKeyboardReport = usbBasicKeyboardReports;

bool UsbBasicKeyboard_CapsLockOn = false;
bool UsbBasicKeyboard_NumLockOn = false;
bool UsbBasicKeyboard_ScrollLockOn = false;

static usb_basic_keyboard_report_t* GetInactiveUsbBasicKeyboardReport(void)
{
return ActiveUsbBasicKeyboardReport == usbBasicKeyboardReports ? usbBasicKeyboardReports+1 : usbBasicKeyboardReports;
Expand Down Expand Up @@ -70,6 +76,14 @@ usb_status_t UsbBasicKeyboardCheckReportReady()
return UsbBasicKeyboardCheckIdleElapsed();
}

static void processStateChange(bool *targetVar, bool value, bool *onChange)
{
if (value != *targetVar) {
*targetVar = value;
*onChange = true;
}
}

usb_status_t UsbBasicKeyboardCallback(class_handle_t handle, uint32_t event, void *param)
{
usb_device_hid_struct_t *hidHandle = (usb_device_hid_struct_t *)handle;
Expand Down Expand Up @@ -110,6 +124,11 @@ usb_status_t UsbBasicKeyboardCallback(class_handle_t handle, uint32_t event, voi
usb_device_hid_report_struct_t *report = (usb_device_hid_report_struct_t*)param;
if (report->reportType == USB_DEVICE_HID_REQUEST_GET_REPORT_TYPE_OUPUT && report->reportId == 0 && report->reportLength == sizeof(usbBasicKeyboardOutBuffer)) {
LedDisplay_SetIcon(LedDisplayIcon_CapsLock, report->reportBuffer[0] & HID_KEYBOARD_LED_CAPSLOCK);

processStateChange(&UsbBasicKeyboard_CapsLockOn, report->reportBuffer[0] & HID_KEYBOARD_LED_CAPSLOCK, &MacroEvent_CapsLockStateChanged );
processStateChange(&UsbBasicKeyboard_NumLockOn, report->reportBuffer[0] & HID_KEYBOARD_LED_NUMLOCK, &MacroEvent_NumLockStateChanged );
processStateChange(&UsbBasicKeyboard_ScrollLockOn, report->reportBuffer[0] & HID_KEYBOARD_LED_SCROLLLOCK, &MacroEvent_ScrollLockStateChanged);

error = kStatus_USB_Success;
} else {
error = kStatus_USB_InvalidRequest;
Expand Down
3 changes: 3 additions & 0 deletions right/src/usb_interfaces/usb_interface_basic_keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@

// Variables:

extern bool UsbBasicKeyboard_CapsLockOn;
extern bool UsbBasicKeyboard_NumLockOn;
extern bool UsbBasicKeyboard_ScrollLockOn;
extern uint32_t UsbBasicKeyboardActionCounter;
extern usb_basic_keyboard_report_t* ActiveUsbBasicKeyboardReport;

Expand Down

0 comments on commit 685f626

Please sign in to comment.