UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
keyboard_control.cpp File Reference
#include <cassert>
#include <cctype>
#include <chrono>
#include <climits>
#include <cmath>
#include <condition_variable>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <queue>
#include <set>
#include <sstream>
#include <string>
#include <thread>
#include <utility>
#include <sys/select.h>
#include "compat/time.h"
#include "debug.h"
#include "host.h"
#include "keyboard_control.h"
#include "messaging.h"
#include "module.h"
#include "types.h"
#include "utils/color_out.h"
#include "utils/thread.h"
#include "video.h"
#include <conio.h>
Include dependency graph for keyboard_control.cpp:

Go to the source code of this file.

Classes

class  keyboard_control::impl
 
struct  keyboard_control::impl::m_mod
 

Macros

#define CHECK_EOF(x)
 
#define CONFIG_FILE   "ug-key-map.txt"
 
#define G(key)
 
#define GETCH   getch
 
#define MOD_NAME   "[key control] "
 

Functions

 ADD_TO_PARAM ("disable-keyboard-control", "* disable-keyboard-control[=no]\n" " disables keyboard control (usable mainly for non-interactive runs)\n")
 
static int count_utf8_bytes (unsigned int i)
 
static int64_t get_ansi_code ()
 
void get_keycode_name (int64_t ch, char *buf, size_t buflen)
 
static string get_keycode_representation (int64_t ch)
 
static int64_t get_utf8_code (int c)
 
static string get_utf8_representation (int64_t ch)
 
static bool is_utf8 (int64_t ch)
 
bool keycontrol_register_key (struct module *receiver_mod, int64_t key, const char *message, const char *description)
 Registers callback message for given key.
 
void keycontrol_send_key (struct module *root, int64_t key)
 
static void print_start_elapsed_time (time_t start_time)
 
void restore_old_tio (void)
 
static bool set_tio ()
 

Detailed Description

Author
Martin Pulec pulec.nosp@m.@ces.nosp@m.net.c.nosp@m.z

With code taken from Olivier Mehani (set_tio()).

Todo
Consider using some abstraction over terminal control mainly because differencies between Linux and Windows (ANSI mode or terminfo?)
Todo
  • key should not be overriden (eg. preset and custom)
  • preset keys and customs ones should be unified (in key mapping)

Definition in file keyboard_control.cpp.

Macro Definition Documentation

◆ CHECK_EOF

#define CHECK_EOF ( x)
Value:
do { \
if ((x) < 0) { \
"Unexpected " << ((x) == -1 ? "EOF" : "error") \
<< " detected!\n"; \
return EOF; \
} \
} while (0)
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
#define MOD_NAME

Definition at line 319 of file keyboard_control.cpp.

319#define CHECK_EOF(x) \
320 do { \
321 if ((x) < 0) { \
322 LOG(LOG_LEVEL_WARNING) << MOD_NAME \
323 "Unexpected " << ((x) == -1 ? "EOF" : "error") \
324 << " detected!\n"; \
325 return EOF; \
326 } \
327 } while (0)

Referenced by get_ansi_code(), keyboard_control::impl::get_next_key(), and get_utf8_code().

◆ CONFIG_FILE

#define CONFIG_FILE   "ug-key-map.txt"

◆ G

#define G ( key)
Value:
(guarded_keys.find(key) != guarded_keys.end() ? " [g]" : "")
const char * key
Definition cineform.cpp:125

Definition at line 808 of file keyboard_control.cpp.

◆ GETCH

#define GETCH   getch

◆ MOD_NAME

#define MOD_NAME   "[key control] "

Definition at line 103 of file keyboard_control.cpp.

Function Documentation

◆ ADD_TO_PARAM()

ADD_TO_PARAM ( "disable-keyboard-control" ,
"* disable-keyboard-control\n" " disables keyboard control (usable mainly for non-interactive runs)\n" [=no] )

◆ count_utf8_bytes()

static int count_utf8_bytes ( unsigned int i)
static

Definition at line 380 of file keyboard_control.cpp.

380 {
381 int count = 0;
382 while ((i & 0x80) != 0u) {
383 count++;
384 i <<= 1;
385 }
386 return count;
387}
int count
Definition hd-rum.c:44

References count.

Referenced by get_utf8_code().

◆ get_ansi_code()

static int64_t get_ansi_code ( )
static

Tries to parse at least some small subset of ANSI control sequences not to be ArrowUp interpreted as '\033', '[' and 'A' individually.

Return values
<0on error
Todo
  • improve coverage and/or find some suitable implemnation (ideally one file, not ncurses)
  • the function can be moved to separate util file

