ptyfwd: add support for additional out-of-band hotkeys in ptyfwd

Let's add the ability that ptyfwd tools can register additional hotkeys
that they then can handle.

So far the only hotkey we support is ^]^]^] to exit the ptyfwd session
abruptly. Staying close to this let's add ^]^]<char> for additional
commands.
This commit is contained in:
Lennart Poettering
2025-03-02 21:40:50 +01:00
parent decae96905
commit b1f9d0e46b
2 changed files with 52 additions and 11 deletions

View File

@@ -103,6 +103,8 @@ struct PTYForward {
PTYForwardHangupHandler hangup_handler;
void *hangup_userdata;
PTYForwardHotkeyHandler hotkey_handler;
void *hotkey_userdata;
char *background_color;
AnsiColorState ansi_color_state;
@@ -198,17 +200,26 @@ static int pty_forward_done(PTYForward *f, int rcode) {
return sd_event_exit(e, rcode < 0 ? EXIT_FAILURE : rcode);
}
static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
const char *p;
typedef enum RequestOperation {
REQUEST_NOP,
REQUEST_EXIT,
REQUEST_HOTKEY_BASE,
REQUEST_HOTKEY_A = REQUEST_HOTKEY_BASE + 'a',
REQUEST_HOTKEY_Z = REQUEST_HOTKEY_BASE + 'z',
_REQUEST_OPERATION_MAX,
_REQUEST_OPERATION_INVALID = -EINVAL,
} RequestOperation;
static RequestOperation look_for_escape(PTYForward *f, const char *buffer, size_t n) {
assert(f);
assert(buffer);
assert(n > 0);
for (p = buffer; p < buffer + n; p++) {
for (const char *p = buffer; p < buffer + n; p++) {
/* Check for ^] */
if (*p == 0x1D) {
switch (*p) {
case 0x1D: { /* Check for ^] */
usec_t nw = now(CLOCK_MONOTONIC);
if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC) {
@@ -218,15 +229,29 @@ static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
(f->escape_counter)++;
if (f->escape_counter >= 3)
return true;
return REQUEST_EXIT;
}
} else {
break;
}
case 'a'...'z':
if (f->escape_counter == 2 &&
now(CLOCK_MONOTONIC) <= f->escape_timestamp + ESCAPE_USEC) {
f->escape_timestamp = 0;
f->escape_counter = 0;
return REQUEST_HOTKEY_BASE + *p;
}
_fallthrough_;
default:
f->escape_timestamp = 0;
f->escape_counter = 0;
}
}
return false;
return REQUEST_NOP;
}
static bool ignore_vhangup(PTYForward *f) {
@@ -654,10 +679,17 @@ static int do_shovel(PTYForward *f) {
} else {
/* Check if ^] has been pressed three times within one second. If we get this we quite
* immediately. */
if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k))
return -ECANCELED;
RequestOperation q = look_for_escape(f, f->in_buffer + f->in_buffer_full, k);
f->in_buffer_full += (size_t) k;
if (q < 0)
return q;
if (q == REQUEST_EXIT)
return -ECANCELED;
if (q >= REQUEST_HOTKEY_A && q <= REQUEST_HOTKEY_Z && f->hotkey_handler) {
r = f->hotkey_handler(f, q - REQUEST_HOTKEY_BASE, f->hotkey_userdata);
if (r < 0)
return r;
}
}
did_something = true;
@@ -1091,6 +1123,13 @@ void pty_forward_set_hangup_handler(PTYForward *f, PTYForwardHangupHandler cb, v
f->hangup_userdata = userdata;
}
void pty_forward_set_hotkey_handler(PTYForward *f, PTYForwardHotkeyHandler cb, void *userdata) {
assert(f);
f->hotkey_handler = cb;
f->hotkey_userdata = userdata;
}
bool pty_forward_drain(PTYForward *f) {
assert(f);

View File

@@ -24,6 +24,7 @@ typedef enum PTYForwardFlags {
} PTYForwardFlags;
typedef int (*PTYForwardHangupHandler)(PTYForward *f, int rcode, void *userdata);
typedef int (*PTYForwardHotkeyHandler)(PTYForward *f, char key, void *userdata);
#define N_PTY_FORWARD_SIGNALS 7
extern const int pty_forward_signals[N_PTY_FORWARD_SIGNALS];
@@ -35,6 +36,7 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup);
bool pty_forward_get_ignore_vhangup(PTYForward *f);
void pty_forward_set_hangup_handler(PTYForward *f, PTYForwardHangupHandler handler, void *userdata);
void pty_forward_set_hotkey_handler(PTYForward *f, PTYForwardHotkeyHandler handler, void *userdata);
bool pty_forward_drain(PTYForward *f);