diff options
Diffstat (limited to 'tdekbdledsync')
-rw-r--r-- | tdekbdledsync/CMakeLists.txt | 28 | ||||
-rw-r--r-- | tdekbdledsync/main.cpp | 182 |
2 files changed, 210 insertions, 0 deletions
diff --git a/tdekbdledsync/CMakeLists.txt b/tdekbdledsync/CMakeLists.txt new file mode 100644 index 000000000..fd3a37f8f --- /dev/null +++ b/tdekbdledsync/CMakeLists.txt @@ -0,0 +1,28 @@ +################################################# +# +# (C) 2013 Timothy Pearson +# kb9vqf (AT) pearsoncomputing.net +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### tdekbdledsync (executable) ################ + +tde_add_executable( tdekbdledsync + SOURCES main.cpp + LINK udev X11 + DESTINATION ${BIN_INSTALL_DIR} +) diff --git a/tdekbdledsync/main.cpp b/tdekbdledsync/main.cpp new file mode 100644 index 000000000..f33df68c9 --- /dev/null +++ b/tdekbdledsync/main.cpp @@ -0,0 +1,182 @@ +/* +Copyright 2011-2013 Timothy Pearson <[email protected]> + +This file is part of tdekbdledsync, the TDE Keyboard LED Synchronization Daemon + +tdekbdledsync is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 +of the License, or (at your option) any later version. + +tdekbdledsync is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public +License along with tdekbdledsync. If not, see http://www.gnu.org/licenses/. + +*/ + +// The idea here is to periodically read the Xorg core keyboard state, and then forcibly set the physical LED states on all attached keyboards to match (via the event interface) +// Once every half second should work well enough on most systems + +#include <stdio.h> +#include <stdlib.h> +#include <exception> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <dirent.h> +#include <linux/input.h> +#include <linux/uinput.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/select.h> +#include <sys/time.h> +#include <termios.h> +#include <signal.h> +extern "C" { +#include <libudev.h> +} +#include <libgen.h> + +#include <X11/Xlib.h> +#include <X11/XKBlib.h> +#include <X11/extensions/XTest.h> +#include <X11/keysym.h> + +using namespace std; + +// WARNING +// MAX_KEYBOARDS must be greater than or equal to MAX_INPUT_NODE +#define MAX_KEYBOARDS 128 +#define MAX_INPUT_NODE 128 + +#define TestBit(bit, array) (array[(bit) / 8] & (1 << ((bit) % 8))) + +typedef unsigned char byte; + +char filename[32]; +char key_bitmask[(KEY_MAX + 7) / 8]; + +int keyboard_fd_num; +int keyboard_fds[MAX_KEYBOARDS]; + +Display* display = NULL; + +int find_keyboards() { + int i, j; + int fd; + + keyboard_fd_num = 0; + for (i=0; i<MAX_KEYBOARDS; i++) { + keyboard_fds[i] = 0; + } + + for (i=0; i<MAX_INPUT_NODE; i++) { + snprintf(filename, sizeof(filename), "/dev/input/event%d", i); + + fd = open(filename, O_RDWR|O_SYNC); + if (fd >= 0) { + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask); + + struct input_id input_info; + ioctl (fd, EVIOCGID, &input_info); + if ((input_info.vendor != 0) && (input_info.product != 0)) { + /* We assume that anything that has an alphabetic key in the + QWERTYUIOP range in it is the main keyboard. */ + for (j = KEY_Q; j <= KEY_P; j++) { + if (TestBit(j, key_bitmask)) { + keyboard_fds[keyboard_fd_num] = fd; + } + } + } + + if (keyboard_fds[keyboard_fd_num] == 0) { + close(fd); + } + else { + keyboard_fd_num++; + } + } + } + return 0; +} + +int main() { + int current_keyboard; + char name[256] = "Unknown"; + unsigned int states; + struct input_event ev; + + bool num_lock_set = false; + bool caps_lock_set = false; + bool scroll_lock_set = false; + + // Open X11 display + display = XOpenDisplay(NULL); + if (!display) { + printf ("[tdekbdledsync] Unable to open X11 display!\n"); + return -1; + } + + // Find keyboards + find_keyboards(); + if (keyboard_fd_num == 0) { + printf ("[tdekbdledsync] Could not find any usable keyboard(s)!\n"); + return -2; + } + else { + fprintf(stderr, "[tdekbdledsync] Found %d keyboard(s)\n", keyboard_fd_num); + + for (current_keyboard=0;current_keyboard<keyboard_fd_num;current_keyboard++) { + // Print device name + ioctl(keyboard_fds[current_keyboard], EVIOCGNAME (sizeof (name)), name); + fprintf(stderr, "[tdekbdledsync] Syncing keyboard: (%s)\n", name); + } + + while (1) { + // Get Virtual Core keyboard status + if (XkbGetIndicatorState(display, XkbUseCoreKbd, &states) != Success) { + fprintf(stderr, "[tdekbdledsync] Unable to query X11 Virtual Core keyboard!\n"); + return -3; + } + + // From "xset -q" + caps_lock_set = (states & 0x01); + num_lock_set = (states & 0x02); + scroll_lock_set = (states & 0x04); + + for (current_keyboard=0;current_keyboard<keyboard_fd_num;current_keyboard++) { + // Set LEDs + ev.type = EV_LED; + ev.code = LED_CAPSL; + ev.value = caps_lock_set; + if (write(keyboard_fds[current_keyboard], &ev, sizeof(ev)) < 0) { + fprintf(stderr, "[tdekbdledsync] Unable to set LED state\n"); + } + + ev.type = EV_LED; + ev.code = LED_NUML; + ev.value = num_lock_set; + if (write(keyboard_fds[current_keyboard], &ev, sizeof(ev)) < 0) { + fprintf(stderr, "[tdekbdledsync] Unable to set LED state\n"); + } + + ev.type = EV_LED; + ev.code = LED_SCROLLL; + ev.value = scroll_lock_set; + if (write(keyboard_fds[current_keyboard], &ev, sizeof(ev)) < 0) { + fprintf(stderr, "[tdekbdledsync] Unable to set LED state\n"); + } + } + + usleep(500*1000); + } + } + + return 0; +}
\ No newline at end of file |