Definition at line 340 of file keyboard_control.cpp.

340 {
341 int64_t c = GETCH();
342 debug_msg(MOD_NAME "Pressed %" PRId64 "\n", c);
343 if (c == '[') { // CSI
344 c = '\033' << 8 | '[';
345 while (true) {
346 if ((c >> 56u) != 0) {
347 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Long control sequence detected!\n";
348 return INT64_MIN;
349 }
350 c <<= 8;
351 int tmp = GETCH();
352 assert(tmp < 0x80); // ANSI esc seq should use only 7-bit encoding
353 debug_msg(MOD_NAME "Pressed %d\n", tmp);
354 CHECK_EOF(tmp);
355 c |= tmp;
356 if (tmp >= 0x40 && tmp <= 0xee) { // final byte
357 break;
358 }
359 }
360 } else if (c == 'N' // is this even used?
361 || c == 'O') { // eg. \EOP - F1-F4 (the rest of Fn is CSI)
362 int tmp = GETCH();
363 debug_msg(MOD_NAME "Pressed %d\n", tmp);
364 CHECK_EOF(tmp);
365 c = '\033' << 16 | c << 8 | tmp;
366 } else if (c >= 'a' && c <= 'z') {
367 return K_ALT(c);
368 } else {
369 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Unknown control sequence!\n";
370 return INT64_MIN;
371 }
372
373 return c;
374}
#define LOG(level)
Definition debug.h:313
#define debug_msg(...)
#define debug_msg(...) log_msg(LOG_LEVEL_DEBUG, "[pid/%d +%d %s] ", getpid(), LINE,...
Definition debug.h:92
#define CHECK_EOF(x)
#define GETCH
#define K_ALT(x)

References CHECK_EOF, debug_msg, GETCH, K_ALT, LOG, LOG_LEVEL_WARNING, and MOD_NAME.

Referenced by keyboard_control::impl::get_next_key().

◆ get_keycode_name()

void get_keycode_name ( int64_t ch,
char * buf,
size_t buflen )

Definition at line 1063 of file keyboard_control.cpp.

1063 {
1064 if (buflen == 0) {
1065 return;
1066 }
1067 string name = get_keycode_representation(ch);
1068 strncpy(buf, name.c_str(), buflen - 1);
1069 buf[buflen - 1] = '\0';
1070}
static string get_keycode_representation(int64_t ch)
const char * name
Definition settings.cpp:193

References get_keycode_representation(), and name.

Referenced by gl_show_help(), and glfw_key_callback().

Here is the call graph for this function:

◆ get_keycode_representation()

static string get_keycode_representation ( int64_t ch)
static

Definition at line 453 of file keyboard_control.cpp.

453 {
454 switch (ch) {
455 case K_UP: return "Up";
456 case K_DOWN: return "Down";
457 case K_LEFT: return "Left";
458 case K_RIGHT: return "Right";
459 case K_PGUP: return "Page-Up";
460 case K_PGDOWN: return "Page-Dn";
461 case K_CTRL_UP: return "Ctrl-Up";
462 case K_CTRL_DOWN: return "Ctrl-Dn";
463 }
464
465 if (ch >= 1 && ch <= 'z' - 'a' + 1) {
466 return string("Ctrl-") + string(1, 'a' + ch - 1);
467 }
468
469 if (ch >> 16 == 0 && ch >> 8 == '\033') {
470 return string("Alt-") + string(1, ch & 0xff);
471 }
472
473 if (ch <= UCHAR_MAX && isprint(ch)) {
474 return string("'") + string(1, ch) + "'";
475 }
476
477 if (is_utf8(ch)) {
478 return string("'") + get_utf8_representation(ch) + "'";
479 }
480
481 stringstream oss;
482 oss << "0x" << hex << ch;
483 return oss.str();
484}
static string get_utf8_representation(int64_t ch)
static bool is_utf8(int64_t ch)
#define K_CTRL_DOWN
#define K_LEFT
#define K_CTRL_UP
#define K_RIGHT
#define K_PGDOWN
#define K_DOWN
#define K_PGUP
#define K_UP

References get_utf8_representation(), is_utf8(), K_CTRL_DOWN, K_CTRL_UP, K_DOWN, K_LEFT, K_PGDOWN, K_PGUP, K_RIGHT, and K_UP.

Referenced by keyboard_control::impl::exec_local_command(), get_keycode_name(), keyboard_control::impl::run(), and keyboard_control::impl::usage().

Here is the call graph for this function:

◆ get_utf8_code()

static int64_t get_utf8_code ( int c)
static
Return values
<0on error

Definition at line 390 of file keyboard_control.cpp.

