@@ -57,6 +57,11 @@ namespace {
57
57
}) != cend (sequence);
58
58
}
59
59
60
+ bool contains (const KeySequence& sequence, KeyEvent event) {
61
+ return std::find (cbegin (sequence),
62
+ cend (sequence), event) != cend (sequence);
63
+ }
64
+
60
65
void replace_key (KeySequence& sequence, Key both, Key key) {
61
66
std::for_each (begin (sequence), end (sequence),
62
67
[&](KeyEvent& event) {
@@ -178,9 +183,12 @@ Config ParseConfig::operator()(std::istream& is,
178
183
m_after_empty_context_block = false ;
179
184
m_enforce_lowercase_commands = { };
180
185
m_allow_unmapped_commands = { };
186
+ m_forward_modifiers.clear ();
181
187
182
188
// add default context
183
- m_config.contexts .push_back ({ true , {}, {} });
189
+ auto & default_context = m_config.contexts .emplace_back ();
190
+ default_context.begin_stage = true ;
191
+ default_context.system_filter_matched = true ;
184
192
185
193
// register common logical keys
186
194
add_logical_key (" Shift" , Key::ShiftLeft, Key::ShiftRight);
@@ -195,6 +203,9 @@ Config ParseConfig::operator()(std::istream& is,
195
203
if (!command.mapped )
196
204
throw ConfigError (" Command '" + command.name + " ' was not mapped" );
197
205
206
+ // prepend forward-modifier mappings in each stage. e.g.: ShiftLeft >> ShiftLeft
207
+ prepend_forward_modifier_mappings ();
208
+
198
209
// remove contexts of other systems or which are empty
199
210
optimize_contexts ();
200
211
@@ -204,6 +215,8 @@ Config ParseConfig::operator()(std::istream& is,
204
215
replace_logical_key (both, left, right);
205
216
}
206
217
218
+ suppress_forwarded_modifiers_in_outputs ();
219
+
207
220
// collect virtual key aliases
208
221
for (const auto & [name, value] : m_macros)
209
222
if (auto key = get_key_by_name (value); is_virtual_key (key))
@@ -391,6 +404,9 @@ void ParseConfig::parse_directive(It it, const It end) {
391
404
else if (ident == " allow-unmapped-commands" ) {
392
405
m_allow_unmapped_commands = read_optional_bool ();
393
406
}
407
+ else if (ident == " forward-modifiers" ) {
408
+ m_forward_modifiers = parse_forward_modifiers_list (&it, end);
409
+ }
394
410
else {
395
411
error (" Unknown directive '" + ident + " '" );
396
412
}
@@ -453,6 +469,30 @@ KeySequence ParseConfig::parse_modifier_list(std::string_view string) {
453
469
return sequence;
454
470
}
455
471
472
+ std::vector<Key> ParseConfig::parse_forward_modifiers_list (It* it, const It end) {
473
+ auto modifiers = std::vector<Key>();
474
+ while (*it != end) {
475
+ const auto name = read_ident (it, end);
476
+ if (name.empty ())
477
+ error (" Key name expected" );
478
+
479
+ const auto it2 = std::find_if (m_logical_keys.begin (), m_logical_keys.end (),
480
+ [&](const LogicalKey& key) { return key.name == name; });
481
+ if (it2 != m_logical_keys.end ()) {
482
+ modifiers.push_back (it2->left );
483
+ modifiers.push_back (it2->right );
484
+ }
485
+ else if (const auto key = ::get_key_by_name (name); is_device_key (key)) {
486
+ modifiers.push_back (key);
487
+ }
488
+ else {
489
+ error (" Invalid key '" + name + " '" );
490
+ }
491
+ skip_space (it, end);
492
+ }
493
+ return modifiers;
494
+ }
495
+
456
496
void ParseConfig::parse_context (It it, const It end) {
457
497
auto & context = m_config.contexts .emplace_back ();
458
498
context.system_filter_matched = true ;
@@ -931,7 +971,7 @@ void ParseConfig::optimize_contexts() {
931
971
const auto has_no_effect = (
932
972
context.inputs .empty () &&
933
973
context.command_outputs .empty () &&
934
- !context.begin_stage );
974
+ (i == 0 || !context.begin_stage ) );
935
975
936
976
if (can_not_match || has_no_effect) {
937
977
// convert fallthrough context when removing the non-fallthrough context
@@ -953,3 +993,53 @@ void ParseConfig::optimize_contexts() {
953
993
}
954
994
}
955
995
}
996
+
997
+ void ParseConfig::prepend_forward_modifier_mappings () {
998
+ for (auto & context : m_config.contexts )
999
+ if (context.begin_stage )
1000
+ for (auto it = m_forward_modifiers.rbegin ();
1001
+ it != m_forward_modifiers.rend (); ++it) {
1002
+ const auto key = *it;
1003
+ context.inputs .insert (context.inputs .begin (), {
1004
+ KeySequence{
1005
+ KeyEvent (key, KeyState::Down),
1006
+ KeyEvent (key, KeyState::UpAsync)
1007
+ },
1008
+ static_cast <int >(context.outputs .size ())
1009
+ });
1010
+ context.outputs .push_back ({
1011
+ KeyEvent (key, KeyState::Down)
1012
+ });
1013
+ }
1014
+ }
1015
+
1016
+ void ParseConfig::suppress_forwarded_modifiers_in_outputs () {
1017
+ if (m_forward_modifiers.empty ())
1018
+ return ;
1019
+
1020
+ for (auto & context : m_config.contexts ) {
1021
+ for (auto key : m_forward_modifiers) {
1022
+ const auto down_event = KeyEvent (key, KeyState::Down);
1023
+ const auto not_event = KeyEvent (key, KeyState::Not);
1024
+ const auto prepend_not_event = [&](KeySequence& output) {
1025
+ if (!contains (output, down_event) &&
1026
+ !contains (output, not_event))
1027
+ output.insert (output.begin (), not_event);
1028
+ };
1029
+
1030
+ for (auto & input : context.inputs )
1031
+ if (contains (input.input , down_event)) {
1032
+ if (input.output_index >= 0 ) {
1033
+ prepend_not_event (context.outputs [input.output_index ]);
1034
+ }
1035
+ else {
1036
+ for (auto & following_context : m_config.contexts ) {
1037
+ for (auto & command : following_context.command_outputs )
1038
+ if (command.index == input.output_index )
1039
+ prepend_not_event (command.output );
1040
+ }
1041
+ }
1042
+ }
1043
+ }
1044
+ }
1045
+ }
0 commit comments