UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
keyboard_control::impl Class Reference
Collaboration diagram for keyboard_control::impl:
Collaboration graph

Classes

struct  m_mod
 

Public Member Functions

 impl (struct module *parent)
 
 ~impl ()
 
void msg_received ()
 
void start ()
 
void stop ()
 

Static Public Member Functions

static void msg_received_func (struct module *)
 

Private Member Functions

void exec_external_commands (const char *commands)
 
bool exec_local_command (const char *command)
 
void execute_command (const char *command)
 
int64_t get_next_key ()
 
void info ()
 
void load_config_map ()
 
void read_command (bool multiple)
 
void run ()
 
void signal ()
 
void usage ()
 

Private Attributes

std::set< int64_t > guarded_keys { '*', '/', '9', '0', 'c', 'C', 'm', 'M', '>', '<', 'e', 's', 'S', 'v', 'V', 'r' }
 
std::map< int64_t, std::pair< std::string, std::string > > key_mapping
 
std::condition_variable m_cv
 
std::thread m_keyboard_thread
 
std::mutex m_lock
 
bool m_locked_against_changes {true}
 
struct module * m_parent
 
std::queue< int64_t > m_pressed_keys
 
struct module * m_root {nullptr}
 
bool m_should_exit {false}
 
std::time_t m_start_time
 
bool m_started {false}
 

Detailed Description

Definition at line 134 of file keyboard_control.cpp.

Constructor & Destructor Documentation

◆ impl()

keyboard_control::impl::impl ( struct module * parent)
explicit

Definition at line 203 of file keyboard_control.cpp.

203 :
205{
206 m_start_time = time(NULL);
207
209
210#ifdef HAVE_TERMIOS_H
211 /* get the terminal settings for stdin */
212 /* we want to keep the old setting to restore them a the end */
213 tcgetattr(STDIN_FILENO,&old_tio);
214#endif
215
216 module_init_default(&m_mod);
217 m_mod.cls = MODULE_CLASS_KEYCONTROL;
218 m_mod.priv_data = this;
220 module_register(&m_mod, m_parent);
221}
static void msg_received_func(struct module *)
void module_init_default(struct module *module_data)
Definition module.c:48
void module_register(struct module *module_data, struct module *parent)
Definition module.c:82
struct module * get_root_module(struct module *node)
Definition module.c:203
@ MODULE_CLASS_KEYCONTROL
Definition module.h:97
const char * parent
Definition settings.cpp:198

References get_root_module(), m_parent, m_root, m_start_time, MODULE_CLASS_KEYCONTROL, module_init_default(), module_register(), msg_received_func(), and parent.

Here is the call graph for this function:

◆ ~impl()

keyboard_control::impl::~impl ( )

Definition at line 223 of file keyboard_control.cpp.

223 {
224 stop(); // in case that it was not called explicitly
225}

References keyboard_control::stop().

Here is the call graph for this function:

Member Function Documentation

◆ exec_external_commands()

void keyboard_control::impl::exec_external_commands ( const char * commands)
private

Definition at line 914 of file keyboard_control.cpp.

915{
916 char *cpy = strdup(commands);
917 char *item, *save_ptr, *tmp = cpy;
918 while ((item = strtok_r(tmp, ";", &save_ptr))) {
919 struct msg_universal *m = (struct msg_universal *) new_message(sizeof(struct msg_universal));
920 strcpy(m->text, "execute ");
921 strncat(m->text, item, sizeof m->text - strlen(m->text) - 1);
924 LOG(LOG_LEVEL_ERROR) << "Could not send a message!\n";
925 }
926 free_response(r);
927
928 tmp = nullptr;
929 }
930 free(cpy);
931}
static void new_message(struct module *m)
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG(level)
Definition debug.h:313
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
Definition hd-rum.c:27
char text[8192]
Definition messaging.h:157

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

Here is the call graph for this function:

◆ exec_local_command()

bool keyboard_control::impl::exec_local_command ( const char * command)
private

Definition at line 862 of file keyboard_control.cpp.

