Skip to content

Commit fbcaef4

Browse files
authored
Merge pull request #831 from o-sdn-o/gui-bridge
Make the emulation of tmux-like prefix keys more consistent
2 parents 19b81d2 + 318db5d commit fbcaef4

File tree

9 files changed

+54
-31
lines changed

9 files changed

+54
-31
lines changed

doc/architecture.md

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -525,21 +525,25 @@ It is possible to emulate the tmux-like keyboard prefix approach by using a glob
525525
```xml
526526
<config>
527527
<events>
528-
<applet> <!-- Key bindings for the application window. -->
529-
<if_mod_on="if (not kbmodifier) then return; end;"/> <!-- `if_mod_on` macro definition. Do nothing if `kbmodifier` is false. -->
528+
<desktop>
530529
<script="kbmodifier = not kbmodifier; log('kbmodifier=', kbmodifier);" on="Ctrl+B"/> <!-- Emulate tmux-like prefix key. The expression `log('kbmodifier=', kbmodifier);` is for debugging purposes only (the output is visible in the `Log Monitor`). -->
531-
<script=if_mod_on | MoveAppletLeft on="preview: LeftArrow" /> <!-- The ` | ` operator concatenates script fragments/macros. -->
532-
<script=if_mod_on | MoveAppletRight on="preview: RightArrow"/> <!-- Use "preview:..." to get the key event before the terminal/application. -->
533-
<script=if_mod_on | MoveAppletUp on="preview: UpArrow" /> <!-- When kbmodifier is true, you can move windows using the arrow keys. -->
534-
<script=if_mod_on | MoveAppletDown on="preview: DownArrow" /> <!-- Macros like `MoveApplet...` are defined in the default configuration. You can list them with `vtm -l`. -->
535-
<script=if_mod_on | MoveAppletTopLeft on="preview: LeftArrow+UpArrow | UpArrow+LeftArrow" /> <!-- Simultaneous key presses should also be processed if supported. -->
536-
<script=if_mod_on | MoveAppletBottomLeft on="preview: LeftArrow+DownArrow | DownArrow+LeftArrow" /> <!-- It is convenient to specify multiple keyboard shortcuts in one definition separated by `|`. -->
537-
<script=if_mod_on | MoveAppletTopRight on="preview: RightArrow+UpArrow | UpArrow+RightArrow" />
538-
<script=if_mod_on | MoveAppletBottomRight on="preview: RightArrow+DownArrow | DownArrow+RightArrow"/>
539-
<script=if_mod_on | IncreaseAppletWidth on="preview: Ctrl+RightArrow" />
540-
<script=if_mod_on | DecreaseAppletWidth on="preview: Ctrl+LeftArrow" />
541-
<script=if_mod_on | IncreaseAppletHeight on="preview: Ctrl+DownArrow" />
542-
<script=if_mod_on | DecreaseAppletHeight on="preview: Ctrl+UpArrow" />
530+
</desktop>
531+
<applet> <!-- Key bindings for the application window. -->
532+
<KeyFilter="if (not kbmodifier and vtm.gear.Bypass()) then return; end; "/> <!-- `KeyFilter` macro. Do nothing if `kbmodifier` is false. Calling vtm.gear.Bypass() always returns true. -->
533+
<script=KeyFilter | MoveAppletLeft prerun=KeyFilter on="LeftArrow" /> <!-- The ` | ` operator concatenates script fragments/macros. If for some reason the keyboard event is not processed by anyone, it will then return and fire on this object, so the KeyFilter is also reused at the beginning of the `script="..."`. -->
534+
<script=KeyFilter | MoveAppletRight prerun=KeyFilter on="RightArrow" /> <!-- The `prerun` attribute contains a Lua script that will be executed during pre-polling to filter out key events. -->
535+
<script=KeyFilter | MoveAppletUp prerun=KeyFilter on="UpArrow" /> <!-- When kbmodifier is true, you can move windows using the arrow keys. -->
536+
<script=KeyFilter | MoveAppletDown prerun=KeyFilter on="DownArrow" /> <!-- Macros like `MoveApplet...` are defined in the default configuration. You can list them with `vtm -l`. -->
537+
<script=KeyFilter | MoveAppletTopLeft prerun=KeyFilter on="LeftArrow+UpArrow | UpArrow+LeftArrow" /> <!-- Simultaneous key presses should also be processed if supported. -->
538+
<script=KeyFilter | MoveAppletBottomLeft prerun=KeyFilter on="LeftArrow+DownArrow | DownArrow+LeftArrow" /> <!-- It is convenient to specify multiple keyboard shortcuts in one definition separated by `|`. -->
539+
<script=KeyFilter | MoveAppletTopRight prerun=KeyFilter on="RightArrow+UpArrow | UpArrow+RightArrow" />
540+
<script=KeyFilter | MoveAppletBottomRight prerun=KeyFilter on="RightArrow+DownArrow | DownArrow+RightArrow"/>
541+
<script=KeyFilter | IncreaseAppletWidth prerun=KeyFilter on="Ctrl+RightArrow" />
542+
<script=KeyFilter | DecreaseAppletWidth prerun=KeyFilter on="Ctrl+LeftArrow" />
543+
<script=KeyFilter | IncreaseAppletHeight prerun=KeyFilter on="Ctrl+DownArrow" />
544+
<script=KeyFilter | DecreaseAppletHeight prerun=KeyFilter on="Ctrl+UpArrow" />
545+
<script=KeyFilter | FocusPrevWindow prerun=KeyFilter on="PageUp" />
546+
<script=KeyFilter | FocusNextWindow prerun=KeyFilter on="PageDown" />
543547
</applet>
544548
</events>
545549
</config>

doc/settings.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ The syntax for defining event bindings is:
398398

399399
```xml
400400
<dom_element1>
401-
<script="script body" on="EventID1" ... on="preview:EventId2" ...>
401+
<script="script body" prerun="prerun script body" on="EventID1" ... on="preview:EventId2" ...>
402402
<on="EventID"/>
403403
<on="EventID" source="OptionalEventSourceObjectID"/>
404404
...
@@ -416,6 +416,7 @@ Tag | Belongs to | Value | Description
416416
`<dom_element>` | | ObjectID | Visual tree object id.
417417
`id` | `<dom_element>` | UTF-8 string | Additional id for the visual tree object.
418418
`script` | `<dom_element>` | UTF-8 string | A Lua script that will be executed when the events specified by the `on` tags occur.
419+
`prerun` | `script` | UTF-8 string | A Lua script that will be executed during pre-polling prior the non-preview keyboard events specified in the `on` tags occurs. This is mostly used to indicate that the event is not expected on the source object.
419420
`on` | `script` | EventID | Specific event id text string.
420421
`source` | `on` | ObjectID | Visual tree object id.
421422

