svkbd

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

svkbd.c (31225B)


      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 	XChangeKeyboardMapping(dpy, tmp_keycode, 1, &keysym, 1);
    485 	XSync(dpy, False);
    486 
    487 	return tmp_keycode;
    488 }
    489 
    490 void
    491 printkey(Key *k, KeySym mod)
    492 {
    493 	int i, shift;
    494 
    495 	shift = (mod == XK_Shift_L) || (mod == XK_Shift_R) || (mod == XK_Shift_Lock);
    496 	if (!shift) {
    497 		for (i = 0; i < numkeys; i++) {
    498 			if ((keys[i].pressed) && ((keys[i].keysym == XK_Shift_L) ||
    499 			    (keys[i].keysym == XK_Shift_R) || (keys[i].keysym == XK_Shift_Lock))) {
    500 				shift = True;
    501 				break;
    502 			}
    503 		}
    504 	}
    505 	printdbg("Printing key %ld (shift=%d)\n", k->keysym, shift);
    506 	if (k->keysym == XK_Cancel)
    507 		return;
    508 
    509 	KeySym *keysym = &(k->keysym);
    510 	XIM xim = XOpenIM(dpy, 0, 0, 0);
    511 	XIC xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, NULL);
    512 
    513 	XKeyPressedEvent event;
    514 	event.type = KeyPress;
    515 	event.display = dpy;
    516 	event.state = shift ? ShiftMask : 0;
    517 	event.keycode = XKeysymToKeycode(dpy, *keysym);
    518 	if (event.keycode == 0)
    519 		event.keycode = tmp_remap(*keysym);
    520 
    521 	char buffer[32];
    522 	KeySym ignore;
    523 	Status return_status;
    524 	int l = Xutf8LookupString(xic, &event, buffer, sizeof(buffer), &ignore, &return_status);
    525 	buffer[l] = '\0';
    526 	printdbg("Print buffer: [%s] (length=%d)\n", &buffer, l);
    527 	printf("%s", buffer);
    528 
    529 	XDestroyIC(xic);
    530 	XCloseIM(xim);
    531 }
    532 
    533 void
    534 simulate_keypress(KeySym keysym)
    535 {
    536 	KeyCode code;
    537 
    538 	if (!simulateoutput)
    539 		return;
    540 
    541 	code = XKeysymToKeycode(dpy, keysym);
    542 	if (code == 0)
    543 		code = tmp_remap(keysym);
    544 	XTestFakeKeyEvent(dpy, code, True, 0);
    545 }
    546 
    547 void
    548 simulate_keyrelease(KeySym keysym)
    549 {
    550 	KeyCode code;
    551 
    552 	if (!simulateoutput)
    553 		return;
    554 
    555 	code = XKeysymToKeycode(dpy, keysym);
    556 	if (code == 0)
    557 		code = tmp_remap(keysym);
    558 	XTestFakeKeyEvent(dpy, code, False, 0);
    559 }
    560 
    561 double
    562 get_press_duration(void)
    563 {
    564 	struct timeval now;
    565 
    566 	gettimeofday(&now, NULL);
    567 
    568 	return (double) ((now.tv_sec * 1000000L + now.tv_usec) -
    569 		   (pressbegin.tv_sec * 1000000L + pressbegin.tv_usec)) /
    570 		   (double) 1000000L;
    571 }
    572 
    573 void
    574 unpress(Key *k, KeySym buttonmod)
    575 {
    576 	int i;
    577 	Bool neutralizebuttonmod = False;
    578 
    579 	if (k) {
    580 		switch(k->keysym) {
    581 		case XK_Cancel:
    582 			cyclelayer();
    583 			break;
    584 		case XK_script_switch:
    585 			togglelayer();
    586 			break;
    587 		case XK_KP_Insert:
    588 			enableoverlays = !enableoverlays;
    589 			break;
    590 		case XK_Break:
    591 			running = False;
    592 			break;
    593 		default:
    594 			break;
    595 		}
    596 	}
    597 
    598 	if ((pressbegin.tv_sec || pressbegin.tv_usec) && (enableoverlays || pressonrelease) && k && k->keysym == ispressingkeysym) {
    599 		printdbg("Delayed simulation of press after release: %ld\n", k->keysym);
    600 		/* simulate the press event, as we postponed it earlier in press() */
    601 		for (i = 0; i < numkeys; i++) {
    602 			if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
    603 				if (keys[i].keysym == buttonmod)
    604 					neutralizebuttonmod = True;
    605 				else
    606 					simulate_keypress(keys[i].keysym);
    607 			}
    608 		}
    609 		if (buttonmod && !neutralizebuttonmod) {
    610 			simulate_keypress(buttonmod);
    611 		}
    612 		simulate_keypress(k->keysym);
    613 		pressbegin.tv_sec = 0;
    614 		pressbegin.tv_usec = 0;
    615 	}
    616 
    617 	if (k)
    618 		printdbg("Simulation of release: %ld\n", k->keysym);
    619 	else
    620 		printdbg("Simulation of release (all keys)\n");
    621 
    622 	for (i = 0; i < numkeys; i++) {
    623 		if (keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
    624 			simulate_keyrelease(keys[i].keysym);
    625 			if (printoutput)
    626 				printkey(&keys[i], buttonmod);
    627 			keys[i].pressed = 0;
    628 			drawkey(&keys[i], True);
    629 		}
    630 	}
    631 
    632 	if (buttonmod && !neutralizebuttonmod) {
    633 		simulate_keyrelease(buttonmod);
    634 	}
    635 
    636 	if (k == NULL || !IsModifierKey(k->keysym)) {
    637 		for (i = 0; i < numkeys; i++) {
    638 			if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
    639 				if (!(keys[i].keysym == buttonmod && neutralizebuttonmod))
    640 					simulate_keyrelease(keys[i].keysym);
    641 				keys[i].pressed = 0;
    642 				drawkey(&keys[i], True);
    643 			}
    644 		}
    645 	}
    646 
    647 	if (enableoverlays && currentoverlay != -1 &&
    648 	    (k == NULL || !IsModifierKey(k->keysym))) {
    649 		if (releaseprotect) {
    650 			releaseprotect = 0;
    651 		} else {
    652 			hideoverlay();
    653 		}
    654 	}
    655 }
    656 
    657 void
    658 run(void)
    659 {
    660 	XEvent ev;
    661 	int xfd;
    662 	fd_set fds;
    663 	struct timeval tv;
    664 	double duration;
    665 	int overlayidx;
    666 	int i, r;
    667 
    668 	xfd = ConnectionNumber(dpy);
    669 	tv.tv_sec = 0;
    670 	tv.tv_usec = scan_rate;
    671 
    672 	XFlush(dpy);
    673 
    674 	while (running) {
    675 		usleep(100000L); /* 100ms */
    676 		FD_ZERO(&fds);
    677 		FD_SET(xfd, &fds);
    678 		r = select(xfd + 1, &fds, NULL, NULL, &tv);
    679 		if (r) {
    680 			while (XPending(dpy)) {
    681 				XNextEvent(dpy, &ev);
    682 				if (handler[ev.type]) {
    683 					(handler[ev.type])(&ev); /* call handler */
    684 				}
    685 			}
    686 		} else {
    687 			/* time-out expired without anything interesting happening, check for long-presses */
    688 			if (ispressing && ispressingkeysym) {
    689 				duration = get_press_duration();
    690 				if (debug >= 2)
    691 					printdbg("%f\n", duration);
    692 				overlayidx = hasoverlay(ispressingkeysym);
    693 				duration = get_press_duration();
    694 				if (overlayidx != -1 && duration >= overlay_delay) {
    695 					printdbg("press duration %f, activating overlay\n", duration);
    696 					showoverlay(overlayidx);
    697 					pressbegin.tv_sec = 0;
    698 					pressbegin.tv_usec = 0;
    699 					ispressingkeysym = 0;
    700 				} else if ((overlayidx == -1) && (duration >= repeat_delay)) {
    701 					printdbg("press duration %f, activating repeat\n", duration);
    702 					simulate_keyrelease(ispressingkeysym);
    703 					simulate_keypress(ispressingkeysym);
    704 					XSync(dpy, False);
    705 				}
    706 			}
    707 		}
    708 		if (r == -1 || sigtermd) {
    709 			/* an error occurred or we received a signal */
    710 			/* E.g. Generally in scripts we want to call SIGTERM on svkbd in which case
    711 					if the user is holding for example the enter key (to execute
    712 					the kill or script that does the kill), that causes an issue
    713 					since then X doesn't know the keyup is never coming.. (since
    714 					process will be dead before finger lifts - in that case we
    715 					just trigger out fake up presses for all keys */
    716 			printdbg("signal received, releasing all keys");
    717 			for (i = 0; i < numkeys; i++) {
    718 				XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0);
    719 			}
    720 			running = False;
    721 		}
    722 	}
    723 }
    724 
    725 void
    726 readxresources(void)
    727 {
    728 	XrmDatabase xdb;
    729 	XrmValue xval;
    730 	char *type, *xrm;
    731 
    732 	XrmInitialize();
    733 
    734 	if ((xrm = XResourceManagerString(drw->dpy))) {
    735 		xdb = XrmGetStringDatabase(xrm);
    736 
    737 		if (XrmGetResource(xdb, "svkbd.font", "*", &type, &xval) && !fonts[0])
    738 			fonts[0] = estrdup(xval.addr);
    739 
    740 		if (XrmGetResource(xdb, "svkbd.background", "*", &type, &xval) && !colors[SchemeNorm][ColBg])
    741 			colors[SchemeNorm][ColBg] = estrdup(xval.addr);
    742 		if (XrmGetResource(xdb, "svkbd.foreground", "*", &type, &xval) && !colors[SchemeNorm][ColFg])
    743 			colors[SchemeNorm][ColFg] = estrdup(xval.addr);
    744 
    745 		if (XrmGetResource(xdb, "svkbd.shiftforeground", "*", &type, &xval) && !colors[SchemeNormShift][ColFg])
    746 			colors[SchemeNormShift][ColFg] = estrdup(xval.addr);
    747 		if (XrmGetResource(xdb, "svkbd.shiftbackground", "*", &type, &xval) && !colors[SchemeNormShift][ColBg])
    748 			colors[SchemeNormShift][ColBg] = estrdup(xval.addr);
    749 
    750 		if (XrmGetResource(xdb, "svkbd.ABCforeground", "*", &type, &xval) && !colors[SchemeNormABC][ColFg])
    751 			colors[SchemeNormABC][ColFg] = estrdup(xval.addr);
    752 		if (XrmGetResource(xdb, "svkbd.ABCbackground", "*", &type, &xval) && !colors[SchemeNormABC][ColBg])
    753 			colors[SchemeNormABC][ColBg] = estrdup(xval.addr);
    754 
    755 		if (XrmGetResource(xdb, "svkbd.ABCshiftforeground", "*", &type, &xval) && !colors[SchemeNormABCShift][ColFg])
    756 			colors[SchemeNormABCShift][ColFg] = estrdup(xval.addr);
    757 		if (XrmGetResource(xdb, "svkbd.ABCshiftbackground", "*", &type, &xval) && !colors[SchemeNormABCShift][ColBg])
    758 			colors[SchemeNormABCShift][ColBg] = estrdup(xval.addr);
    759 
    760 		if (XrmGetResource(xdb, "svkbd.pressbackground", "*", &type, &xval) && !colors[SchemePress][ColBg])
    761 			colors[SchemePress][ColBg] = estrdup(xval.addr);
    762 		if (XrmGetResource(xdb, "svkbd.pressforeground", "*", &type, &xval) && !colors[SchemePress][ColFg])
    763 			colors[SchemePress][ColFg] = estrdup(xval.addr);
    764 
    765 		if (XrmGetResource(xdb, "svkbd.pressshiftbackground", "*", &type, &xval) && !colors[SchemePressShift][ColBg])
    766 			colors[SchemePressShift][ColBg] = estrdup(xval.addr);
    767 		if (XrmGetResource(xdb, "svkbd.pressshiftforeground", "*", &type, &xval) && !colors[SchemePressShift][ColFg])
    768 			colors[SchemePressShift][ColFg] = estrdup(xval.addr);
    769 
    770 		if (XrmGetResource(xdb, "svkbd.highlightbackground", "*", &type, &xval) && !colors[SchemeHighlight][ColBg])
    771 			colors[SchemeHighlight][ColBg] = estrdup(xval.addr);
    772 		if (XrmGetResource(xdb, "svkbd.highlightforeground", "*", &type, &xval) && !colors[SchemeHighlight][ColFg])
    773 			colors[SchemeHighlight][ColFg] = estrdup(xval.addr);
    774 
    775 		if (XrmGetResource(xdb, "svkbd.highlightshiftbackground", "*", &type, &xval) && !colors[SchemeHighlightShift][ColBg])
    776 			colors[SchemeHighlightShift][ColBg] = estrdup(xval.addr);
    777 		if (XrmGetResource(xdb, "svkbd.highlightshiftforeground", "*", &type, &xval) && !colors[SchemeHighlightShift][ColFg])
    778 			colors[SchemeHighlightShift][ColFg] = estrdup(xval.addr);
    779 
    780 		if (XrmGetResource(xdb, "svkbd.overlaybackground", "*", &type, &xval) && !colors[SchemeOverlay][ColBg])
    781 			colors[SchemeOverlay][ColBg] = estrdup(xval.addr);
    782 		if (XrmGetResource(xdb, "svkbd.overlayforeground", "*", &type, &xval) && !colors[SchemeOverlay][ColFg])
    783 			colors[SchemeOverlay][ColFg] = estrdup(xval.addr);
    784 
    785 		if (XrmGetResource(xdb, "svkbd.overlayshiftbackground", "*", &type, &xval) && !colors[SchemeOverlayShift][ColBg])
    786 			colors[SchemeOverlayShift][ColBg] = estrdup(xval.addr);
    787 		if (XrmGetResource(xdb, "svkbd.overlayshiftforeground", "*", &type, &xval) && !colors[SchemeOverlayShift][ColFg])
    788 			colors[SchemeOverlayShift][ColFg] = estrdup(xval.addr);
    789 
    790 		if (XrmGetResource(xdb, "svkbd.windowbackground", "*", &type, &xval) && !colors[SchemeWindow][ColBg])
    791 			colors[SchemeWindow][ColBg] = estrdup(xval.addr);
    792 		if (XrmGetResource(xdb, "svkbd.windowforeground", "*", &type, &xval) && !colors[SchemeWindow][ColFg])
    793 			colors[SchemeWindow][ColFg] = estrdup(xval.addr);
    794 
    795 		XrmDestroyDatabase(xdb);
    796 	}
    797 }
    798 
    799 void
    800 setup(void)
    801 {
    802 	XSetWindowAttributes wa;
    803 	XTextProperty str;
    804 	XSizeHints *sizeh = NULL;
    805 	XClassHint *ch;
    806 	XWMHints *wmh;
    807 	Atom atype = -1;
    808 	int i, j, sh, sw;
    809 
    810 #ifdef XINERAMA
    811 	XineramaScreenInfo *info = NULL;
    812 #endif
    813 
    814 	/* init screen */
    815 	screen = DefaultScreen(dpy);
    816 	root = RootWindow(dpy, screen);
    817 #ifdef XINERAMA
    818 	if (XineramaIsActive(dpy)) {
    819 		info = XineramaQueryScreens(dpy, &i);
    820 		sw = info[0].width;
    821 		sh = info[0].height;
    822 		XFree(info);
    823 	} else
    824 #endif
    825 	{
    826 		sw = DisplayWidth(dpy, screen);
    827 		sh = DisplayHeight(dpy, screen);
    828 	}
    829 	drw = drw_create(dpy, screen, root, sw, sh);
    830 
    831 	readxresources();
    832 
    833 	/* Apply defaults to font and colors*/
    834 	if (!fonts[0])
    835 		fonts[0] = estrdup(defaultfonts[0]);
    836 	for (i = 0; i < SchemeLast; ++i) {
    837 		for (j = 0; j < 2; ++j) {
    838 			if (!colors[i][j])
    839 				colors[i][j] = estrdup(defaultcolors[i][j]);
    840 		}
    841 	}
    842 
    843 	if (!drw_fontset_create(drw, (const char **) fonts, LENGTH(fonts)))
    844 		die("no fonts could be loaded");
    845 	free(fonts[0]);
    846 
    847 	drw_setscheme(drw, scheme[SchemeNorm]);
    848 
    849 	/* find an unused keycode to use as a temporary keycode (derived from source:
    850 	   https://stackoverflow.com/questions/44313966/c-xtest-emitting-key-presses-for-every-unicode-character) */
    851 	KeySym *keysyms;
    852 	int keysyms_per_keycode = 0;
    853 	int keycode_low, keycode_high;
    854 	Bool key_is_empty;
    855 	int symindex;
    856 
    857 	XDisplayKeycodes(dpy, &keycode_low, &keycode_high);
    858 	keysyms = XGetKeyboardMapping(dpy, keycode_low, keycode_high - keycode_low, &keysyms_per_keycode);
    859 	for (i = keycode_low; i <= keycode_high; i++) {
    860 		key_is_empty = True;
    861 		for (j = 0; j < keysyms_per_keycode; j++) {
    862 			symindex = (i - keycode_low) * keysyms_per_keycode + j;
    863 			if (keysyms[symindex] != 0) {
    864 				key_is_empty = False;
    865 			} else {
    866 				break;
    867 			}
    868 		}
    869 		if (key_is_empty) {
    870 			tmp_keycode = i;
    871 			break;
    872 		}
    873 	}
    874 
    875 	/* init appearance */
    876 	for (j = 0; j < SchemeLast; j++)
    877 		scheme[j] = drw_scm_create(drw, (const char **) colors[j], 2);
    878 
    879 	for (j = 0; j < SchemeLast; ++j) {
    880 		free(colors[j][ColFg]);
    881 		free(colors[j][ColBg]);
    882 	}
    883 
    884 	/* init atoms */
    885 	if (isdock) {
    886 		netatom[NetWMWindowType] = XInternAtom(dpy,
    887 				"_NET_WM_WINDOW_TYPE", False);
    888 		atype = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
    889 	}
    890 
    891 	/* init appearance */
    892 	countrows();
    893 	if (!ww)
    894 		ww = sw;
    895 	if (!wh)
    896 		wh = sh * rows / heightfactor;
    897 
    898 	if (!wx)
    899 		wx = 0;
    900 	if (wx < 0)
    901 		wx = sw + wx - ww;
    902 	if (!wy)
    903 		wy = sh - wh;
    904 	if (wy < 0)
    905 		wy = sh + wy - wh;
    906 
    907 	for (i = 0; i < numkeys; i++)
    908 		keys[i].pressed = 0;
    909 
    910 	wa.override_redirect = !wmborder;
    911 	wa.border_pixel = scheme[SchemeNorm][ColFg].pixel;
    912 	wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
    913 	win = XCreateWindow(dpy, root, wx, wy, ww, wh, 0,
    914 			CopyFromParent, CopyFromParent, CopyFromParent,
    915 			CWOverrideRedirect | CWBorderPixel |
    916 			CWBackingPixel, &wa);
    917 	XSelectInput(dpy, win, StructureNotifyMask|ButtonReleaseMask|
    918 			ButtonPressMask|ExposureMask|LeaveWindowMask|
    919 			PointerMotionMask);
    920 
    921 	wmh = XAllocWMHints();
    922 	wmh->input = False;
    923 	wmh->flags = InputHint;
    924 	if (!isdock) {
    925 		sizeh = XAllocSizeHints();
    926 		sizeh->flags = PMaxSize | PMinSize;
    927 		sizeh->min_width = sizeh->max_width = ww;
    928 		sizeh->min_height = sizeh->max_height = wh;
    929 	}
    930 	XStringListToTextProperty(&name, 1, &str);
    931 	ch = XAllocClassHint();
    932 	ch->res_class = name;
    933 	ch->res_name = name;
    934 
    935 	XSetWMProperties(dpy, win, &str, &str, NULL, 0, sizeh, wmh, ch);
    936 
    937 	XFree(keysyms);
    938 	XFree(ch);
    939 	XFree(wmh);
    940 	XFree(str.value);
    941 	if (sizeh != NULL)
    942 		XFree(sizeh);
    943 
    944 	if (isdock) {
    945 		XChangeProperty(dpy, win, netatom[NetWMWindowType], XA_ATOM,
    946 				32, PropModeReplace,
    947 				(unsigned char *)&atype, 1);
    948 	}
    949 
    950 	XMapRaised(dpy, win);
    951 	drw_resize(drw, ww, wh);
    952 	updatekeys();
    953 	drawkeyboard();
    954 }
    955 
    956 void
    957 updatekeys(void)
    958 {
    959 	int i, j;
    960 	double base;
    961 	int x, y = 0, h, r = rows;
    962 
    963 	h = (wh - 1) / rows;
    964 	for (i = 0; i < numkeys; i++, r--) {
    965 		for (j = i, base = 0; j < numkeys && keys[j].keysym != 0; j++)
    966 			base += keys[j].width;
    967 		for (x = 0; i < numkeys && keys[i].keysym != 0; i++) {
    968 			keys[i].x = x + xspacing;
    969 			keys[i].y = y + yspacing;
    970 			keys[i].w = keys[i].width * ww / base;
    971 			keys[i].h = r == 1 ? wh - y - 1 : h;
    972 			x += keys[i].w;
    973 			keys[i].w = keys[i].w - (xspacing * 2);
    974 			keys[i].h = keys[i].h - (yspacing * 2);
    975 		}
    976 		if (base != 0)
    977 			keys[i - 1].w = ww - 1 - keys[i - 1].x;
    978 		y += h;
    979 	}
    980 }
    981 
    982 void
    983 usage(char *argv0)
    984 {
    985 	fprintf(stderr, "usage: %s [-hdnovDOR] [-g geometry] [-fn font] [-l layers] [-s initial_layer]\n", argv0);
    986 	fprintf(stderr, "Options:\n");
    987 	fprintf(stderr, "  -d         - Set Dock Window Type\n");
    988 	fprintf(stderr, "  -D         - Enable debug\n");
    989 	fprintf(stderr, "  -O         - Disable overlays\n");
    990 	fprintf(stderr, "  -R         - Disable press-on-release\n");
    991 	fprintf(stderr, "  -n         - Do not simulate key presses for X\n");
    992 	fprintf(stderr, "  -o         - Print to standard output\n");
    993 	fprintf(stderr, "  -l         - Comma separated list of layers to enable\n");
    994 	fprintf(stderr, "  -s         - Layer to select on program start\n");
    995 	fprintf(stderr, "  -H [int]   - Height fraction, one key row takes 1/x of the screen height\n");
    996 	fprintf(stderr, "  -fn [font] - Set font (Xft, e.g: DejaVu Sans:bold:size=20)\n");
    997 	fprintf(stderr, "  -g         - Set the window position or size using the X geometry format\n");
    998 	exit(1);
    999 }
   1000 
   1001 void
   1002 setlayer(void)
   1003 {
   1004 	numkeys = countkeys(layers[currentlayer]);
   1005 	memcpy(&keys, layers[currentlayer], sizeof(Key) * numkeys);
   1006 	countrows();
   1007 }
   1008 
   1009 void
   1010 cyclelayer(void)
   1011 {
   1012 	currentlayer++;
   1013 	if (currentlayer >= numlayers)
   1014 		currentlayer = 0;
   1015 	printdbg("Cycling to layer %d\n", currentlayer);
   1016 	setlayer();
   1017 	updatekeys();
   1018 	drawkeyboard();
   1019 }
   1020 
   1021 void
   1022 togglelayer(void)
   1023 {
   1024 	if (currentlayer > 0) {
   1025 		currentlayer = 0;
   1026 	} else if (numlayers > 1) {
   1027 		currentlayer = 1;
   1028 	}
   1029 	printdbg("Toggling layer %d\n", currentlayer);
   1030 	setlayer();
   1031 	updatekeys();
   1032 	drawkeyboard();
   1033 }
   1034 
   1035 void
   1036 showoverlay(int idx)
   1037 {
   1038 	int i, j;
   1039 
   1040 	printdbg("Showing overlay %d\n", idx);
   1041 
   1042 	/* unpress existing key (visually only) */
   1043 	for (i = 0; i < numkeys; i++) {
   1044 		if (keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
   1045 			keys[i].pressed = 0;
   1046 			drawkey(&keys[i], True);
   1047 			break;
   1048 		}
   1049 	}
   1050 
   1051 	for (i = idx, j = 0; i < OVERLAYS; i++, j++) {
   1052 		if (overlay[i].keysym == XK_Cancel) {
   1053 			break;
   1054 		}
   1055 		/* certain modifier keys and basic keys are excluded from being overlayed: */
   1056 		while (keys[j].keysym == 0 || keys[j].keysym == XK_Shift_L ||
   1057 				keys[j].keysym == XK_Shift_R || keys[j].keysym == XK_Control_L ||
   1058 				keys[j].keysym == XK_Control_R || keys[j].keysym == XK_Alt_L ||
   1059 				keys[j].keysym == XK_Alt_R || keys[j].keysym == XK_BackSpace ||
   1060 				keys[j].keysym == XK_Return || keys[j].keysym == XK_space ||
   1061 				keys[j].keysym == XK_Cancel)
   1062 			j++;
   1063 		if (overlay[i].width > 1)
   1064 			j += overlay[i].width - 1;
   1065 		if (j >= numkeys)
   1066 			break;
   1067 		keys[j].label = overlay[i].label;
   1068 		keys[j].label2 = overlay[i].label2;
   1069 		keys[j].keysym = overlay[i].keysym;
   1070 		keys[j].modifier = overlay[i].modifier;
   1071 		keys[j].isoverlay = True;
   1072 	}
   1073 	currentoverlay = idx;
   1074 	overlaykeysym = ispressingkeysym;
   1075 	releaseprotect = 1;
   1076 	updatekeys();
   1077 	drawkeyboard();
   1078 	XSync(dpy, False);
   1079 }
   1080 
   1081 void
   1082 hideoverlay(void)
   1083 {
   1084 	printdbg("Hiding overlay, overlay was #%d\n", currentoverlay);
   1085 	currentoverlay = -1;
   1086 	overlaykeysym = 0;
   1087 	currentlayer--;
   1088 	cyclelayer();
   1089 }
   1090 
   1091 void
   1092 sigterm(int signo)
   1093 {
   1094 	running = False;
   1095 	sigtermd = True;
   1096 	printdbg("SIGTERM received\n");
   1097 }
   1098 
   1099 void
   1100 init_layers(char *layer_names_list, const char *initial_layer_name)
   1101 {
   1102 	char *s;
   1103 	int j, found;
   1104 
   1105 	if (layer_names_list == NULL) {
   1106 		numlayers = LAYERS;
   1107 		memcpy(&layers, &available_layers, sizeof(available_layers));
   1108 		if (initial_layer_name != NULL) {
   1109 			for (j = 0; j < LAYERS; j++) {
   1110 				if (strcmp(layer_names[j], initial_layer_name) == 0) {
   1111 					currentlayer = j;
   1112 					break;
   1113 				}
   1114 			}
   1115 		}
   1116 	} else {
   1117 		s = strtok(layer_names_list, ",");
   1118 		while (s != NULL) {
   1119 			if (numlayers + 1 > LAYERS)
   1120 				die("too many layers specified");
   1121 			found = 0;
   1122 			for (j = 0; j < LAYERS; j++) {
   1123 				if (strcmp(layer_names[j], s) == 0) {
   1124 					fprintf(stderr, "Adding layer %s\n", s);
   1125 					layers[numlayers] = available_layers[j];
   1126 					if (initial_layer_name != NULL && strcmp(layer_names[j], initial_layer_name) == 0) {
   1127 						currentlayer = numlayers;
   1128 					}
   1129 					found = 1;
   1130 					break;
   1131 				}
   1132 			}
   1133 			if (!found) {
   1134 				fprintf(stderr, "Undefined layer: %s\n", s);
   1135 				exit(3);
   1136 			}
   1137 			numlayers++;
   1138 			s = strtok(NULL,",");
   1139 		}
   1140 	}
   1141 	setlayer();
   1142 }
   1143 
   1144 void
   1145 printdbg(const char *fmt, ...)
   1146 {
   1147 	if (!debug)
   1148 		return;
   1149 
   1150 	va_list ap;
   1151 	va_start(ap, fmt);
   1152 	vfprintf(stderr, fmt, ap);
   1153 	va_end(ap);
   1154 	fflush(stderr);
   1155 }
   1156 
   1157 int
   1158 main(int argc, char *argv[])
   1159 {
   1160 	char *initial_layer_name = NULL;
   1161 	char *layer_names_list = NULL;
   1162 	char *tmp;
   1163 	int i, xr, yr, bitm;
   1164 	unsigned int wr, hr;
   1165 
   1166 	signal(SIGTERM, sigterm);
   1167 
   1168 	if (OVERLAYS <= 1) {
   1169 		enableoverlays = 0;
   1170 	} else {
   1171 		if ((tmp = getenv("SVKBD_ENABLEOVERLAYS")))
   1172 			enableoverlays = atoi(tmp);
   1173 	}
   1174 	if ((tmp = getenv("SVKBD_LAYERS")))
   1175 		layer_names_list = estrdup(tmp);
   1176 
   1177 	if ((tmp = getenv("SVKBD_HEIGHTFACTOR")))
   1178 		heightfactor = atoi(tmp);
   1179 
   1180 	if ((tmp = getenv("SVKBD_PRESSONRELEASE"))) /* defaults to 1 */
   1181 		pressonrelease = atoi(tmp);
   1182 
   1183 	/* parse command line arguments */
   1184 	for (i = 1; argv[i]; i++) {
   1185 		if (!strcmp(argv[i], "-v")) {
   1186 			die("svkbd-"VERSION);
   1187 		} else if (!strcmp(argv[i], "-d")) {
   1188 			isdock = True;
   1189 		} else if (!strncmp(argv[i], "-g", 2)) {
   1190 			if (i >= argc - 1)
   1191 				usage(argv[0]);
   1192 
   1193 			bitm = XParseGeometry(argv[++i], &xr, &yr, &wr, &hr);
   1194 			if (bitm & XValue)
   1195 				wx = xr;
   1196 			if (bitm & YValue)
   1197 				wy = yr;
   1198 			if (bitm & WidthValue)
   1199 				ww = (int)wr;
   1200 			if (bitm & HeightValue)
   1201 				wh = (int)hr;
   1202 			if (bitm & XNegative && wx == 0)
   1203 				wx = -1;
   1204 			if (bitm & YNegative && wy == 0)
   1205 				wy = -1;
   1206 		} else if (!strcmp(argv[i], "-fn")) { /* font or font set */
   1207 			if (i >= argc - 1)
   1208 				usage(argv[0]);
   1209 			fonts[0] = estrdup(argv[++i]);
   1210 		} else if (!strcmp(argv[i], "-D")) {
   1211 			debug = 1;
   1212 		} else if (!strcmp(argv[i], "-h")) {
   1213 			usage(argv[0]);
   1214 		} else if (!strcmp(argv[i], "-O")) {
   1215 			enableoverlays = 0;
   1216 		} else if (!strcmp(argv[i], "-o")) {
   1217 			printoutput = 1;
   1218 		} else if (!strcmp(argv[i], "-n")) {
   1219 			simulateoutput = 0;
   1220 		} else if (!strcmp(argv[i], "-R")) {
   1221 			pressonrelease = 0;
   1222 		} else if (!strcmp(argv[i], "-l")) {
   1223 			if (i >= argc - 1)
   1224 				usage(argv[0]);
   1225 			free(layer_names_list);
   1226 			layer_names_list = estrdup(argv[++i]);
   1227 		} else if (!strcmp(argv[i], "-s")) {
   1228 			if (i >= argc - 1)
   1229 				usage(argv[0]);
   1230 			initial_layer_name = argv[++i];
   1231 		} else if (!strcmp(argv[i], "-H")) {
   1232 			if (i >= argc - 1)
   1233 				usage(argv[0]);
   1234 			heightfactor = atoi(argv[++i]);
   1235 		} else {
   1236 			fprintf(stderr, "Invalid argument: %s\n", argv[i]);
   1237 			usage(argv[0]);
   1238 		}
   1239 	}
   1240 
   1241 	if (printoutput)
   1242 		setbuf(stdout, NULL); /* unbuffered output */
   1243 
   1244 	if (heightfactor <= 0)
   1245 		die("height factor must be a positive integer");
   1246 
   1247 	init_layers(layer_names_list, initial_layer_name);
   1248 
   1249 	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
   1250 		fprintf(stderr, "warning: no locale support");
   1251 	if (!(dpy = XOpenDisplay(0)))
   1252 		die("cannot open display");
   1253 	setup();
   1254 	run();
   1255 	cleanup();
   1256 	XCloseDisplay(dpy);
   1257 	free(layer_names_list);
   1258 
   1259 	return 0;
   1260 }