svkbd

simple virtual keyboard
git clone git://git.suckless.org/svkbd
Log | Files | Refs | README | LICENSE

svkbd.c (21680B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <sys/select.h>
      3 #include <sys/time.h>
      4 
      5 #include <locale.h>
      6 #include <signal.h>
      7 #include <stdarg.h>
      8 #include <stdio.h>
      9 #include <string.h>
     10 #include <stdlib.h>
     11 #include <time.h>
     12 #include <unistd.h>
     13 
     14 #include <X11/keysym.h>
     15 #include <X11/keysymdef.h>
     16 #include <X11/XF86keysym.h>
     17 #include <X11/Xatom.h>
     18 #include <X11/Xlib.h>
     19 #include <X11/Xutil.h>
     20 #include <X11/Xproto.h>
     21 #include <X11/extensions/XTest.h>
     22 #include <X11/Xft/Xft.h>
     23 #ifdef XINERAMA
     24 #include <X11/extensions/Xinerama.h>
     25 #endif
     26 
     27 #include "drw.h"
     28 #include "util.h"
     29 
     30 /* macros */
     31 #define LENGTH(x)         (sizeof x / sizeof x[0])
     32 #define TEXTW(X)          (drw_fontset_getwidth(drw, (X)))
     33 #define STRINGTOKEYSYM(X) (XStringToKeySym(X))
     34 
     35 /* enums */
     36 enum { SchemeNorm, SchemePress, SchemeHighlight, SchemeLast };
     37 enum { NetWMWindowType, NetLast };
     38 
     39 /* typedefs */
     40 typedef struct {
     41 	char *label;
     42 	KeySym keysym;
     43 	unsigned int width;
     44 	int x, y, w, h;
     45 	Bool pressed;
     46 	Bool highlighted;
     47 } Key;
     48 
     49 typedef struct {
     50 	KeySym mod;
     51 	unsigned int button;
     52 } Buttonmod;
     53 
     54 /* function declarations */
     55 static void printdbg(const char *fmt, ...);
     56 static void motionnotify(XEvent *e);
     57 static void buttonpress(XEvent *e);
     58 static void buttonrelease(XEvent *e);
     59 static void cleanup(void);
     60 static void configurenotify(XEvent *e);
     61 static void countrows();
     62 static int countkeys(Key *layer);
     63 static void drawkeyboard(void);
     64 static void drawkey(Key *k);
     65 static void expose(XEvent *e);
     66 static Key *findkey(int x, int y);
     67 static void leavenotify(XEvent *e);
     68 static void press(Key *k, KeySym mod);
     69 static double get_press_duration();
     70 static void run(void);
     71 static void setup(void);
     72 static void simulate_keypress(KeySym keysym);
     73 static void simulate_keyrelease(KeySym keysym);
     74 static void showoverlay(int idx);
     75 static void hideoverlay();
     76 static void cyclelayer();
     77 static void setlayer();
     78 static void togglelayer();
     79 static void unpress(Key *k, KeySym mod);
     80 static void updatekeys();
     81 
     82 /* variables */
     83 static int screen;
     84 static void (*handler[LASTEvent]) (XEvent *) = {
     85 	[ButtonPress] = buttonpress,
     86 	[ButtonRelease] = buttonrelease,
     87 	[ConfigureNotify] = configurenotify,
     88 	[Expose] = expose,
     89 	[LeaveNotify] = leavenotify,
     90 	[MotionNotify] = motionnotify
     91 };
     92 static Atom netatom[NetLast];
     93 static Display *dpy;
     94 static Drw *drw;
     95 static Window root, win;
     96 static Clr* scheme[SchemeLast];
     97 static Bool running = True, isdock = False;
     98 static KeySym pressedmod = 0;
     99 static struct timeval pressbegin;
    100 static int currentlayer = 0;
    101 static int enableoverlays = 1;
    102 static int currentoverlay = -1; /* -1 = no overlay */
    103 static KeySym overlaykeysym = 0; /* keysym for which the overlay is presented */
    104 static int releaseprotect = 0; /* set to 1 after overlay is shown, protecting against immediate release */
    105 static int tmp_keycode = 1;
    106 static int rows = 0, ww = 0, wh = 0, wx = 0, wy = 0;
    107 static char *name = "svkbd";
    108 static int debug = 0;
    109 static int numlayers = 0;
    110 static int numkeys = 0;
    111 
    112 static KeySym ispressingkeysym;
    113 
    114 Bool ispressing = False;
    115 Bool sigtermd = False;
    116 
    117 /* configuration, allows nested code to access above variables */
    118 #include "config.h"
    119 #ifndef LAYOUT
    120 #error "make sure to define LAYOUT"
    121 #endif
    122 #include LAYOUT
    123 
    124 static Key keys[KEYS] = { NULL };
    125 static Key* layers[LAYERS];
    126 
    127 void
    128 motionnotify(XEvent *e)
    129 {
    130 	XPointerMovedEvent *ev = &e->xmotion;
    131 	int i;
    132 
    133 	for (i = 0; i < numkeys; i++) {
    134 		if (keys[i].keysym && ev->x > keys[i].x
    135 				&& ev->x < keys[i].x + keys[i].w
    136 				&& ev->y > keys[i].y
    137 				&& ev->y < keys[i].y + keys[i].h) {
    138 			if (keys[i].highlighted != True) {
    139 				if (ispressing) {
    140 					keys[i].pressed = True;
    141 				} else {
    142 					keys[i].highlighted = True;
    143 				}
    144 				drawkey(&keys[i]);
    145 			}
    146 			continue;
    147 		}
    148 
    149 		if (!IsModifierKey(keys[i].keysym) && keys[i].pressed == True) {
    150 			unpress(&keys[i], 0);
    151 
    152 			drawkey(&keys[i]);
    153 		}
    154 		if (keys[i].highlighted == True) {
    155 			keys[i].highlighted = False;
    156 			drawkey(&keys[i]);
    157 		}
    158 	}
    159 }
    160 
    161 void
    162 buttonpress(XEvent *e)
    163 {
    164 	XButtonPressedEvent *ev = &e->xbutton;
    165 	Key *k;
    166 	KeySym mod = 0;
    167 	int i;
    168 
    169 	ispressing = True;
    170 
    171 	for (i = 0; i < LENGTH(buttonmods); i++) {
    172 		if (ev->button == buttonmods[i].button) {
    173 			mod = buttonmods[i].mod;
    174 			break;
    175 		}
    176 	}
    177 	if ((k = findkey(ev->x, ev->y)))
    178 		press(k, mod);
    179 }
    180 
    181 void
    182 buttonrelease(XEvent *e)
    183 {
    184 	XButtonPressedEvent *ev = &e->xbutton;
    185 	Key *k;
    186 	KeySym mod = 0;
    187 	int i;
    188 
    189 	ispressing = False;
    190 
    191 	for (i = 0; i < LENGTH(buttonmods); i++) {
    192 		if (ev->button == buttonmods[i].button) {
    193 			mod = buttonmods[i].mod;
    194 			break;
    195 		}
    196 	}
    197 
    198 	if (ev->x < 0 || ev->y < 0) {
    199 		unpress(NULL, mod);
    200 	} else {
    201 		if ((k = findkey(ev->x, ev->y)))
    202 			unpress(k, mod);
    203 	}
    204 }
    205 
    206 void
    207 cleanup(void)
    208 {
    209 	int i;
    210 
    211 	for (i = 0; i < SchemeLast; i++)
    212 		free(scheme[i]);
    213 	drw_sync(drw);
    214 	drw_free(drw);
    215 	XSync(dpy, False);
    216 	drw_free(drw);
    217 	XDestroyWindow(dpy, win);
    218 	XSync(dpy, False);
    219 	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
    220 }
    221 
    222 void
    223 configurenotify(XEvent *e)
    224 {
    225 	XConfigureEvent *ev = &e->xconfigure;
    226 
    227 	if (ev->window == win && (ev->width != ww || ev->height != wh)) {
    228 		ww = ev->width;
    229 		wh = ev->height;
    230 		drw_resize(drw, ww, wh);
    231 		updatekeys();
    232 	}
    233 }
    234 
    235 void
    236 countrows(void)
    237 {
    238 	int i;
    239 
    240 	for (i = 0, rows = 1; i < numkeys; i++) {
    241 		if (keys[i].keysym == 0)
    242 			rows++;
    243 	}
    244 }
    245 
    246 int
    247 countkeys(Key *layer)
    248 {
    249 	int i, nkeys = 0;
    250 
    251 	for (i = 0; i < KEYS; i++) {
    252 		if (i > 0 && layer[i].keysym == 0 && layer[i - 1].keysym == 0) {
    253 			nkeys--;
    254 			break;
    255 		}
    256 		nkeys++;
    257 	}
    258 
    259 	return nkeys;
    260 }
    261 
    262 void
    263 drawkeyboard(void)
    264 {
    265 	int i;
    266 
    267 	for (i = 0; i < numkeys; i++) {
    268 		if (keys[i].keysym != 0)
    269 			drawkey(&keys[i]);
    270 	}
    271 }
    272 
    273 void
    274 drawkey(Key *k)
    275 {
    276 	int x, y, w, h;
    277 	const char *l;
    278 
    279 	if (k->pressed)
    280 		drw_setscheme(drw, scheme[SchemePress]);
    281 	else if (k->highlighted)
    282 		drw_setscheme(drw, scheme[SchemeHighlight]);
    283 	else
    284 		drw_setscheme(drw, scheme[SchemeNorm]);
    285 	drw_rect(drw, k->x, k->y, k->w, k->h, 1, 1);
    286 	drw_rect(drw, k->x, k->y, k->w, k->h, 0, 0);
    287 
    288 	if (k->keysym == XK_KP_Insert) {
    289 		if (enableoverlays) {
    290 			l = "≅";
    291 		} else {
    292 			l = "≇";
    293 		}
    294 	} else if (k->label) {
    295 		l = k->label;
    296 	} else {
    297 		l = XKeysymToString(k->keysym);
    298 	}
    299 	h = fontsize * 2;
    300 	y = k->y + (k->h / 2) - (h / 2);
    301 	w = TEXTW(l);
    302 	x = k->x + (k->w / 2) - (w / 2);
    303 	drw_text(drw, x, y, w, h, 0, l, 0);
    304 	drw_map(drw, win, k->x, k->y, k->w, k->h);
    305 }
    306 
    307 void
    308 expose(XEvent *e)
    309 {
    310 	XExposeEvent *ev = &e->xexpose;
    311 
    312 	if (ev->count == 0 && (ev->window == win))
    313 		drawkeyboard();
    314 }
    315 
    316 Key *
    317 findkey(int x, int y) {
    318 	int i;
    319 
    320 	for (i = 0; i < numkeys; i++) {
    321 		if (keys[i].keysym && x > keys[i].x &&
    322 				x < keys[i].x + keys[i].w &&
    323 				y > keys[i].y && y < keys[i].y + keys[i].h) {
    324 			return &keys[i];
    325 		}
    326 	}
    327 	return NULL;
    328 }
    329 
    330 int
    331 hasoverlay(KeySym keysym)
    332 {
    333 	int begin, i;
    334 
    335 	begin = 0;
    336 	for (i = 0; i < OVERLAYS; i++) {
    337 		if (overlay[i].keysym == XK_Cancel) {
    338 			begin = i+1;
    339 		} else if (overlay[i].keysym == keysym) {
    340 			return begin+1;
    341 		}
    342 	}
    343 	return -1;
    344 }
    345 
    346 void
    347 leavenotify(XEvent *e)
    348 {
    349 	if (currentoverlay != -1)
    350 		hideoverlay();
    351 	unpress(NULL, 0);
    352 }
    353 
    354 void
    355 record_press_begin(KeySym ks)
    356 {
    357 	/* record the begin of the press, don't simulate the actual keypress yet */
    358 	gettimeofday(&pressbegin, NULL);
    359 	ispressingkeysym = ks;
    360 }
    361 
    362 void
    363 press(Key *k, KeySym mod)
    364 {
    365 	int i;
    366 	int overlayidx = -1;
    367 
    368 	k->pressed = !k->pressed;
    369 
    370 	if (debug) printdbg("Begin press: %ld\n", k->keysym);
    371 	pressbegin.tv_sec = 0;
    372 	pressbegin.tv_usec = 0;
    373 	ispressingkeysym = 0;
    374 
    375 	if (!IsModifierKey(k->keysym)) {
    376 		if (enableoverlays && currentoverlay == -1)
    377 			overlayidx = hasoverlay(k->keysym);
    378 		if (enableoverlays && overlayidx != -1) {
    379 			if (!pressbegin.tv_sec && !pressbegin.tv_usec) {
    380 				/*record the begin of the press, don't simulate the actual keypress yet */
    381 				record_press_begin(k->keysym);
    382 			}
    383 		} else {
    384 			if (debug) printdbg("Simulating press: %ld\n", k->keysym);
    385 			for (i = 0; i < numkeys; i++) {
    386 				if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
    387 					simulate_keypress(keys[i].keysym);
    388 				}
    389 			}
    390 			pressedmod = mod;
    391 			if (pressedmod) {
    392 				simulate_keypress(mod);
    393 			}
    394 			simulate_keypress(k->keysym);
    395 
    396 			for (i = 0; i < numkeys; i++) {
    397 				if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
    398 					simulate_keyrelease(keys[i].keysym);
    399 				}
    400 			}
    401 		}
    402 	}
    403 	drawkey(k);
    404 }
    405 
    406 int
    407 tmp_remap(KeySym keysym)
    408 {
    409 	XChangeKeyboardMapping(dpy, tmp_keycode, 1, &keysym, 1);
    410 	XSync(dpy, False);
    411 
    412 	return tmp_keycode;
    413 }
    414 
    415 void
    416 simulate_keypress(KeySym keysym)
    417 {
    418 	KeyCode code = XKeysymToKeycode(dpy, keysym);
    419 	if (code == 0)
    420 		code = tmp_remap(keysym);
    421 	XTestFakeKeyEvent(dpy, code, True, 0);
    422 }
    423 
    424 void
    425 simulate_keyrelease(KeySym keysym)
    426 {
    427 	KeyCode code = XKeysymToKeycode(dpy, keysym);
    428 	if (code == 0)
    429 		code = tmp_remap(keysym);
    430 	XTestFakeKeyEvent(dpy, code, False, 0);
    431 }
    432 
    433 double
    434 get_press_duration(void)
    435 {
    436 	struct timeval now;
    437 
    438 	gettimeofday(&now, NULL);
    439 
    440 	return (double) ((now.tv_sec * 1000000L + now.tv_usec) -
    441 	       (pressbegin.tv_sec * 1000000L + pressbegin.tv_usec)) /
    442 	       (double) 1000000L;
    443 }
    444 
    445 void
    446 unpress(Key *k, KeySym mod)
    447 {
    448 	int i;
    449 
    450 	if (k != NULL) {
    451 		switch(k->keysym) {
    452 		case XK_Cancel:
    453 			cyclelayer();
    454 			break;
    455 		case XK_script_switch:
    456 			togglelayer();
    457 			break;
    458 		case XK_KP_Insert:
    459 			enableoverlays = !enableoverlays;
    460 			break;
    461 		case XK_Break:
    462 			running = False;
    463 			break;
    464 		default:
    465 			break;
    466 		}
    467 	}
    468 
    469 	if ((pressbegin.tv_sec || pressbegin.tv_usec) && enableoverlays && k && k->keysym == ispressingkeysym) {
    470 		if (currentoverlay == -1) {
    471 			if (get_press_duration() < overlay_delay) {
    472 				if (debug) printdbg("Delayed simulation of press after release: %ld\n", k->keysym);
    473 				/* simulate the press event, as we postponed it earlier in press() */
    474 				for (i = 0; i < numkeys; i++) {
    475 					if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
    476 						simulate_keypress(keys[i].keysym);
    477 					}
    478 				}
    479 				pressedmod = mod;
    480 				if (pressedmod) {
    481 					simulate_keypress(mod);
    482 				}
    483 				simulate_keypress(k->keysym);
    484 				pressbegin.tv_sec = 0;
    485 				pressbegin.tv_usec = 0;
    486 			} else {
    487 				return;
    488 			}
    489 		}
    490 	}
    491 
    492 	if (debug) {
    493 		if (k) {
    494 			printdbg("Simulation of release: %ld\n", k->keysym);
    495 		} else {
    496 			printdbg("Simulation of release (all keys)\n");
    497 		}
    498 	}
    499 
    500 
    501 	for (i = 0; i < numkeys; i++) {
    502 		if (keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
    503 			simulate_keyrelease(keys[i].keysym);
    504 			keys[i].pressed = 0;
    505 			drawkey(&keys[i]);
    506 			break;
    507 		}
    508 	}
    509 	if (i != numkeys) {
    510 		if (pressedmod) {
    511 			simulate_keyrelease(mod);
    512 		}
    513 		pressedmod = 0;
    514 
    515 		for (i = 0; i < numkeys; i++) {
    516 			if (keys[i].pressed) {
    517 				simulate_keyrelease(keys[i].keysym);
    518 				keys[i].pressed = 0;
    519 				drawkey(&keys[i]);
    520 			}
    521 		}
    522 	}
    523 
    524 	if (enableoverlays && currentoverlay != -1) {
    525 		if (releaseprotect) {
    526 			releaseprotect = 0;
    527 		} else {
    528 			hideoverlay();
    529 		}
    530 	}
    531 }
    532 
    533 void
    534 run(void)
    535 {
    536 	XEvent ev;
    537 	int xfd;
    538 	fd_set fds;
    539 	struct timeval tv;
    540 	double duration = 0.0;
    541 	int i, r;
    542 
    543 	xfd = ConnectionNumber(dpy);
    544 	tv.tv_sec = 1;
    545 	tv.tv_usec = 0;
    546 
    547 	XFlush(dpy);
    548 
    549 	while (running) {
    550 		usleep(100000L); /* 100ms */
    551 		FD_ZERO(&fds);
    552 		FD_SET(xfd, &fds);
    553 		r = select(xfd + 1, &fds, NULL, NULL, &tv);
    554 		if (r) {
    555 			while (XPending(dpy)) {
    556 				XNextEvent(dpy, &ev);
    557 				if (handler[ev.type]) {
    558 					(handler[ev.type])(&ev); /* call handler */
    559 				}
    560 			}
    561 		} else {
    562 			/* time-out expired without anything interesting happening, check for long-presses */
    563 			if (ispressing && ispressingkeysym) {
    564 				duration = get_press_duration();
    565 				if (debug == 2) printdbg("%f\n", duration);
    566 				if (get_press_duration() >= overlay_delay) {
    567 					if (debug) printdbg("press duration %f\n", duration);
    568 					showoverlay(hasoverlay(ispressingkeysym));
    569 					pressbegin.tv_sec = 0;
    570 					pressbegin.tv_usec = 0;
    571 					ispressingkeysym = 0;
    572 				}
    573 			}
    574 		}
    575 		if (r == -1 || sigtermd) {
    576 			/* an error occurred or we received a signal */
    577 			/* E.g. Generally in scripts we want to call SIGTERM on svkbd in which case
    578 			        if the user is holding for example the enter key (to execute
    579 			        the kill or script that does the kill), that causes an issue
    580 			        since then X doesn't know the keyup is never coming.. (since
    581 			        process will be dead before finger lifts - in that case we
    582 			        just trigger out fake up presses for all keys */
    583 			if (debug) printdbg("signal received, releasing all keys");
    584 			for (i = 0; i < numkeys; i++) {
    585 				XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0);
    586 			}
    587 			running = False;
    588 		}
    589 	}
    590 }
    591 
    592 void
    593 setup(void)
    594 {
    595 	XSetWindowAttributes wa;
    596 	XTextProperty str;
    597 	XSizeHints *sizeh = NULL;
    598 	XClassHint *ch;
    599 	XWMHints *wmh;
    600 	Atom atype = -1;
    601 	int i, j, sh, sw;
    602 
    603 #ifdef XINERAMA
    604 	XineramaScreenInfo *info = NULL;
    605 #endif
    606 
    607 	/* init screen */
    608 	screen = DefaultScreen(dpy);
    609 	root = RootWindow(dpy, screen);
    610 #ifdef XINERAMA
    611 	if (XineramaIsActive(dpy)) {
    612 		info = XineramaQueryScreens(dpy, &i);
    613 		sw = info[0].width;
    614 		sh = info[0].height;
    615 		XFree(info);
    616 	} else
    617 #endif
    618 	{
    619 		sw = DisplayWidth(dpy, screen);
    620 		sh = DisplayHeight(dpy, screen);
    621 	}
    622 	drw = drw_create(dpy, screen, root, sw, sh);
    623 	if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
    624 		die("no fonts could be loaded");
    625 	drw_setscheme(drw, scheme[SchemeNorm]);
    626 
    627 	/* find an unused keycode to use as a temporary keycode (derived from source:
    628 	   https://stackoverflow.com/questions/44313966/c-xtest-emitting-key-presses-for-every-unicode-character) */
    629 	KeySym *keysyms;
    630 	int keysyms_per_keycode = 0;
    631 	int keycode_low, keycode_high;
    632 	Bool key_is_empty;
    633 	int symindex;
    634 
    635 	XDisplayKeycodes(dpy, &keycode_low, &keycode_high);
    636 	keysyms = XGetKeyboardMapping(dpy, keycode_low, keycode_high - keycode_low, &keysyms_per_keycode);
    637 	for (i = keycode_low; i <= keycode_high; i++) {
    638 		key_is_empty = True;
    639 		for (j = 0; j < keysyms_per_keycode; j++) {
    640 			symindex = (i - keycode_low) * keysyms_per_keycode + j;
    641 			if (keysyms[symindex] != 0) {
    642 				key_is_empty = False;
    643 			} else {
    644 				break;
    645 			}
    646 		}
    647 		if (key_is_empty) {
    648 			tmp_keycode = i;
    649 			break;
    650 		}
    651 	}
    652 
    653 	/* init appearance */
    654 	for (j = 0; j < SchemeLast; j++)
    655 		scheme[j] = drw_scm_create(drw, colors[j], 2);
    656 
    657 	/* init atoms */
    658 	if (isdock) {
    659 		netatom[NetWMWindowType] = XInternAtom(dpy,
    660 				"_NET_WM_WINDOW_TYPE", False);
    661 		atype = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
    662 	}
    663 
    664 	/* init appearance */
    665 	countrows();
    666 	if (!ww)
    667 		ww = sw;
    668 	if (!wh)
    669 		wh = sh * rows / heightfactor;
    670 
    671 	if (!wx)
    672 		wx = 0;
    673 	if (wx < 0)
    674 		wx = sw + wx - ww;
    675 	if (!wy)
    676 		wy = sh - wh;
    677 	if (wy < 0)
    678 		wy = sh + wy - wh;
    679 
    680 	for (i = 0; i < numkeys; i++)
    681 		keys[i].pressed = 0;
    682 
    683 	wa.override_redirect = !wmborder;
    684 	wa.border_pixel = scheme[SchemeNorm][ColFg].pixel;
    685 	wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
    686 	win = XCreateWindow(dpy, root, wx, wy, ww, wh, 0,
    687 			CopyFromParent, CopyFromParent, CopyFromParent,
    688 			CWOverrideRedirect | CWBorderPixel |
    689 			CWBackingPixel, &wa);
    690 	XSelectInput(dpy, win, StructureNotifyMask|ButtonReleaseMask|
    691 			ButtonPressMask|ExposureMask|LeaveWindowMask|
    692 			PointerMotionMask);
    693 
    694 	wmh = XAllocWMHints();
    695 	wmh->input = False;
    696 	wmh->flags = InputHint;
    697 	if (!isdock) {
    698 		sizeh = XAllocSizeHints();
    699 		sizeh->flags = PMaxSize | PMinSize;
    700 		sizeh->min_width = sizeh->max_width = ww;
    701 		sizeh->min_height = sizeh->max_height = wh;
    702 	}
    703 	XStringListToTextProperty(&name, 1, &str);
    704 	ch = XAllocClassHint();
    705 	ch->res_class = name;
    706 	ch->res_name = name;
    707 
    708 	XSetWMProperties(dpy, win, &str, &str, NULL, 0, sizeh, wmh,
    709 			ch);
    710 
    711 	XFree(keysyms);
    712 	XFree(ch);
    713 	XFree(wmh);
    714 	XFree(str.value);
    715 	if (sizeh != NULL)
    716 		XFree(sizeh);
    717 
    718 	if (isdock) {
    719 		XChangeProperty(dpy, win, netatom[NetWMWindowType], XA_ATOM,
    720 				32, PropModeReplace,
    721 				(unsigned char *)&atype, 1);
    722 	}
    723 
    724 	XMapRaised(dpy, win);
    725 	drw_resize(drw, ww, wh);
    726 	updatekeys();
    727 	drawkeyboard();
    728 }
    729 
    730 void
    731 updatekeys(void)
    732 {
    733 	int i, j;
    734 	int x = 0, y = 0, h, base, r = rows;
    735 
    736 	h = (wh - 1) / rows;
    737 	for (i = 0; i < numkeys; i++, r--) {
    738 		for (j = i, base = 0; j < numkeys && keys[j].keysym != 0; j++)
    739 			base += keys[j].width;
    740 		for (x = 0; i < numkeys && keys[i].keysym != 0; i++) {
    741 			keys[i].x = x;
    742 			keys[i].y = y;
    743 			keys[i].w = keys[i].width * (ww - 1) / base;
    744 			keys[i].h = r == 1 ? wh - y - 1 : h;
    745 			x += keys[i].w;
    746 		}
    747 		if (base != 0)
    748 			keys[i - 1].w = ww - 1 - keys[i - 1].x;
    749 		y += h;
    750 	}
    751 }
    752 
    753 void
    754 usage(char *argv0)
    755 {
    756 	fprintf(stderr, "usage: %s [-hdvDO] [-g geometry] [-fn font] [-l layers] [-s initial_layer]\n", argv0);
    757 	fprintf(stderr, "Options:\n");
    758 	fprintf(stderr, "  -d         - Set Dock Window Type\n");
    759 	fprintf(stderr, "  -D         - Enable debug\n");
    760 	fprintf(stderr, "  -O         - Disable overlays\n");
    761 	fprintf(stderr, "  -l         - Comma separated list of layers to enable\n");
    762 	fprintf(stderr, "  -s         - Layer to select on program start\n");
    763 	fprintf(stderr, "  -H [int]   - Height fraction, one key row takes 1/x of the screen height");
    764 	fprintf(stderr, "  -fn [font] - Set font (Xft, e.g: DejaVu Sans:bold:size=20)\n");
    765 	exit(1);
    766 }
    767 
    768 void
    769 setlayer(void)
    770 {
    771 	numkeys = countkeys(layers[currentlayer]);
    772 	memcpy(&keys, layers[currentlayer], sizeof(Key) * numkeys);
    773 }
    774 
    775 void
    776 cyclelayer(void)
    777 {
    778 	currentlayer++;
    779 	if (currentlayer >= numlayers)
    780 		currentlayer = 0;
    781 	if (debug) printdbg("Cycling to layer %d\n", currentlayer);
    782 	setlayer();
    783 	updatekeys();
    784 	drawkeyboard();
    785 }
    786 
    787 void
    788 togglelayer(void)
    789 {
    790 	if (currentlayer > 0) {
    791 		currentlayer = 0;
    792 	} else if (numlayers > 1) {
    793 		currentlayer = 1;
    794 	}
    795 	if (debug) printdbg("Toggling layer %d\n", currentlayer);
    796 	setlayer();
    797 	updatekeys();
    798 	drawkeyboard();
    799 }
    800 
    801 void
    802 showoverlay(int idx)
    803 {
    804 	if (debug) printdbg("Showing overlay %d\n", idx);
    805 	int i,j;
    806 
    807 	/* unpress existing key (visually only) */
    808 	for (i = 0; i < numkeys; i++) {
    809 		if (keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
    810 			keys[i].pressed = 0;
    811 			drawkey(&keys[i]);
    812 			break;
    813 		}
    814 	}
    815 
    816 	for (i = idx, j=0; i < OVERLAYS; i++, j++) {
    817 		if (overlay[i].keysym == XK_Cancel)
    818 			break;
    819 		while (keys[j].keysym == 0)
    820 			j++;
    821 		keys[j].label = overlay[i].label;
    822 		keys[j].keysym = overlay[i].keysym;
    823 	}
    824 	currentoverlay = idx;
    825 	overlaykeysym = ispressingkeysym;
    826 	releaseprotect = 1;
    827 	updatekeys();
    828 	drawkeyboard();
    829 	XSync(dpy, False);
    830 }
    831 
    832 void
    833 hideoverlay(void)
    834 {
    835 	if (debug) printdbg("Hiding overlay, overlay was #%d\n", currentoverlay);
    836 	currentoverlay = -1;
    837 	overlaykeysym = 0;
    838 	currentlayer = -1;
    839 	cyclelayer();
    840 }
    841 
    842 void
    843 sigterm(int signo)
    844 {
    845 	running = False;
    846 	sigtermd = True;
    847 	if (debug) printdbg("SIGTERM received\n");
    848 }
    849 
    850 void
    851 init_layers(char *layer_names_list, const char *initial_layer_name)
    852 {
    853 	char *s;
    854 	int j, found;
    855 
    856 	if (layer_names_list == NULL) {
    857 		numlayers = LAYERS;
    858 		memcpy(&layers, &available_layers, sizeof(available_layers));
    859 		if (initial_layer_name != NULL) {
    860 			for (j = 0; j < LAYERS; j++) {
    861 				if (strcmp(layer_names[j], initial_layer_name) == 0) {
    862 					currentlayer = j;
    863 					break;
    864 				}
    865 			}
    866 		}
    867 	} else {
    868 		s = strtok(layer_names_list, ",");
    869 		while (s != NULL) {
    870 			if (numlayers+1 > LAYERS)
    871 				die("too many layers specified");
    872 			found = 0;
    873 			for (j = 0; j < LAYERS; j++) {
    874 				if (strcmp(layer_names[j], s) == 0) {
    875 					fprintf(stderr, "Adding layer %s\n", s);
    876 					layers[numlayers] = available_layers[j];
    877 					if (initial_layer_name != NULL && strcmp(layer_names[j], initial_layer_name) == 0) {
    878 						currentlayer = numlayers;
    879 					}
    880 					found = 1;
    881 					break;
    882 				}
    883 			}
    884 			if (!found) {
    885 				fprintf(stderr, "Undefined layer: %s\n", s);
    886 				exit(3);
    887 			}
    888 			numlayers++;
    889 			s = strtok(NULL,",");
    890 		}
    891 	}
    892 	setlayer();
    893 }
    894 
    895 void
    896 printdbg(const char *fmt, ...)
    897 {
    898 	va_list ap;
    899 	va_start(ap, fmt);
    900 	vfprintf(stderr, fmt, ap);
    901 	va_end(ap);
    902 	fflush(stderr);
    903 }
    904 
    905 int
    906 main(int argc, char *argv[])
    907 {
    908 	char *initial_layer_name = NULL;
    909 	char *layer_names_list = NULL;
    910 	char *tmp;
    911 	int i, xr, yr, bitm;
    912 	unsigned int wr, hr;
    913 
    914 	signal(SIGTERM, sigterm);
    915 
    916 	if (OVERLAYS <= 1) {
    917 		enableoverlays = 0;
    918 	} else {
    919 		if ((tmp = getenv("SVKBD_ENABLEOVERLAYS")))
    920 			enableoverlays = atoi(tmp);
    921 	}
    922 	if ((tmp = getenv("SVKBD_LAYERS"))) {
    923 		if (!(layer_names_list = strdup(tmp)))
    924 			die("memory allocation error");
    925 	}
    926 
    927 	if ((tmp = getenv("SVKBD_HEIGHTFACTOR")))
    928 		heightfactor = atoi(tmp);
    929 
    930 	/* parse command line arguments */
    931 	for (i = 1; argv[i]; i++) {
    932 		if (!strcmp(argv[i], "-v")) {
    933 			die("svkbd-"VERSION);
    934 		} else if (!strcmp(argv[i], "-d")) {
    935 			isdock = True;
    936 			continue;
    937 		} else if (!strncmp(argv[i], "-g", 2)) {
    938 			if (i >= argc - 1)
    939 				continue;
    940 
    941 			bitm = XParseGeometry(argv[i + 1], &xr, &yr, &wr, &hr);
    942 			if (bitm & XValue)
    943 				wx = xr;
    944 			if (bitm & YValue)
    945 				wy = yr;
    946 			if (bitm & WidthValue)
    947 				ww = (int)wr;
    948 			if (bitm & HeightValue)
    949 				wh = (int)hr;
    950 			if (bitm & XNegative && wx == 0)
    951 				wx = -1;
    952 			if (bitm & YNegative && wy == 0)
    953 				wy = -1;
    954 			i++;
    955 		} else if (!strcmp(argv[i], "-fn")) { /* font or font set */
    956 			fonts[0] = argv[++i];
    957 		} else if (!strcmp(argv[i], "-D")) {
    958 			debug = 1;
    959 		} else if (!strcmp(argv[i], "-h")) {
    960 			usage(argv[0]);
    961 		} else if (!strcmp(argv[i], "-O")) {
    962 			enableoverlays = 0;
    963 		} else if (!strcmp(argv[i], "-l")) {
    964 			if (i >= argc - 1)
    965 				continue;
    966 			free(layer_names_list);
    967 			if (!(layer_names_list = strdup(argv[++i])))
    968 				die("memory allocation error");
    969 		} else if (!strcmp(argv[i], "-s")) {
    970 			if (i >= argc - 1)
    971 				continue;
    972 			initial_layer_name = argv[++i];
    973 		} else if (!strcmp(argv[i], "-H")) {
    974 			if (i >= argc - 1)
    975 				continue;
    976 			heightfactor = atoi(argv[++i]);
    977 		} else {
    978 			fprintf(stderr, "Invalid argument: %s\n", argv[i]);
    979 			exit(2);
    980 		}
    981 	}
    982 
    983 	if (heightfactor <= 0)
    984 		die("height factor must be a positive integer");
    985 
    986 	init_layers(layer_names_list, initial_layer_name);
    987 
    988 	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
    989 		fprintf(stderr, "warning: no locale support");
    990 	if (!(dpy = XOpenDisplay(0)))
    991 		die("cannot open display");
    992 	setup();
    993 	run();
    994 	cleanup();
    995 	XCloseDisplay(dpy);
    996 	free(layer_names_list);
    997 
    998 	return 0;
    999 }