@@ -594,6 +595,7 @@ Standard object names
594595
| | | `vtm.gear.Interrupt()` | Interrupt the key event processing.
595596
| | | `vtm.gear.RepeatWhilePressed(ref ObjectId)` | Capture the mouse by ObjectId and trigger the mouse button pressed event to repeat while pressed.
596597
| | | `vtm.gear.Focus(ref ObjectId) -> bool` | Set input focus to the object. Returns true if focus is already set.
598+
| | | `vtm.gate.Bypass() -> bool` | Indicates that the keyboard event being processed is not expected by this object. Used by the `prerun` function inside `script` to bypass tier::keybdrelease events. Always return true.
597599
|`desktop` | Desktop environment | `vtm.desktop.Cleanup(bool b)` | Clean up temporary internal structures of the desktop environment and optionally report the state of registry objects.
598600
| | | `vtm.desktop.EventList()` | Print all available generic event IDs.
599601
| | | `vtm.desktop.Shutdown()` | Close all windows and shutdown the desktop.
@@ -666,14 +668,15 @@ Standard object names
666668

667669
Key bindings:
668670

669-
Configuration | Interpretation
670-
---------------------------------------------------------------|-----------------
671-
`<script=ScriptReference on="Key+Chord"/>` | Append existing bindings using an indirect reference (the `ScriptReference` variable without quotes).
672-
`<script="text" on="Key+Chord \| Another+Chord"/>` | Append existing bindings for `Key+Chord | Another+Chord`.
673-
`<script="text" on="Key+Chord"/>` | Append existing bindings with the directly specified Lua script body.
674-
`<script="text"><on="Key+Chord" source="ObjectID"/></script>` | Binding to an event source using a specific ObjectID.
675-
`<script="" on="Key+Chord"/>` | Remove all existing bindings for the specified key combination "Key+Chord".
676-
`<script="..." on="" />` | Do nothing.
671+
Configuration | Interpretation
672+
-------------------------------------------------------------|-----------------
673+
`<script=ScriptReference on="KeyChord"/>` | Append existing bindings using an indirect reference (the `ScriptReference` variable without quotes).
674+
`<script="..." on="KeyChord \| AnotherChord"/>` | Append existing bindings for `KeyChord | AnotherChord`.
675+
`<script="..." on="KeyChord"/>` | Append existing bindings with the directly specified Lua script body.
676+
`<script="..."><on="KeyChord" source="ObjectID"/></script>` | Binding to an event source using a specific `ObjectID`.
677+
`<script="" on="KeyChord"/>` | Remove all existing bindings for the specified key combination `KeyChord`.
678+
`<script="..." on="KeyChord" prerun="if (something) vtm.gear.Bypass() end"/>` | Bypass the `KeyChord` event if something. Works only with non-preview KeyChords.
679+
`<script="..." on="" />` | Do nothing.
677680