863{
864 if (strcmp(command, "localhelp") == 0) {
865 printf("Local help:\n"
866 "\tmap <X> cmd1[;cmd2..][#name]\n"
867 "\t\tmaps key to command(s) for control socket (with an optional\n\t\tidentifier)\n"
868 "\tmap #<X> cmd1[;cmd2..][#name]\n"
869 "\t\tmaps key number (ASCII or multi-byte for non-ASCII) to\n\t\tcommand(s) for control socket (with an optional identifier)\n"
870 "\tunmap <X>\n"
871 "\tpress <num>\n"
872 "\t\tEmulate pressing key <num>\n\n"
873 "Mappings can be stored in " CONFIG_FILE "\n\n");
874 return true;
875 } else if (strstr(command, "map ") == command) {
876 char *ccpy = strdup(command + strlen("map "));
877 if (strlen(ccpy) > 3) {
878 int64_t key = ccpy[0];
879 char *command = ccpy + 2;
880 if (key == '#' && isdigit(ccpy[1])) {
881 key = strtoll(ccpy + 1, &command, 10);
882 command += 1; // skip ' '
883 }
884 const char *name = "";
885 if (strchr(command, '#')) {
886 name = strchr(command, '#') + 1;
887 *strchr(command, '#') = '\0';
888 }
889 if (key_mapping.find(key) == key_mapping.end()) {
890 key_mapping.insert({key, make_pair(command, name)});
891 } else {
892 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Trying to register key shortcut " << get_keycode_representation(key) << ", which is already regestered, ignoring.\n";
893 }
894 }
895 free(ccpy);
896 return true;
897 } else if (strstr(command, "unmap ") == command) {
898 command += strlen("unmap ");
899 if (strlen(command) >= 1) {
900 key_mapping.erase(command[0]);
901 }
902 return true;
903 } else if (strstr(command, "press ") == command) {
904 const char *key = command + strlen("press ");
905 int64_t k;
906 sscanf(key, "%" SCNi64, &k);
907 m_pressed_keys.push(k);
908 signal();
909 return true;
910 }
911 return false;
912}
#define MOD_NAME
Definition audio.cpp:110
std::queue< int64_t > m_pressed_keys
std::map< int64_t, std::pair< std::string, std::string > > key_mapping
#define CONFIG_FILE
static string get_keycode_representation(int64_t ch)
const char * name
Definition settings.cpp:193
const char * key
Definition cineform.cpp:125

References CONFIG_FILE, get_keycode_representation(), key, LOG, LOG_LEVEL_ERROR, MOD_NAME, and name.

Here is the call graph for this function:

◆ execute_command()

void keyboard_control::impl::execute_command ( const char * command)
private

Definition at line 933 of file keyboard_control.cpp.

933 {
934 if (!exec_local_command(command)) {
935 exec_external_commands(command);
936 }
937}
void exec_external_commands(const char *commands)
bool exec_local_command(const char *command)

◆ get_next_key()

int64_t keyboard_control::impl::get_next_key ( )
private
Returns
next key either from keyboard or received via control socket.
Return values
0if m_should_exit is set
-1on EOF
-2on error

Definition at line 492 of file keyboard_control.cpp.

