Keyboard updates
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Wed, 14 Apr 2021 05:10:35 +0000 (22:10 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Wed, 14 Apr 2021 06:08:18 +0000 (23:08 -0700)
18 files changed:
.gitignore
CHANGELOG.md
MANIFEST.in
Makefile
scripts/camotics-build.sh [deleted file]
scripts/camotics-init-build.sh [deleted file]
scripts/camotics-init-dev-img.sh [deleted file]
scripts/install.sh
scripts/ratpoisonrc
setup.py
src/kbd/.gitignore [new file with mode: 0644]
src/kbd/Makefile
src/kbd/bbkbd.c
src/kbd/button.c
src/kbd/button.h
src/kbd/config.h
src/kbd/keyboard.c
src/kbd/keyboard.h

index 2157929c18c3ae32ce549628b5cb245d9e36eeea..c8c0a407b427bd8fa91e80a137f9da6a4b0c77ae 100644 (file)
@@ -18,7 +18,7 @@ __pycache__
 *.so
 *.deb
 *.zip
-/rpi-share
+/share
 /package-lock.json
 
 *.elf
index e1d5042f606f39ca06d2b9f706af8cf88b6ef03f..fdb916dff67632bf98d1c98d99481c9c2fc0db64 100644 (file)
@@ -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.
index 66f5329ba6538e657c169dd7eb1e13ce04ce1640..0c523b7b8a300ee3e3a361c109442702f225fe3a 100644 (file)
@@ -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
index 9234d4fe4edb273c723098b4f12c381c3ef4bce0..32388baf23da6d7ebd24377d5aa0028261182f62 100644 (file)
--- 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 (executable)
index 97b419f..0000000
+++ /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 (executable)
index 006a9f9..0000000
+++ /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 (executable)
index bd53de8..0000000
+++ /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
index a5d3a3fc9f0d7b7ffbd343c6651ba5ec8a49c3d5..c14f474794fd0a9b5a0a9edd1bbc6185b5cf6e4a 100755 (executable)
@@ -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
index d51e773ee7cc66c6601c6467b8754d0fa07bf69b..02ddc1a95c54ddb17492cdf0d6144464b287566c 100755 (executable)
@@ -1,2 +1,3 @@
 startup_message off
 set framemsgwait -1
+escape Super_L
index 38c1a9bfbd22c97786ac15e8f2e0df5b223389f8..1deda91a0843309c25f18c6d344e218d5556d85a 100755 (executable)
--- 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 (file)
index 0000000..685aa09
--- /dev/null
@@ -0,0 +1,2 @@
+build
+bbkbd
index 4a624fa6f7a9b72ddbb478ddf3e6dd98fc5126a5..233b68f51169831bdef9025ad3ca249f1a171f1f 100644 (file)
@@ -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)
index ee9ce344981c9978f48e034426f32b1e27ae0cd8..38920afa8cd6495d7afcbe2cbf97195752b97912 100644 (file)
 #include <signal.h>
 #include <locale.h>
 
-#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>  - Font string, default: " DEFAULT_FONT "\n"
-    "  -b <x> <y> - Button screen position.\n";
+    "  -b <x> <y> - Button screen position. Values between 0 and 1.\n"
+    "  -S <cmd>   - Command to run before showing the keyboard.\n"
+    "  -H <cmd>   - Command to run after hiding the keyboard.\n"
+    "  -s <int>   - 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) {
index 14940d74803e4fbce420431ede7ef4bb37749e28..9ef4191c7cebdb6f8079c9ce0871a9db08119641 100644 (file)
@@ -29,6 +29,7 @@
 #include "util.h"
 
 #include <X11/Xatom.h>
+#include <X11/Xcursor/Xcursor.h>
 
 
 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);
 
index 1a8b669c761badc165105678e74b435ee4a71653..8ade83ae5650fc1a2c8a49a28ce4893dada385ae 100644 (file)
 
 #pragma once
 
-#include "keyboard.h"
+#include "drw.h"
 
 #include <stdbool.h>
 
+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);
index 164235acc83bad44fcde4006ce4d4b5195301de7..7cd7e953a581c419ca0940e7721de6a04fccb52b 100644 (file)
 
 
 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};
index 147aadc1a71902036d0464cd5b0febe803aeb977..2e568575f9f8a5421dbfc07934d5a3df76730e8a 100644 (file)
 #include "keyboard.h"
 
 #include <X11/Xatom.h>
+#include <X11/Xcursor/Xcursor.h>
 
 #include <signal.h>
+#include <unistd.h>
 
 
-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);
 }
index 826db844592dd032204b451da66f00a0838b6bd4..29fbae990fbf1b7c15ee3197d8b48722d5a393d0 100644 (file)
 
 
 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);