678681
EventId's:
679682

@@ -1143,7 +1146,7 @@ Notes
11431146
<script="vtm.defapp.ShowClosingPreview(faux);" on="Any" /> <!-- Preview for "Any" is always triggered after all other previews. Non-preview "Any" is triggered before all other keys. -->
11441147
<script="if (not vtm.gear.IsKeyRepeated()) then vtm.defapp.ShowClosingPreview(true); end" on="Esc" /> <!-- Window pred-close action (close when releasing the Esc key). -->
11451148
<script="if (vtm.defapp.ShowClosingPreview()) then vtm.defapp.Close() end" on="preview:-Esc"/> <!-- Close the window on Esc release. -->
1146-
<script="if (vtm.defapp.ShowClosingPreview()) then local focus_count=vtm(); vtm.defapp.ShowClosingPreview(focus_count~=0) end" source="applet" on="release:e2::form::state::focus::count" /> <!-- Disable window closing preview when focus is lost. -->
1149+
<script="if (vtm.defapp.ShowClosingPreview()) then local focus_count=vtm(); vtm.defapp.ShowClosingPreview(focus_count~=0) end" on="release:e2::form::state::focus::count" /> <!-- Disable window closing preview when focus is lost. -->
11471150
<script="vtm.defapp.ScrollViewportByPage( 0, 1)" on="PageUp" />
11481151
<script="vtm.defapp.ScrollViewportByPage( 0,-1)" on="PageDown" />
11491152
<script="vtm.defapp.ScrollViewportByStep( 0, 3)" on="UpArrow" />

