466 lines
18 KiB
C
466 lines
18 KiB
C
#include <math.h>
|
|
#include "key_action.h"
|
|
#include "led_display.h"
|
|
#include "layer.h"
|
|
#include "usb_interfaces/usb_interface_mouse.h"
|
|
#include "keymap.h"
|
|
#include "peripherals/test_led.h"
|
|
#include "slave_drivers/is31fl3731_driver.h"
|
|
#include "slave_drivers/uhk_module_driver.h"
|
|
#include "macros.h"
|
|
#include "key_states.h"
|
|
#include "right_key_matrix.h"
|
|
#include "layer.h"
|
|
#include "usb_report_updater.h"
|
|
#include "timer.h"
|
|
#include "key_debouncer.h"
|
|
#include "config_parser/parse_keymap.h"
|
|
#include "usb_commands/usb_command_get_debug_buffer.h"
|
|
|
|
static uint32_t mouseUsbReportUpdateTime = 0;
|
|
static uint32_t mouseElapsedTime;
|
|
|
|
uint16_t DoubleTapSwitchLayerTimeout = 150;
|
|
static uint16_t DoubleTapSwitchLayerReleaseTimeout = 100;
|
|
|
|
static bool activeMouseStates[ACTIVE_MOUSE_STATES_COUNT];
|
|
|
|
volatile uint8_t UsbReportUpdateSemaphore = 0;
|
|
|
|
mouse_kinetic_state_t MouseMoveState = {
|
|
.isScroll = false,
|
|
.upState = SerializedMouseAction_MoveUp,
|
|
.downState = SerializedMouseAction_MoveDown,
|
|
.leftState = SerializedMouseAction_MoveLeft,
|
|
.rightState = SerializedMouseAction_MoveRight,
|
|
.intMultiplier = 25,
|
|
.initialSpeed = 5,
|
|
.acceleration = 35,
|
|
.deceleratedSpeed = 10,
|
|
.baseSpeed = 40,
|
|
.acceleratedSpeed = 80,
|
|
};
|
|
|
|
mouse_kinetic_state_t MouseScrollState = {
|
|
.isScroll = true,
|
|
.upState = SerializedMouseAction_ScrollDown,
|
|
.downState = SerializedMouseAction_ScrollUp,
|
|
.leftState = SerializedMouseAction_ScrollLeft,
|
|
.rightState = SerializedMouseAction_ScrollRight,
|
|
.intMultiplier = 1,
|
|
.initialSpeed = 20,
|
|
.acceleration = 20,
|
|
.deceleratedSpeed = 10,
|
|
.baseSpeed = 20,
|
|
.acceleratedSpeed = 50,
|
|
};
|
|
|
|
static void processMouseKineticState(mouse_kinetic_state_t *kineticState)
|
|
{
|
|
float initialSpeed = kineticState->intMultiplier * kineticState->initialSpeed;
|
|
float acceleration = kineticState->intMultiplier * kineticState->acceleration;
|
|
float deceleratedSpeed = kineticState->intMultiplier * kineticState->deceleratedSpeed;
|
|
float baseSpeed = kineticState->intMultiplier * kineticState->baseSpeed;
|
|
float acceleratedSpeed = kineticState->intMultiplier * kineticState->acceleratedSpeed;
|
|
|
|
if (!kineticState->wasMoveAction && !activeMouseStates[SerializedMouseAction_Decelerate]) {
|
|
kineticState->currentSpeed = initialSpeed;
|
|
}
|
|
|
|
bool isMoveAction = activeMouseStates[kineticState->upState] ||
|
|
activeMouseStates[kineticState->downState] ||
|
|
activeMouseStates[kineticState->leftState] ||
|
|
activeMouseStates[kineticState->rightState];
|
|
|
|
mouse_speed_t mouseSpeed = MouseSpeed_Normal;
|
|
if (activeMouseStates[SerializedMouseAction_Accelerate]) {
|
|
kineticState->targetSpeed = acceleratedSpeed;
|
|
mouseSpeed = MouseSpeed_Accelerated;
|
|
} else if (activeMouseStates[SerializedMouseAction_Decelerate]) {
|
|
kineticState->targetSpeed = deceleratedSpeed;
|
|
mouseSpeed = MouseSpeed_Decelerated;
|
|
} else if (isMoveAction) {
|
|
kineticState->targetSpeed = baseSpeed;
|
|
}
|
|
|
|
if (mouseSpeed == MouseSpeed_Accelerated || (kineticState->wasMoveAction && isMoveAction && (kineticState->prevMouseSpeed != mouseSpeed))) {
|
|
kineticState->currentSpeed = kineticState->targetSpeed;
|
|
}
|
|
|
|
if (isMoveAction) {
|
|
if (kineticState->currentSpeed < kineticState->targetSpeed) {
|
|
kineticState->currentSpeed += acceleration * (float)mouseElapsedTime / 1000.0f;
|
|
if (kineticState->currentSpeed > kineticState->targetSpeed) {
|
|
kineticState->currentSpeed = kineticState->targetSpeed;
|
|
}
|
|
} else {
|
|
kineticState->currentSpeed -= acceleration * (float)mouseElapsedTime / 1000.0f;
|
|
if (kineticState->currentSpeed < kineticState->targetSpeed) {
|
|
kineticState->currentSpeed = kineticState->targetSpeed;
|
|
}
|
|
}
|
|
|
|
float distance = kineticState->currentSpeed * (float)mouseElapsedTime / 1000.0f;
|
|
|
|
|
|
if (kineticState->isScroll && !kineticState->wasMoveAction) {
|
|
kineticState->xSum = 0;
|
|
kineticState->ySum = 0;
|
|
}
|
|
|
|
// Update horizontal state
|
|
|
|
bool horizontalMovement = true;
|
|
if (activeMouseStates[kineticState->leftState]) {
|
|
kineticState->xSum -= distance;
|
|
} else if (activeMouseStates[kineticState->rightState]) {
|
|
kineticState->xSum += distance;
|
|
} else {
|
|
horizontalMovement = false;
|
|
}
|
|
|
|
float xSumInt;
|
|
float xSumFrac = modff(kineticState->xSum, &xSumInt);
|
|
kineticState->xSum = xSumFrac;
|
|
kineticState->xOut = xSumInt;
|
|
|
|
if (kineticState->isScroll && !kineticState->wasMoveAction && kineticState->xOut == 0 && horizontalMovement) {
|
|
kineticState->xOut = kineticState->xSum > 0 ? 1 : -1;
|
|
kineticState->xSum = 0;
|
|
}
|
|
|
|
// Update vertical state
|
|
|
|
bool verticalMovement = true;
|
|
if (activeMouseStates[kineticState->upState]) {
|
|
kineticState->ySum -= distance;
|
|
} else if (activeMouseStates[kineticState->downState]) {
|
|
kineticState->ySum += distance;
|
|
} else {
|
|
verticalMovement = false;
|
|
}
|
|
|
|
float ySumInt;
|
|
float ySumFrac = modff(kineticState->ySum, &ySumInt);
|
|
kineticState->ySum = ySumFrac;
|
|
kineticState->yOut = ySumInt;
|
|
|
|
if (kineticState->isScroll && !kineticState->wasMoveAction && kineticState->yOut == 0 && verticalMovement) {
|
|
kineticState->yOut = kineticState->ySum > 0 ? 1 : -1;
|
|
kineticState->ySum = 0;
|
|
}
|
|
} else {
|
|
kineticState->currentSpeed = 0;
|
|
}
|
|
|
|
kineticState->prevMouseSpeed = mouseSpeed;
|
|
kineticState->wasMoveAction = isMoveAction;
|
|
}
|
|
|
|
static void processMouseActions()
|
|
{
|
|
mouseElapsedTime = Timer_GetElapsedTimeAndSetCurrent(&mouseUsbReportUpdateTime);
|
|
|
|
processMouseKineticState(&MouseMoveState);
|
|
ActiveUsbMouseReport->x = MouseMoveState.xOut;
|
|
ActiveUsbMouseReport->y = MouseMoveState.yOut;
|
|
MouseMoveState.xOut = 0;
|
|
MouseMoveState.yOut = 0;
|
|
|
|
processMouseKineticState(&MouseScrollState);
|
|
ActiveUsbMouseReport->wheelX = MouseScrollState.xOut;
|
|
ActiveUsbMouseReport->wheelY = MouseScrollState.yOut;
|
|
MouseScrollState.xOut = 0;
|
|
MouseScrollState.yOut = 0;
|
|
|
|
// The following line makes the firmware crash for some reason:
|
|
// SetDebugBufferFloat(60, mouseScrollState.currentSpeed);
|
|
// TODO: Figure out why.
|
|
// Oddly, the following line (which is the inlined version of the above) works:
|
|
// *(float*)(DebugBuffer + 60) = mouseScrollState.currentSpeed;
|
|
// The value parameter of SetDebugBufferFloat() seems to be the culprit because
|
|
// if it's not used within the function it doesn't crash anymore.
|
|
|
|
if (activeMouseStates[SerializedMouseAction_LeftClick]) {
|
|
ActiveUsbMouseReport->buttons |= MouseButton_Left;
|
|
}
|
|
if (activeMouseStates[SerializedMouseAction_MiddleClick]) {
|
|
ActiveUsbMouseReport->buttons |= MouseButton_Middle;
|
|
}
|
|
if (activeMouseStates[SerializedMouseAction_RightClick]) {
|
|
ActiveUsbMouseReport->buttons |= MouseButton_Right;
|
|
}
|
|
}
|
|
|
|
static layer_id_t previousLayer = LayerId_Base;
|
|
|
|
static void handleSwitchLayerAction(key_state_t *keyState, key_action_t *action)
|
|
{
|
|
static key_state_t *doubleTapSwitchLayerKey;
|
|
static uint32_t doubleTapSwitchLayerStartTime;
|
|
static uint32_t doubleTapSwitchLayerTriggerTime;
|
|
static bool isLayerDoubleTapToggled;
|
|
|
|
if (doubleTapSwitchLayerKey && doubleTapSwitchLayerKey != keyState && !keyState->previous) {
|
|
doubleTapSwitchLayerKey = NULL;
|
|
}
|
|
|
|
if (!keyState->previous && isLayerDoubleTapToggled && ToggledLayer == action->switchLayer.layer) {
|
|
ToggledLayer = LayerId_Base;
|
|
isLayerDoubleTapToggled = false;
|
|
}
|
|
|
|
if (action->type != KeyActionType_SwitchLayer) {
|
|
return;
|
|
}
|
|
|
|
if (keyState->previous && doubleTapSwitchLayerKey == keyState &&
|
|
Timer_GetElapsedTime(&doubleTapSwitchLayerTriggerTime) > DoubleTapSwitchLayerReleaseTimeout)
|
|
{
|
|
ToggledLayer = LayerId_Base;
|
|
}
|
|
|
|
if (!keyState->previous && previousLayer == LayerId_Base && action->switchLayer.mode == SwitchLayerMode_HoldAndDoubleTapToggle) {
|
|
if (doubleTapSwitchLayerKey && Timer_GetElapsedTimeAndSetCurrent(&doubleTapSwitchLayerStartTime) < DoubleTapSwitchLayerTimeout) {
|
|
ToggledLayer = action->switchLayer.layer;
|
|
isLayerDoubleTapToggled = true;
|
|
doubleTapSwitchLayerTriggerTime = Timer_GetCurrentTime();
|
|
} else {
|
|
doubleTapSwitchLayerKey = keyState;
|
|
}
|
|
doubleTapSwitchLayerStartTime = Timer_GetCurrentTime();
|
|
}
|
|
}
|
|
|
|
static uint8_t basicScancodeIndex = 0;
|
|
static uint8_t mediaScancodeIndex = 0;
|
|
static uint8_t systemScancodeIndex = 0;
|
|
|
|
static void applyKeyAction(key_state_t *keyState, key_action_t *action)
|
|
{
|
|
if (keyState->suppressed) {
|
|
return;
|
|
}
|
|
|
|
handleSwitchLayerAction(keyState, action);
|
|
|
|
switch (action->type) {
|
|
case KeyActionType_Keystroke:
|
|
ActiveUsbBasicKeyboardReport->modifiers |= action->keystroke.modifiers;
|
|
|
|
switch (action->keystroke.keystrokeType) {
|
|
case KeystrokeType_Basic:
|
|
if (basicScancodeIndex >= USB_BASIC_KEYBOARD_MAX_KEYS || action->keystroke.scancode == 0) {
|
|
break;
|
|
}
|
|
ActiveUsbBasicKeyboardReport->scancodes[basicScancodeIndex++] = action->keystroke.scancode;
|
|
break;
|
|
case KeystrokeType_Media:
|
|
if (mediaScancodeIndex >= USB_MEDIA_KEYBOARD_MAX_KEYS) {
|
|
break;
|
|
}
|
|
ActiveUsbMediaKeyboardReport->scancodes[mediaScancodeIndex++] = action->keystroke.scancode;
|
|
break;
|
|
case KeystrokeType_System:
|
|
if (systemScancodeIndex >= USB_SYSTEM_KEYBOARD_MAX_KEYS) {
|
|
break;
|
|
}
|
|
ActiveUsbSystemKeyboardReport->scancodes[systemScancodeIndex++] = action->keystroke.scancode;
|
|
break;
|
|
}
|
|
break;
|
|
case KeyActionType_Mouse:
|
|
activeMouseStates[action->mouseAction] = true;
|
|
break;
|
|
case KeyActionType_SwitchLayer:
|
|
// Handled by handleSwitchLayerAction()
|
|
break;
|
|
case KeyActionType_SwitchKeymap:
|
|
if (!keyState->previous) {
|
|
SwitchKeymapById(action->switchKeymap.keymapId);
|
|
}
|
|
break;
|
|
case KeyActionType_PlayMacro:
|
|
if (!keyState->previous) {
|
|
Macros_StartMacro(action->playMacro.macroId);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uint8_t secondaryRoleState = SecondaryRoleState_Released;
|
|
static uint8_t secondaryRoleSlotId;
|
|
static uint8_t secondaryRoleKeyId;
|
|
static secondary_role_t secondaryRole;
|
|
static bool simulateKeypresses = false;
|
|
static bool sendChar = false;
|
|
|
|
static void updateActiveUsbReports(void)
|
|
{
|
|
static uint8_t previousModifiers = 0;
|
|
|
|
if (MacroPlaying) {
|
|
Macros_ContinueMacro();
|
|
memcpy(ActiveUsbMouseReport, &MacroMouseReport, sizeof MacroMouseReport);
|
|
memcpy(ActiveUsbBasicKeyboardReport, &MacroBasicKeyboardReport, sizeof MacroBasicKeyboardReport);
|
|
memcpy(ActiveUsbMediaKeyboardReport, &MacroMediaKeyboardReport, sizeof MacroMediaKeyboardReport);
|
|
memcpy(ActiveUsbSystemKeyboardReport, &MacroSystemKeyboardReport, sizeof MacroSystemKeyboardReport);
|
|
return;
|
|
}
|
|
|
|
memset(activeMouseStates, 0, ACTIVE_MOUSE_STATES_COUNT);
|
|
|
|
basicScancodeIndex = 0;
|
|
mediaScancodeIndex = 0;
|
|
systemScancodeIndex = 0;
|
|
|
|
for (uint8_t keyId=0; keyId < RIGHT_KEY_MATRIX_KEY_COUNT; keyId++) {
|
|
KeyStates[SlotId_RightKeyboardHalf][keyId].current = RightKeyMatrix.keyStates[keyId];
|
|
}
|
|
|
|
layer_id_t activeLayer = LayerId_Base;
|
|
if (secondaryRoleState == SecondaryRoleState_Triggered && IS_SECONDARY_ROLE_LAYER_SWITCHER(secondaryRole)) {
|
|
activeLayer = SECONDARY_ROLE_LAYER_TO_LAYER_ID(secondaryRole);
|
|
}
|
|
if (activeLayer == LayerId_Base) {
|
|
activeLayer = GetActiveLayer();
|
|
}
|
|
bool layerGotReleased = previousLayer != LayerId_Base && activeLayer == LayerId_Base;
|
|
LedDisplay_SetLayer(activeLayer);
|
|
|
|
key_state_t *testKeyState = &KeyStates[SlotId_LeftKeyboardHalf][0];
|
|
if (!testKeyState->previous && testKeyState->current && activeLayer == LayerId_Fn) {
|
|
simulateKeypresses = !simulateKeypresses;
|
|
}
|
|
|
|
if (simulateKeypresses) {
|
|
sendChar = !sendChar;
|
|
ActiveUsbBasicKeyboardReport->scancodes[basicScancodeIndex++] = sendChar ? HID_KEYBOARD_SC_A : HID_KEYBOARD_SC_BACKSPACE;
|
|
}
|
|
|
|
for (uint8_t slotId=0; slotId<SLOT_COUNT; slotId++) {
|
|
for (uint8_t keyId=0; keyId<MAX_KEY_COUNT_PER_MODULE; keyId++) {
|
|
key_state_t *keyState = &KeyStates[slotId][keyId];
|
|
key_action_t *action = &CurrentKeymap[activeLayer][slotId][keyId];
|
|
|
|
if (keyState->debounceCounter) {
|
|
keyState->current = keyState->previous;
|
|
} else if (!keyState->previous && keyState->current) {
|
|
keyState->debounceCounter = KEY_DEBOUNCER_TIMEOUT_MSEC + 1;
|
|
}
|
|
|
|
if (keyState->current) {
|
|
key_action_t *baseAction = &CurrentKeymap[LayerId_Base][slotId][keyId];
|
|
if (layerGotReleased && !(baseAction->type == KeyActionType_Keystroke && baseAction->keystroke.scancode == 0 && baseAction->keystroke.modifiers)) {
|
|
keyState->suppressed = true;
|
|
}
|
|
|
|
if (action->type == KeyActionType_Keystroke && action->keystroke.secondaryRole) {
|
|
// Press released secondary role key.
|
|
if (!keyState->previous && action->type == KeyActionType_Keystroke && action->keystroke.secondaryRole && secondaryRoleState == SecondaryRoleState_Released) {
|
|
secondaryRoleState = SecondaryRoleState_Pressed;
|
|
secondaryRoleSlotId = slotId;
|
|
secondaryRoleKeyId = keyId;
|
|
secondaryRole = action->keystroke.secondaryRole;
|
|
keyState->suppressed = true;
|
|
}
|
|
} else {
|
|
// Trigger secondary role.
|
|
if (!keyState->previous && secondaryRoleState == SecondaryRoleState_Pressed) {
|
|
secondaryRoleState = SecondaryRoleState_Triggered;
|
|
} else {
|
|
applyKeyAction(keyState, action);
|
|
}
|
|
}
|
|
} else {
|
|
if (keyState->suppressed) {
|
|
keyState->suppressed = false;
|
|
}
|
|
|
|
// Release secondary role key.
|
|
if (keyState->previous && secondaryRoleSlotId == slotId && secondaryRoleKeyId == keyId) {
|
|
// Trigger primary role.
|
|
if (secondaryRoleState == SecondaryRoleState_Pressed) {
|
|
applyKeyAction(keyState, action);
|
|
}
|
|
secondaryRoleState = SecondaryRoleState_Released;
|
|
}
|
|
}
|
|
|
|
keyState->previous = keyState->current;
|
|
}
|
|
}
|
|
|
|
processMouseActions();
|
|
|
|
// When a layer switcher key gets pressed along with another key that produces some modifiers
|
|
// and the accomanying key gets released then keep the related modifiers active a long as the
|
|
// layer switcher key stays pressed. Useful for Alt+Tab keymappings and the like.
|
|
if (activeLayer != LayerId_Base && activeLayer == PreviousHeldLayer && basicScancodeIndex == 0) {
|
|
ActiveUsbBasicKeyboardReport->modifiers |= previousModifiers;
|
|
}
|
|
|
|
if (secondaryRoleState == SecondaryRoleState_Triggered && IS_SECONDARY_ROLE_MODIFIER(secondaryRole)) {
|
|
ActiveUsbBasicKeyboardReport->modifiers |= SECONDARY_ROLE_MODIFIER_TO_HID_MODIFIER(secondaryRole);
|
|
}
|
|
|
|
previousModifiers = ActiveUsbBasicKeyboardReport->modifiers;
|
|
previousLayer = activeLayer;
|
|
}
|
|
|
|
uint32_t UsbReportUpdateCounter;
|
|
usb_status_t status;
|
|
|
|
if (UsbReportUpdateSemaphore && !IsHostSleeping) {
|
|
return;
|
|
}
|
|
|
|
UsbReportUpdateCounter++;
|
|
|
|
ResetActiveUsbBasicKeyboardReport();
|
|
ResetActiveUsbMediaKeyboardReport();
|
|
ResetActiveUsbSystemKeyboardReport();
|
|
ResetActiveUsbMouseReport();
|
|
|
|
updateActiveUsbReports();
|
|
|
|
bool HasUsbBasicKeyboardReportChanged = memcmp(ActiveUsbBasicKeyboardReport, GetInactiveUsbBasicKeyboardReport(), sizeof(usb_basic_keyboard_report_t)) != 0;
|
|
bool HasUsbMediaKeyboardReportChanged = memcmp(ActiveUsbMediaKeyboardReport, GetInactiveUsbMediaKeyboardReport(), sizeof(usb_media_keyboard_report_t)) != 0;
|
|
bool HasUsbSystemKeyboardReportChanged = memcmp(ActiveUsbSystemKeyboardReport, GetInactiveUsbSystemKeyboardReport(), sizeof(usb_system_keyboard_report_t)) != 0;
|
|
bool HasUsbMouseReportChanged = memcmp(ActiveUsbMouseReport, GetInactiveUsbMouseReport(), sizeof(usb_mouse_report_t)) != 0;
|
|
|
|
if (IsHostSleeping && (previousLayer != LayerId_Base || HasUsbBasicKeyboardReportChanged || HasUsbMediaKeyboardReportChanged || HasUsbSystemKeyboardReportChanged || HasUsbMouseReportChanged)) {
|
|
WakeUpHost(true); // Wake up the host if any key is pressed and the computer is sleeping.
|
|
}
|
|
|
|
if (HasUsbBasicKeyboardReportChanged) {
|
|
status = UsbBasicKeyboardAction();
|
|
if (status == kStatus_USB_Success) {
|
|
UsbReportUpdateSemaphore |= 1 << USB_BASIC_KEYBOARD_INTERFACE_INDEX;
|
|
}
|
|
}
|
|
|
|
if (HasUsbMediaKeyboardReportChanged) {
|
|
status = UsbMediaKeyboardAction();
|
|
if (status == kStatus_USB_Success) {
|
|
UsbReportUpdateSemaphore |= 1 << USB_MEDIA_KEYBOARD_INTERFACE_INDEX;
|
|
}
|
|
}
|
|
|
|
if (HasUsbSystemKeyboardReportChanged) {
|
|
status = UsbSystemKeyboardAction();
|
|
if (status == kStatus_USB_Success) {
|
|
UsbReportUpdateSemaphore |= 1 << USB_SYSTEM_KEYBOARD_INTERFACE_INDEX;
|
|
}
|
|
}
|
|
|
|
// Send out the mouse position and wheel values continuously if the report is not zeros, but only send the mouse button states when they change.
|
|
if (HasUsbMouseReportChanged || ActiveUsbMouseReport->x || ActiveUsbMouseReport->y ||
|
|
ActiveUsbMouseReport->wheelX || ActiveUsbMouseReport->wheelY) {
|
|
status = UsbMouseAction();
|
|
if (status == kStatus_USB_Success) {
|
|
UsbReportUpdateSemaphore |= 1 << USB_MOUSE_INTERFACE_INDEX;
|
|
}
|
|
}
|
|
}
|