493{
494 int64_t c;
495 while (1) {
496 {
497 unique_lock<mutex> lk(m_lock);
498 if (m_should_exit) {
499 return 0;
500 }
501
502 if (!m_pressed_keys.empty()) {
503 c = m_pressed_keys.front();
504 m_pressed_keys.pop();
505 return c;
506 }
507 }
508#ifdef HAVE_TERMIOS_H
509 fd_set set;
510 FD_ZERO(&set);
511 FD_SET(0, &set);
512 FD_SET(m_event_pipe[0], &set);
513 select(m_event_pipe[0] + 1, &set, NULL, NULL, NULL);
514
515 if (FD_ISSET(m_event_pipe[0], &set)) {
516 char c;
517 int bytes = read(m_event_pipe[0], &c, 1);
518 assert(bytes == 1);
519 continue; // skip to event processing
520 }
521
522 if (FD_ISSET(0, &set)) {
523#else
524 if (kbhit()) {
525#endif
526 int64_t c = GETCH();
527 debug_msg(MOD_NAME "Pressed %" PRId64 "\n", c);
528 if (c == '\033') {
529 if ((c = get_ansi_code()) < 0) {
530 continue;
531 }
532#ifdef _WIN32
533 } else if (c == 0x0 || c == 0xe0) { // Win keycodes
534 int tmp = GETCH();
535 debug_msg(MOD_NAME "Pressed %d\n", tmp);
536 CHECK_EOF(tmp);
537 if ((c = convert_win_to_ansi_keycode(c << 8 | tmp)) == -1) {
538 continue;
539 }
540#else
541 } else if (c > 0x80) {
542 if ((c = get_utf8_code(c)) < 0) {
543 continue;
544 }
545#endif
546 }
547 return c;
548 }
549#if !defined HAVE_TERMIOS_H
550 // kbhit is non-blockinkg - we don't want to busy-wait
551 unique_lock<mutex> lk(m_lock);
552 m_cv.wait_for(lk, milliseconds(200));
553 lk.unlock();
554#endif
555 }
556}
std::condition_variable m_cv
#define debug_msg(...)
#define debug_msg(...) log_msg(LOG_LEVEL_DEBUG, "[pid/%d +%d %s] ", getpid(), LINE,...
Definition debug.h:92
static int64_t get_utf8_code(int c)
#define CHECK_EOF(x)
static int64_t get_ansi_code()
#define GETCH

References CHECK_EOF, debug_msg, get_ansi_code(), get_utf8_code(), GETCH, and MOD_NAME.

Here is the call graph for this function:

◆ info()

void keyboard_control::impl::info ( )
private

Definition at line 717 of file keyboard_control.cpp.

718{
719 col() << TBOLD("UltraGrid version: ") << get_version_details() << "\n";
721 col() << TBOLD("Verbosity level: ") << log_level << (log_level == LOG_LEVEL_INFO ? " (default)" : "") << "\n";
722 col() << TBOLD("Locked against changes: ") << (m_locked_against_changes ? "true" : "false") << "\n";
723 col() << TBOLD("Audio playback delay: ") << get_audio_delay() << " ms\n";
724
725 {
726 int muted_sender = -1;
727 int muted_receiver = -1;
728 const char *path = "audio.receiver";
729 auto *m_recv = reinterpret_cast<struct msg_receiver *>(new_message(sizeof(struct msg_receiver)));
731 auto *resp = send_message_sync(m_root, path, reinterpret_cast<struct message *>(m_recv), 100, SEND_MESSAGE_FLAG_QUIET | SEND_MESSAGE_FLAG_NO_STORE);
732 if (response_get_status(resp) == 200) {
733 double vol = 0;
734 sscanf(response_get_text(resp), "%lf,%d", &vol, &muted_receiver);
735 double db = 20.0 * log10(vol);
736 color_printf(TBOLD("Playback volume: ") "%.2f%% (%s%.2f dB)\n", vol * 100.0, (db >= 0.0 ? "+" : ""), db);
737 }
738 free_response(resp);
739 path = "audio.sender";
740 auto *m_send = reinterpret_cast<struct msg_sender *>(new_message(sizeof(struct msg_sender)));
741 m_send->type = SENDER_MSG_GET_STATUS;
742 resp = send_message_sync(m_root, path, reinterpret_cast<struct message *>(m_send), 100, SEND_MESSAGE_FLAG_QUIET | SEND_MESSAGE_FLAG_NO_STORE);
743 if (response_get_status(resp) == 200) {
744 sscanf(response_get_text(resp), "%d", &muted_sender);
745 }
746 free_response(resp);
747 auto muted_to_string = [](int val) {
748 switch (val) {
749 case 0: return "false";
750 case 1: return "true";
751 default: return "(unknown)";
752 }
753 };
754 col() << TBOLD("Audio muted - sender: ") << muted_to_string(muted_sender) << ", " << TBOLD("receiver: ") << muted_to_string(muted_receiver) << "\n";
755 }
756
757 {
758 struct video_desc desc{};
759 struct msg_sender *m = (struct msg_sender *) new_message(sizeof(struct msg_sender));
763 const char *text = response_get_text(r);
764 istringstream iss(text);
765 iss >> desc;
766 col() << TBOLD("Captured video format: ") << desc << "\n";
767 }
768 free_response(r);
769 }
770
771 {
772 struct video_desc desc{};
773 struct msg_universal *m = (struct msg_universal *) new_message(sizeof(struct msg_universal));
774 strcpy(m->text, "get_format");
775 struct response *r = send_message_sync(m_root, "receiver.decoder", (struct message *) m, 100, SEND_MESSAGE_FLAG_QUIET | SEND_MESSAGE_FLAG_NO_STORE);
777 const char *text = response_get_text(r);
778 istringstream iss(text);
779 iss >> desc;
780 col() << TBOLD("Received video format: ") << desc << "\n";
781 }
782 free_response(r);
783 }
784
785 {
786 struct msg_universal *m = (struct msg_universal *) new_message(sizeof(struct msg_universal));
787 strcpy(m->text, "get_port");
790 col() << TBOLD("Control port: ") << response_get_text(r) << "\n";
791 }
792 free_response(r);
793 }
794
795 {
796 struct msg_universal *m = (struct msg_universal *) new_message(sizeof(struct msg_universal));
797 strcpy(m->text, "status");
798 struct response *r = send_message_sync(m_root, "exporter", (struct message *) m, 100, SEND_MESSAGE_FLAG_QUIET | SEND_MESSAGE_FLAG_NO_STORE);
800 col() << TBOLD("Exporting: ") << response_get_text(r) << "\n";
801 }
802 free_response(r);
803 }
804
805 cout << "\n";
806}
int void * val
struct video_desc desc
int color_printf(const char *format,...)
#define TBOLD(x)
Definition color_out.h:70
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
int get_audio_delay(void)
Definition host.cpp:909
const char * get_version_details()
Definition host.cpp:856
volatile int log_level
Definition debug.cpp:65
static void print_start_elapsed_time(time_t start_time)
const char * response_get_text(struct response *r)
@ SENDER_MSG_QUERY_VIDEO_MODE
Definition messaging.h:91
@ SENDER_MSG_GET_STATUS
Definition messaging.h:86
@ RECEIVER_MSG_GET_AUDIO_STATUS
Definition messaging.h:111
enum msg_receiver_type type
Definition messaging.h:120
enum msg_sender_type type
Definition messaging.h:97