390 {
391 if (c < 0xc0) {
392 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Wrong UTF sequence!\n";
393 return INT64_MIN;
394 }
395 int ones = count_utf8_bytes(c);
396 for (int i = 1; i < ones; ++i) {
397 c = (c & 0x7fffff) << 8;
398 int tmp = GETCH();
399 debug_msg(MOD_NAME "Pressed %d\n", tmp);
400 CHECK_EOF(tmp);
401 c |= tmp;
402 }
403 if (ones > 7) {
404 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Unsupported UTF sequence length!\n";
405 return INT64_MIN;
406 }
407 return c;
408}
static int count_utf8_bytes(unsigned int i)

References CHECK_EOF, count_utf8_bytes(), debug_msg, GETCH, LOG, LOG_LEVEL_WARNING, and MOD_NAME.

Referenced by keyboard_control::impl::get_next_key().

Here is the call graph for this function:

◆ get_utf8_representation()

static string get_utf8_representation ( int64_t ch)
static

Definition at line 443 of file keyboard_control.cpp.

443 {
444 char str[9] = "";
445 while (ch != 0) {
446 memmove(str + 1, str, 8);
447 str[0] = ch & 0xff;
448 ch >>= 8;
449 }
450 return str;
451}

Referenced by get_keycode_representation().

◆ is_utf8()

static bool is_utf8 ( int64_t ch)
static
Todo
UTF-8 validity check would be nice

Definition at line 426 of file keyboard_control.cpp.

426 {
427#ifdef _WIN32
428 return false;
429#endif
430
431 if (ch <= 0) {
432 return false;
433 }
434
435 unsigned char first_byte = 0U;
436 while (ch != 0) {
437 first_byte = ch & 0xff;
438 ch >>= 8;
439 }
440 return first_byte & 0x80;
441}

Referenced by get_keycode_representation().

◆ keycontrol_register_key()

bool keycontrol_register_key ( struct module * receiver_mod,
int64_t key,
const char * message,
const char * description )

Registers callback message for given key.

message_universal message type is always sent

Parameters
[in]sender_modmodule that should receive the message for key
[in]messagetext that will be passed back to receiver_mod if key was pressed
[in]descriptionoptional decription that will be displayed in keyboard control help (may be NULL)

Definition at line 1041 of file keyboard_control.cpp.

1041 {
1042 assert(strchr(message, '#') == nullptr && (description == nullptr || strchr(description, '#') == nullptr));
1043 char receiver_path[1024];
1044 if (!module_get_path_str(receiver_mod, receiver_path, sizeof receiver_path)) {
1045 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot format format path for sender!\n");
1046 return false;
1047 }
1048 struct msg_universal *m = (struct msg_universal *) new_message(sizeof(struct msg_universal));
1049 if (description == nullptr) {
1051 }
1052 snprintf(m->text, sizeof m->text, "map #%" PRId64 " %s %s#%s", key, receiver_path, message, description);
1053 struct response *r = send_message_sync(get_root_module(receiver_mod), "keycontrol", (struct message *) m, 100, SEND_MESSAGE_FLAG_QUIET | SEND_MESSAGE_FLAG_NO_STORE);
1054 if (response_get_status(r) != RESPONSE_OK) {
1055 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot register keyboard control (error %d)!\n", response_get_status(r));
1056 free_response(r);
1057 return false;
1058 }
1059 free_response(r);
1060 return true;
1061}
static void new_message(struct module *m)
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
bool module_get_path_str(struct module *mod, char *buf, size_t buflen)
Definition module.c:408
struct module * get_root_module(struct module *node)
Definition module.c:203
struct response * send_message_sync(struct module *root, const char *const_path, struct message *msg, int timeout_ms, int flags)
Sends message without waiting for response.
int response_get_status(struct response *r)
void free_response(struct response *r)
#define SEND_MESSAGE_FLAG_QUIET
do not print error messages on console (recv not found/queue full)
Definition messaging.h:171
#define RESPONSE_OK
Definition messaging.h:57
#define SEND_MESSAGE_FLAG_NO_STORE
if receiver doesn't exist, doesn't store it and retur 404 instead
Definition messaging.h:172
struct message m
Definition messaging.h:156
const char * description
Definition cineform.cpp:126

References description, free_response(), get_root_module(), key, LOG_LEVEL_ERROR, log_msg(), MOD_NAME, module_get_path_str(), new_message(), response_get_status(), RESPONSE_OK, SEND_MESSAGE_FLAG_NO_STORE, SEND_MESSAGE_FLAG_QUIET, send_message_sync(), and msg_universal::text.