src/netxs/desktopio/ansivt.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,7 @@ namespace netxs::ansi
891891
{
892892
auto count = 1;
893893
auto width = 0_sz;
894-
auto total = std::count(text::begin(), text::end(), '\n');
894+
auto total = std::count(text::begin(), text::end(), '\n') + 1;
895895
while (total)
896896
{
897897
total /= 10;

src/netxs/desktopio/application.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace netxs::app
2222

2323
namespace netxs::app::shared
2424
{
25-
static const auto version = "v2025.10.17";
25+
static const auto version = "v2025.10.18";
2626
static const auto repository = "https://github.com/directvt/vtm";
2727
static const auto usr_config = "~/.config/vtm/settings.xml"s;
2828
static const auto sys_config = "/etc/vtm/settings.xml"s;

src/netxs/desktopio/console.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,11 @@ namespace netxs::ui
512512
gear.indexer.expire();
513513
luafx.set_return();
514514
}},
515+
{ "Bypass", [&]
516+
{
517+
gear.touched = {};
518+
luafx.set_return(true);
519+
}},
515520
{ "SetHandled", [&]
516521
{
517522
auto dismiss = luafx.get_args_or(1, faux);

src/netxs/desktopio/controls.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1759,7 +1759,7 @@ namespace netxs::ui
17591759
keybd(base&&) = delete;
17601760
keybd(base& boss)
17611761
: skill{ boss },
1762-
instance_id{ datetime::now().time_since_epoch().count() }
1762+
instance_id{ datetime::uniqueid() }
17631763
{
17641764
boss.LISTEN(tier::general, input::events::die, gear, memo)
17651765
{

src/netxs/desktopio/events.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ namespace netxs::events
4242
static constexpr auto mouserelease = __COUNTER__ - counter; // events: Run in subscription order for object tree.
4343
static constexpr auto keybdpreview = __COUNTER__ - counter; // events: Run in subscription order for focused objects.
4444
static constexpr auto keybdrelease = __COUNTER__ - counter; // events: Run in subscription order for focused objects.
45+
static constexpr auto keybd_prerun = __COUNTER__ - counter; // events: Run in subscription order for focused objects (fires during keybdpreview stage; subscribers can reset gear.touch if their current keybdrelease subscription is not confirmed).
4546
static constexpr auto unknown = __COUNTER__ - counter; // events: .
4647
static constexpr auto str = std::to_array({ "release"sv,
4748
"preview"sv,
@@ -52,6 +53,7 @@ namespace netxs::events
5253
"mouserelease"sv,
5354
"keybdpreview"sv,
5455
"keybdrelease"sv,
56+
"keybd_prerun"sv,
5557
"unknown"sv, });
5658
static constexpr auto order = std::to_array({ feed::fwd,
5759
feed::rev,
@@ -61,6 +63,7 @@ namespace netxs::events
6163
feed::none,
6264
feed::none,
6365
feed::none,
66+
feed::none,
6467
feed::none, });
6568
};
6669

src/netxs/desktopio/input.hpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2125,6 +2125,7 @@ namespace netxs::input
21252125
text chord;
21262126
txts sources; // Event source list.
21272127
netxs::sptr<std::pair<ui64, text>> script_ptr;
2128+
netxs::sptr<std::pair<ui64, text>> prerun_ptr;
21282129
};
21292130
using vector = std::vector<binding_t>;
21302131

@@ -2202,13 +2203,14 @@ namespace netxs::input
22022203
}
22032204
}
22042205
}
2205-
auto keybind(base& boss, qiew chord_str, auto&& script_body, txts const& sources = {})
2206+
auto keybind(base& boss, qiew chord_str, auto&& script_body, netxs::sptr<std::pair<ui64, text>> prerun_body = {}, txts const& sources = {})
22062207
{
22072208
if (!chord_str) return;
22082209
auto [chords, is_preview] = input::bindings::get_chords(chord_str);
22092210
if (chords.size())
22102211
{
22112212
auto script_ptr = ptr::shared<script_ref>(boss.indexer, boss, script_body);
2213+
auto prerun_ptr = prerun_body ? ptr::shared<script_ref>(boss.indexer, boss, prerun_body) : netxs::sptr<script_ref>{};
22122214
auto reset_handler = !(script_ptr->script_body_ptr && script_ptr->script_body_ptr->second.size());
22132215
for (auto& binary_chord : chords) if (binary_chord.size()) // Scripts always store their sensors at the boss side, since the lifetime of base::scripting_context depends on the boss.
22142216
{
@@ -2237,6 +2239,10 @@ namespace netxs::input
22372239
auto event_id = boss.indexer.get_kbchord_hint(binary_chord);
22382240
auto tier_id = is_preview ? tier::keybdpreview : tier::keybdrelease;
22392241
set_handler(reset_handler, boss, tier_id, event_id, sources, script_ptr);
2242+
if (prerun_ptr)
2243+
{
2244+
set_handler(reset_handler, boss, tier::keybd_prerun, event_id, sources, prerun_ptr);
2245+
}
22402246
}
22412247
}
22422248
}
@@ -2245,7 +2251,7 @@ namespace netxs::input
22452251
{
22462252
for (auto& r : bindings)
22472253
{
2248-
keybind(boss, r.chord, r.script_ptr, r.sources);
2254+
keybind(boss, r.chord, r.script_ptr, r.prerun_ptr, r.sources);
22492255
}
22502256
}
22512257
void dispatch(auto& boss, auto& instance_id, hids& gear, si32 tier_id, hint event_id)
@@ -2255,6 +2261,7 @@ namespace netxs::input
22552261
&& boss.bell::has_handlers(tier::keybdrelease, event_id))
22562262
{
22572263
gear.touched = instance_id;
2264+
boss.base::signal(tier::keybd_prerun, event_id, gear);
22582265
}
22592266
}
22602267
auto load(settings& config, auto& script_list)
@@ -2265,6 +2272,7 @@ namespace netxs::input
22652272
//todo revise
22662273
//auto script_context = config.settings::push_context(script_ptr);
22672274
auto script_body_ptr = ptr::shared(std::pair<ui64, text>{ 0, config.settings::take_value(script_ptr) });
2275+
auto prerun_body_ptr = ptr::shared(std::pair<ui64, text>{ 0, config.settings::take_value_from(script_ptr, "prerun", ""s) });
22682276
auto on_ptr_list = config.settings::take_ptr_list_of(script_ptr, "on");
22692277
for (auto event_ptr : on_ptr_list)
22702278
{
@@ -2277,7 +2285,7 @@ namespace netxs::input
22772285
// log("chord='%%' \tpreview=%% source='%%' script=%%", on_rec, (si32)preview, source, ansi::hi(script_body_ptr->second));
22782286
// }
22792287
//}
2280-
bindings.push_back({ .chord = std::move(on_rec), .sources = std::move(sources), .script_ptr = script_body_ptr });
2288+
bindings.push_back({ .chord = std::move(on_rec), .sources = std::move(sources), .script_ptr = script_body_ptr, .prerun_ptr = prerun_body_ptr });
22812289
}
22822290
}
22832291
return bindings;