References color_printf(), desc, free_response(), get_audio_delay(), get_version_details(), log_level, LOG_LEVEL_INFO, new_message(), print_start_elapsed_time(), RECEIVER_MSG_GET_AUDIO_STATUS, response_get_status(), response_get_text(), RESPONSE_OK, SEND_MESSAGE_FLAG_NO_STORE, SEND_MESSAGE_FLAG_QUIET, send_message_sync(), SENDER_MSG_GET_STATUS, SENDER_MSG_QUERY_VIDEO_MODE, TBOLD, msg_universal::text, msg_receiver::type, msg_sender::type, and val.

Here is the call graph for this function:

◆ load_config_map()

void keyboard_control::impl::load_config_map ( )
private

Definition at line 841 of file keyboard_control.cpp.

841 {
842 FILE *in = fopen(CONFIG_FILE, "r");
843 if (!in) {
844 return;
845 }
846 char buf[1024];
847 while (fgets(buf, sizeof buf - 1, in)) {
848 if (strlen(buf) > 0 && buf[strlen(buf) - 1] == '\n') { // trim NL
849 buf[strlen(buf) - 1] = '\0';
850 }
851 if (strlen(buf) == 0) {
852 continue;
853 }
854 if (!exec_local_command(buf)) {
855 LOG(LOG_LEVEL_ERROR) << "Wrong config: " << buf << "\n";
856 continue;
857 }
858 }
859 fclose(in);
860}

References CONFIG_FILE, LOG, and LOG_LEVEL_ERROR.

◆ msg_received()

void keyboard_control::impl::msg_received ( )

Definition at line 1013 of file keyboard_control.cpp.

