Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc-dev/reference-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ COMMAND = set module.touchpad.holdContinuationTimeout <0-65535 (INT)>
COMMAND = set secondaryRole.defaultStrategy { simple | advanced }
COMMAND = set secondaryRole.advanced.timeout <ms, 0-500 (INT)>
COMMAND = set secondaryRole.advanced.timeoutAction { primary | secondary | none }
COMMAND = set secondaryRole.advanced.timeoutType { active | passive } <defines whether the secondary role should perform it's timeout action when the timeout is reached or when the key is released after having been pressed beyond timeout without triggering secondary>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming is obviously up for debate. I see passive as the timeout passively change the primary action while active actively executes the timeout action. It is entirely reasonable to see it as passive as timeout executes passively while you do nothing and active is that the timeout executes on the activity of release. Probably different wording is in order.

COMMAND = set secondaryRole.advanced.safetyMargin <ms, higher value adjusts sensitivity towards primary role -50-50 (INT)>
COMMAND = set secondaryRole.advanced.triggeringEvent { press | release | none } <determines whether secondary role is triggered when another key is pressed or when another key is pressed and released while the secondary role key is being held, or not at all from other keys being pressed>
COMMAND = set secondaryRole.advanced.triggerByMouse <trigger secondary role immediately on mouse move (BOOL)
Expand Down Expand Up @@ -704,6 +705,7 @@ Key actions can be parametrized with macro arguments. These arguments can be exp
- advanced strategy may trigger secondary role depending on timeout, or depending on key release order.
- `set secondaryRole.advanced.timeout <timeout in ms, 350 (INT)>` if this timeout is reached, `timeoutAction` (secondary by default) role is activated.
- `set secondaryRole.advanced.timeoutAction { primary | secondary | none}` defines whether the primary action or the secondary role, or no action at all, should be activated when timeout is reached
- `set secondaryRole.advanced.timeoutType { active | passive }` defines whether the secondary role should perform it's timeout action when the timeout is reached or when the key is released after having been pressed beyond timeout without triggering secondary
- `set secondaryRole.advanced.triggeringEvent { press | release | none }` determines whether a secondary role key is triggered as secondary if another key is pressed while it's held, if another key is pressed and released while it's held, or not at all from other keys being held. The third option allows usage of the timeout feature without inter-key triggers.
- `set secondaryRole.advanced.triggerByMouse BOOL` if enabled, any mouse (module) activity triggers secondary role immediately.
- `set secondaryRole.advanced.minimumHoldTime <ms, 0-255 (INT)>` sets the minimum time that a key must be held before it is allowed to trigger as secondary role.
Expand Down
8 changes: 5 additions & 3 deletions doc-dev/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,9 +479,10 @@ Firngrod's Home Row Mods (HRM) configuration (Recommended):
set secondaryRole.defaultStrategy advanced
set secondaryRole.advanced.triggeringEvent release
set secondaryRole.advanced.timeout 300
set secondaryRole.advanced.timeoutAction secondary
set secondaryRole.advanced.timeoutType passive
set secondaryRole.advanced.timeoutAction none
Comment on lines +482 to +483
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are my settings, don't know if they should be recommended. The idea has been described on the Forum. Basically, mods only activate in combinations with a non-mod, otherwise not. If I need to activate a mod (combo) on it's own, I will activate it with a secondary key on the other half, but hold that key long enough to get a none timeout action. With active secondary timeout, I would definitely recommend these settings still.

set secondaryRole.advanced.doubletapToPrimary 1
set secondaryRole.advanced.safetyMargin -100
set secondaryRole.advanced.safetyMargin -40
set secondaryRole.advanced.triggerByMouse 1
set secondaryRole.advanced.minimumHoldTime 150
set secondaryRole.advanced.acceptTriggersFromSameHalf 0
Expand All @@ -491,8 +492,9 @@ Full set of advanced strategy config values follows (for copy-paste convenience)