Referenced by display_gl_init(), display_sdl2_init(), display_sdl_init(), and playback_register_keyboard_ctl().

Here is the call graph for this function:

◆ keycontrol_send_key()

void keycontrol_send_key ( struct module * root,
int64_t key )
Todo
all UG core functions should have the 'used' attribute

Definition at line 1024 of file keyboard_control.cpp.

1024 {
1025 struct msg_universal *m = (struct msg_universal *) new_message(sizeof(struct msg_universal));
1026 snprintf(m->text, sizeof m->text, "press %" PRId64, key);
1027 struct response *r = send_message_sync(root, "keycontrol", (struct message *) m, 100, SEND_MESSAGE_FLAG_QUIET | SEND_MESSAGE_FLAG_NO_STORE);
1028 if (response_get_status(r) != RESPONSE_OK) {
1029 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot set key to keycontrol (error %d)!\n", response_get_status(r));
1030 }
1031 free_response(r);
1032}

References free_response(), key, LOG_LEVEL_ERROR, log_msg(), MOD_NAME, new_message(), response_get_status(), RESPONSE_OK, SEND_MESSAGE_FLAG_NO_STORE, SEND_MESSAGE_FLAG_QUIET, send_message_sync(), and msg_universal::text.

Referenced by display_sdl2_run(), and glfw_key_callback().

Here is the call graph for this function:

◆ print_start_elapsed_time()

static void print_start_elapsed_time ( time_t start_time)
static

Definition at line 696 of file keyboard_control.cpp.

697{
698 enum {
699 ELAPS_LEN = 9,
700 START_LEN = 25,
701 };
702 struct tm tm_buf = {};
703
704 localtime_s(&start_time, &tm_buf);
705 char start[START_LEN] = "";
706 strftime(start, sizeof start, "%c", &tm_buf);
707
708 const time_t diff = time(nullptr) - start_time;
709 gmtime_s(&diff, &tm_buf);
710 char elapsed[ELAPS_LEN] = "";
711 strftime(elapsed, sizeof elapsed, "%T", &tm_buf);
712
713 col() << TBOLD("Start time: ") << start << " (elapsed " << elapsed
714 << ")\n";
715}
#define TBOLD(x)
Definition color_out.h:70
#define gmtime_s
Definition time.h:50
#define localtime_s
Definition time.h:51
static void start()
Definition sdp.c:202
static struct timeval start_time
Definition tv.c:57

References gmtime_s, localtime_s, keyboard_control::start(), start_time, and TBOLD.

Referenced by keyboard_control::impl::info().

Here is the call graph for this function:

◆ restore_old_tio()

void restore_old_tio ( void )

Definition at line 188 of file keyboard_control.cpp.

189{
190#ifdef HAVE_TERMIOS_H
191 struct sigaction sa, sa_old;
192 memset(&sa, 0, sizeof sa);
193 sa.sa_handler = SIG_IGN;
194 sigemptyset(&sa.sa_mask);
195 sigaction(SIGTTOU, &sa, &sa_old);
196 /* set the new settings immediately */
197 /* restore the former settings */
198 tcsetattr(STDIN_FILENO,TCSANOW,&old_tio);
199 sigaction(SIGTTOU, &sa_old, NULL);
200#endif
201}

Referenced by crash_signal_handler(), keyboard_control::impl::read_command(), and keyboard_control::impl::start().

◆ set_tio()

static bool set_tio ( )
static

From http://shtrom.ssji.net/skb/getc.html

Definition at line 982 of file keyboard_control.cpp.

983{
984#ifdef HAVE_TERMIOS_H
985 struct termios new_tio = old_tio;
986 /* disable canonical mode (buffered i/o) and local echo */
987 new_tio.c_lflag &=(~ICANON & ~ECHO);
988 // Wrap calling of tcsetattr() by handling SIGTTOU. SIGTTOU can be raised if task is
989 // run in background and trying to call tcsetattr(). If so, we disable keyboard
990 // control.
991 struct sigaction sa, sa_old;
992 memset(&sa, 0, sizeof sa);
993 sa.sa_handler = catch_signal;
994 sigemptyset(&sa.sa_mask);
995 sigaction(SIGTTOU, &sa, &sa_old);
996 /* set the new settings immediately */
997 tcsetattr(STDIN_FILENO,TCSANOW,&new_tio);
998 sigaction(SIGTTOU, &sa_old, NULL);
999
1000 if (!signal_catched) {
1001 return true;
1002 }
1003#endif
1004 return false;
1005}

Referenced by keyboard_control::impl::read_command(), and keyboard_control::impl::start().