1013 {
1014 struct message *msg;
1015 while ((msg = check_message(&m_mod))) {
1016 auto m = reinterpret_cast<struct msg_universal *>(msg);
1017 lock_guard<mutex> lk(m_lock);
1018 execute_command(m->text);
1019 struct response *r = new_response(RESPONSE_OK, nullptr);
1020 free_message((struct message *) m, r);
1021 }
1022}
void execute_command(const char *command)
void free_message(struct message *msg, struct response *r)
struct response * new_response(int status, const char *text)
struct message * check_message(struct module *mod)
struct msghdr msg

References check_message(), free_message(), msg, new_response(), and RESPONSE_OK.

Referenced by msg_received_func().

Here is the call graph for this function:

◆ msg_received_func()

void keyboard_control::impl::msg_received_func ( struct module * m)
static

Definition at line 1007 of file keyboard_control.cpp.

1007 {
1008 auto s = static_cast<keyboard_control::impl *>(m->priv_data);
1009
1010 s->msg_received();
1011}
void * priv_data
Definition module.h:126

References msg_received(), and module::priv_data.

Referenced by impl().

Here is the call graph for this function:

◆ read_command()

void keyboard_control::impl::read_command ( bool multiple)
private

Definition at line 939 of file keyboard_control.cpp.

940{
941 int saved_log_level = log_level;
944 if (multiple) {
945 printf("Enter commands for control (try \"help\" or \"localhelp\").\n");
946 printf("Exit the mode with blank line.\n");
947 } else {
948 printf("Enter a command for control (try \"help\" or \"localhelp\"):\n");
949 }
950 while (1) {
952 printf("control> ");
953 char buf[sizeof msg_universal::text];
954 if (!fgets(buf, sizeof buf - 1, stdin)) {
955 log_level = saved_log_level;
956 break;
957 }
958 log_level = saved_log_level;
959
960 if (strlen(buf) == 0 || (strlen(buf) == 1 && buf[0] == '\n')) { // empty input
961 break;
962 }
963 if (buf[strlen(buf) - 1] == '\n') { // strip newline
964 buf[strlen(buf) - 1] = '\0';
965 }
966
967 m_lock.lock();
968 execute_command(buf);
969 m_lock.unlock();
970
971 if (!multiple) {
972 break;
973 }
974 }
975
976 set_tio();
977}
#define LOG_LEVEL_QUIET
suppress all logging
Definition debug.h:57
static bool set_tio()
EXTERN_C void restore_old_tio(void)

References log_level, LOG_LEVEL_QUIET, restore_old_tio(), set_tio(), and msg_universal::text.

Here is the call graph for this function:

◆ run()

void keyboard_control::impl::run ( )
private

Definition at line 558 of file keyboard_control.cpp.

