*.so
*.deb
*.zip
-/rpi-share
+/share
/package-lock.json
*.elf
- 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.
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
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))
$(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
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):
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
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
startup_message off
set framemsgwait -1
+escape Super_L
'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,
--- /dev/null
+build
+bbkbd
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)
#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) {
" -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);
} 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]);
}
+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);
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) {
#include "util.h"
#include <X11/Xatom.h>
+#include <X11/Xcursor/Xcursor.h>
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;
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;
}
-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;
// Enable window events
XSelectInput(dpy, btn->win, ButtonReleaseMask | ButtonPressMask |
- ExposureMask | PointerMotionMask);
+ ExposureMask | PointerMotionMask | VisibilityChangeMask);
// Set window properties
XWMHints *wmHints = XAllocWMHints();
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);
#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);
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},
{"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},
{"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},
{"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};
#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 |
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;
}
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;
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;
}
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;
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;
}
}
-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);
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;
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);
}
-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:
}
-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");
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;
}
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);
}
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;
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);