svkbd

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

svkbd.c (31610B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <sys/select.h>
      3 #include <sys/time.h>
      4 
      5 #include <ctype.h>
      6 #include <locale.h>
      7 #include <signal.h>
      8 #include <stdarg.h>
      9 #include <stdio.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 #include <time.h>
     13 #include <unistd.h>
     14 
     15 #include <X11/keysym.h>
     16 #include <X11/keysymdef.h>
     17 #include <X11/XF86keysym.h>
     18 #include <X11/Xatom.h>
     19 #include <X11/Xlib.h>
     20 #include <X11/Xutil.h>
     21 #include <X11/Xproto.h>
     22 #include <X11/extensions/XTest.h>
     23 #include <X11/Xft/Xft.h>
     24 #include <X11/Xresource.h>
     25 #ifdef XINERAMA
     26 #include <X11/extensions/Xinerama.h>
     27 #endif
     28 
     29 #include "drw.h"
     30 #include "util.h"
     31 
     32 /* macros */
     33 #define LENGTH(x)         (sizeof x / sizeof x[0])
     34 #define STRINGTOKEYSYM(X) (XStringToKeySym(X))
     35 #define TEXTW(X)          (drw_fontset_getwidth(drw, (X)))
     36 
     37 /* enums */
     38 enum {
     39 	SchemeNorm, SchemeNormABC, SchemeNormABCShift, SchemeNormShift, SchemePress,
     40 	SchemePressShift, SchemeHighlight, SchemeHighlightShift, SchemeOverlay,
     41 	SchemeOverlayShift, SchemeWindow, SchemeLast
     42 };
     43 enum { NetWMWindowType, NetLast };
     44 
     45 /* typedefs */
     46 typedef struct {
     47 	char *label;
     48 	char *label2;
     49 	KeySym keysym;
     50 	double width;
     51 	KeySym modifier;
     52 	int x, y, w, h;
     53 	Bool pressed;
     54 	Bool highlighted;
     55 	Bool isoverlay;
     56 } Key;
     57 
     58 typedef struct {
     59 	KeySym mod;
     60 	unsigned int button;
     61 } Buttonmod;
     62 
     63 /* function declarations */
     64 static void printdbg(const char *fmt, ...);
     65 static void motionnotify(XEvent *e);
     66 static void buttonpress(XEvent *e);
     67 static void buttonrelease(XEvent *e);
     68 static void cleanup(void);
     69 static void configurenotify(XEvent *e);
     70 static void countrows();
     71 static int countkeys(Key *layer);
     72 static void drawkeyboard(void);
     73 static void drawkey(Key *k, Bool map);
     74 static void expose(XEvent *e);
     75 static Key *findkey(int x, int y);
     76 static void leavenotify(XEvent *e);
     77 static void press(Key *k, KeySym buttonmod);
     78 static double get_press_duration();
     79 static void run(void);
     80 static void setup(void);
     81 static void simulate_keypress(KeySym keysym);
     82 static void simulate_keyrelease(KeySym keysym);
     83 static void showoverlay(int idx);
     84 static void hideoverlay();
     85 static void cyclelayer();
     86 static void setlayer();
     87 static void togglelayer();
     88 static void unpress(Key *k, KeySym buttonmod);
     89 static void updatekeys();
     90 static void printkey(Key *k, KeySym mod);
     91 
     92 /* variables */
     93 static int screen;
     94 static void (*handler[LASTEvent]) (XEvent *) = {
     95 	[ButtonPress] = buttonpress,
     96 	[ButtonRelease] = buttonrelease,
     97 	[ConfigureNotify] = configurenotify,
     98 	[Expose] = expose,
     99 	[LeaveNotify] = leavenotify,
    100 	[MotionNotify] = motionnotify
    101 };
    102 static Atom netatom[NetLast];
    103 static Display *dpy;
    104 static Drw *drw;
    105 static Window root, win;
    106 static Clr* scheme[SchemeLast];
    107 static Bool running = True, isdock = False;
    108 static struct timeval pressbegin;
    109 static int currentlayer = 0;
    110 static int enableoverlays = 1;
    111 static int currentoverlay = -1; /* -1 = no overlay */
    112 static int pressonrelease = 1;
    113 static KeySym overlaykeysym = 0; /* keysym for which the overlay is presented */
    114 static int releaseprotect = 0; /* set to 1 after overlay is shown, protecting against immediate release */
    115 static int tmp_keycode = 1;
    116 static int rows = 0, ww = 0, wh = 0, wx = 0, wy = 0;
    117 static int simulateoutput = 1; /* simulate key presses for X */
    118 static int printoutput = 0; /* print key pressed to stdout */
    119 static char *name = "svkbd";
    120 static int debug = 0;
    121 static int numlayers = 0;
    122 static int numkeys = 0;
    123 
    124 static char *colors[11][2]; /* 11 schemes, 2 colors each */
    125 static char *fonts[] = { 0 };
    126 
    127 static KeySym ispressingkeysym;
    128 
    129 Bool ispressing = False;
    130 Bool sigtermd = False;
    131 
    132 /* configuration, allows nested code to access above variables */
    133 #include "config.h"
    134 #ifndef LAYOUT
    135 #error "make sure to define LAYOUT"
    136 #endif
    137 #include LAYOUT
    138 
    139 static Key keys[KEYS];
    140 static Key *layers[LAYERS];
    141 
    142 void
    143 motionnotify(XEvent *e)
    144 {
    145 	XPointerMovedEvent *ev = &e->xmotion;
    146 	int i;
    147 	int lostfocus = -1;
    148 	int gainedfocus = -1;
    149 
    150 	for (i = 0; i < numkeys; i++) {
    151 		if (keys[i].keysym && ev->x > keys[i].x
    152 				&& ev->x < keys[i].x + keys[i].w
    153 				&& ev->y > keys[i].y
    154 				&& ev->y < keys[i].y + keys[i].h) {
    155 			if (keys[i].highlighted != True) {
    156 				if (ispressing) {
    157 					gainedfocus = i;
    158 				} else {
    159 					keys[i].highlighted = True;
    160 				}
    161 				drawkey(&keys[i], True);
    162 			}
    163 			continue;
    164 		} else if (keys[i].highlighted == True) {
    165 			keys[i].highlighted = False;
    166 			drawkey(&keys[i], True);
    167 		}
    168 	}
    169 
    170 	for (i = 0; i < numkeys; i++) {
    171 		if (!IsModifierKey(keys[i].keysym) && keys[i].pressed == True &&
    172 		    lostfocus != gainedfocus) {
    173 			printdbg("Pressed key lost focus: %ld\n", keys[i].keysym);
    174 			lostfocus = i;
    175 			ispressingkeysym = 0;
    176 			keys[i].pressed = 0;
    177 			drawkey(&keys[i], True);
    178 		}
    179 	}
    180 
    181 	if ((lostfocus != -1) && (gainedfocus != -1) && (lostfocus != gainedfocus)) {
    182 		printdbg("Clicking new key that gained focus\n");
    183 		press(&keys[gainedfocus], 0);
    184 		keys[gainedfocus].pressed = True;
    185 		keys[gainedfocus].highlighted = True;
    186 	}
    187 }
    188 
    189 void
    190 buttonpress(XEvent *e)
    191 {
    192 	XButtonPressedEvent *ev = &e->xbutton;
    193 	Key *k;
    194 	KeySym mod = 0;
    195 	int i;
    196 
    197 	ispressing = True;
    198 
    199 	if (!(k = findkey(ev->x, ev->y)))
    200 		return;
    201 
    202 	for (i = 0; i < LENGTH(buttonmods); i++) {
    203 		if (ev->button == buttonmods[i].button) {
    204 			mod = buttonmods[i].mod;
    205 			break;
    206 		}
    207 	}
    208 
    209 	if (k->modifier) {
    210 		if (mod == k->modifier)
    211 			mod = 0;
    212 		else
    213 			mod = k->modifier;
    214 	}
    215 	press(k, mod);
    216 }
    217 
    218 void
    219 buttonrelease(XEvent *e)
    220 {
    221 	XButtonPressedEvent *ev = &e->xbutton;
    222 	Key *k;
    223 	KeySym mod = 0;
    224 	int i;
    225 
    226 	ispressing = False;
    227 
    228 	for (i = 0; i < LENGTH(buttonmods); i++) {
    229 		if (ev->button == buttonmods[i].button) {
    230 			mod = buttonmods[i].mod;
    231 			break;
    232 		}
    233 	}
    234 
    235 	if (ev->x < 0 || ev->y < 0) {
    236 		unpress(NULL, mod);
    237 	} else if ((k = findkey(ev->x, ev->y))) {
    238 		if (k->modifier == mod)
    239 			unpress(k, 0);
    240 		else if (k->modifier)
    241 			unpress(k, k->modifier);
    242 		else
    243 			unpress(k, mod);
    244 	}
    245 }
    246 
    247 void
    248 cleanup(void)
    249 {
    250 	int i;
    251 
    252 	for (i = 0; i < SchemeLast; i++)
    253 		free(scheme[i]);
    254 	drw_sync(drw);
    255 	drw_free(drw);
    256 	XSync(dpy, False);
    257 	XDestroyWindow(dpy, win);
    258 	XSync(dpy, False);
    259 	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
    260 }
    261 
    262 void
    263 configurenotify(XEvent *e)
    264 {
    265 	XConfigureEvent *ev = &e->xconfigure;
    266 
    267 	if (ev->window == win && (ev->width != ww || ev->height != wh)) {
    268 		ww = ev->width;
    269 		wh = ev->height;
    270 		drw_resize(drw, ww, wh);
    271 		updatekeys();
    272 	}
    273 }
    274 
    275 void
    276 countrows(void)
    277 {
    278 	int i;
    279 
    280 	for (i = 0, rows = 1; i < numkeys; i++) {
    281 		if (keys[i].keysym == 0) {
    282 			rows++;
    283 			if ((i > 0) && (keys[i-1].keysym == 0)) {
    284 				rows--;
    285 				break;
    286 			}
    287 		}
    288 	}
    289 }
    290 
    291 int
    292 countkeys(Key *layer)
    293 {
    294 	int i, nkeys = 0;
    295 
    296 	for (i = 0; i < KEYS; i++) {
    297 		if (i > 0 && layer[i].keysym == 0 && layer[i - 1].keysym == 0) {
    298 			nkeys--;
    299 			break;
    300 		}
    301 		nkeys++;
    302 	}
    303 
    304 	return nkeys;
    305 }
    306 
    307 void
    308 drawkeyboard(void)
    309 {
    310 	int i;
    311 
    312 	drw_setscheme(drw, scheme[SchemeWindow]);
    313 	drw_rect(drw, 0, 0, ww, wh, 1, 1);
    314 	for (i = 0; i < numkeys; i++) {
    315 		if (keys[i].keysym != 0)
    316 			drawkey(&keys[i], False);
    317 	}
    318 	drw_map(drw, win, 0, 0, ww, wh);
    319 }
    320 
    321 void
    322 drawkey(Key *k, Bool map)
    323 {
    324 	int x, y, w, h;
    325 	const char *l;
    326 
    327 	int use_scheme = SchemeNorm;
    328 
    329 	if (k->pressed)
    330 		use_scheme = SchemePress;
    331 	else if (k->highlighted)
    332 		use_scheme = SchemeHighlight;
    333 	else if (k->isoverlay)
    334 		use_scheme = SchemeOverlay;
    335 	else if ((k->keysym == XK_Return) ||
    336 			((k->keysym >= XK_a) && (k->keysym <= XK_z)) ||
    337 			((k->keysym >= XK_Cyrillic_io) && (k->keysym <= XK_Cyrillic_hardsign)))
    338 		use_scheme = SchemeNormABC;
    339 	else
    340 		use_scheme = SchemeNorm;
    341 
    342 	drw_setscheme(drw, scheme[use_scheme]);
    343 	drw_rect(drw, k->x, k->y, k->w, k->h, 1, 1);
    344 
    345 	if (k->keysym == XK_KP_Insert) {
    346 		if (enableoverlays) {
    347 			l = "≅";
    348 		} else {
    349 			l = "≇";
    350 		}
    351 	} else if (k->label) {
    352 		l = k->label;
    353 	} else {
    354 		l = XKeysymToString(k->keysym);
    355 	}
    356 	h = fontsize * 2;
    357 	y = k->y + (k->h / 2) - (h / 2);
    358 	w = TEXTW(l);
    359 	x = k->x + (k->w / 2) - (w / 2);
    360 	drw_text(drw, x, y, w, h, 0, l, 0);
    361 	if (k->label2) {
    362 		if (use_scheme == SchemeNorm)
    363 			use_scheme = SchemeNormShift;
    364 		else if (use_scheme == SchemeNormABC)
    365 			use_scheme = SchemeNormABCShift;
    366 		else if (use_scheme == SchemePress)
    367 			use_scheme = SchemePressShift;
    368 		else if (use_scheme == SchemeHighlight)
    369 			use_scheme = SchemeHighlightShift;
    370 		else if (use_scheme == SchemeOverlay)
    371 			use_scheme = SchemeOverlayShift;
    372 		drw_setscheme(drw, scheme[use_scheme]);
    373 		x += w;
    374 		y -= 15;
    375 		l = k->label2;
    376 		w = TEXTW(l);
    377 		drw_text(drw, x, y, w, h, 0, l, 0);
    378 	}
    379 	if (map)
    380 		drw_map(drw, win, k->x, k->y, k->w, k->h);
    381 }
    382 
    383 void
    384 expose(XEvent *e)
    385 {
    386 	XExposeEvent *ev = &e->xexpose;
    387 
    388 	if (ev->count == 0 && (ev->window == win))
    389 		drawkeyboard();
    390 }
    391 
    392 Key *
    393 findkey(int x, int y) {
    394 	int i;
    395 
    396 	for (i = 0; i < numkeys; i++) {
    397 		if (keys[i].keysym && x > keys[i].x &&
    398 				x < keys[i].x + keys[i].w &&
    399 				y > keys[i].y && y < keys[i].y + keys[i].h) {
    400 			return &keys[i];
    401 		}
    402 	}
    403 	return NULL;
    404 }
    405 
    406 int
    407 hasoverlay(KeySym keysym)
    408 {
    409 	int begin, i;
    410 
    411 	begin = 0;
    412 	for (i = 0; i < OVERLAYS; i++) {
    413 		if (overlay[i].keysym == XK_Cancel) {
    414 			begin = i + 1;
    415 		} else if (overlay[i].keysym == keysym) {
    416 			return begin + 1;
    417 		}
    418 	}
    419 	return -1;
    420 }
    421 
    422 void
    423 leavenotify(XEvent *e)
    424 {
    425 	if (currentoverlay != -1)
    426 		hideoverlay();
    427 	ispressingkeysym = 0;
    428 	unpress(NULL, 0);
    429 }
    430 
    431 void
    432 record_press_begin(KeySym ks)
    433 {
    434 	/* record the begin of the press, don't simulate the actual keypress yet */
    435 	gettimeofday(&pressbegin, NULL);
    436 	ispressingkeysym = ks;
    437 }
    438 
    439 void
    440 press(Key *k, KeySym buttonmod)
    441 {
    442 	int i;
    443 	int overlayidx = -1;
    444 
    445 	k->pressed = !k->pressed;
    446 
    447 	printdbg("Begin click: %ld\n", k->keysym);
    448 	pressbegin.tv_sec = 0;
    449 	pressbegin.tv_usec = 0;
    450 	ispressingkeysym = 0;
    451 
    452 	if (!IsModifierKey(k->keysym)) {
    453 		if (enableoverlays && currentoverlay == -1)
    454 			overlayidx = hasoverlay(k->keysym);
    455 		if ((pressonrelease) || (enableoverlays && overlayidx != -1)) {
    456 			/* record the begin of the press, don't simulate the actual keypress yet */
    457 			record_press_begin(k->keysym);
    458 		} else {
    459 			printdbg("Simulating press: %ld (mod %ld)\n", k->keysym, buttonmod);
    460 			for (i = 0; i < numkeys; i++) {
    461 				if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
    462 					simulate_keypress(keys[i].keysym);
    463 				}
    464 			}
    465 			if (buttonmod)
    466 				simulate_keypress(buttonmod);
    467 			simulate_keypress(k->keysym);
    468 			if (printoutput)
    469 				printkey(k, buttonmod);
    470 
    471 			for (i = 0; i < numkeys; i++) {
    472 				if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
    473 					simulate_keyrelease(keys[i].keysym);
    474 				}
    475 			}
    476 		}
    477 	}
    478 	drawkey(k, True);
    479 }
    480 
    481 int
    482 tmp_remap(KeySym keysym)
    483 {
    484 	/* map lower and upper case of keysym to the temporary keycode */
    485 	KeySym syms[2];
    486 	XConvertCase(keysym, &syms[0], &syms[1]);
    487 
    488 	/* if keysym is capital letter then swap upper and lower case */
    489 	if (keysym == syms[1])
    490 		syms[1] = syms[0], syms[0] = keysym;
    491 
    492 	XChangeKeyboardMapping(dpy, tmp_keycode, syms[0] == syms[1] ? 1 : 2, syms, 1);
    493 	XSync(dpy, False);
    494 
    495 	printdbg("Temporary map keysym %ld (%ld, %ld) to keycode %d\n", keysym, syms[0], syms[1], tmp_keycode);
    496 	return tmp_keycode;
    497 }
    498 
    499 void
    500 printkey(Key *k, KeySym mod)
    501 {
    502 	int i, shift;
    503 
    504 	shift = (mod == XK_Shift_L) || (mod == XK_Shift_R) || (mod == XK_Shift_Lock);
    505 	if (!shift) {
    506 		for (i = 0; i < numkeys; i++) {
    507 			if ((keys[i].pressed) && ((keys[i].keysym == XK_Shift_L) ||
    508 			    (keys[i].keysym == XK_Shift_R) || (keys[i].keysym == XK_Shift_Lock))) {
    509 				shift = True;
    510 				break;
    511 			}
    512 		}
    513 	}
    514 	printdbg("Printing key %ld (shift=%d)\n", k->keysym, shift);
    515 	if (k->keysym == XK_Cancel)
    516 		return;
    517 
    518 	KeySym *keysym = &(k->keysym);
    519 	XIM xim = XOpenIM(dpy, 0, 0, 0);
    520 	XIC xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, NULL);
    521 
    522 	XKeyPressedEvent event;
    523 	event.type = KeyPress;
    524 	event.display = dpy;
    525 	event.state = shift ? ShiftMask : 0;
    526 	event.keycode = XKeysymToKeycode(dpy, *keysym);
    527 	if (event.keycode == 0)
    528 		event.keycode = tmp_remap(*keysym);
    529 
    530 	char buffer[32];
    531 	KeySym ignore;
    532 	Status return_status;
    533 	int l = Xutf8LookupString(xic, &event, buffer, sizeof(buffer), &ignore, &return_status);
    534 	buffer[l] = '\0';
    535 	printdbg("Print buffer: [%s] (length=%d)\n", &buffer, l);
    536 	printf("%s", buffer);
    537 
    538 	XDestroyIC(xic);
    539 	XCloseIM(xim);
    540 }
    541 
    542 void
    543 simulate_keypress(KeySym keysym)
    544 {
    545 	KeyCode code;
    546 
    547 	if (!simulateoutput)
    548 		return;
    549 
    550 	code = XKeysymToKeycode(dpy, keysym);
    551 	if (code == 0)
    552 		code = tmp_remap(keysym);
    553 	XTestFakeKeyEvent(dpy, code, True, 0);
    554 }
    555 
    556 void
    557 simulate_keyrelease(KeySym keysym)
    558 {
    559 	KeyCode code;
    560 
    561 	if (!simulateoutput)
    562 		return;
    563 
    564 	code = XKeysymToKeycode(dpy, keysym);
    565 	if (code == 0)
    566 		code = tmp_remap(keysym);
    567 	XTestFakeKeyEvent(dpy, code, False, 0);
    568 }
    569 
    570 double
    571 get_press_duration(void)
    572 {
    573 	struct timeval now;
    574 
    575 	gettimeofday(&now, NULL);
    576 
    577 	return (double) ((now.tv_sec * 1000000L + now.tv_usec) -
    578 		   (pressbegin.tv_sec * 1000000L + pressbegin.tv_usec)) /
    579 		   (double) 1000000L;
    580 }
    581 
    582 void
    583 unpress(Key *k, KeySym buttonmod)
    584 {
    585 	int i;
    586 	Bool neutralizebuttonmod = False;
    587 
    588 	if (k) {
    589 		switch(k->keysym) {
    590 		case XK_Cancel:
    591 			cyclelayer();
    592 			break;
    593 		case XK_script_switch:
    594 			togglelayer();
    595 			break;
    596 		case XK_KP_Insert:
    597 			enableoverlays = !enableoverlays;
    598 			break;
    599 		case XK_Break:
    600 			running = False;
    601 			break;
    602 		default:
    603 			break;
    604 		}
    605 	}
    606 
    607 	if ((pressbegin.tv_sec || pressbegin.tv_usec) && (enableoverlays || pressonrelease) && k && k->keysym == ispressingkeysym) {
    608 		printdbg("Delayed simulation of press after release: %ld\n", k->keysym);
    609 		/* simulate the press event, as we postponed it earlier in press() */
    610 		for (i = 0; i < numkeys; i++) {
    611 			if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
    612 				if (keys[i].keysym == buttonmod)
    613 					neutralizebuttonmod = True;
    614 				else
    615 					simulate_keypress(keys[i].keysym);
    616 			}
    617 		}
    618 		if (buttonmod && !neutralizebuttonmod) {
    619 			simulate_keypress(buttonmod);
    620 		}
    621 		simulate_keypress(k->keysym);
    622 		pressbegin.tv_sec = 0;
    623 		pressbegin.tv_usec = 0;
    624 	}
    625 
    626 	if (k)
    627 		printdbg("Simulation of release: %ld\n", k->keysym);
    628 	else
    629 		printdbg("Simulation of release (all keys)\n");
    630 
    631 	for (i = 0; i < numkeys; i++) {
    632 		if (keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
    633 			simulate_keyrelease(keys[i].keysym);
    634 			if (printoutput)
    635 				printkey(&keys[i], buttonmod);
    636 			keys[i].pressed = 0;
    637 			drawkey(&keys[i], True);
    638 		}
    639 	}
    640 
    641 	if (buttonmod && !neutralizebuttonmod) {
    642 		simulate_keyrelease(buttonmod);
    643 	}
    644 
    645 	if (k == NULL || !IsModifierKey(k->keysym)) {
    646 		for (i = 0; i < numkeys; i++) {
    647 			if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
    648 				if (!(keys[i].keysym == buttonmod && neutralizebuttonmod))
    649 					simulate_keyrelease(keys[i].keysym);
    650 				keys[i].pressed = 0;
    651 				drawkey(&keys[i], True);
    652 			}
    653 		}
    654 	}
    655 
    656 	if (enableoverlays && currentoverlay != -1 &&
    657 	    (k == NULL || !IsModifierKey(k->keysym))) {
    658 		if (releaseprotect) {
    659 			releaseprotect = 0;
    660 		} else {
    661 			hideoverlay();
    662 		}
    663 	}
    664 }
    665 
    666 void
    667 run(void)
    668 {
    669 	XEvent ev;
    670 	int xfd;
    671 	fd_set fds;
    672 	struct timeval tv;
    673 	double duration;
    674 	int overlayidx;
    675 	int i, r;
    676 
    677 	xfd = ConnectionNumber(dpy);
    678 	tv.tv_sec = 0;
    679 	tv.tv_usec = scan_rate;
    680 
    681 	XFlush(dpy);
    682 
    683 	while (running) {
    684 		usleep(100000L); /* 100ms */
    685 		FD_ZERO(&fds);
    686 		FD_SET(xfd, &fds);
    687 		r = select(xfd + 1, &fds, NULL, NULL, &tv);
    688 		if (r) {
    689 			while (XPending(dpy)) {
    690 				XNextEvent(dpy, &ev);
    691 				if (handler[ev.type]) {
    692 					(handler[ev.type])(&ev); /* call handler */
    693 				}
    694 			}
    695 		} else {
    696 			/* time-out expired without anything interesting happening, check for long-presses */
    697 			if (ispressing && ispressingkeysym) {
    698 				duration = get_press_duration();
    699 				if (debug >= 2)
    700 					printdbg("%f\n", duration);
    701 				overlayidx = hasoverlay(ispressingkeysym);
    702 				duration = get_press_duration();
    703 				if (overlayidx != -1 && duration >= overlay_delay) {
    704 					printdbg("press duration %f, activating overlay\n", duration);
    705 					showoverlay(overlayidx);
    706 					pressbegin.tv_sec = 0;
    707 					pressbegin.tv_usec = 0;
    708 					ispressingkeysym = 0;
    709 				} else if ((overlayidx == -1) && (duration >= repeat_delay)) {
    710 					printdbg("press duration %f, activating repeat\n", duration);
    711 					simulate_keyrelease(ispressingkeysym);
    712 					simulate_keypress(ispressingkeysym);
    713 					XSync(dpy, False);
    714 				}
    715 			}
    716 		}
    717 		if (r == -1 || sigtermd) {
    718 			/* an error occurred or we received a signal */
    719 			/* E.g. Generally in scripts we want to call SIGTERM on svkbd in which case
    720 					if the user is holding for example the enter key (to execute
    721 					the kill or script that does the kill), that causes an issue
    722 					since then X doesn't know the keyup is never coming.. (since
    723 					process will be dead before finger lifts - in that case we
    724 					just trigger out fake up presses for all keys */
    725 			printdbg("signal received, releasing all keys");
    726 			for (i = 0; i < numkeys; i++) {
    727 				XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0);
    728 			}
    729 			running = False;
    730 		}
    731 	}
    732 }
    733 
    734 void
    735 readxresources(void)
    736 {
    737 	XrmDatabase xdb;
    738 	XrmValue xval;
    739 	char *type, *xrm;
    740 
    741 	XrmInitialize();
    742 
    743 	if ((xrm = XResourceManagerString(drw->dpy))) {
    744 		xdb = XrmGetStringDatabase(xrm);
    745 
    746 		if (XrmGetResource(xdb, "svkbd.font", "*", &type, &xval) && !fonts[0])
    747 			fonts[0] = estrdup(xval.addr);
    748 
    749 		if (XrmGetResource(xdb, "svkbd.background", "*", &type, &xval) && !colors[SchemeNorm][ColBg])
    750 			colors[SchemeNorm][ColBg] = estrdup(xval.addr);
    751 		if (XrmGetResource(xdb, "svkbd.foreground", "*", &type, &xval) && !colors[SchemeNorm][ColFg])
    752 			colors[SchemeNorm][ColFg] = estrdup(xval.addr);
    753 
    754 		if (XrmGetResource(xdb, "svkbd.shiftforeground", "*", &type, &xval) && !colors[SchemeNormShift][ColFg])
    755 			colors[SchemeNormShift][ColFg] = estrdup(xval.addr);
    756 		if (XrmGetResource(xdb, "svkbd.shiftbackground", "*", &type, &xval) && !colors[SchemeNormShift][ColBg])
    757 			colors[SchemeNormShift][ColBg] = estrdup(xval.addr);
    758 
    759 		if (XrmGetResource(xdb, "svkbd.ABCforeground", "*", &type, &xval) && !colors[SchemeNormABC][ColFg])
    760 			colors[SchemeNormABC][ColFg] = estrdup(xval.addr);
    761 		if (XrmGetResource(xdb, "svkbd.ABCbackground", "*", &type, &xval) && !colors[SchemeNormABC][ColBg])
    762 			colors[SchemeNormABC][ColBg] = estrdup(xval.addr);
    763 
    764 		if (XrmGetResource(xdb, "svkbd.ABCshiftforeground", "*", &type, &xval) && !colors[SchemeNormABCShift][ColFg])
    765 			colors[SchemeNormABCShift][ColFg] = estrdup(xval.addr);
    766 		if (XrmGetResource(xdb, "svkbd.ABCshiftbackground", "*", &type, &xval) && !colors[SchemeNormABCShift][ColBg])
    767 			colors[SchemeNormABCShift][ColBg] = estrdup(xval.addr);
    768 
    769 		if (XrmGetResource(xdb, "svkbd.pressbackground", "*", &type, &xval) && !colors[SchemePress][ColBg])
    770 			colors[SchemePress][ColBg] = estrdup(xval.addr);
    771 		if (XrmGetResource(xdb, "svkbd.pressforeground", "*", &type, &xval) && !colors[SchemePress][ColFg])
    772 			colors[SchemePress][ColFg] = estrdup(xval.addr);
    773 
    774 		if (XrmGetResource(xdb, "svkbd.pressshiftbackground", "*", &type, &xval) && !colors[SchemePressShift][ColBg])
    775 			colors[SchemePressShift][ColBg] = estrdup(xval.addr);
    776 		if (XrmGetResource(xdb, "svkbd.pressshiftforeground", "*", &type, &xval) && !colors[SchemePressShift][ColFg])
    777 			colors[SchemePressShift][ColFg] = estrdup(xval.addr);
    778 
    779 		if (XrmGetResource(xdb, "svkbd.highlightbackground", "*", &type, &xval) && !colors[SchemeHighlight][ColBg])
    780 			colors[SchemeHighlight][ColBg] = estrdup(xval.addr);
    781 		if (XrmGetResource(xdb, "svkbd.highlightforeground", "*", &type, &xval) && !colors[SchemeHighlight][ColFg])
    782 			colors[SchemeHighlight][ColFg] = estrdup(xval.addr);
    783 
    784 		if (XrmGetResource(xdb, "svkbd.highlightshiftbackground", "*", &type, &xval) && !colors[SchemeHighlightShift][ColBg])
    785 			colors[SchemeHighlightShift][ColBg] = estrdup(xval.addr);
    786 		if (XrmGetResource(xdb, "svkbd.highlightshiftforeground", "*", &type, &xval) && !colors[SchemeHighlightShift][ColFg])
    787 			colors[SchemeHighlightShift][ColFg] = estrdup(xval.addr);
    788 
    789 		if (XrmGetResource(xdb, "svkbd.overlaybackground", "*", &type, &xval) && !colors[SchemeOverlay][ColBg])
    790 			colors[SchemeOverlay][ColBg] = estrdup(xval.addr);
    791 		if (XrmGetResource(xdb, "svkbd.overlayforeground", "*", &type, &xval) && !colors[SchemeOverlay][ColFg])
    792 			colors[SchemeOverlay][ColFg] = estrdup(xval.addr);
    793 
    794 		if (XrmGetResource(xdb, "svkbd.overlayshiftbackground", "*", &type, &xval) && !colors[SchemeOverlayShift][ColBg])
    795 			colors[SchemeOverlayShift][ColBg] = estrdup(xval.addr);
    796 		if (XrmGetResource(xdb, "svkbd.overlayshiftforeground", "*", &type, &xval) && !colors[SchemeOverlayShift][ColFg])
    797 			colors[SchemeOverlayShift][ColFg] = estrdup(xval.addr);
    798 
    799 		if (XrmGetResource(xdb, "svkbd.windowbackground", "*", &type, &xval) && !colors[SchemeWindow][ColBg])
    800 			colors[SchemeWindow][ColBg] = estrdup(xval.addr);
    801 		if (XrmGetResource(xdb, "svkbd.windowforeground", "*", &type, &xval) && !colors[SchemeWindow][ColFg])
    802 			colors[SchemeWindow][ColFg] = estrdup(xval.addr);
    803 
    804 		XrmDestroyDatabase(xdb);
    805 	}
    806 }
    807 
    808 void
    809 setup(void)
    810 {
    811 	XSetWindowAttributes wa;
    812 	XTextProperty str;
    813 	XSizeHints *sizeh = NULL;
    814 	XClassHint *ch;
    815 	XWMHints *wmh;
    816 	Atom atype = -1;
    817 	int i, j, sh, sw;
    818 
    819 #ifdef XINERAMA
    820 	XineramaScreenInfo *info = NULL;
    821 #endif
    822 
    823 	/* init screen */
    824 	screen = DefaultScreen(dpy);
    825 	root = RootWindow(dpy, screen);
    826 #ifdef XINERAMA
    827 	if (XineramaIsActive(dpy)) {
    828 		info = XineramaQueryScreens(dpy, &i);
    829 		sw = info[0].width;
    830 		sh = info[0].height;
    831 		XFree(info);
    832 	} else
    833 #endif
    834 	{
    835 		sw = DisplayWidth(dpy, screen);
    836 		sh = DisplayHeight(dpy, screen);
    837 	}
    838 	drw = drw_create(dpy, screen, root, sw, sh);
    839 
    840 	readxresources();
    841 
    842 	/* Apply defaults to font and colors*/
    843 	if (!fonts[0])
    844 		fonts[0] = estrdup(defaultfonts[0]);
    845 	for (i = 0; i < SchemeLast; ++i) {
    846 		for (j = 0; j < 2; ++j) {
    847 			if (!colors[i][j])
    848 				colors[i][j] = estrdup(defaultcolors[i][j]);
    849 		}
    850 	}
    851 
    852 	if (!drw_fontset_create(drw, (const char **) fonts, LENGTH(fonts)))
    853 		die("no fonts could be loaded");
    854 	free(fonts[0]);
    855 
    856 	drw_setscheme(drw, scheme[SchemeNorm]);
    857 
    858 	/* find an unused keycode to use as a temporary keycode (derived from source:
    859 	   https://stackoverflow.com/questions/44313966/c-xtest-emitting-key-presses-for-every-unicode-character) */
    860 	KeySym *keysyms;
    861 	int keysyms_per_keycode = 0;
    862 	int keycode_low, keycode_high;
    863 	Bool key_is_empty;
    864 	int symindex;
    865 
    866 	XDisplayKeycodes(dpy, &keycode_low, &keycode_high);
    867 	keysyms = XGetKeyboardMapping(dpy, keycode_low, keycode_high - keycode_low, &keysyms_per_keycode);
    868 	for (i = keycode_low; i <= keycode_high; i++) {
    869 		key_is_empty = True;
    870 		for (j = 0; j < keysyms_per_keycode; j++) {
    871 			symindex = (i - keycode_low) * keysyms_per_keycode + j;
    872 			if (keysyms[symindex] != 0) {
    873 				key_is_empty = False;
    874 			} else {
    875 				break;
    876 			}
    877 		}
    878 		if (key_is_empty) {
    879 			tmp_keycode = i;
    880 			break;
    881 		}
    882 	}
    883 
    884 	/* init appearance */
    885 	for (j = 0; j < SchemeLast; j++)
    886 		scheme[j] = drw_scm_create(drw, (const char **) colors[j], 2);
    887 
    888 	for (j = 0; j < SchemeLast; ++j) {
    889 		free(colors[j][ColFg]);
    890 		free(colors[j][ColBg]);
    891 	}
    892 
    893 	/* init atoms */
    894 	if (isdock) {
    895 		netatom[NetWMWindowType] = XInternAtom(dpy,
    896 				"_NET_WM_WINDOW_TYPE", False);
    897 		atype = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
    898 	}
    899 
    900 	/* init appearance */
    901 	countrows();
    902 	if (!ww)
    903 		ww = sw;
    904 	if (!wh)
    905 		wh = sh * rows / heightfactor;
    906 
    907 	if (!wx)
    908 		wx = 0;
    909 	if (wx < 0)
    910 		wx = sw + wx - ww;
    911 	if (!wy)
    912 		wy = sh - wh;
    913 	if (wy < 0)
    914 		wy = sh + wy - wh;
    915 
    916 	for (i = 0; i < numkeys; i++)
    917 		keys[i].pressed = 0;
    918 
    919 	wa.override_redirect = !wmborder;
    920 	wa.border_pixel = scheme[SchemeNorm][ColFg].pixel;
    921 	wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
    922 	win = XCreateWindow(dpy, root, wx, wy, ww, wh, 0,
    923 			CopyFromParent, CopyFromParent, CopyFromParent,
    924 			CWOverrideRedirect | CWBorderPixel |
    925 			CWBackingPixel, &wa);
    926 	XSelectInput(dpy, win, StructureNotifyMask|ButtonReleaseMask|
    927 			ButtonPressMask|ExposureMask|LeaveWindowMask|
    928 			PointerMotionMask);
    929 
    930 	wmh = XAllocWMHints();
    931 	wmh->input = False;
    932 	wmh->flags = InputHint;
    933 	if (!isdock) {
    934 		sizeh = XAllocSizeHints();
    935 		sizeh->flags = PMaxSize | PMinSize;
    936 		sizeh->min_width = sizeh->max_width = ww;
    937 		sizeh->min_height = sizeh->max_height = wh;
    938 	}
    939 	XStringListToTextProperty(&name, 1, &str);
    940 	ch = XAllocClassHint();
    941 	ch->res_class = name;
    942 	ch->res_name = name;
    943 
    944 	XSetWMProperties(dpy, win, &str, &str, NULL, 0, sizeh, wmh, ch);
    945 
    946 	XFree(keysyms);
    947 	XFree(ch);
    948 	XFree(wmh);
    949 	XFree(str.value);
    950 	if (sizeh != NULL)
    951 		XFree(sizeh);
    952 
    953 	if (isdock) {
    954 		XChangeProperty(dpy, win, netatom[NetWMWindowType], XA_ATOM,
    955 				32, PropModeReplace,
    956 				(unsigned char *)&atype, 1);
    957 	}
    958 
    959 	XMapRaised(dpy, win);
    960 	drw_resize(drw, ww, wh);
    961 	updatekeys();
    962 	drawkeyboard();
    963 }
    964 
    965 void
    966 updatekeys(void)
    967 {
    968 	int i, j;
    969 	double base;
    970 	int x, y = 0, h, r = rows;
    971 
    972 	h = (wh - 1) / rows;
    973 	for (i = 0; i < numkeys; i++, r--) {
    974 		for (j = i, base = 0; j < numkeys && keys[j].keysym != 0; j++)
    975 			base += keys[j].width;
    976 		for (x = 0; i < numkeys && keys[i].keysym != 0; i++) {
    977 			keys[i].x = x + xspacing;
    978 			keys[i].y = y + yspacing;
    979 			keys[i].w = keys[i].width * ww / base;
    980 			keys[i].h = r == 1 ? wh - y - 1 : h;
    981 			x += keys[i].w;
    982 			keys[i].w = keys[i].w - (xspacing * 2);
    983 			keys[i].h = keys[i].h - (yspacing * 2);
    984 		}
    985 		if (base != 0)
    986 			keys[i - 1].w = ww - 1 - keys[i - 1].x;
    987 		y += h;
    988 	}
    989 }
    990 
    991 void
    992 usage(char *argv0)
    993 {
    994 	fprintf(stderr, "usage: %s [-hdnovDOR] [-g geometry] [-fn font] [-l layers] [-s initial_layer]\n", argv0);
    995 	fprintf(stderr, "Options:\n");
    996 	fprintf(stderr, "  -d         - Set Dock Window Type\n");
    997 	fprintf(stderr, "  -D         - Enable debug\n");
    998 	fprintf(stderr, "  -O         - Disable overlays\n");
    999 	fprintf(stderr, "  -R         - Disable press-on-release\n");
   1000 	fprintf(stderr, "  -n         - Do not simulate key presses for X\n");
   1001 	fprintf(stderr, "  -o         - Print to standard output\n");
   1002 	fprintf(stderr, "  -l         - Comma separated list of layers to enable\n");
   1003 	fprintf(stderr, "  -s         - Layer to select on program start\n");
   1004 	fprintf(stderr, "  -H [int]   - Height fraction, one key row takes 1/x of the screen height\n");
   1005 	fprintf(stderr, "  -fn [font] - Set font (Xft, e.g: DejaVu Sans:bold:size=20)\n");
   1006 	fprintf(stderr, "  -g         - Set the window position or size using the X geometry format\n");
   1007 	exit(1);
   1008 }
   1009 
   1010 void
   1011 setlayer(void)
   1012 {
   1013 	numkeys = countkeys(layers[currentlayer]);
   1014 	memcpy(&keys, layers[currentlayer], sizeof(Key) * numkeys);
   1015 	countrows();
   1016 }
   1017 
   1018 void
   1019 cyclelayer(void)
   1020 {
   1021 	currentlayer++;
   1022 	if (currentlayer >= numlayers)
   1023 		currentlayer = 0;
   1024 	printdbg("Cycling to layer %d\n", currentlayer);
   1025 	setlayer();
   1026 	updatekeys();
   1027 	drawkeyboard();
   1028 }
   1029 
   1030 void
   1031 togglelayer(void)
   1032 {
   1033 	if (currentlayer > 0) {
   1034 		currentlayer = 0;
   1035 	} else if (numlayers > 1) {
   1036 		currentlayer = 1;
   1037 	}
   1038 	printdbg("Toggling layer %d\n", currentlayer);
   1039 	setlayer();
   1040 	updatekeys();
   1041 	drawkeyboard();
   1042 }
   1043 
   1044 void
   1045 showoverlay(int idx)
   1046 {
   1047 	int i, j;
   1048 
   1049 	printdbg("Showing overlay %d\n", idx);
   1050 
   1051 	/* unpress existing key (visually only) */
   1052 	for (i = 0; i < numkeys; i++) {
   1053 		if (keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
   1054 			keys[i].pressed = 0;
   1055 			drawkey(&keys[i], True);
   1056 			break;
   1057 		}
   1058 	}
   1059 
   1060 	for (i = idx, j = 0; i < OVERLAYS; i++, j++) {
   1061 		if (overlay[i].keysym == XK_Cancel) {
   1062 			break;
   1063 		}
   1064 		/* certain modifier keys and basic keys are excluded from being overlayed: */
   1065 		while (keys[j].keysym == 0 || keys[j].keysym == XK_Shift_L ||
   1066 				keys[j].keysym == XK_Shift_R || keys[j].keysym == XK_Control_L ||
   1067 				keys[j].keysym == XK_Control_R || keys[j].keysym == XK_Alt_L ||
   1068 				keys[j].keysym == XK_Alt_R || keys[j].keysym == XK_BackSpace ||
   1069 				keys[j].keysym == XK_Return || keys[j].keysym == XK_space ||
   1070 				keys[j].keysym == XK_Cancel)
   1071 			j++;
   1072 		if (overlay[i].width > 1)
   1073 			j += overlay[i].width - 1;
   1074 		if (j >= numkeys)
   1075 			break;
   1076 		keys[j].label = overlay[i].label;
   1077 		keys[j].label2 = overlay[i].label2;
   1078 		keys[j].keysym = overlay[i].keysym;
   1079 		keys[j].modifier = overlay[i].modifier;
   1080 		keys[j].isoverlay = True;
   1081 	}
   1082 	currentoverlay = idx;
   1083 	overlaykeysym = ispressingkeysym;
   1084 	releaseprotect = 1;
   1085 	updatekeys();
   1086 	drawkeyboard();
   1087 	XSync(dpy, False);
   1088 }
   1089 
   1090 void
   1091 hideoverlay(void)
   1092 {
   1093 	printdbg("Hiding overlay, overlay was #%d\n", currentoverlay);
   1094 	currentoverlay = -1;
   1095 	overlaykeysym = 0;
   1096 	currentlayer--;
   1097 	cyclelayer();
   1098 }
   1099 
   1100 void
   1101 sigterm(int signo)
   1102 {
   1103 	running = False;
   1104 	sigtermd = True;
   1105 	printdbg("SIGTERM received\n");
   1106 }
   1107 
   1108 void
   1109 init_layers(char *layer_names_list, const char *initial_layer_name)
   1110 {
   1111 	char *s;
   1112 	int j, found;
   1113 
   1114 	if (layer_names_list == NULL) {
   1115 		numlayers = LAYERS;
   1116 		memcpy(&layers, &available_layers, sizeof(available_layers));
   1117 		if (initial_layer_name != NULL) {
   1118 			for (j = 0; j < LAYERS; j++) {
   1119 				if (strcmp(layer_names[j], initial_layer_name) == 0) {
   1120 					currentlayer = j;
   1121 					break;
   1122 				}
   1123 			}
   1124 		}
   1125 	} else {
   1126 		s = strtok(layer_names_list, ",");
   1127 		while (s != NULL) {
   1128 			if (numlayers + 1 > LAYERS)
   1129 				die("too many layers specified");
   1130 			found = 0;
   1131 			for (j = 0; j < LAYERS; j++) {
   1132 				if (strcmp(layer_names[j], s) == 0) {
   1133 					fprintf(stderr, "Adding layer %s\n", s);
   1134 					layers[numlayers] = available_layers[j];
   1135 					if (initial_layer_name != NULL && strcmp(layer_names[j], initial_layer_name) == 0) {
   1136 						currentlayer = numlayers;
   1137 					}
   1138 					found = 1;
   1139 					break;
   1140 				}
   1141 			}
   1142 			if (!found) {
   1143 				fprintf(stderr, "Undefined layer: %s\n", s);
   1144 				exit(3);
   1145 			}
   1146 			numlayers++;
   1147 			s = strtok(NULL,",");
   1148 		}
   1149 	}
   1150 	setlayer();
   1151 }
   1152 
   1153 void
   1154 printdbg(const char *fmt, ...)
   1155 {
   1156 	if (!debug)
   1157 		return;
   1158 
   1159 	va_list ap;
   1160 	va_start(ap, fmt);
   1161 	vfprintf(stderr, fmt, ap);
   1162 	va_end(ap);
   1163 	fflush(stderr);
   1164 }
   1165 
   1166 int
   1167 main(int argc, char *argv[])
   1168 {
   1169 	char *initial_layer_name = NULL;
   1170 	char *layer_names_list = NULL;
   1171 	char *tmp;
   1172 	int i, xr, yr, bitm;
   1173 	unsigned int wr, hr;
   1174 
   1175 	signal(SIGTERM, sigterm);
   1176 
   1177 	if (OVERLAYS <= 1) {
   1178 		enableoverlays = 0;
   1179 	} else {
   1180 		if ((tmp = getenv("SVKBD_ENABLEOVERLAYS")))
   1181 			enableoverlays = atoi(tmp);
   1182 	}
   1183 	if ((tmp = getenv("SVKBD_LAYERS")))
   1184 		layer_names_list = estrdup(tmp);
   1185 
   1186 	if ((tmp = getenv("SVKBD_HEIGHTFACTOR")))
   1187 		heightfactor = atoi(tmp);
   1188 
   1189 	if ((tmp = getenv("SVKBD_PRESSONRELEASE"))) /* defaults to 1 */
   1190 		pressonrelease = atoi(tmp);
   1191 
   1192 	/* parse command line arguments */
   1193 	for (i = 1; argv[i]; i++) {
   1194 		if (!strcmp(argv[i], "-v")) {
   1195 			die("svkbd-"VERSION);
   1196 		} else if (!strcmp(argv[i], "-d")) {
   1197 			isdock = True;
   1198 		} else if (!strncmp(argv[i], "-g", 2)) {
   1199 			if (i >= argc - 1)
   1200 				usage(argv[0]);
   1201 
   1202 			bitm = XParseGeometry(argv[++i], &xr, &yr, &wr, &hr);
   1203 			if (bitm & XValue)
   1204 				wx = xr;
   1205 			if (bitm & YValue)
   1206 				wy = yr;
   1207 			if (bitm & WidthValue)
   1208 				ww = (int)wr;
   1209 			if (bitm & HeightValue)
   1210 				wh = (int)hr;
   1211 			if (bitm & XNegative && wx == 0)
   1212 				wx = -1;
   1213 			if (bitm & YNegative && wy == 0)
   1214 				wy = -1;
   1215 		} else if (!strcmp(argv[i], "-fn")) { /* font or font set */
   1216 			if (i >= argc - 1)
   1217 				usage(argv[0]);
   1218 			fonts[0] = estrdup(argv[++i]);
   1219 		} else if (!strcmp(argv[i], "-D")) {
   1220 			debug = 1;
   1221 		} else if (!strcmp(argv[i], "-h")) {
   1222 			usage(argv[0]);
   1223 		} else if (!strcmp(argv[i], "-O")) {
   1224 			enableoverlays = 0;
   1225 		} else if (!strcmp(argv[i], "-o")) {
   1226 			printoutput = 1;
   1227 		} else if (!strcmp(argv[i], "-n")) {
   1228 			simulateoutput = 0;
   1229 		} else if (!strcmp(argv[i], "-R")) {
   1230 			pressonrelease = 0;
   1231 		} else if (!strcmp(argv[i], "-l")) {
   1232 			if (i >= argc - 1)
   1233 				usage(argv[0]);
   1234 			free(layer_names_list);
   1235 			layer_names_list = estrdup(argv[++i]);
   1236 		} else if (!strcmp(argv[i], "-s")) {
   1237 			if (i >= argc - 1)
   1238 				usage(argv[0]);
   1239 			initial_layer_name = argv[++i];
   1240 		} else if (!strcmp(argv[i], "-H")) {
   1241 			if (i >= argc - 1)
   1242 				usage(argv[0]);
   1243 			heightfactor = atoi(argv[++i]);
   1244 		} else {
   1245 			fprintf(stderr, "Invalid argument: %s\n", argv[i]);
   1246 			usage(argv[0]);
   1247 		}
   1248 	}
   1249 
   1250 	if (printoutput)
   1251 		setbuf(stdout, NULL); /* unbuffered output */
   1252 
   1253 	if (heightfactor <= 0)
   1254 		die("height factor must be a positive integer");
   1255 
   1256 	init_layers(layer_names_list, initial_layer_name);
   1257 
   1258 	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
   1259 		fprintf(stderr, "warning: no locale support");
   1260 	if (!(dpy = XOpenDisplay(0)))
   1261 		die("cannot open display");
   1262 	setup();
   1263 	run();
   1264 	cleanup();
   1265 	XCloseDisplay(dpy);
   1266 	free(layer_names_list);
   1267 
   1268 	return 0;
   1269 }