559{
560 set_thread_name("keyboard-control");
561 int saved_log_level = -1;
562 int64_t c;
563
564 while ((c = get_next_key())) {
565 if (c == -1) {
567 "EOF detected! Exiting keyboard control.\n";
568 break;
569 }
570 if (c == -2) {
571 perror(MOD_NAME "Error detected");
572 continue;
573 }
574 if (c == K_CTRL('X')) {
576 col() << TGREEN("Keyboard control: " << (m_locked_against_changes ? "" : "un") << "locked against changes\n");
577 continue;
578 }
579 if (m_locked_against_changes && guarded_keys.find(c) != guarded_keys.end()) {
580 col() << TGREEN("Keyboard control: locked against changes, press 'Ctrl-x' to unlock or 'h' for help.\n");
581 continue;
582 }
583
584 m_lock.lock();
585 if (key_mapping.find(c) != key_mapping.end()) { // user defined mapping exists
586 string cmd = key_mapping.at(c).first;
587 exec_external_commands(cmd.c_str());
588 m_lock.unlock();
589 continue;
590 }
591 m_lock.unlock();
592
593 switch (c) {
594 case '*':
595 case '/':
596 case '9':
597 case '0':
598 case 'm':
599 {
600 char path[] = "audio.receiver";
601 auto m = (struct msg_receiver *) new_message(sizeof(struct msg_receiver));
602 switch (c) {
603 case '0':
604 case '*': m->type = RECEIVER_MSG_INCREASE_VOLUME; break;
605 case '9':
606 case '/': m->type = RECEIVER_MSG_DECREASE_VOLUME; break;
607 case 'm': m->type = RECEIVER_MSG_MUTE_TOGGLE; break;
608 }
609
610 auto resp = send_message(m_root, path, (struct message *) m);
611 free_response(resp);
612 break;
613 }
614 case 'M':
615 {
616 char path[] = "audio.sender";
617 auto m = (struct msg_sender *) new_message(sizeof(struct msg_sender));
618 m->type = SENDER_MSG_MUTE_TOGGLE;
619
620 auto resp = send_message(m_root, path, (struct message *) m);
621 free_response(resp);
622 break;
623 }
624 case '>':
625 case '<':
626 {
627 int audio_delay = get_audio_delay();
628 audio_delay += c == '>' ? 10 : -10;
629 log_msg(LOG_LEVEL_INFO, "New audio delay: %d ms.\n", audio_delay);
630 set_audio_delay(audio_delay);
631 break;
632 }
633 case 'e':
634 {
635 char path[] = "exporter";
636 auto m = (struct message *) new_message(sizeof(struct msg_universal));
637 strcpy(((struct msg_universal *) m)->text, "toggle");
638 auto resp = send_message(m_root, path, (struct message *) m);
639 free_response(resp);
640 break;
641 }
642 case 'c':
643 case 'C':
644 read_command(c == 'C');
645 break;
646 case 'v':
647 case 'V':
648 {
649 if (islower(c)) {
650 log_level = (log_level + 1) % (LOG_LEVEL_MAX + 1);
651 } else {
652 log_level = (log_level - 1 + (LOG_LEVEL_MAX + 1)) % (LOG_LEVEL_MAX + 1);
653 }
654 cout << "Log level: " << log_level << "\n";
655 break;
656 }
657 case 'r':
658 {
659 bool skip = !get_log_output().get_skip_repeats();
661 cout << "Skip repeated messages: " << std::boolalpha << skip << std::noboolalpha << "\n";
662 break;
663 }
664 case 's':
665 if (saved_log_level == -1) {
666 saved_log_level = log_level;
667 col() << TGREEN("Output suspended, press 'q' to continue.\n");
669 }
670 break;
671 case 'S':
672 if (saved_log_level != -1) {
673 log_level = saved_log_level;
674 saved_log_level = -1;
675 col() << TGREEN( "Output resumed.\n");
676 }
677 break;
678 case 'h':
679 usage();
680 break;
681 case 'i':
682 cout << "\n";
683 info();
684 break;
685 case '\n':
686 case '\r':
687 cout << endl;
688 break;
689 default:
690 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Unsupported key " << get_keycode_representation(c) << " pressed. Press 'h' to help.\n";
691 }
692 }
693}
void set_skip_repeats(bool val)
Definition debug.h:155
bool get_skip_repeats() const
Definition debug.h:156
void read_command(bool multiple)
std::set< int64_t > guarded_keys
#define TGREEN(x)
Definition color_out.h:72
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_MAX
Definition debug.h:69
Log_output & get_log_output()
Definition debug.h:266
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
void set_audio_delay(int audio_delay)
Definition host.cpp:914
#define K_CTRL(x)
struct response * send_message(struct module *root, const char *const_path, struct message *msg)
Sends message without waiting for response.
@ SENDER_MSG_MUTE_TOGGLE
Definition messaging.h:89
@ RECEIVER_MSG_MUTE_TOGGLE
Definition messaging.h:116
@ RECEIVER_MSG_DECREASE_VOLUME
Definition messaging.h:113
@ RECEIVER_MSG_INCREASE_VOLUME
Definition messaging.h:112
void set_thread_name(const char *name)
Definition thread.c:72

