From 97d0d1292b591dbecff01798ba3d1f4e994768ba Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Tue, 13 Apr 2021 22:10:35 -0700 Subject: [PATCH] Keyboard updates --- .gitignore | 2 +- CHANGELOG.md | 2 + MANIFEST.in | 4 + Makefile | 27 ++-- scripts/camotics-build.sh | 7 - scripts/camotics-init-build.sh | 59 ------- scripts/camotics-init-dev-img.sh | 11 -- scripts/install.sh | 11 +- scripts/ratpoisonrc | 1 + setup.py | 2 + src/kbd/.gitignore | 2 + src/kbd/Makefile | 2 +- src/kbd/bbkbd.c | 46 ++++-- src/kbd/button.c | 30 +++- src/kbd/button.h | 10 +- src/kbd/config.h | 208 ++++++------------------- src/kbd/keyboard.c | 258 +++++++++++++++---------------- src/kbd/keyboard.h | 29 ++-- 18 files changed, 292 insertions(+), 419 deletions(-) delete mode 100755 scripts/camotics-build.sh delete mode 100755 scripts/camotics-init-build.sh delete mode 100755 scripts/camotics-init-dev-img.sh create mode 100644 src/kbd/.gitignore diff --git a/.gitignore b/.gitignore index 2157929..c8c0a40 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ __pycache__ *.so *.deb *.zip -/rpi-share +/share /package-lock.json *.elf diff --git a/CHANGELOG.md b/CHANGELOG.md index e1d5042..fdb916d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ Buildbotics CNC Controller Firmware Changelog - Moved documentation pages to ``DOCS`` page. - Show program messages in 3D view. - Fix bug where rehoming fails after stop. #294 + - Fixed splash screen centering. + - Much improved onscreen keyboard for touch screen operation. ## v0.4.16 - Improved axis under/over warning tooltip. diff --git a/MANIFEST.in b/MANIFEST.in index 66f5329..0c523b7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -11,4 +11,8 @@ include scripts/xinitrc include scripts/rc.local include scripts/bbctrl.service include scripts/11-automount.rules +include scripts/kbd-show +include scripts/kbd-hide +include scripts/ratpoisonrc +include share/bbctrl-firmware/src/kbd/bbkbd global-exclude .gitignore diff --git a/Makefile b/Makefile index 9234d4f..32388ba 100644 --- a/Makefile +++ b/Makefile @@ -11,10 +11,10 @@ RESOURCES := $(shell find src/resources -type f) RESOURCES := $(patsubst src/resources/%,$(TARGET_DIR)/%,$(RESOURCES)) TEMPLS := $(wildcard src/pug/templates/*.pug) -AVR_FIRMWARE := src/avr/bbctrl-avr-firmware.hex -CAMOTICS_MOD := rpi-share/camotics/build/camotics.so +SHARE := share +AVR_FIRMWARE := src/avr/bbctrl-avr-firmware.hex +CAMOTICS_MOD := $(SHARE)/camotics/build/camotics.so CAMOTICS_TARGET := src/py/bbctrl/camotics.so -CAMOTICS_IMG := camotics-dev.img RSYNC_EXCLUDE := \*.pyc __pycache__ \*.egg-info \\\#* \*~ .\\\#\* RSYNC_EXCLUDE := $(patsubst %,--exclude %,$(RSYNC_EXCLUDE)) @@ -54,7 +54,7 @@ bbemu: $(MAKE) -C src/avr/emu pkg: all $(AVR_FIRMWARE) bbserial - cp -a rpi-share/camotics/tpl_lib src/py/bbctrl/ + cp -a $(SHARE)/camotics/tpl_lib src/py/bbctrl/ ./setup.py sdist beta-pkg: pkg @@ -63,22 +63,19 @@ beta-pkg: pkg bbserial: $(MAKE) -C src/bbserial +container-update: + ./scripts/container-update + camotics: $(CAMOTICS_TARGET) $(CAMOTICS_TARGET): $(CAMOTICS_MOD) cp $< $@ -$(CAMOTICS_MOD): $(CAMOTICS_IMG) - ./scripts/camotics-init-build.sh - git -C rpi-share/cbang fetch - git -C rpi-share/cbang reset --hard FETCH_HEAD - git -C rpi-share/camotics fetch - git -C rpi-share/camotics reset --hard FETCH_HEAD - cp ./scripts/camotics-build.sh rpi-share/ - sudo ./scripts/rpi-chroot.sh $(CAMOTICS_IMG) /mnt/host/camotics-build.sh - -$(CAMOTICS_IMG): - ./scripts/camotics-init-build.sh +$(CAMOTICS_MOD): container-update + ./scripts/container-run ./bbctrl-firmware/scripts/container-make-camotics + +bbkbd: container-update + ./scripts/container-run ./bbctrl-firmware/scripts/container-make-kbd .PHONY: $(AVR_FIRMWARE) $(AVR_FIRMWARE): diff --git a/scripts/camotics-build.sh b/scripts/camotics-build.sh deleted file mode 100755 index 97b419f..0000000 --- a/scripts/camotics-build.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -ex - -cd /mnt/host -export V8_INCLUDE=/opt/embedded-v8/include/ V8_LIBPATH=/opt/embedded-v8/out/ -scons -C cbang disable_local="re2 libevent" -export CBANG_HOME="/mnt/host/cbang" -scons -C camotics build/camotics.so with_gui=0 diff --git a/scripts/camotics-init-build.sh b/scripts/camotics-init-build.sh deleted file mode 100755 index 006a9f9..0000000 --- a/scripts/camotics-init-build.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash -ex - -IMG_DATE=2017-11-29 -IMG_BASE=${IMG_DATE}-raspbian-stretch-lite -BASE_URL=https://downloads.raspberrypi.org/raspbian_lite/images -IMG_URL=$BASE_URL/raspbian_lite-2017-12-01/$IMG_BASE.zip -CAMOTICS_IMG=camotics-dev.img - -# Create dev image -if [ ! -e $CAMOTICS_IMG ]; then - - # Get base image - if [ ! -e $IMG_BASE.img ]; then - if [ ! -e $IMG_BASE.zip ]; then - wget $IMG_URL - fi - - unzip $IMG_BASE.zip - fi - - # Copy base image - cp $IMG_BASE.img $CAMOTICS_IMG.tmp - - # Init image - mkdir -p rpi-share - cp ./scripts/camotics-init-dev-img.sh rpi-share - sudo ./scripts/rpi-chroot.sh $CAMOTICS_IMG.tmp /mnt/host/camotics-init-dev-img.sh - - # Move image - mv $CAMOTICS_IMG.tmp $CAMOTICS_IMG -fi - -# Get repos -function fetch_local_repo() { - mkdir -p $1 - git -C $1 init - git -C $1 fetch -t "$2" $3 - git -C $1 reset --hard FETCH_HEAD -} - -mkdir -p rpi-share || true - -if [ ! -e rpi-share/cbang ]; then - if [ "$CBANG_HOME" != "" ]; then - fetch_local_repo rpi-share/cbang "$CBANG_HOME" master - else - git clone https://github.com/CauldronDevelopmentLLC/cbang \ - rpi-share/cbang - fi -fi - -if [ ! -e rpi-share/camotics ]; then - if [ "$CAMOTICS_HOME" != "" ]; then - fetch_local_repo rpi-share/camotics "$CAMOTICS_HOME" master - else - git clone https://github.com/CauldronDevelopmentLLC/camotics \ - rpi-share/camotics - fi -fi diff --git a/scripts/camotics-init-dev-img.sh b/scripts/camotics-init-dev-img.sh deleted file mode 100755 index bd53de8..0000000 --- a/scripts/camotics-init-dev-img.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -e - -export LC_ALL=C -cd /mnt/host - -# Update the system -apt-get update -#apt-get dist-upgrade -y - -# Install packages -apt-get install -y scons build-essential libssl-dev python3-dev nodejs-dev diff --git a/scripts/install.sh b/scripts/install.sh index a5d3a3f..c14f474 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -111,8 +111,15 @@ cp src/splash/* /usr/share/plymouth/themes/buildbotics/ cp scripts/rc.local /etc/ # Install bbkbd -if [ ! -e /usr/local/bin/bbkbd ]; then REBOOT=true; fi -cp src/kbd/bbkbd-arm /usr/local/bin/bbkbd +diff share/bbctrl-firmware/src/kbd/bbkbd /usr/local/bin/bbkbd 2>&1 >/dev/null +if [ $? -ne 0 ]; then + REBOOT=true + killall -9 bbkbd + cp share/bbctrl-firmware/src/kbd/bbkbd /usr/local/bin/ +fi + +# Remove xontab keyboard +rm -rf /home/pi/.config/chromium/Default/Extensions/pflmllfnnabikmfkkaddkoolinlfninn # Install bbctrl if $UPDATE_PY; then diff --git a/scripts/ratpoisonrc b/scripts/ratpoisonrc index d51e773..02ddc1a 100755 --- a/scripts/ratpoisonrc +++ b/scripts/ratpoisonrc @@ -1,2 +1,3 @@ startup_message off set framemsgwait -1 +escape Super_L diff --git a/setup.py b/setup.py index 38c1a9b..1deda91 100755 --- a/setup.py +++ b/setup.py @@ -36,6 +36,8 @@ setup( 'scripts/browser', 'scripts/mount-usb', 'scripts/eject-usb', + 'scripts/kbd-show', + 'scripts/kbd-hide', ], install_requires = 'tornado sockjs-tornado pyserial pyudev smbus2'.split(), zip_safe = False, diff --git a/src/kbd/.gitignore b/src/kbd/.gitignore new file mode 100644 index 0000000..685aa09 --- /dev/null +++ b/src/kbd/.gitignore @@ -0,0 +1,2 @@ +build +bbkbd diff --git a/src/kbd/Makefile b/src/kbd/Makefile index 4a624fa..233b68f 100644 --- a/src/kbd/Makefile +++ b/src/kbd/Makefile @@ -1,7 +1,7 @@ NAME = bbkbd PKG_CONFIG = pkg-config -PKGS = fontconfig freetype2 x11 xtst xft xinerama +PKGS = fontconfig freetype2 x11 xtst xft xinerama xcursor CDEFS = -D_DEFAULT_SOURCE -DXINERAMA CFLAGS += -I. `$(PKG_CONFIG) --cflags $(PKGS)` $(CDEFS) diff --git a/src/kbd/bbkbd.c b/src/kbd/bbkbd.c index ee9ce34..38920af 100644 --- a/src/kbd/bbkbd.c +++ b/src/kbd/bbkbd.c @@ -34,12 +34,15 @@ #include #include -#define DEFAULT_FONT "DejaVu Sans:bold:size=22" +#define DEFAULT_FONT "DejaVu Sans:size=18" static const char *font = DEFAULT_FONT; -static int button_x = -60; -static int button_y = 0; +static float button_x = 1; +static float button_y = 0; static bool running = true; +static const char *show_cmd = 0; +static const char *hide_cmd = 0; +static int space = 4; void signaled(int sig) { @@ -55,7 +58,10 @@ void usage(char *argv0, int ret) { " -h - Print this help screen and exit\n" " -d - Enable debug\n" " -f - Font string, default: " DEFAULT_FONT "\n" - " -b - Button screen position.\n"; + " -b - Button screen position. Values between 0 and 1.\n" + " -S - Command to run before showing the keyboard.\n" + " -H - Command to run after hiding the keyboard.\n" + " -s - Space between buttons.\n"; fprintf(ret ? stderr : stdout, usage, argv0); exit(ret); @@ -72,8 +78,20 @@ void parse_args(int argc, char *argv[]) { } else if (!strcmp(argv[i], "-b")) { if (argc - 2 <= i) usage(argv[0], 1); - button_x = atoi(argv[++i]); - button_y = atoi(argv[++i]); + button_x = atof(argv[++i]); + button_y = atof(argv[++i]); + + } else if (!strcmp(argv[i], "-S")) { + if (argc - 1 <= i) usage(argv[0], 1); + show_cmd = argv[++i]; + + } else if (!strcmp(argv[i], "-H")) { + if (argc - 1 <= i) usage(argv[0], 1); + hide_cmd = argv[++i]; + + } else if (!strcmp(argv[i], "-s")) { + if (argc - 1 <= i) usage(argv[0], 1); + space = atoi(argv[++i]); } else { fprintf(stderr, "Invalid argument: %s\n", argv[i]); @@ -83,6 +101,13 @@ void parse_args(int argc, char *argv[]) { } +void button_callback(Keyboard *kbd) { + if (!kbd->visible && show_cmd) system(show_cmd); + keyboard_toggle(kbd); + if (!kbd->visible && hide_cmd) system(hide_cmd); +} + + int main(int argc, char *argv[]) { signal(SIGTERM, signaled); signal(SIGINT, signaled); @@ -97,11 +122,10 @@ int main(int argc, char *argv[]) { Display *dpy = XOpenDisplay(0); if (!dpy) die("cannot open display"); - int size = 60; - Button *btn = button_create(dpy, 0, button_x, button_y, size, size * 0.5, - font); - Keyboard *kbd = keyboard_create(dpy, layers, font, colors); - btn->kbd = kbd; + Button *btn = button_create(dpy, button_x, button_y, 55, 35, font); + Keyboard *kbd = keyboard_create(dpy, keys, space, font, colors); + + button_set_callback(btn, button_callback, kbd); // Event loop while (running) { diff --git a/src/kbd/button.c b/src/kbd/button.c index 14940d7..9ef4191 100644 --- a/src/kbd/button.c +++ b/src/kbd/button.c @@ -29,6 +29,7 @@ #include "util.h" #include +#include void button_draw(Button *btn) { @@ -47,6 +48,12 @@ void button_draw(Button *btn) { void button_event(Button *btn, XEvent *e) { switch (e->type) { + case VisibilityNotify: { + if (e->xvisibility.state == VisibilityFullyObscured) + XRaiseWindow(btn->drw->dpy, btn->win); + break; + } + case MotionNotify: { int x = e->xmotion.x; int y = e->xmotion.y; @@ -57,8 +64,8 @@ void button_event(Button *btn, XEvent *e) { case ButtonPress: break; case ButtonRelease: - if (e->xbutton.button == 1 && btn->mouse_in && btn->kbd) - keyboard_toggle(btn->kbd); + if (e->xbutton.button == 1 && btn->mouse_in && btn->cb) + btn->cb(btn->cb_data); break; case Expose: if (!e->xexpose.count) button_draw(btn); break; @@ -66,18 +73,23 @@ void button_event(Button *btn, XEvent *e) { } -Button *button_create(Display *dpy, Keyboard *kbd, int x, int y, int w, int h, +void button_set_callback(Button *btn, button_cb cb, void *data) { + btn->cb = cb; + btn->cb_data = data; +} + + +Button *button_create(Display *dpy, float x, float y, int w, int h, const char *font) { Button *btn = (Button *)calloc(1, sizeof(Button)); - btn->kbd = kbd; int screen = DefaultScreen(dpy); Window root = RootWindow(dpy, screen); // Dimensions Dim dim = get_display_dims(dpy, screen); - x = x < 0 ? dim.width - w : 0; - y = y < 0 ? dim.height - h : 0; + x *= dim.width - w; + y *= dim.height - h; btn->w = w; btn->h = h; @@ -101,7 +113,7 @@ Button *button_create(Display *dpy, Keyboard *kbd, int x, int y, int w, int h, // Enable window events XSelectInput(dpy, btn->win, ButtonReleaseMask | ButtonPressMask | - ExposureMask | PointerMotionMask); + ExposureMask | PointerMotionMask | VisibilityChangeMask); // Set window properties XWMHints *wmHints = XAllocWMHints(); @@ -128,6 +140,10 @@ Button *button_create(Display *dpy, Keyboard *kbd, int x, int y, int w, int h, XChangeProperty(dpy, btn->win, atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type, 1); + // Set cursor + Cursor c = XcursorLibraryLoadCursor(dpy, "hand2"); + XDefineCursor(dpy, btn->win, c); + // Raise window to top of stack XMapRaised(dpy, btn->win); diff --git a/src/kbd/button.h b/src/kbd/button.h index 1a8b669..8ade83a 100644 --- a/src/kbd/button.h +++ b/src/kbd/button.h @@ -27,24 +27,28 @@ #pragma once -#include "keyboard.h" +#include "drw.h" #include +typedef void (*button_cb)(); typedef struct { - Keyboard *kbd; Window win; Drw *drw; Clr *scheme; + button_cb cb; + void *cb_data; + int w; int h; bool mouse_in; } Button; -Button *button_create(Display *dpy, Keyboard *kbd, int x, int y, int w, int h, +Button *button_create(Display *dpy, float x, float y, int w, int h, const char *font); void button_destroy(Button *btn); +void button_set_callback(Button *btn, button_cb cb, void *data); void button_event(Button *btn, XEvent *e); diff --git a/src/kbd/config.h b/src/kbd/config.h index 164235a..7cd7e95 100644 --- a/src/kbd/config.h +++ b/src/kbd/config.h @@ -31,15 +31,35 @@ static const char *colors[SchemeLast][2] = { - // fg bg - [SchemeNorm] = {"#bbbbbb", "#132a33"}, - [SchemeNormABC] = {"#ffffff", "#14313d"}, - [SchemePress] = {"#ffffff", "#259937"}, - [SchemeHighlight] = {"#58a7c6", "#005577"}, + // fg bg + [SchemeNorm] = {"#bbbbbb", "#272a2b"}, + [SchemeNormABC] = {"#ffffff", "#383c3d"}, + [SchemePress] = {"#ffffff", "#e5aa3d"}, + [SchemeHighlight] = {"#bbbbbb", "#666666"}, + [SchemeBG] = {"#ffffff", "#000000"}, }; -static Key _main[] = { +static Key row0[] = { + {"`", "~", XK_grave, 1}, + {"1", "!", XK_1, 1}, + {"2", "@", XK_2, 1}, + {"3", "#", XK_3, 1}, + {"4", "$", XK_4, 1}, + {"5", "%", XK_5, 1}, + {"6", "^", XK_6, 1}, + {"7", "&", XK_7, 1}, + {"8", "*", XK_8, 1}, + {"9", "(", XK_9, 1}, + {"0", ")", XK_0, 1}, + {"-", "_", XK_minus, 1}, + {"=", "+", XK_equal, 1}, + {"Back", 0, XK_BackSpace, 1}, + {0} +}; + +static Key row1[] = { + {"Tab ➡", "Tab ⬅", XK_Tab, 1}, {"q", "Q", XK_q, 1}, {"w", "W", XK_w, 1}, {"e", "E", XK_e, 1}, @@ -50,13 +70,14 @@ static Key _main[] = { {"i", "I", XK_i, 1}, {"o", "O", XK_o, 1}, {"p", "P", XK_p, 1}, - {"7", "&", XK_7, 1}, - {"8", "*", XK_8, 1}, - {"9", "(", XK_9, 1}, - {"-", "_", XK_minus, 1}, - - {0}, // New row + {"[", "{", XK_bracketleft, 1}, + {"]", "}", XK_bracketright, 1}, + {"\\", "|", XK_backslash, 1}, + {0} +}; +static Key row2[] = { + {"Esc", 0, XK_Escape, 1}, {"a", "A", XK_a, 1}, {"s", "S", XK_s, 1}, {"d", "D", XK_d, 1}, @@ -67,13 +88,13 @@ static Key _main[] = { {"k", "K", XK_k, 1}, {"l", "L", XK_l, 1}, {";", ":", XK_colon, 1}, - {"4", "$", XK_4, 1}, - {"5", "%", XK_5, 1}, - {"6", "^", XK_6, 1}, - {"=", "+", XK_equal, 1}, - - {0}, // New row + {"\"", "'", XK_quotedbl, 1}, + {"↲ Enter", 0, XK_Return, 2}, + {0} +}; +static Key row3[] = { + {"⬆ Shift", 0, XK_Shift_L, 2}, {"z", "Z", XK_z, 1}, {"x", "X", XK_x, 1}, {"c", "C", XK_c, 1}, @@ -81,151 +102,18 @@ static Key _main[] = { {"b", "B", XK_b, 1}, {"n", "N", XK_n, 1}, {"m", "M", XK_m, 1}, - {"Tab", 0, XK_Tab, 1}, - {"⇍ Bksp", 0, XK_BackSpace, 2}, - {"1", "!", XK_1, 1}, - {"2", "@", XK_2, 1}, - {"3", "#", XK_3, 1}, - {"/", "?", XK_slash, 1}, - - {0}, // New row - {"⌨", 0, XK_Cancel, 1}, - {"Shift", 0, XK_Shift_L, 1}, - {"↓", 0, XK_Down, 1}, - {"↑", 0, XK_Up, 1}, - {"Space", 0, XK_space, 2}, - {"Esc", 0, XK_Escape, 1}, - {"Ctrl", 0, XK_Control_L, 1}, - {"↲ Enter", 0, XK_Return, 2}, - {"0", ")", XK_0, 1}, - {",", "<", XK_comma, 1}, - {".", ">", XK_period, 1}, - {"\\", "|", XK_slash, 1}, - - {0}, {0} // End -}; - - -static Key _alt[] = { - {0, 0, XK_Q, 1}, - {0, 0, XK_W, 1}, - {0, 0, XK_E, 1}, - {0, 0, XK_R, 1}, - {0, 0, XK_T, 1}, - {0, 0, XK_Y, 1}, - {0, 0, XK_U, 1}, - {0, 0, XK_I, 1}, - {0, 0, XK_O, 1}, - {0, 0, XK_P, 1}, - {"7", 0, XK_7, 1}, - {"8", 0, XK_8, 1}, - {"9", 0, XK_9, 1}, - {"-", 0, XK_minus, 1}, - - {0}, // New row - - {0, 0, XK_A, 1}, - {0, 0, XK_S, 1}, - {0, 0, XK_D, 1}, - {0, 0, XK_F, 1}, - {0, 0, XK_G, 1}, - {0, 0, XK_H, 1}, - {0, 0, XK_J, 1}, - {0, 0, XK_K, 1}, - {0, 0, XK_L, 1}, - {";",":", XK_colon, 1}, - {"4", 0, XK_4, 1}, - {"5", 0, XK_5, 1}, - {"6", 0, XK_6, 1}, - {"+", 0, XK_plus, 1}, - - {0}, // New row - - {0, 0, XK_Z, 1}, - {0, 0, XK_X, 1}, - {0, 0, XK_C, 1}, - {0, 0, XK_V, 1}, - {0, 0, XK_B, 1}, - {0, 0, XK_N, 1}, - {0, 0, XK_M, 1}, - {"Tab", 0, XK_Tab, 1}, - {"⇍ Bksp", 0, XK_BackSpace, 2}, - {"1", 0, XK_1, 1}, - {"2", 0, XK_2, 1}, - {"3", 0, XK_3, 1}, - {"/", 0, XK_slash, 1}, - - {0}, // New row - {"⌨", 0, XK_Cancel, 1}, - {"Shift", 0, XK_Shift_L, 1}, - {"↓", 0, XK_Down, 1}, - {"↑", 0, XK_Up, 1}, - {"Space", 0, XK_space, 2}, - {"Esc", 0, XK_Escape, 1}, - {"Ctrl", 0, XK_Control_L, 1}, - {"↲ Enter", 0, XK_Return, 2}, - {"0", 0, XK_0, 1}, - {".", 0, XK_period, 1}, - {"=", 0, XK_equal, 1}, - {"*", 0, XK_asterisk, 1}, - - {0}, {0} // End -}; - - -Key _symbols[] = { - {"1", "!", XK_1, 1}, - {"2", "@", XK_2, 1}, - {"3", "#", XK_3, 1}, - {"4", "$", XK_4, 1}, - {"5", "%", XK_5, 1}, - {"6", "^", XK_6, 1}, - {"7", "&", XK_7, 1}, - {"8", "*", XK_8, 1}, - {"9", "(", XK_9, 1}, - {"0", ")", XK_0, 1}, - - {0}, // New row - - {"'", "\"", XK_apostrophe, 1}, - {"`", "~", XK_grave, 1}, - {"-", "_", XK_minus, 1}, - {"=", "+", XK_plus, 1}, - {"[", "{", XK_bracketleft, 1}, - {"]", "}", XK_bracketright, 1}, {",", "<", XK_comma, 1}, {".", ">", XK_period, 1}, {"/", "?", XK_slash, 1}, - {"\\", "|", XK_backslash, 1}, - - {0}, // New row - - {"", 0, XK_Shift_L|XK_bar, 1}, - {"⇤", 0, XK_Home, 1}, - {"←", 0, XK_Left, 1}, - {"→", 0, XK_Right, 1}, - {"⇥", 0, XK_End, 1}, - {"⇊", 0, XK_Next, 1}, - {"⇈", 0, XK_Prior, 1}, - {"Tab", 0, XK_Tab, 1}, - {"⇍ Bksp", 0, XK_BackSpace, 2}, - - {0}, // New row - {"⌨", 0, XK_Cancel, 1}, - {"Shift", 0, XK_Shift_L, 1}, - {"↓", 0, XK_Down, 1}, - {"↑", 0, XK_Up, 1}, - {"", 0, XK_space, 2}, - {"Esc", 0, XK_Escape, 1}, - {"Ctrl", 0, XK_Control_L, 1}, - {"↲ Enter", 0, XK_Return, 2}, - - {0}, {0} // End + {"⬆ Shift", 0, XK_Shift_L, 2}, + {0} }; - -static Key *layers[] = { - _main, - _alt, - 0 +static Key row4[] = { + {"Ctrl", 0, XK_Control_L, 2}, + {"Space", 0, XK_space, 10}, + {"Alt", 0, XK_Alt_R, 2}, + {0} }; + +static Key *keys[] = {row0, row1, row2, row3, row4, 0}; diff --git a/src/kbd/keyboard.c b/src/kbd/keyboard.c index 147aadc..2e56857 100644 --- a/src/kbd/keyboard.c +++ b/src/kbd/keyboard.c @@ -28,21 +28,22 @@ #include "keyboard.h" #include +#include #include +#include -static int create_window(Display *dpy, int root, const char *name, Dim dim, - int x, int y, bool override, unsigned long fg, - unsigned long bg) { +static int create_window(Display *dpy, int root, const char *name, int w, int h, + int x, int y, unsigned long fg, unsigned long bg) { XSetWindowAttributes wa; - wa.override_redirect = override; + wa.override_redirect = false; wa.border_pixel = fg; wa.background_pixel = bg; int win = XCreateWindow - (dpy, root, x, y, dim.width, dim.height, 0, CopyFromParent, CopyFromParent, - CopyFromParent, CWOverrideRedirect | CWBorderPixel | CWBackingPixel, &wa); + (dpy, root, x, y, w, h, 0, CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBorderPixel | CWBackingPixel, &wa); // Enable window events XSelectInput(dpy, win, StructureNotifyMask | ButtonReleaseMask | @@ -73,6 +74,10 @@ static int create_window(Display *dpy, int root, const char *name, Dim dim, XChangeProperty(dpy, win, atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type, 1); + // Set cursor + Cursor c = XcursorLibraryLoadCursor(dpy, "hand1"); + XDefineCursor(dpy, win, c); + return win; } @@ -81,10 +86,13 @@ static bool is_modifier(Key *k) {return k && IsModifierKey(k->keysym);} static int key_scheme(Keyboard *kbd, Key *k) { - if (k->pressed) return SchemePress; + if (k->pressed || (kbd->shift && k->keysym == XK_Shift_L) || + (kbd->meta && k->keysym == XK_Cancel)) + return SchemePress; + if (k == kbd->focus) return SchemeHighlight; - if (k->keysym == XK_Return || (XK_a <= k->keysym && k->keysym <= XK_z) || - (XK_Cyrillic_io <= k->keysym && k->keysym <= XK_Cyrillic_hardsign)) + + if (!is_modifier(k) && k->keysym != XK_space && k->keysym != XK_Cancel) return SchemeNormABC; return SchemeNorm; @@ -92,12 +100,15 @@ static int key_scheme(Keyboard *kbd, Key *k) { Key *keyboard_find_key(Keyboard *kbd, int x, int y) { - Key *keys = kbd->keys; - for (int i = 0; i < kbd->nkeys; i++) - if (keys[i].keysym && keys[i].x < x && x < keys[i].x + keys[i].w && - keys[i].y < y && y < keys[i].y + keys[i].h) - return &keys[i]; + for (int r = 0; r < kbd->rows; r++) { + Key *keys = kbd->keys[r]; + + for (int c = 0; keys[c].keysym; c++) + if (keys[c].x < x && x < keys[c].x + keys[c].w && + keys[c].y < y && y < keys[c].y + keys[c].h) + return &keys[c]; + } return 0; } @@ -111,7 +122,7 @@ void keyboard_draw_key(Keyboard *kbd, Key *k) { const char *label = k->label; if (!label) label = XKeysymToString(k->keysym); - if (kbd->shifted && k->label2) label = k->label2; + if (kbd->shift && k->label2) label = k->label2; int h = drw->fonts[0].xfont->height * 2; int y = k->y + (k->h - h) / 2; @@ -124,33 +135,34 @@ void keyboard_draw_key(Keyboard *kbd, Key *k) { void keyboard_draw(Keyboard *kbd) { - for (int i = 0; i < kbd->nkeys; i++) - if (kbd->keys[i].keysym) - keyboard_draw_key(kbd, &kbd->keys[i]); -} + drw_setscheme(kbd->drw, kbd->scheme[SchemeBG]); + drw_rect(kbd->drw, 0, 0, kbd->w, kbd->h, 1, 1); + drw_map(kbd->drw, kbd->win, 0, 0, kbd->w, kbd->h); + for (int r = 0; r < kbd->rows; r++) + for (int c = 0; kbd->keys[r][c].keysym; c++) + keyboard_draw_key(kbd, &kbd->keys[r][c]); +} -void keyboard_update(Keyboard *kbd) { - int y = 0; - int r = kbd->nrows; - int h = (kbd->dim.height - 1) / r; - Key *keys = kbd->keys; - for (int i = 0; i < kbd->nkeys; i++, r--) { - int base = 0; +void keyboard_layout(Keyboard *kbd) { + int w = (kbd->w - kbd->space) / kbd->cols; + int h = (kbd->h - kbd->space) / kbd->rows; + int y = (kbd->h - h * kbd->rows + kbd->space) / 2; + int xOffset = (kbd->w - w * kbd->cols + kbd->space) / 2; - for (int j = i; j < kbd->nkeys && keys[j].keysym; j++) - base += keys[j].width; + for (int r = 0; r < kbd->rows; r++) { + Key *keys = kbd->keys[r]; + int x = xOffset; - for (int x = 0; i < kbd->nkeys && keys[i].keysym; i++) { - keys[i].x = x; - keys[i].y = y; - keys[i].w = keys[i].width * (kbd->dim.width - 1) / base; - keys[i].h = r == 1 ? kbd->dim.height - y - 1 : h; - x += keys[i].w; + for (int c = 0; keys[c].keysym; c++) { + keys[c].x = x; + keys[c].y = y; + keys[c].w = keys[c].width * w - kbd->space; + keys[c].h = h - kbd->space; + x += keys[c].w + kbd->space; } - if (base) keys[i - 1].w = kbd->dim.width - 1 - keys[i - 1].x; y += h; } @@ -158,58 +170,31 @@ void keyboard_update(Keyboard *kbd) { } -void keyboard_init_layer(Keyboard *kbd) { - Key *layer = kbd->layers[kbd->layer]; - - // Count keys - kbd->nkeys = 0; - - for (int i = 0; ; i++) { - if (0 < i && !layer[i].keysym && !layer[i - 1].keysym) { - kbd->nkeys--; - break; - } +void keyboard_press_key(Keyboard *kbd, Key *k) { + if (k->pressed) return; - kbd->nkeys++; + if (k->keysym == XK_Cancel) { + kbd->meta = !kbd->meta; + keyboard_draw_key(kbd, k); + return; } - kbd->keys = calloc(1, sizeof(Key) * kbd->nkeys); - memcpy(kbd->keys, layer, sizeof(Key) * kbd->nkeys); - - // Count rows - kbd->nrows = 1; - - for (int i = 0; i < kbd->nkeys; i++) - if (!kbd->keys[i].keysym) { - kbd->nrows++; - - if (i && !kbd->keys[i - 1].keysym) { - kbd->nrows--; - break; - } - } -} - - -void keyboard_next_layer(Keyboard *kbd) { - if (!kbd->layers[++kbd->layer]) kbd->layer = 0; - - print_dbg("Cycling to layer %d\n", kbd->layer); - - keyboard_init_layer(kbd); - keyboard_update(kbd); -} - - -void keyboard_press_key(Keyboard *kbd, Key *k) { - if (k->pressed) return; + if (k->keysym == XK_Shift_L) { + kbd->shift = !kbd->shift; + simulate_key(kbd->drw->dpy, XK_Shift_L, kbd->shift); + keyboard_draw(kbd); + return; + } - if (k->keysym == XK_Shift_L || k->keysym == XK_Shift_R) { - kbd->shifted = true; + if (!is_modifier(k) && kbd->meta) { + simulate_key(kbd->drw->dpy, XK_Super_L, true); + usleep(100000); + simulate_key(kbd->drw->dpy, XK_Super_L, false); + usleep(100000); + kbd->meta = false; keyboard_draw(kbd); } - simulate_key(kbd->drw->dpy, k->modifier, true); simulate_key(kbd->drw->dpy, k->keysym, true); k->pressed = true; keyboard_draw_key(kbd, k); @@ -219,26 +204,21 @@ void keyboard_press_key(Keyboard *kbd, Key *k) { void keyboard_unpress_key(Keyboard *kbd, Key *k) { if (!k->pressed) return; - if (k->keysym == XK_Shift_L || k->keysym == XK_Shift_R) { - kbd->shifted = false; - keyboard_draw(kbd); - } - simulate_key(kbd->drw->dpy, k->keysym, false); - simulate_key(kbd->drw->dpy, k->modifier, false); k->pressed = false; keyboard_draw_key(kbd, k); } void keyboard_unpress_all(Keyboard *kbd) { - for (int i = 0; i < kbd->nkeys; i++) - keyboard_unpress_key(kbd, &kbd->keys[i]); + for (int r = 0; r < kbd->rows; r++) + for (int c = 0; kbd->keys[r][c].keysym; c++) + keyboard_unpress_key(kbd, &kbd->keys[r][c]); } -void keyboard_mouse_motion(Keyboard *kbd, int x, int y) { - Key *k = keyboard_find_key(kbd, x, y); +void keyboard_mouse_motion(Keyboard *kbd, XMotionEvent *e) { + Key *k = e ? keyboard_find_key(kbd, e->x, e->y) : 0; Key *focus = kbd->focus; if (k == focus) return; @@ -247,7 +227,7 @@ void keyboard_mouse_motion(Keyboard *kbd, int x, int y) { if (focus && !is_modifier(focus)) keyboard_unpress_key(kbd, focus); - if (k && kbd->is_pressing && !is_modifier(k)) + if (k && kbd->pressed && !is_modifier(k)) keyboard_press_key(kbd, k); if (k) keyboard_draw_key(kbd, k); @@ -255,59 +235,55 @@ void keyboard_mouse_motion(Keyboard *kbd, int x, int y) { } -void keyboard_mouse_press(Keyboard *kbd, int x, int y) { - kbd->is_pressing = true; - - Key *k = keyboard_find_key(kbd, x, y); +void keyboard_mouse_press(Keyboard *kbd, XButtonEvent *e) { + Key *k = keyboard_find_key(kbd, e->x, e->y); if (k) { - if (is_modifier(k) && k->pressed) keyboard_unpress_key(kbd, k); - else keyboard_press_key(kbd, k); + if (is_modifier(k)) { + if (k->pressed) keyboard_unpress_key(kbd, k); + else keyboard_press_key(kbd, k); + + } else { + keyboard_press_key(kbd, k); + kbd->pressed = k; + } } } -void keyboard_mouse_release(Keyboard *kbd, int x, int y) { - kbd->is_pressing = false; - - Key *k = keyboard_find_key(kbd, x, y); - if (k) { - switch (k->keysym) { - case XK_Cancel: keyboard_next_layer(kbd); break; - case XK_Break: raise(SIGINT); break; - default: break; - } - - if (!is_modifier(k)) keyboard_unpress_all(kbd); +void keyboard_mouse_release(Keyboard *kbd, XButtonEvent *e) { + if (kbd->pressed) { + keyboard_unpress_key(kbd, kbd->pressed); + kbd->pressed = 0; } } void keyboard_resize(Keyboard *kbd, int width, int height) { - if (width == kbd->dim.width && height == kbd->dim.height) return; + if (width == kbd->w && height == kbd->h) return; - kbd->dim.width = width; - kbd->dim.height = height; + kbd->w = width; + kbd->h = height; drw_resize(kbd->drw, width, height); - keyboard_update(kbd); + keyboard_layout(kbd); } void keyboard_event(Keyboard *kbd, XEvent *e) { switch (e->type) { - case LeaveNotify: keyboard_mouse_motion(kbd, -1, -1); break; + case LeaveNotify: keyboard_mouse_motion(kbd, 0); break; case MotionNotify: - keyboard_mouse_motion(kbd, e->xmotion.x, e->xmotion.y); + keyboard_mouse_motion(kbd, &e->xmotion); break; case ButtonPress: if (e->xbutton.button == 1) - keyboard_mouse_press(kbd, e->xbutton.x, e->xbutton.y); + keyboard_mouse_press(kbd, &e->xbutton); break; case ButtonRelease: if (e->xbutton.button == 1) - keyboard_mouse_release(kbd, e->xbutton.x, e->xbutton.y); + keyboard_mouse_release(kbd, &e->xbutton); break; case ConfigureNotify: @@ -332,26 +308,43 @@ void keyboard_toggle(Keyboard *kbd) { } -Keyboard *keyboard_create(Display *dpy, Key **layers, const char *font, +Keyboard *keyboard_create(Display *dpy, Key **keys, int space, const char *font, const char *colors[SchemeLast][2]) { Keyboard *kbd = calloc(1, sizeof(Keyboard)); - kbd->layers = layers; + kbd->space = space; + + // Count rows & colums + for (; keys[kbd->rows]; kbd->rows++) { + Key *row = keys[kbd->rows]; + + int cols; + for (cols = 0; row[cols].keysym; cols++) continue; + if (kbd->cols < cols) kbd->cols = cols; + } + + kbd->keys = calloc(kbd->rows, sizeof(Key *)); + + // Copy keys + for (int r = 0; r < kbd->rows; r++) { + kbd->keys[r] = calloc(kbd->cols + 1, sizeof(Key)); + + for (int c = 0; keys[r][c].keysym; c++) + kbd->keys[r][c] = keys[r][c]; + } // Init screen int screen = DefaultScreen(dpy); Window root = RootWindow(dpy, screen); - // Get display size + // Dimensions Dim dim = get_display_dims(dpy, screen); - - // Init keyboard layer - keyboard_init_layer(kbd); // Computes kbd->nrows - kbd->dim.width = dim.width; - kbd->dim.height = dim.height * kbd->nrows / 18; + kbd->w = dim.width; + kbd->h = kbd->rows * 50; + kbd->x = 0; + kbd->y = dim.height - kbd->h; // Create drawable - Drw *drw = kbd->drw = - drw_create(dpy, screen, root, kbd->dim.width, kbd->dim.height); + Drw *drw = kbd->drw = drw_create(dpy, screen, root, kbd->w, kbd->h); // Setup fonts if (!drw_fontset_create(drw, &font, 1)) die("no fonts could be loaded"); @@ -363,13 +356,12 @@ Keyboard *keyboard_create(Display *dpy, Key **layers, const char *font, drw_setscheme(drw, kbd->scheme[SchemeNorm]); // Create window - int y = dim.height - kbd->dim.height; Clr *clr = kbd->scheme[SchemeNorm]; - kbd->win = create_window(dpy, root, "bbkbd", kbd->dim, 0, y, false, + kbd->win = create_window(dpy, root, "bbkbd", kbd->w, kbd->h, kbd->x, kbd->y, clr[ColFg].pixel, clr[ColBg].pixel); // Init keyboard - keyboard_update(kbd); + keyboard_layout(kbd); return kbd; } @@ -391,6 +383,10 @@ void keyboard_destroy(Keyboard *kbd) { XSync(dpy, false); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); - free(kbd->keys); + if (kbd->keys) { + for (int r = 0; r < kbd->rows; r++) + free(kbd->keys[r]); + free(kbd->keys); + } free(kbd); } diff --git a/src/kbd/keyboard.h b/src/kbd/keyboard.h index 826db84..29fbae9 100644 --- a/src/kbd/keyboard.h +++ b/src/kbd/keyboard.h @@ -34,15 +34,16 @@ enum { - SchemeNorm, SchemeNormABC, SchemePress, SchemeHighlight, SchemeLast + SchemeNorm, SchemeNormABC, SchemePress, SchemeHighlight, SchemeBG, SchemeLast }; +typedef void (*keyboard_show_cb)(bool show); + typedef struct { char *label; char *label2; KeySym keysym; unsigned width; - KeySym modifier; int x, y, w, h; bool pressed; } Key; @@ -50,24 +51,30 @@ typedef struct { typedef struct { Window win; Drw *drw; - Dim dim; - int layer; - int nrows; - int nkeys; - bool is_pressing; - bool shifted; + + int space; + int w, h; + int x, y; + int rows; + int cols; + + bool meta; + bool shift; bool visible; + Key *pressed; Key *focus; - Key **layers; - Key *keys; + Key **keys; + char *font; Clr *scheme[SchemeLast]; + + keyboard_show_cb show_cb; } Keyboard; void keyboard_destroy(Keyboard *kbd); -Keyboard *keyboard_create(Display *dpy, Key **layers, const char *font, +Keyboard *keyboard_create(Display *dpy, Key **keys, int space, const char *font, const char *colors[SchemeLast][2]); void keyboard_event(Keyboard *kbd, XEvent *e); -- 2.27.0