src/vtm.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ R"==(
376376
<script="vtm.defapp.ShowClosingPreview(faux);" on="Any" /> <!-- Preview for "Any" is always triggered after all other previews. Non-preview "Any" is triggered before all other keys. -->
377377
<script="if (not vtm.gear.IsKeyRepeated()) then vtm.defapp.ShowClosingPreview(true); end" on="Esc" /> <!-- Window pred-close action (close when releasing the Esc key). -->
378378
<script="if (vtm.defapp.ShowClosingPreview()) then vtm.defapp.Close() end" on="preview:-Esc"/> <!-- Close the window on Esc release. -->
379-
<script="if (vtm.defapp.ShowClosingPreview()) then local focus_count=vtm(); vtm.defapp.ShowClosingPreview(focus_count~=0) end" source="applet" on="release:e2::form::state::focus::count" /> <!-- Disable window closing preview when focus is lost. -->
379+
<script="if (vtm.defapp.ShowClosingPreview()) then local focus_count=vtm(); vtm.defapp.ShowClosingPreview(focus_count~=0) end" on="release:e2::form::state::focus::count" /> <!-- Disable window closing preview when focus is lost. -->
380380
<script="vtm.defapp.ScrollViewportByPage( 0, 1)" on="PageUp" />
381381
<script="vtm.defapp.ScrollViewportByPage( 0,-1)" on="PageDown" />
382382
<script="vtm.defapp.ScrollViewportByStep( 0, 3)" on="UpArrow" />

0 commit comments

Comments
 (0)