References free_response(), get_audio_delay(), get_keycode_representation(), get_log_output(), Log_output::get_skip_repeats(), K_CTRL, LOG, log_level, LOG_LEVEL_INFO, LOG_LEVEL_MAX, LOG_LEVEL_QUIET, LOG_LEVEL_WARNING, log_msg(), MOD_NAME, new_message(), RECEIVER_MSG_DECREASE_VOLUME, RECEIVER_MSG_INCREASE_VOLUME, RECEIVER_MSG_MUTE_TOGGLE, send_message(), SENDER_MSG_MUTE_TOGGLE, set_audio_delay(), Log_output::set_skip_repeats(), set_thread_name(), TGREEN, and usage().

Referenced by start().

Here is the call graph for this function:

◆ signal()

void keyboard_control::impl::signal ( )
private

Definition at line 276 of file keyboard_control.cpp.

277{
278#ifdef HAVE_TERMIOS_H
279 char c = 0;
280 if (write(m_event_pipe[1], &c, 1) != 1) {
281 perror(MOD_NAME "m_event_pipe write");
282 }
283#else
284 m_cv.notify_one();
285#endif
286}

References MOD_NAME.

◆ start()

void keyboard_control::impl::start ( )

Definition at line 229 of file keyboard_control.cpp.

230{
231 bool force_key_control = false;
232 if (get_commandline_param("disable-keyboard-control")) {
233 if (strcmp(get_commandline_param("disable-keyboard-control"),
234 "no") != 0) {
235 return;
236 }
237 force_key_control = true;
238 }
239
240 if (!force_key_control) {
241 if (running_in_debugger()) {
243 << MOD_NAME "Running inside gdb - disabling "
244 "interactive keyboard control\n";
245 return;
246 }
247#ifdef HAVE_TERMIOS_H
248 if (!isatty(STDIN_FILENO)) {
250 "[key control] Stdin is not a TTY - disabling "
251 "keyboard control.\n");
252 return;
253 }
254#endif // defined HAVE_TERMIOS_H
255 }
256
257#ifdef HAVE_TERMIOS_H
258 if (pipe(m_event_pipe) != 0) {
259 log_msg(LOG_LEVEL_ERROR, "[key control] Cannot create control pipe!\n");
260 return;
261 }
262
263 if (!set_tio()) {
264 log_msg(LOG_LEVEL_WARNING, "[key control] Background task - disabling keyboard control.\n");
265 return;
266 }
267 atexit(restore_old_tio);
268#endif
269
271
273 m_started = true;
274}
bool running_in_debugger()
Definition host.cpp:1190
const char * get_commandline_param(const char *key)
Definition host.cpp:894

References get_commandline_param(), LOG, LOG_LEVEL_ERROR, LOG_LEVEL_WARNING, log_msg(), MOD_NAME, restore_old_tio(), run(), running_in_debugger(), and set_tio().

Referenced by keyboard_control::start().

Here is the call graph for this function:

◆ stop()

void keyboard_control::impl::stop ( )

Definition at line 288 of file keyboard_control.cpp.

289{
290 module_done(&m_mod);
291 unique_lock<mutex> lk(m_lock);
292 if (!m_started) {
293 return;
294 }
295 m_should_exit = true;
296 m_started = false;
297 lk.unlock();
298 signal();
299 m_keyboard_thread.join();
300
301#ifdef HAVE_TERMIOS_H
302 close(m_event_pipe[0]);
303 close(m_event_pipe[1]);
304#endif
305}
void module_done(struct module *module_data)
Definition module.c:93

References module_done().

Here is the call graph for this function:

◆ usage()

void keyboard_control::impl::usage ( )
private

Definition at line 810 of file keyboard_control.cpp.

