svkbd

Simple X11 onscreen keyboard.
git clone git://r-36.net/svkbd
Log | Files | Refs | LICENSE

commit 1aa6bd5ebe5e58142af641c34dcb2f02420c5dbc
parent 5f9a5d2068f288f4f5067265bd48fe65561248f4
Author: Enno Boland (Gottox) <gottox@s01.de>
Date:   Tue, 15 Jul 2008 22:12:55 +0200

it works :)
Diffstat:
config.def.h | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
config.h | 72+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
config.mk | 2+-
svkbd.c | 199++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
4 files changed, 283 insertions(+), 61 deletions(-)

diff --git a/config.def.h b/config.def.h @@ -1,13 +1,72 @@ static const char font[] = "-*-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*"; static const char normbgcolor[] = "#cccccc"; static const char normfgcolor[] = "#000000"; -static const char selbgcolor[] = "#0066ff"; -static const char selfgcolor[] = "#ffffff"; -static const char hovbgcolor[] = "#0066ff"; -static const char hovfgcolor[] = "#cccccc"; +static const char hovbgcolor[] = "#ffffff"; +static const char hovfgcolor[] = "#000000"; +static const char pressbgcolor[] = "#0000cc"; +static const char pressfgcolor[] = "#ffffff"; static Key keys[] = { - { 1, XK_Return }, + { "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_plus, 1 }, + { "<-", XK_BackSpace, 2 }, { 0 }, - { 1, XK_space }, + { "->|", XK_Tab, 1 }, + { 0, XK_q, 1 }, + { 0, XK_w, 1 }, + { 0, XK_e, 1 }, + { 0, XK_r, 1 }, + { 0, XK_t, 1 }, + { 0, XK_y, 1 }, + { 0, XK_u, 1 }, + { 0, XK_i, 1 }, + { 0, XK_o, 1 }, + { 0, XK_p, 1 }, + { "[", XK_bracketleft, 1 }, + { "]", XK_bracketright, 1 }, + { "Return", XK_Return, 3 }, + { 0 }, + { 0, XK_Caps_Lock, 2 }, + { 0, XK_a, 1 }, + { 0, XK_s, 1 }, + { 0, XK_d, 1 }, + { 0, XK_f, 1 }, + { 0, XK_g, 1 }, + { 0, XK_h, 1 }, + { 0, XK_j, 1 }, + { 0, XK_k, 1 }, + { 0, XK_l, 1 }, + { ":;", XK_semicolon, 1 }, + { "'\"", XK_exclam, 1 }, + { "\\|", XK_backslash, 1 }, + { 0 }, + { 0, XK_Shift_L, 2 }, + { "<>|", XK_greater, 1 }, + { 0, XK_z, 1 }, + { 0, XK_x, 1 }, + { 0, XK_c, 1 }, + { 0, XK_v, 1 }, + { 0, XK_b, 1 }, + { 0, XK_n, 1 }, + { 0, XK_m, 1 }, + { ",", XK_colon, 1 }, + { ".", XK_period, 1 }, + { "/?", XK_slash, 1 }, + { 0, XK_Shift_R, 2 }, + { 0 }, + { "Ctrl", XK_Control_L, 2 }, + { "Alt", XK_Alt_L, 2 }, + { "", XK_space, 5 }, + { "Alt", XK_Alt_R, 2 }, + { "Ctrl", XK_Control_R, 2 }, }; diff --git a/config.h b/config.h @@ -1,14 +1,72 @@ static const char font[] = "-*-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*"; static const char normbgcolor[] = "#cccccc"; static const char normfgcolor[] = "#000000"; -static const char selbgcolor[] = "#0066ff"; -static const char selfgcolor[] = "#ffffff"; -static const char hovbgcolor[] = "#0066ff"; -static const char hovfgcolor[] = "#cccccc"; +static const char hovbgcolor[] = "#ffffff"; +static const char hovfgcolor[] = "#000000"; +static const char pressbgcolor[] = "#0000cc"; +static const char pressfgcolor[] = "#ffffff"; static Key keys[] = { - { 2, XK_Return }, - { 1, XK_space }, + { "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_plus, 1 }, + { "<-", XK_BackSpace, 2 }, { 0 }, - { 1, XK_space }, + { "->|", XK_Tab, 1 }, + { 0, XK_q, 1 }, + { 0, XK_w, 1 }, + { 0, XK_e, 1 }, + { 0, XK_r, 1 }, + { 0, XK_t, 1 }, + { 0, XK_y, 1 }, + { 0, XK_u, 1 }, + { 0, XK_i, 1 }, + { 0, XK_o, 1 }, + { 0, XK_p, 1 }, + { "[", XK_bracketleft, 1 }, + { "]", XK_bracketright, 1 }, + { "Return", XK_Return, 3 }, + { 0 }, + { 0, XK_Caps_Lock, 2 }, + { 0, XK_a, 1 }, + { 0, XK_s, 1 }, + { 0, XK_d, 1 }, + { 0, XK_f, 1 }, + { 0, XK_g, 1 }, + { 0, XK_h, 1 }, + { 0, XK_j, 1 }, + { 0, XK_k, 1 }, + { 0, XK_l, 1 }, + { ":;", XK_semicolon, 1 }, + { "'\"", XK_exclam, 1 }, + { "\\|", XK_backslash, 1 }, + { 0 }, + { 0, XK_Shift_L, 2 }, + { "<>|", XK_greater, 1 }, + { 0, XK_z, 1 }, + { 0, XK_x, 1 }, + { 0, XK_c, 1 }, + { 0, XK_v, 1 }, + { 0, XK_b, 1 }, + { 0, XK_n, 1 }, + { 0, XK_m, 1 }, + { ",", XK_colon, 1 }, + { ".", XK_period, 1 }, + { "/?", XK_slash, 1 }, + { 0, XK_Shift_R, 2 }, + { 0 }, + { "Ctrl", XK_Control_L, 2 }, + { "Alt", XK_Alt_L, 2 }, + { "", XK_space, 5 }, + { "Alt", XK_Alt_R, 2 }, + { "Ctrl", XK_Control_R, 2 }, }; diff --git a/config.mk b/config.mk @@ -12,7 +12,7 @@ X11LIB = /usr/X11R6/lib # includes and libs INCS = -I. -I/usr/include -I${X11INC} -LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 +LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lXtst # flags CPPFLAGS = -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} diff --git a/svkbd.c b/svkbd.c @@ -27,21 +27,14 @@ #include <locale.h> #include <stdarg.h> #include <stdio.h> -#include <stdlib.h> #include <string.h> -#include <unistd.h> -#include <sys/select.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <X11/cursorfont.h> +#include <stdlib.h> #include <X11/keysym.h> #include <X11/Xatom.h> #include <X11/Xlib.h> #include <X11/Xproto.h> #include <X11/Xutil.h> -#ifdef XINERAMA -#include <X11/extensions/Xinerama.h> -#endif +#include <X11/extensions/XTest.h> /* macros */ #define MAX(a, b) ((a) > (b) ? (a) : (b)) @@ -53,7 +46,6 @@ #define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define TAGMASK ((int)((1LL << LENGTH(tags)) - 1)) #define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height) -#define ISVISIBLE(x) (x->tags & tagset[seltags]) /* enums */ enum { ColFG, ColBG, ColLast }; @@ -64,7 +56,7 @@ typedef unsigned long ulong; typedef struct { ulong norm[ColLast]; - ulong sel[ColLast]; + ulong press[ColLast]; ulong hover[ColLast]; Drawable drawable; GC gc; @@ -78,21 +70,24 @@ typedef struct { } DC; /* draw context */ typedef struct { - uint width; + char *label; KeySym keysym; + uint width; int x, y, w, h; - Bool sel; + Bool pressed; } Key; /* function declarations */ static void buttonpress(XEvent *e); +static void buttonrelease(XEvent *e); static void cleanup(void); static void configurenotify(XEvent *e); static void destroynotify(XEvent *e); static void die(const char *errstr, ...); static void drawkeyboard(void); -static void drawkey(Key *k, ulong col[ColLast]); +static void drawkey(Key *k); static void expose(XEvent *e); +static Key *findkey(int x, int y); static ulong getcolor(const char *colstr); static void initfont(const char *fontstr); static void leavenotify(XEvent *e); @@ -100,12 +95,14 @@ static void motionnotify(XEvent *e); static void run(void); static void setup(void); static int textnw(const char *text, uint len); +static void updatekeys(); /* variables */ static int screen; static int wx, wy, ww, wh; static void (*handler[LASTEvent]) (XEvent *) = { [ButtonPress] = buttonpress, + [ButtonRelease] = buttonrelease, [ConfigureNotify] = configurenotify, [DestroyNotify] = destroynotify, [Expose] = expose, @@ -116,11 +113,57 @@ static Display *dpy; static DC dc; static Window root, win; static Bool running = True; +static Key *hover = NULL, *pressed = NULL; /* configuration, allows nested code to access above variables */ #include "config.h" void buttonpress(XEvent *e) { + XButtonPressedEvent *ev = &e->xbutton; + Key *k; + + if((k = findkey(ev->x, ev->y))) { + if(k->pressed && IsModifierKey(k->keysym)) { + XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, k->keysym), False, 0); + k->pressed = 0; + pressed = NULL; + } + else { + pressed = k; + k->pressed = True; + XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, k->keysym), True, 0); + } + drawkey(k); + XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, wh, 0, 0); + } +} + +void +buttonrelease(XEvent *e) { + int i; + XButtonPressedEvent *ev = &e->xbutton; + Key *k = findkey(ev->x, ev->y); + + if(pressed && k && !IsModifierKey(k->keysym)) { + if(k != pressed) { + XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, k->keysym), True, 0); + k->pressed = 1; + } + for(i = 0; i < LENGTH(keys); i++) { + if(keys[i].pressed && !IsModifierKey(keys[i].keysym)) { + XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0); + keys[i].pressed = 0; + } + } + for(i = 0; i < LENGTH(keys); i++) { + if(keys[i].pressed) { + XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0); + keys[i].pressed = 0; + } + } + pressed = NULL; + } + drawkeyboard(); } void @@ -145,7 +188,7 @@ configurenotify(XEvent *e) { wh = ev->height; XFreePixmap(dpy, dc.drawable); dc.drawable = XCreatePixmap(dpy, root, ww, wh, DefaultDepth(dpy, screen)); - drawkeyboard(); + updatekeys(); } } @@ -161,58 +204,50 @@ die(const char *errstr, ...) { void drawkeyboard(void) { - int rows, i, j; - int x = 0, y = 0, h, base; + int i; - for(i = 0, rows = 1; i < LENGTH(keys); i++) - if(keys[i].keysym == 0) - rows++; - h = wh / rows; - for(i = 0; i < LENGTH(keys); i++) { - for(j = i, base = 0; j < LENGTH(keys) && keys[j].keysym != 0; j++) - base += keys[j].width; - for(x = 0; i < LENGTH(keys) && keys[i].keysym != 0; i++) { - keys[i].x = x; - keys[i].y = y; - keys[i].w = keys[i].width * ww / base; - keys[i].h = h; - x += keys[i].w; - printf("%i %i %i %i\n", x, y, keys[i].w, h); - } - y += h; - } for(i = 0; i < LENGTH(keys); i++) { if(keys[i].keysym != 0) - drawkey(&keys[i], dc.norm); + drawkey(&keys[i]); } XSync(dpy, False); XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, wh, 0, 0); } void -drawkey(Key *k, ulong col[ColLast]) { +drawkey(Key *k) { int x, y, h, len; XRectangle r = { k->x, k->y, k->w, k->h}; - const char *text; + const char *l; + ulong *col; + if(k->pressed) + col = dc.press; + else if(hover == k) + col = dc.hover; + else + col = dc.norm; XSetForeground(dpy, dc.gc, col[ColBG]); XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); XSetForeground(dpy, dc.gc, col[ColFG]); XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); - text = XKeysymToString(k->keysym); - len = strlen(text); + if(k->label) + l = k->label; + else + l = XKeysymToString(k->keysym); + len = strlen(l); h = dc.font.ascent + dc.font.descent; y = k->y + (k->h / 2) - (h / 2) + dc.font.ascent; - x = k->x + (k->w / 2) - (textnw(text, len) / 2); + x = k->x + (k->w / 2) - (textnw(l, len) / 2); if(dc.font.set) - XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, text, len); + XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, l, len); else - XDrawString(dpy, dc.drawable, dc.gc, x, y, text, len); + XDrawString(dpy, dc.drawable, dc.gc, x, y, l, len); } void destroynotify(XEvent *e) { - + running = False; } void @@ -223,6 +258,18 @@ expose(XEvent *e) { drawkeyboard(); } +Key * +findkey(int x, int y) { + int i; + + for(i = 0; i < LENGTH(keys); i++) + if(keys[i].keysym && x > keys[i].x && + x < keys[i].x + keys[i].w && + y > keys[i].y && y < keys[i].y + keys[i].h) + return &keys[i]; + return NULL; +} + ulong getcolor(const char *colstr) { Colormap cmap = DefaultColormap(dpy, screen); @@ -275,12 +322,29 @@ initfont(const char *fontstr) { void leavenotify(XEvent *e) { + Key *h = hover; + if(!hover) + return; + hover = NULL; + drawkey(h); + XCopyArea(dpy, dc.drawable, win, dc.gc, h->x, h->y, h->w, h->h, h->x, h->y); } void motionnotify(XEvent *e) { - + XMotionEvent *ev = &e->xmotion; + Key *h = findkey(ev->x, ev->y), *oh; + + if(h != hover) { + oh = hover;; + hover = h; + if(oh) + drawkey(oh); + if(hover) + drawkey(hover); + } + XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, wh, 0, 0); } void @@ -298,6 +362,9 @@ run(void) { void setup(void) { + int i; + XWMHints *wmh; + /* init screen */ screen = DefaultScreen(dpy); root = RootWindow(dpy, screen); @@ -310,18 +377,28 @@ setup(void) { wy = DisplayHeight(dpy, screen) - wh; dc.norm[ColBG] = getcolor(normbgcolor); dc.norm[ColFG] = getcolor(normfgcolor); - dc.sel[ColBG] = getcolor(selbgcolor); - dc.sel[ColFG] = getcolor(selfgcolor); + dc.press[ColBG] = getcolor(pressbgcolor); + dc.press[ColFG] = getcolor(pressfgcolor); dc.hover[ColBG] = getcolor(hovbgcolor); dc.hover[ColFG] = getcolor(hovfgcolor); dc.drawable = XCreatePixmap(dpy, root, ww, wh, DefaultDepth(dpy, screen)); dc.gc = XCreateGC(dpy, root, 0, 0); if(!dc.font.set) XSetFont(dpy, dc.gc, dc.font.xfont->fid); + for(i = 0; i < LENGTH(keys); i++) + keys[i].pressed = 0; win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0, dc.norm[ColFG], dc.norm[ColBG]); - XSelectInput(dpy, win, StructureNotifyMask | PointerMotionMask | ExposureMask); + XSelectInput(dpy, win, StructureNotifyMask|PointerMotionMask| + ButtonReleaseMask|ButtonPressMask|ExposureMask| + LeaveWindowMask); + wmh = XAllocWMHints(); + wmh->input = False; + wmh->flags = InputHint; + XSetWMHints(dpy, win, wmh); + XFree(wmh); XMapRaised(dpy, win); + updatekeys(); drawkeyboard(); } @@ -336,6 +413,34 @@ textnw(const char *text, uint len) { return XTextWidth(dc.font.xfont, text, len); } +void +updatekeys() { + int rows, i, j; + int x = 0, y = 0, h, base; + + for(i = 0, rows = 1; i < LENGTH(keys); i++) + if(keys[i].keysym == 0) + rows++; + h = wh / rows; + for(i = 0; i < LENGTH(keys); i++, rows--) { + for(j = i, base = 0; j < LENGTH(keys) && keys[j].keysym != 0; j++) + base += keys[j].width; + for(x = 0; i < LENGTH(keys) && keys[i].keysym != 0; i++) { + keys[i].x = x; + keys[i].y = y; + keys[i].w = keys[i].width * (ww - 1) / base; + if(rows) + keys[i].h = h; + else + keys[i].h = (wh - 1) - y; + x += keys[i].w; + } + if(base != 0) + keys[i - 1].w = (ww - 1) - keys[i - 1].x; + y += h; + } +} + int main(int argc, char *argv[]) { if(argc == 2 && !strcmp("-v", argv[1]))