```
set secondaryRole.defaultStrategy advanced
set secondaryRole.advanced.timeout 500
set secondaryRole.advanced.timeout 200
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The text below states 200 ms. On second thought, I should update the text instead. 200 ms timeout is aggressive.

set secondaryRole.advanced.timeoutAction secondary
set secondaryRole.advanced.timeoutType active
set secondaryRole.advanced.safetyMargin 0
set secondaryRole.advanced.triggeringEvent press
set secondaryRole.advanced.triggerByMouse 0
Expand Down
1 change: 1 addition & 0 deletions right/src/config_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ const config_t DefaultCfg = (config_t){
.SecondaryRoles_AdvancedStrategyMinimumHoldTime = 0,
.SecondaryRoles_AdvancedStrategyDoubletapToPrimary = true,
.SecondaryRoles_AdvancedStrategyTimeoutAction = SecondaryRoleState_Secondary,
.SecondaryRoles_AdvancedStrategyTimeoutType = SecondaryRoleTimeoutType_Active,
.SecondaryRoles_AdvancedStrategyAcceptTriggersFromSameHalf = true,
.SecondaryRoles_Strategy = SecondaryRoleStrategy_Simple,
.StickyModifierStrategy = Stick_Smart,
Expand Down
1 change: 1 addition & 0 deletions right/src/config_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
bool SecondaryRoles_AdvancedStrategyTriggerByMouse;
bool SecondaryRoles_AdvancedStrategyDoubletapToPrimary;
secondary_role_state_t SecondaryRoles_AdvancedStrategyTimeoutAction;
secondary_role_timeout_type_t SecondaryRoles_AdvancedStrategyTimeoutType;
uint8_t SecondaryRoles_AdvancedStrategyMinimumHoldTime;

// mouse keys
Expand Down
28 changes: 14 additions & 14 deletions right/src/key_states.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,21 @@

// Inline functions

static inline bool KeyState_Active(key_state_t* s) { return s->current; };
static inline bool KeyState_Inactive(key_state_t* s) { return !s->current; };
static inline bool KeyState_ActivatedNow(key_state_t* s) { return !s->previous && s->current; };
static inline bool KeyState_DeactivatedNow(key_state_t* s) { return s->previous && !s->current; };
static inline bool KeyState_ActivatedEarlier(key_state_t* s) { return s->previous && s->current; };
static inline bool KeyState_DeactivatedEarlier(key_state_t* s) { return !s->previous && !s->current; };
static inline bool KeyState_NonZero(key_state_t* s) { return s->previous || s->current; };
static inline bool KeyState_Active(const key_state_t* s) { return s->current; };
static inline bool KeyState_Inactive(const key_state_t* s) { return !s->current; };
static inline bool KeyState_ActivatedNow(const key_state_t* s) { return !s->previous && s->current; };
static inline bool KeyState_DeactivatedNow(const key_state_t* s) { return s->previous && !s->current; };
static inline bool KeyState_ActivatedEarlier(const key_state_t* s) { return s->previous && s->current; };
static inline bool KeyState_DeactivatedEarlier(const key_state_t* s) { return !s->previous && !s->current; };
static inline bool KeyState_NonZero(const key_state_t* s) { return s->previous || s->current; };

static inline bool KeyState_IsRightHalf(key_state_t* s) { return s < &KeyStates[1][0]; };
static inline bool KeyState_IsLeftHalf(key_state_t* s) { return s < &KeyStates[2][0] && s >= &KeyStates[1][0]; };
static inline bool KeyState_IsKeyCluster(key_state_t* s) { return s >= &KeyStates[2][0] && s < &KeyStates[3][0]; };
static inline bool KeyState_IsMouseModule(key_state_t* s) { return s >= &KeyStates[3][0]; };
static inline bool KeyState_IsModule(key_state_t* s) { return s >= &KeyStates[2][0]; };
static inline bool KeyState_IsRightSide(key_state_t* s) { return KeyState_IsRightHalf(s) || KeyState_IsMouseModule(s); };
static inline bool KeyState_IsLeftSide(key_state_t* s) { return s >= &KeyStates[1][0] && s < &KeyStates[3][0]; };
static inline bool KeyState_IsRightHalf(const key_state_t* s) { return s < &KeyStates[1][0]; };
static inline bool KeyState_IsLeftHalf(const key_state_t* s) { return s < &KeyStates[2][0] && s >= &KeyStates[1][0]; };
static inline bool KeyState_IsKeyCluster(const key_state_t* s) { return s >= &KeyStates[2][0] && s < &KeyStates[3][0]; };
static inline bool KeyState_IsMouseModule(const key_state_t* s) { return s >= &KeyStates[3][0]; };
static inline bool KeyState_IsModule(const key_state_t* s) { return s >= &KeyStates[2][0]; };
static inline bool KeyState_IsRightSide(const key_state_t* s) { return KeyState_IsRightHalf(s) || KeyState_IsMouseModule(s); };
static inline bool KeyState_IsLeftSide(const key_state_t* s) { return s >= &KeyStates[1][0] && s < &KeyStates[3][0]; };


#endif
21 changes: 19 additions & 2 deletions right/src/macros/key_timing.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "macros/key_timing.h"
#include "key_states.h"
#include "macros/status_buffer.h"
#include "secondary_role_driver.h"
#include "utils.h"
#include "timer.h"

Expand All @@ -23,15 +24,31 @@ void KeyTiming_RecordReport(usb_basic_keyboard_report_t* report)
Utils_PrintReport(" OUT", ActiveUsbBasicKeyboardReport);
}

void KeyTiming_RecordComment(key_state_t* keyState, const char* comment)
void KeyTiming_RecordComment(key_state_t* keyState, secondary_role_state_t state, int32_t resolutionLine)
{
const char* keyAbbreviation = Utils_KeyAbbreviation(keyState);

Macros_SetStatusNumSpaced(Timer_GetCurrentTime(), false);
Macros_SetStatusChar(' ');
Macros_SetStatusString(keyAbbreviation, NULL);
Macros_SetStatusChar(' ');
Macros_SetStatusString(comment, NULL);
switch (state) {
case SecondaryRoleState_Primary:
Macros_SetStatusChar('P');
break;
case SecondaryRoleState_Secondary:
Macros_SetStatusChar('S');
break;
case SecondaryRoleState_NoOp:
Macros_SetStatusChar('N');
break;
case SecondaryRoleState_DontKnowYet:
Macros_SetStatusChar('D');
break;
}
Macros_SetStatusChar(':');
Macros_SetStatusNumSpaced(resolutionLine, false);
Macros_SetStatusChar('\n');

}

4 changes: 1 addition & 3 deletions right/src/macros/key_timing.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

#define KEY_TIMING(code) if (RecordKeyTiming) { code; }

#define KEY_TIMING2(condition, code) if (RecordKeyTiming && condition) { code; }

// Typedefs:


Expand All @@ -23,6 +21,6 @@

void KeyTiming_RecordKeystroke(key_state_t *keyState, bool active, uint32_t pressTime, uint32_t activationTime);
void KeyTiming_RecordReport(usb_basic_keyboard_report_t* report);
void KeyTiming_RecordComment(key_state_t* keyState, const char* comment);
void KeyTiming_RecordComment(key_state_t* keyState, secondary_role_state_t state, int32_t resolutionLine);

#endif
4 changes: 4 additions & 0 deletions right/src/macros/set_command.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@ static macro_variable_t secondaryRoleAdvanced(parser_context_t* ctx, set_command
DEFINE_NONE_LIMITS();
ASSIGN_CUSTOM(int32_t, intVar, Cfg.SecondaryRoles_AdvancedStrategyTimeoutAction, ConsumeSecondaryRoleTimeoutAction(ctx));
}
else if (ConsumeToken(ctx, "timeoutType")) {
DEFINE_NONE_LIMITS();
ASSIGN_CUSTOM(int32_t, intVar, Cfg.SecondaryRoles_AdvancedStrategyTimeoutType, ConsumeSecondaryRoleTimeoutType(ctx));
}
else if (ConsumeToken(ctx, "safetyMargin")) {
DEFINE_INT_LIMITS(-32768, 32767);
ASSIGN_INT(Cfg.SecondaryRoles_AdvancedStrategySafetyMargin);
Expand Down
22 changes: 5 additions & 17 deletions right/src/postponer.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,33 +404,22 @@ void PostponerQuery_InfoByKeystate(key_state_t* key, postponer_buffer_record_typ
}
}

void PostponerQuery_FindFirstPressed(postponer_buffer_record_type_t** press, postponer_buffer_record_type_t** release,
key_state_t *opposingKey)
void PostponerQuery_FindFirstPressed(const postponer_buffer_record_type_t** press, const key_state_t *opposingKey)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Release doesn't matter for the trigger on press and vice versa. We only actually need the time of the event to enforce safety margin. I don't know if we should reduce the function to just return a timestamp?

{
bool opposingIsRight = KeyState_IsRightSide(opposingKey);
for ( int i = 0; i < bufferSize; i++ ) {
*press = &buffer[POS(i)];
if ((*press)->event.type == PostponerEventType_PressKey) {
if (opposingKey == NULL || opposingIsRight != KeyState_IsRightSide((*press)->event.key.keyState)) {
for ( int j = i + 1; j < bufferSize; j++ ) {
*release = &buffer[POS(j)];
if ((*release)->event.type == PostponerEventType_ReleaseKey
&& (*press)->event.key.keyState == (*release)->event.key.keyState ) {
return;
}
}
*release = NULL;
return;
}
}
}
*release = NULL;
*press = NULL;
return;
}

void PostponerQuery_FindFirstReleased(postponer_buffer_record_type_t** press, postponer_buffer_record_type_t** release,
key_state_t *opposingKey)
void PostponerQuery_FindFirstReleased(const postponer_buffer_record_type_t** release, const key_state_t *opposingKey)
{
bool opposingIsRight = KeyState_IsRightSide(opposingKey);
if (bufferSize > 1) {
Expand All @@ -439,17 +428,16 @@ void PostponerQuery_FindFirstReleased(postponer_buffer_record_type_t** press, po
if ((*release)->event.type == PostponerEventType_ReleaseKey) {
if (opposingKey == NULL || opposingIsRight != KeyState_IsRightSide((*release)->event.key.keyState)) {
for ( int j = 0; j < i; j++ ) {
*press = &buffer[POS(j)];
if ((*press)->event.type == PostponerEventType_PressKey
&& (*press)->event.key.keyState == (*release)->event.key.keyState ) {
const postponer_buffer_record_type_t * const press = &buffer[POS(j)];
if (press->event.type == PostponerEventType_PressKey
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we still need to find the press to ensure that it's there.

&& press->event.key.keyState == (*release)->event.key.keyState ) {
return;
}
}
}
}
}
}
*press = NULL;
*release = NULL;
return;
}
Expand Down
4 changes: 2 additions & 2 deletions right/src/postponer.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@
bool PostponerQuery_IsActiveEventually(key_state_t* key);
void PostponerQuery_InfoByKeystate(key_state_t* key, postponer_buffer_record_type_t** press, postponer_buffer_record_type_t** release);
bool PostponerQuery_ContainsKeyId(uint8_t keyid);
void PostponerQuery_FindFirstPressed(postponer_buffer_record_type_t** press, postponer_buffer_record_type_t** release, key_state_t* opposingKey);
void PostponerQuery_FindFirstReleased(postponer_buffer_record_type_t** press, postponer_buffer_record_type_t** release, key_state_t* opposingKey);
void PostponerQuery_FindFirstPressed(const postponer_buffer_record_type_t** press, const key_state_t* opposingKey);
void PostponerQuery_FindFirstReleased(const postponer_buffer_record_type_t** release, const key_state_t* opposingKey);

// Functions (Query APIs extended):
uint16_t PostponerExtended_PendingId(uint16_t idx);
Expand Down
Loading