811{
812 col() << "\nAvailable keybindings:\n" <<
813 TBOLD("\t * 0 ") << "- increase receiver volume" << G('*') << "\n" <<
814 TBOLD("\t / 9 ") << "- decrease receiver volume" << G('/') << "\n" <<
815 TBOLD("\t > ") << "- increase receiver audio delay by 10 ms" << G('>') << "\n" <<
816 TBOLD("\t < ") << "- decrease receiver audio delay by 10 ms" << G('<') << "\n" <<
817 TBOLD("\t m ") << "- mute/unmute receiver" << G('m') << "\n" <<
818 TBOLD("\t M ") << "- mute/unmute sender" << G('M') << "\n" <<
819 TBOLD("\t v ") << "- increase verbosity level" << G('v') << "\n" <<
820 TBOLD("\t V ") << "- decrease verbosity level" << G('V') << "\n" <<
821 TBOLD("\t r ") << "- skip repeated messages (toggle) " << G('r') << "\n" <<
822 TBOLD("\t e ") << "- record captured content (toggle)" << G('e') << "\n" <<
823 TBOLD("\t h ") << "- show help" << G('h') << "\n" <<
824 TBOLD("\t i ") << "- show various information" << G('i') << "\n" <<
825 TBOLD("\t s S ") << "- suspend/resume output" << G('s') << G('S') << "\n" <<
826 TBOLD("\t c C ") << "- execute command through control socket (capital for multiple)" << G('c') << G('C') << "\n" <<
827 TBOLD("\tCtrl-x ") << "- unlock/lock against changes" << G(K_CTRL('X')) << "\n" <<
828 TBOLD("\tCtrl-c ") << "- exit " << G(K_CTRL('c')) << "\n" <<
829 "\n";
830
831 if (key_mapping.size() > 0) {
832 cout << "Custom keybindings:\n";
833 for (auto const &it : key_mapping) {
834 col() << TBOLD("\t" << setw(10) << get_keycode_representation(it.first) << setw(0) <<) << " - " << (it.second.second.empty() ? it.second.first : it.second.second) << G(it.first) << "\n";
835 }
836 cout << "\n";
837 }
838 col() << TBOLD("[g]") << " indicates that that option is guarded (Ctrl-x needs to be pressed first)\n";
839}
#define G(x, y, z)
Definition md5.c:64

References G, get_keycode_representation(), K_CTRL, and TBOLD.

Here is the call graph for this function:

Member Data Documentation

◆ guarded_keys

std::set<int64_t> keyboard_control::impl::guarded_keys { '*', '/', '9', '0', 'c', 'C', 'm', 'M', '>', '<', 'e', 's', 'S', 'v', 'V', 'r' }
private

Definition at line 174 of file keyboard_control.cpp.

174{ '*', '/', '9', '0', 'c', 'C', 'm', 'M', '>', '<', 'e', 's', 'S', 'v', 'V', 'r' };

◆ key_mapping

std::map<int64_t, std::pair<std::string, std::string> > keyboard_control::impl::key_mapping
private

Definition at line 175 of file keyboard_control.cpp.

◆ m_cv

std::condition_variable keyboard_control::impl::m_cv
private

Definition at line 166 of file keyboard_control.cpp.

◆ m_keyboard_thread

std::thread keyboard_control::impl::m_keyboard_thread
private

Definition at line 160 of file keyboard_control.cpp.

◆ m_lock

std::mutex keyboard_control::impl::m_lock
private

Definition at line 176 of file keyboard_control.cpp.

◆ m_locked_against_changes

bool keyboard_control::impl::m_locked_against_changes {true}
private

Definition at line 171 of file keyboard_control.cpp.

171{true};

◆ m_parent

struct module* keyboard_control::impl::m_parent
private

Definition at line 162 of file keyboard_control.cpp.

Referenced by impl().

◆ m_pressed_keys

std::queue<int64_t> keyboard_control::impl::m_pressed_keys
private

Definition at line 169 of file keyboard_control.cpp.

◆ m_root

struct module* keyboard_control::impl::m_root {nullptr}
private

Definition at line 161 of file keyboard_control.cpp.

161{nullptr};

Referenced by impl().

◆ m_should_exit

bool keyboard_control::impl::m_should_exit {false}
private

Definition at line 168 of file keyboard_control.cpp.

168{false};

◆ m_start_time

std::time_t keyboard_control::impl::m_start_time
private

Definition at line 172 of file keyboard_control.cpp.

Referenced by impl().

◆ m_started

bool keyboard_control::impl::m_started {false}
private

Definition at line 170 of file keyboard_control.cpp.

170{false};

The documentation for this class was generated from the following file: