svkbd.c - svkbd - simple virtual keyboard
 (HTM) git clone git://git.suckless.org/svkbd
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) 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 }