tit works :) - svkbd - Simple X11 onscreen keyboard.
 (HTM) git clone git://r-36.net/svkbd
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit 1aa6bd5ebe5e58142af641c34dcb2f02420c5dbc
 (DIR) parent 5f9a5d2068f288f4f5067265bd48fe65561248f4
 (HTM) 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(-)
       ---
 (DIR) diff --git a/config.def.h b/config.def.h
       t@@ -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 },
        };
 (DIR) diff --git a/config.h b/config.h
       t@@ -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 },
        };
 (DIR) diff --git a/config.mk b/config.mk
       t@@ -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}
 (DIR) diff --git a/svkbd.c b/svkbd.c
       t@@ -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))
       t@@ -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 };
       t@@ -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;
       t@@ -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);
       t@@ -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,
       t@@ -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
       t@@ -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();
                }
        }
        
       t@@ -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
       t@@ -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);
       t@@ -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
       t@@ -298,6 +362,9 @@ run(void) {
        
        void
        setup(void) {
       +        int i;
       +        XWMHints *wmh;
       +
                /* init screen */
                screen = DefaultScreen(dpy);
                root = RootWindow(dpy, screen);
       t@@ -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();
        }
        
       t@@ -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]))