mirror of
git://git.suckless.org/svkbd
synced 2024-12-18 04:04:44 +00:00
92795b2a0a
"This patch fixes uppercase in armenian (and perhaps some other) layouts. svkbd was previously registering a key in one modification (xorg accepts up to 4 modifications). with this patch svkbd registers 2 modifications - upper and lower case. function tmp_remap changed in a way that if the key is not found in a current layout, it is temporary added to it. for cyrillic layout uppercase (when shift is pressed) letters were showing anyway, because those are somehow found (modified) by xorg. but for armenian layout that did not work. now works." Patch by նորայր <norayr@arnet.am>
1270 lines
31 KiB
C
1270 lines
31 KiB
C
/* See LICENSE file for copyright and license details. */
|
|
#include <sys/select.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <ctype.h>
|
|
#include <locale.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <X11/keysym.h>
|
|
#include <X11/keysymdef.h>
|
|
#include <X11/XF86keysym.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xproto.h>
|
|
#include <X11/extensions/XTest.h>
|
|
#include <X11/Xft/Xft.h>
|
|
#include <X11/Xresource.h>
|
|
#ifdef XINERAMA
|
|
#include <X11/extensions/Xinerama.h>
|
|
#endif
|
|
|
|
#include "drw.h"
|
|
#include "util.h"
|
|
|
|
/* macros */
|
|
#define LENGTH(x) (sizeof x / sizeof x[0])
|
|
#define STRINGTOKEYSYM(X) (XStringToKeySym(X))
|
|
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)))
|
|
|
|
/* enums */
|
|
enum {
|
|
SchemeNorm, SchemeNormABC, SchemeNormABCShift, SchemeNormShift, SchemePress,
|
|
SchemePressShift, SchemeHighlight, SchemeHighlightShift, SchemeOverlay,
|
|
SchemeOverlayShift, SchemeWindow, SchemeLast
|
|
};
|
|
enum { NetWMWindowType, NetLast };
|
|
|
|
/* typedefs */
|
|
typedef struct {
|
|
char *label;
|
|
char *label2;
|
|
KeySym keysym;
|
|
double width;
|
|
KeySym modifier;
|
|
int x, y, w, h;
|
|
Bool pressed;
|
|
Bool highlighted;
|
|
Bool isoverlay;
|
|
} Key;
|
|
|
|
typedef struct {
|
|
KeySym mod;
|
|
unsigned int button;
|
|
} Buttonmod;
|
|
|
|
/* function declarations */
|
|
static void printdbg(const char *fmt, ...);
|
|
static void motionnotify(XEvent *e);
|
|
static void buttonpress(XEvent *e);
|
|
static void buttonrelease(XEvent *e);
|
|
static void cleanup(void);
|
|
static void configurenotify(XEvent *e);
|
|
static void countrows();
|
|
static int countkeys(Key *layer);
|
|
static void drawkeyboard(void);
|
|
static void drawkey(Key *k, Bool map);
|
|
static void expose(XEvent *e);
|
|
static Key *findkey(int x, int y);
|
|
static void leavenotify(XEvent *e);
|
|
static void press(Key *k, KeySym buttonmod);
|
|
static double get_press_duration();
|
|
static void run(void);
|
|
static void setup(void);
|
|
static void simulate_keypress(KeySym keysym);
|
|
static void simulate_keyrelease(KeySym keysym);
|
|
static void showoverlay(int idx);
|
|
static void hideoverlay();
|
|
static void cyclelayer();
|
|
static void setlayer();
|
|
static void togglelayer();
|
|
static void unpress(Key *k, KeySym buttonmod);
|
|
static void updatekeys();
|
|
static void printkey(Key *k, KeySym mod);
|
|
|
|
/* variables */
|
|
static int screen;
|
|
static void (*handler[LASTEvent]) (XEvent *) = {
|
|
[ButtonPress] = buttonpress,
|
|
[ButtonRelease] = buttonrelease,
|
|
[ConfigureNotify] = configurenotify,
|
|
[Expose] = expose,
|
|
[LeaveNotify] = leavenotify,
|
|
[MotionNotify] = motionnotify
|
|
};
|
|
static Atom netatom[NetLast];
|
|
static Display *dpy;
|
|
static Drw *drw;
|
|
static Window root, win;
|
|
static Clr* scheme[SchemeLast];
|
|
static Bool running = True, isdock = False;
|
|
static struct timeval pressbegin;
|
|
static int currentlayer = 0;
|
|
static int enableoverlays = 1;
|
|
static int currentoverlay = -1; /* -1 = no overlay */
|
|
static int pressonrelease = 1;
|
|
static KeySym overlaykeysym = 0; /* keysym for which the overlay is presented */
|
|
static int releaseprotect = 0; /* set to 1 after overlay is shown, protecting against immediate release */
|
|
static int tmp_keycode = 1;
|
|
static int rows = 0, ww = 0, wh = 0, wx = 0, wy = 0;
|
|
static int simulateoutput = 1; /* simulate key presses for X */
|
|
static int printoutput = 0; /* print key pressed to stdout */
|
|
static char *name = "svkbd";
|
|
static int debug = 0;
|
|
static int numlayers = 0;
|
|
static int numkeys = 0;
|
|
|
|
static char *colors[11][2]; /* 11 schemes, 2 colors each */
|
|
static char *fonts[] = { 0 };
|
|
|
|
static KeySym ispressingkeysym;
|
|
|
|
Bool ispressing = False;
|
|
Bool sigtermd = False;
|
|
|
|
/* configuration, allows nested code to access above variables */
|
|
#include "config.h"
|
|
#ifndef LAYOUT
|
|
#error "make sure to define LAYOUT"
|
|
#endif
|
|
#include LAYOUT
|
|
|
|
static Key keys[KEYS];
|
|
static Key *layers[LAYERS];
|
|
|
|
void
|
|
motionnotify(XEvent *e)
|
|
{
|
|
XPointerMovedEvent *ev = &e->xmotion;
|
|
int i;
|
|
int lostfocus = -1;
|
|
int gainedfocus = -1;
|
|
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (keys[i].keysym && ev->x > keys[i].x
|
|
&& ev->x < keys[i].x + keys[i].w
|
|
&& ev->y > keys[i].y
|
|
&& ev->y < keys[i].y + keys[i].h) {
|
|
if (keys[i].highlighted != True) {
|
|
if (ispressing) {
|
|
gainedfocus = i;
|
|
} else {
|
|
keys[i].highlighted = True;
|
|
}
|
|
drawkey(&keys[i], True);
|
|
}
|
|
continue;
|
|
} else if (keys[i].highlighted == True) {
|
|
keys[i].highlighted = False;
|
|
drawkey(&keys[i], True);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (!IsModifierKey(keys[i].keysym) && keys[i].pressed == True &&
|
|
lostfocus != gainedfocus) {
|
|
printdbg("Pressed key lost focus: %ld\n", keys[i].keysym);
|
|
lostfocus = i;
|
|
ispressingkeysym = 0;
|
|
keys[i].pressed = 0;
|
|
drawkey(&keys[i], True);
|
|
}
|
|
}
|
|
|
|
if ((lostfocus != -1) && (gainedfocus != -1) && (lostfocus != gainedfocus)) {
|
|
printdbg("Clicking new key that gained focus\n");
|
|
press(&keys[gainedfocus], 0);
|
|
keys[gainedfocus].pressed = True;
|
|
keys[gainedfocus].highlighted = True;
|
|
}
|
|
}
|
|
|
|
void
|
|
buttonpress(XEvent *e)
|
|
{
|
|
XButtonPressedEvent *ev = &e->xbutton;
|
|
Key *k;
|
|
KeySym mod = 0;
|
|
int i;
|
|
|
|
ispressing = True;
|
|
|
|
if (!(k = findkey(ev->x, ev->y)))
|
|
return;
|
|
|
|
for (i = 0; i < LENGTH(buttonmods); i++) {
|
|
if (ev->button == buttonmods[i].button) {
|
|
mod = buttonmods[i].mod;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (k->modifier) {
|
|
if (mod == k->modifier)
|
|
mod = 0;
|
|
else
|
|
mod = k->modifier;
|
|
}
|
|
press(k, mod);
|
|
}
|
|
|
|
void
|
|
buttonrelease(XEvent *e)
|
|
{
|
|
XButtonPressedEvent *ev = &e->xbutton;
|
|
Key *k;
|
|
KeySym mod = 0;
|
|
int i;
|
|
|
|
ispressing = False;
|
|
|
|
for (i = 0; i < LENGTH(buttonmods); i++) {
|
|
if (ev->button == buttonmods[i].button) {
|
|
mod = buttonmods[i].mod;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ev->x < 0 || ev->y < 0) {
|
|
unpress(NULL, mod);
|
|
} else if ((k = findkey(ev->x, ev->y))) {
|
|
if (k->modifier == mod)
|
|
unpress(k, 0);
|
|
else if (k->modifier)
|
|
unpress(k, k->modifier);
|
|
else
|
|
unpress(k, mod);
|
|
}
|
|
}
|
|
|
|
void
|
|
cleanup(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SchemeLast; i++)
|
|
free(scheme[i]);
|
|
drw_sync(drw);
|
|
drw_free(drw);
|
|
XSync(dpy, False);
|
|
XDestroyWindow(dpy, win);
|
|
XSync(dpy, False);
|
|
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
|
|
}
|
|
|
|
void
|
|
configurenotify(XEvent *e)
|
|
{
|
|
XConfigureEvent *ev = &e->xconfigure;
|
|
|
|
if (ev->window == win && (ev->width != ww || ev->height != wh)) {
|
|
ww = ev->width;
|
|
wh = ev->height;
|
|
drw_resize(drw, ww, wh);
|
|
updatekeys();
|
|
}
|
|
}
|
|
|
|
void
|
|
countrows(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0, rows = 1; i < numkeys; i++) {
|
|
if (keys[i].keysym == 0) {
|
|
rows++;
|
|
if ((i > 0) && (keys[i-1].keysym == 0)) {
|
|
rows--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
countkeys(Key *layer)
|
|
{
|
|
int i, nkeys = 0;
|
|
|
|
for (i = 0; i < KEYS; i++) {
|
|
if (i > 0 && layer[i].keysym == 0 && layer[i - 1].keysym == 0) {
|
|
nkeys--;
|
|
break;
|
|
}
|
|
nkeys++;
|
|
}
|
|
|
|
return nkeys;
|
|
}
|
|
|
|
void
|
|
drawkeyboard(void)
|
|
{
|
|
int i;
|
|
|
|
drw_setscheme(drw, scheme[SchemeWindow]);
|
|
drw_rect(drw, 0, 0, ww, wh, 1, 1);
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (keys[i].keysym != 0)
|
|
drawkey(&keys[i], False);
|
|
}
|
|
drw_map(drw, win, 0, 0, ww, wh);
|
|
}
|
|
|
|
void
|
|
drawkey(Key *k, Bool map)
|
|
{
|
|
int x, y, w, h;
|
|
const char *l;
|
|
|
|
int use_scheme = SchemeNorm;
|
|
|
|
if (k->pressed)
|
|
use_scheme = SchemePress;
|
|
else if (k->highlighted)
|
|
use_scheme = SchemeHighlight;
|
|
else if (k->isoverlay)
|
|
use_scheme = SchemeOverlay;
|
|
else if ((k->keysym == XK_Return) ||
|
|
((k->keysym >= XK_a) && (k->keysym <= XK_z)) ||
|
|
((k->keysym >= XK_Cyrillic_io) && (k->keysym <= XK_Cyrillic_hardsign)))
|
|
use_scheme = SchemeNormABC;
|
|
else
|
|
use_scheme = SchemeNorm;
|
|
|
|
drw_setscheme(drw, scheme[use_scheme]);
|
|
drw_rect(drw, k->x, k->y, k->w, k->h, 1, 1);
|
|
|
|
if (k->keysym == XK_KP_Insert) {
|
|
if (enableoverlays) {
|
|
l = "≅";
|
|
} else {
|
|
l = "≇";
|
|
}
|
|
} else if (k->label) {
|
|
l = k->label;
|
|
} else {
|
|
l = XKeysymToString(k->keysym);
|
|
}
|
|
h = fontsize * 2;
|
|
y = k->y + (k->h / 2) - (h / 2);
|
|
w = TEXTW(l);
|
|
x = k->x + (k->w / 2) - (w / 2);
|
|
drw_text(drw, x, y, w, h, 0, l, 0);
|
|
if (k->label2) {
|
|
if (use_scheme == SchemeNorm)
|
|
use_scheme = SchemeNormShift;
|
|
else if (use_scheme == SchemeNormABC)
|
|
use_scheme = SchemeNormABCShift;
|
|
else if (use_scheme == SchemePress)
|
|
use_scheme = SchemePressShift;
|
|
else if (use_scheme == SchemeHighlight)
|
|
use_scheme = SchemeHighlightShift;
|
|
else if (use_scheme == SchemeOverlay)
|
|
use_scheme = SchemeOverlayShift;
|
|
drw_setscheme(drw, scheme[use_scheme]);
|
|
x += w;
|
|
y -= 15;
|
|
l = k->label2;
|
|
w = TEXTW(l);
|
|
drw_text(drw, x, y, w, h, 0, l, 0);
|
|
}
|
|
if (map)
|
|
drw_map(drw, win, k->x, k->y, k->w, k->h);
|
|
}
|
|
|
|
void
|
|
expose(XEvent *e)
|
|
{
|
|
XExposeEvent *ev = &e->xexpose;
|
|
|
|
if (ev->count == 0 && (ev->window == win))
|
|
drawkeyboard();
|
|
}
|
|
|
|
Key *
|
|
findkey(int x, int y) {
|
|
int i;
|
|
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (keys[i].keysym && x > keys[i].x &&
|
|
x < keys[i].x + keys[i].w &&
|
|
y > keys[i].y && y < keys[i].y + keys[i].h) {
|
|
return &keys[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
hasoverlay(KeySym keysym)
|
|
{
|
|
int begin, i;
|
|
|
|
begin = 0;
|
|
for (i = 0; i < OVERLAYS; i++) {
|
|
if (overlay[i].keysym == XK_Cancel) {
|
|
begin = i + 1;
|
|
} else if (overlay[i].keysym == keysym) {
|
|
return begin + 1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
leavenotify(XEvent *e)
|
|
{
|
|
if (currentoverlay != -1)
|
|
hideoverlay();
|
|
ispressingkeysym = 0;
|
|
unpress(NULL, 0);
|
|
}
|
|
|
|
void
|
|
record_press_begin(KeySym ks)
|
|
{
|
|
/* record the begin of the press, don't simulate the actual keypress yet */
|
|
gettimeofday(&pressbegin, NULL);
|
|
ispressingkeysym = ks;
|
|
}
|
|
|
|
void
|
|
press(Key *k, KeySym buttonmod)
|
|
{
|
|
int i;
|
|
int overlayidx = -1;
|
|
|
|
k->pressed = !k->pressed;
|
|
|
|
printdbg("Begin click: %ld\n", k->keysym);
|
|
pressbegin.tv_sec = 0;
|
|
pressbegin.tv_usec = 0;
|
|
ispressingkeysym = 0;
|
|
|
|
if (!IsModifierKey(k->keysym)) {
|
|
if (enableoverlays && currentoverlay == -1)
|
|
overlayidx = hasoverlay(k->keysym);
|
|
if ((pressonrelease) || (enableoverlays && overlayidx != -1)) {
|
|
/* record the begin of the press, don't simulate the actual keypress yet */
|
|
record_press_begin(k->keysym);
|
|
} else {
|
|
printdbg("Simulating press: %ld (mod %ld)\n", k->keysym, buttonmod);
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
|
|
simulate_keypress(keys[i].keysym);
|
|
}
|
|
}
|
|
if (buttonmod)
|
|
simulate_keypress(buttonmod);
|
|
simulate_keypress(k->keysym);
|
|
if (printoutput)
|
|
printkey(k, buttonmod);
|
|
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
|
|
simulate_keyrelease(keys[i].keysym);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
drawkey(k, True);
|
|
}
|
|
|
|
int
|
|
tmp_remap(KeySym keysym)
|
|
{
|
|
/* map lower and upper case of keysym to the temporary keycode */
|
|
KeySym syms[2];
|
|
XConvertCase(keysym, &syms[0], &syms[1]);
|
|
|
|
/* if keysym is capital letter then swap upper and lower case */
|
|
if (keysym == syms[1])
|
|
syms[1] = syms[0], syms[0] = keysym;
|
|
|
|
XChangeKeyboardMapping(dpy, tmp_keycode, syms[0] == syms[1] ? 1 : 2, syms, 1);
|
|
XSync(dpy, False);
|
|
|
|
printdbg("Temporary map keysym %ld (%ld, %ld) to keycode %d\n", keysym, syms[0], syms[1], tmp_keycode);
|
|
return tmp_keycode;
|
|
}
|
|
|
|
void
|
|
printkey(Key *k, KeySym mod)
|
|
{
|
|
int i, shift;
|
|
|
|
shift = (mod == XK_Shift_L) || (mod == XK_Shift_R) || (mod == XK_Shift_Lock);
|
|
if (!shift) {
|
|
for (i = 0; i < numkeys; i++) {
|
|
if ((keys[i].pressed) && ((keys[i].keysym == XK_Shift_L) ||
|
|
(keys[i].keysym == XK_Shift_R) || (keys[i].keysym == XK_Shift_Lock))) {
|
|
shift = True;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
printdbg("Printing key %ld (shift=%d)\n", k->keysym, shift);
|
|
if (k->keysym == XK_Cancel)
|
|
return;
|
|
|
|
KeySym *keysym = &(k->keysym);
|
|
XIM xim = XOpenIM(dpy, 0, 0, 0);
|
|
XIC xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, NULL);
|
|
|
|
XKeyPressedEvent event;
|
|
event.type = KeyPress;
|
|
event.display = dpy;
|
|
event.state = shift ? ShiftMask : 0;
|
|
event.keycode = XKeysymToKeycode(dpy, *keysym);
|
|
if (event.keycode == 0)
|
|
event.keycode = tmp_remap(*keysym);
|
|
|
|
char buffer[32];
|
|
KeySym ignore;
|
|
Status return_status;
|
|
int l = Xutf8LookupString(xic, &event, buffer, sizeof(buffer), &ignore, &return_status);
|
|
buffer[l] = '\0';
|
|
printdbg("Print buffer: [%s] (length=%d)\n", &buffer, l);
|
|
printf("%s", buffer);
|
|
|
|
XDestroyIC(xic);
|
|
XCloseIM(xim);
|
|
}
|
|
|
|
void
|
|
simulate_keypress(KeySym keysym)
|
|
{
|
|
KeyCode code;
|
|
|
|
if (!simulateoutput)
|
|
return;
|
|
|
|
code = XKeysymToKeycode(dpy, keysym);
|
|
if (code == 0)
|
|
code = tmp_remap(keysym);
|
|
XTestFakeKeyEvent(dpy, code, True, 0);
|
|
}
|
|
|
|
void
|
|
simulate_keyrelease(KeySym keysym)
|
|
{
|
|
KeyCode code;
|
|
|
|
if (!simulateoutput)
|
|
return;
|
|
|
|
code = XKeysymToKeycode(dpy, keysym);
|
|
if (code == 0)
|
|
code = tmp_remap(keysym);
|
|
XTestFakeKeyEvent(dpy, code, False, 0);
|
|
}
|
|
|
|
double
|
|
get_press_duration(void)
|
|
{
|
|
struct timeval now;
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
return (double) ((now.tv_sec * 1000000L + now.tv_usec) -
|
|
(pressbegin.tv_sec * 1000000L + pressbegin.tv_usec)) /
|
|
(double) 1000000L;
|
|
}
|
|
|
|
void
|
|
unpress(Key *k, KeySym buttonmod)
|
|
{
|
|
int i;
|
|
Bool neutralizebuttonmod = False;
|
|
|
|
if (k) {
|
|
switch(k->keysym) {
|
|
case XK_Cancel:
|
|
cyclelayer();
|
|
break;
|
|
case XK_script_switch:
|
|
togglelayer();
|
|
break;
|
|
case XK_KP_Insert:
|
|
enableoverlays = !enableoverlays;
|
|
break;
|
|
case XK_Break:
|
|
running = False;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((pressbegin.tv_sec || pressbegin.tv_usec) && (enableoverlays || pressonrelease) && k && k->keysym == ispressingkeysym) {
|
|
printdbg("Delayed simulation of press after release: %ld\n", k->keysym);
|
|
/* simulate the press event, as we postponed it earlier in press() */
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
|
|
if (keys[i].keysym == buttonmod)
|
|
neutralizebuttonmod = True;
|
|
else
|
|
simulate_keypress(keys[i].keysym);
|
|
}
|
|
}
|
|
if (buttonmod && !neutralizebuttonmod) {
|
|
simulate_keypress(buttonmod);
|
|
}
|
|
simulate_keypress(k->keysym);
|
|
pressbegin.tv_sec = 0;
|
|
pressbegin.tv_usec = 0;
|
|
}
|
|
|
|
if (k)
|
|
printdbg("Simulation of release: %ld\n", k->keysym);
|
|
else
|
|
printdbg("Simulation of release (all keys)\n");
|
|
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
|
|
simulate_keyrelease(keys[i].keysym);
|
|
if (printoutput)
|
|
printkey(&keys[i], buttonmod);
|
|
keys[i].pressed = 0;
|
|
drawkey(&keys[i], True);
|
|
}
|
|
}
|
|
|
|
if (buttonmod && !neutralizebuttonmod) {
|
|
simulate_keyrelease(buttonmod);
|
|
}
|
|
|
|
if (k == NULL || !IsModifierKey(k->keysym)) {
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (keys[i].pressed && IsModifierKey(keys[i].keysym)) {
|
|
if (!(keys[i].keysym == buttonmod && neutralizebuttonmod))
|
|
simulate_keyrelease(keys[i].keysym);
|
|
keys[i].pressed = 0;
|
|
drawkey(&keys[i], True);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (enableoverlays && currentoverlay != -1 &&
|
|
(k == NULL || !IsModifierKey(k->keysym))) {
|
|
if (releaseprotect) {
|
|
releaseprotect = 0;
|
|
} else {
|
|
hideoverlay();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
run(void)
|
|
{
|
|
XEvent ev;
|
|
int xfd;
|
|
fd_set fds;
|
|
struct timeval tv;
|
|
double duration;
|
|
int overlayidx;
|
|
int i, r;
|
|
|
|
xfd = ConnectionNumber(dpy);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = scan_rate;
|
|
|
|
XFlush(dpy);
|
|
|
|
while (running) {
|
|
usleep(100000L); /* 100ms */
|
|
FD_ZERO(&fds);
|
|
FD_SET(xfd, &fds);
|
|
r = select(xfd + 1, &fds, NULL, NULL, &tv);
|
|
if (r) {
|
|
while (XPending(dpy)) {
|
|
XNextEvent(dpy, &ev);
|
|
if (handler[ev.type]) {
|
|
(handler[ev.type])(&ev); /* call handler */
|
|
}
|
|
}
|
|
} else {
|
|
/* time-out expired without anything interesting happening, check for long-presses */
|
|
if (ispressing && ispressingkeysym) {
|
|
duration = get_press_duration();
|
|
if (debug >= 2)
|
|
printdbg("%f\n", duration);
|
|
overlayidx = hasoverlay(ispressingkeysym);
|
|
duration = get_press_duration();
|
|
if (overlayidx != -1 && duration >= overlay_delay) {
|
|
printdbg("press duration %f, activating overlay\n", duration);
|
|
showoverlay(overlayidx);
|
|
pressbegin.tv_sec = 0;
|
|
pressbegin.tv_usec = 0;
|
|
ispressingkeysym = 0;
|
|
} else if ((overlayidx == -1) && (duration >= repeat_delay)) {
|
|
printdbg("press duration %f, activating repeat\n", duration);
|
|
simulate_keyrelease(ispressingkeysym);
|
|
simulate_keypress(ispressingkeysym);
|
|
XSync(dpy, False);
|
|
}
|
|
}
|
|
}
|
|
if (r == -1 || sigtermd) {
|
|
/* an error occurred or we received a signal */
|
|
/* E.g. Generally in scripts we want to call SIGTERM on svkbd in which case
|
|
if the user is holding for example the enter key (to execute
|
|
the kill or script that does the kill), that causes an issue
|
|
since then X doesn't know the keyup is never coming.. (since
|
|
process will be dead before finger lifts - in that case we
|
|
just trigger out fake up presses for all keys */
|
|
printdbg("signal received, releasing all keys");
|
|
for (i = 0; i < numkeys; i++) {
|
|
XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0);
|
|
}
|
|
running = False;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
readxresources(void)
|
|
{
|
|
XrmDatabase xdb;
|
|
XrmValue xval;
|
|
char *type, *xrm;
|
|
|
|
XrmInitialize();
|
|
|
|
if ((xrm = XResourceManagerString(drw->dpy))) {
|
|
xdb = XrmGetStringDatabase(xrm);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.font", "*", &type, &xval) && !fonts[0])
|
|
fonts[0] = estrdup(xval.addr);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.background", "*", &type, &xval) && !colors[SchemeNorm][ColBg])
|
|
colors[SchemeNorm][ColBg] = estrdup(xval.addr);
|
|
if (XrmGetResource(xdb, "svkbd.foreground", "*", &type, &xval) && !colors[SchemeNorm][ColFg])
|
|
colors[SchemeNorm][ColFg] = estrdup(xval.addr);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.shiftforeground", "*", &type, &xval) && !colors[SchemeNormShift][ColFg])
|
|
colors[SchemeNormShift][ColFg] = estrdup(xval.addr);
|
|
if (XrmGetResource(xdb, "svkbd.shiftbackground", "*", &type, &xval) && !colors[SchemeNormShift][ColBg])
|
|
colors[SchemeNormShift][ColBg] = estrdup(xval.addr);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.ABCforeground", "*", &type, &xval) && !colors[SchemeNormABC][ColFg])
|
|
colors[SchemeNormABC][ColFg] = estrdup(xval.addr);
|
|
if (XrmGetResource(xdb, "svkbd.ABCbackground", "*", &type, &xval) && !colors[SchemeNormABC][ColBg])
|
|
colors[SchemeNormABC][ColBg] = estrdup(xval.addr);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.ABCshiftforeground", "*", &type, &xval) && !colors[SchemeNormABCShift][ColFg])
|
|
colors[SchemeNormABCShift][ColFg] = estrdup(xval.addr);
|
|
if (XrmGetResource(xdb, "svkbd.ABCshiftbackground", "*", &type, &xval) && !colors[SchemeNormABCShift][ColBg])
|
|
colors[SchemeNormABCShift][ColBg] = estrdup(xval.addr);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.pressbackground", "*", &type, &xval) && !colors[SchemePress][ColBg])
|
|
colors[SchemePress][ColBg] = estrdup(xval.addr);
|
|
if (XrmGetResource(xdb, "svkbd.pressforeground", "*", &type, &xval) && !colors[SchemePress][ColFg])
|
|
colors[SchemePress][ColFg] = estrdup(xval.addr);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.pressshiftbackground", "*", &type, &xval) && !colors[SchemePressShift][ColBg])
|
|
colors[SchemePressShift][ColBg] = estrdup(xval.addr);
|
|
if (XrmGetResource(xdb, "svkbd.pressshiftforeground", "*", &type, &xval) && !colors[SchemePressShift][ColFg])
|
|
colors[SchemePressShift][ColFg] = estrdup(xval.addr);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.highlightbackground", "*", &type, &xval) && !colors[SchemeHighlight][ColBg])
|
|
colors[SchemeHighlight][ColBg] = estrdup(xval.addr);
|
|
if (XrmGetResource(xdb, "svkbd.highlightforeground", "*", &type, &xval) && !colors[SchemeHighlight][ColFg])
|
|
colors[SchemeHighlight][ColFg] = estrdup(xval.addr);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.highlightshiftbackground", "*", &type, &xval) && !colors[SchemeHighlightShift][ColBg])
|
|
colors[SchemeHighlightShift][ColBg] = estrdup(xval.addr);
|
|
if (XrmGetResource(xdb, "svkbd.highlightshiftforeground", "*", &type, &xval) && !colors[SchemeHighlightShift][ColFg])
|
|
colors[SchemeHighlightShift][ColFg] = estrdup(xval.addr);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.overlaybackground", "*", &type, &xval) && !colors[SchemeOverlay][ColBg])
|
|
colors[SchemeOverlay][ColBg] = estrdup(xval.addr);
|
|
if (XrmGetResource(xdb, "svkbd.overlayforeground", "*", &type, &xval) && !colors[SchemeOverlay][ColFg])
|
|
colors[SchemeOverlay][ColFg] = estrdup(xval.addr);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.overlayshiftbackground", "*", &type, &xval) && !colors[SchemeOverlayShift][ColBg])
|
|
colors[SchemeOverlayShift][ColBg] = estrdup(xval.addr);
|
|
if (XrmGetResource(xdb, "svkbd.overlayshiftforeground", "*", &type, &xval) && !colors[SchemeOverlayShift][ColFg])
|
|
colors[SchemeOverlayShift][ColFg] = estrdup(xval.addr);
|
|
|
|
if (XrmGetResource(xdb, "svkbd.windowbackground", "*", &type, &xval) && !colors[SchemeWindow][ColBg])
|
|
colors[SchemeWindow][ColBg] = estrdup(xval.addr);
|
|
if (XrmGetResource(xdb, "svkbd.windowforeground", "*", &type, &xval) && !colors[SchemeWindow][ColFg])
|
|
colors[SchemeWindow][ColFg] = estrdup(xval.addr);
|
|
|
|
XrmDestroyDatabase(xdb);
|
|
}
|
|
}
|
|
|
|
void
|
|
setup(void)
|
|
{
|
|
XSetWindowAttributes wa;
|
|
XTextProperty str;
|
|
XSizeHints *sizeh = NULL;
|
|
XClassHint *ch;
|
|
XWMHints *wmh;
|
|
Atom atype = -1;
|
|
int i, j, sh, sw;
|
|
|
|
#ifdef XINERAMA
|
|
XineramaScreenInfo *info = NULL;
|
|
#endif
|
|
|
|
/* init screen */
|
|
screen = DefaultScreen(dpy);
|
|
root = RootWindow(dpy, screen);
|
|
#ifdef XINERAMA
|
|
if (XineramaIsActive(dpy)) {
|
|
info = XineramaQueryScreens(dpy, &i);
|
|
sw = info[0].width;
|
|
sh = info[0].height;
|
|
XFree(info);
|
|
} else
|
|
#endif
|
|
{
|
|
sw = DisplayWidth(dpy, screen);
|
|
sh = DisplayHeight(dpy, screen);
|
|
}
|
|
drw = drw_create(dpy, screen, root, sw, sh);
|
|
|
|
readxresources();
|
|
|
|
/* Apply defaults to font and colors*/
|
|
if (!fonts[0])
|
|
fonts[0] = estrdup(defaultfonts[0]);
|
|
for (i = 0; i < SchemeLast; ++i) {
|
|
for (j = 0; j < 2; ++j) {
|
|
if (!colors[i][j])
|
|
colors[i][j] = estrdup(defaultcolors[i][j]);
|
|
}
|
|
}
|
|
|
|
if (!drw_fontset_create(drw, (const char **) fonts, LENGTH(fonts)))
|
|
die("no fonts could be loaded");
|
|
free(fonts[0]);
|
|
|
|
drw_setscheme(drw, scheme[SchemeNorm]);
|
|
|
|
/* find an unused keycode to use as a temporary keycode (derived from source:
|
|
https://stackoverflow.com/questions/44313966/c-xtest-emitting-key-presses-for-every-unicode-character) */
|
|
KeySym *keysyms;
|
|
int keysyms_per_keycode = 0;
|
|
int keycode_low, keycode_high;
|
|
Bool key_is_empty;
|
|
int symindex;
|
|
|
|
XDisplayKeycodes(dpy, &keycode_low, &keycode_high);
|
|
keysyms = XGetKeyboardMapping(dpy, keycode_low, keycode_high - keycode_low, &keysyms_per_keycode);
|
|
for (i = keycode_low; i <= keycode_high; i++) {
|
|
key_is_empty = True;
|
|
for (j = 0; j < keysyms_per_keycode; j++) {
|
|
symindex = (i - keycode_low) * keysyms_per_keycode + j;
|
|
if (keysyms[symindex] != 0) {
|
|
key_is_empty = False;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (key_is_empty) {
|
|
tmp_keycode = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* init appearance */
|
|
for (j = 0; j < SchemeLast; j++)
|
|
scheme[j] = drw_scm_create(drw, (const char **) colors[j], 2);
|
|
|
|
for (j = 0; j < SchemeLast; ++j) {
|
|
free(colors[j][ColFg]);
|
|
free(colors[j][ColBg]);
|
|
}
|
|
|
|
/* init atoms */
|
|
if (isdock) {
|
|
netatom[NetWMWindowType] = XInternAtom(dpy,
|
|
"_NET_WM_WINDOW_TYPE", False);
|
|
atype = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
|
|
}
|
|
|
|
/* init appearance */
|
|
countrows();
|
|
if (!ww)
|
|
ww = sw;
|
|
if (!wh)
|
|
wh = sh * rows / heightfactor;
|
|
|
|
if (!wx)
|
|
wx = 0;
|
|
if (wx < 0)
|
|
wx = sw + wx - ww;
|
|
if (!wy)
|
|
wy = sh - wh;
|
|
if (wy < 0)
|
|
wy = sh + wy - wh;
|
|
|
|
for (i = 0; i < numkeys; i++)
|
|
keys[i].pressed = 0;
|
|
|
|
wa.override_redirect = !wmborder;
|
|
wa.border_pixel = scheme[SchemeNorm][ColFg].pixel;
|
|
wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
|
|
win = XCreateWindow(dpy, root, wx, wy, ww, wh, 0,
|
|
CopyFromParent, CopyFromParent, CopyFromParent,
|
|
CWOverrideRedirect | CWBorderPixel |
|
|
CWBackingPixel, &wa);
|
|
XSelectInput(dpy, win, StructureNotifyMask|ButtonReleaseMask|
|
|
ButtonPressMask|ExposureMask|LeaveWindowMask|
|
|
PointerMotionMask);
|
|
|
|
wmh = XAllocWMHints();
|
|
wmh->input = False;
|
|
wmh->flags = InputHint;
|
|
if (!isdock) {
|
|
sizeh = XAllocSizeHints();
|
|
sizeh->flags = PMaxSize | PMinSize;
|
|
sizeh->min_width = sizeh->max_width = ww;
|
|
sizeh->min_height = sizeh->max_height = wh;
|
|
}
|
|
XStringListToTextProperty(&name, 1, &str);
|
|
ch = XAllocClassHint();
|
|
ch->res_class = name;
|
|
ch->res_name = name;
|
|
|
|
XSetWMProperties(dpy, win, &str, &str, NULL, 0, sizeh, wmh, ch);
|
|
|
|
XFree(keysyms);
|
|
XFree(ch);
|
|
XFree(wmh);
|
|
XFree(str.value);
|
|
if (sizeh != NULL)
|
|
XFree(sizeh);
|
|
|
|
if (isdock) {
|
|
XChangeProperty(dpy, win, netatom[NetWMWindowType], XA_ATOM,
|
|
32, PropModeReplace,
|
|
(unsigned char *)&atype, 1);
|
|
}
|
|
|
|
XMapRaised(dpy, win);
|
|
drw_resize(drw, ww, wh);
|
|
updatekeys();
|
|
drawkeyboard();
|
|
}
|
|
|
|
void
|
|
updatekeys(void)
|
|
{
|
|
int i, j;
|
|
double base;
|
|
int x, y = 0, h, r = rows;
|
|
|
|
h = (wh - 1) / rows;
|
|
for (i = 0; i < numkeys; i++, r--) {
|
|
for (j = i, base = 0; j < numkeys && keys[j].keysym != 0; j++)
|
|
base += keys[j].width;
|
|
for (x = 0; i < numkeys && keys[i].keysym != 0; i++) {
|
|
keys[i].x = x + xspacing;
|
|
keys[i].y = y + yspacing;
|
|
keys[i].w = keys[i].width * ww / base;
|
|
keys[i].h = r == 1 ? wh - y - 1 : h;
|
|
x += keys[i].w;
|
|
keys[i].w = keys[i].w - (xspacing * 2);
|
|
keys[i].h = keys[i].h - (yspacing * 2);
|
|
}
|
|
if (base != 0)
|
|
keys[i - 1].w = ww - 1 - keys[i - 1].x;
|
|
y += h;
|
|
}
|
|
}
|
|
|
|
void
|
|
usage(char *argv0)
|
|
{
|
|
fprintf(stderr, "usage: %s [-hdnovDOR] [-g geometry] [-fn font] [-l layers] [-s initial_layer]\n", argv0);
|
|
fprintf(stderr, "Options:\n");
|
|
fprintf(stderr, " -d - Set Dock Window Type\n");
|
|
fprintf(stderr, " -D - Enable debug\n");
|
|
fprintf(stderr, " -O - Disable overlays\n");
|
|
fprintf(stderr, " -R - Disable press-on-release\n");
|
|
fprintf(stderr, " -n - Do not simulate key presses for X\n");
|
|
fprintf(stderr, " -o - Print to standard output\n");
|
|
fprintf(stderr, " -l - Comma separated list of layers to enable\n");
|
|
fprintf(stderr, " -s - Layer to select on program start\n");
|
|
fprintf(stderr, " -H [int] - Height fraction, one key row takes 1/x of the screen height\n");
|
|
fprintf(stderr, " -fn [font] - Set font (Xft, e.g: DejaVu Sans:bold:size=20)\n");
|
|
fprintf(stderr, " -g - Set the window position or size using the X geometry format\n");
|
|
exit(1);
|
|
}
|
|
|
|
void
|
|
setlayer(void)
|
|
{
|
|
numkeys = countkeys(layers[currentlayer]);
|
|
memcpy(&keys, layers[currentlayer], sizeof(Key) * numkeys);
|
|
countrows();
|
|
}
|
|
|
|
void
|
|
cyclelayer(void)
|
|
{
|
|
currentlayer++;
|
|
if (currentlayer >= numlayers)
|
|
currentlayer = 0;
|
|
printdbg("Cycling to layer %d\n", currentlayer);
|
|
setlayer();
|
|
updatekeys();
|
|
drawkeyboard();
|
|
}
|
|
|
|
void
|
|
togglelayer(void)
|
|
{
|
|
if (currentlayer > 0) {
|
|
currentlayer = 0;
|
|
} else if (numlayers > 1) {
|
|
currentlayer = 1;
|
|
}
|
|
printdbg("Toggling layer %d\n", currentlayer);
|
|
setlayer();
|
|
updatekeys();
|
|
drawkeyboard();
|
|
}
|
|
|
|
void
|
|
showoverlay(int idx)
|
|
{
|
|
int i, j;
|
|
|
|
printdbg("Showing overlay %d\n", idx);
|
|
|
|
/* unpress existing key (visually only) */
|
|
for (i = 0; i < numkeys; i++) {
|
|
if (keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
|
|
keys[i].pressed = 0;
|
|
drawkey(&keys[i], True);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = idx, j = 0; i < OVERLAYS; i++, j++) {
|
|
if (overlay[i].keysym == XK_Cancel) {
|
|
break;
|
|
}
|
|
/* certain modifier keys and basic keys are excluded from being overlayed: */
|
|
while (keys[j].keysym == 0 || keys[j].keysym == XK_Shift_L ||
|
|
keys[j].keysym == XK_Shift_R || keys[j].keysym == XK_Control_L ||
|
|
keys[j].keysym == XK_Control_R || keys[j].keysym == XK_Alt_L ||
|
|
keys[j].keysym == XK_Alt_R || keys[j].keysym == XK_BackSpace ||
|
|
keys[j].keysym == XK_Return || keys[j].keysym == XK_space ||
|
|
keys[j].keysym == XK_Cancel)
|
|
j++;
|
|
if (overlay[i].width > 1)
|
|
j += overlay[i].width - 1;
|
|
if (j >= numkeys)
|
|
break;
|
|
keys[j].label = overlay[i].label;
|
|
keys[j].label2 = overlay[i].label2;
|
|
keys[j].keysym = overlay[i].keysym;
|
|
keys[j].modifier = overlay[i].modifier;
|
|
keys[j].isoverlay = True;
|
|
}
|
|
currentoverlay = idx;
|
|
overlaykeysym = ispressingkeysym;
|
|
releaseprotect = 1;
|
|
updatekeys();
|
|
drawkeyboard();
|
|
XSync(dpy, False);
|
|
}
|
|
|
|
void
|
|
hideoverlay(void)
|
|
{
|
|
printdbg("Hiding overlay, overlay was #%d\n", currentoverlay);
|
|
currentoverlay = -1;
|
|
overlaykeysym = 0;
|
|
currentlayer--;
|
|
cyclelayer();
|
|
}
|
|
|
|
void
|
|
sigterm(int signo)
|
|
{
|
|
running = False;
|
|
sigtermd = True;
|
|
printdbg("SIGTERM received\n");
|
|
}
|
|
|
|
void
|
|
init_layers(char *layer_names_list, const char *initial_layer_name)
|
|
{
|
|
char *s;
|
|
int j, found;
|
|
|
|
if (layer_names_list == NULL) {
|
|
numlayers = LAYERS;
|
|
memcpy(&layers, &available_layers, sizeof(available_layers));
|
|
if (initial_layer_name != NULL) {
|
|
for (j = 0; j < LAYERS; j++) {
|
|
if (strcmp(layer_names[j], initial_layer_name) == 0) {
|
|
currentlayer = j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
s = strtok(layer_names_list, ",");
|
|
while (s != NULL) {
|
|
if (numlayers + 1 > LAYERS)
|
|
die("too many layers specified");
|
|
found = 0;
|
|
for (j = 0; j < LAYERS; j++) {
|
|
if (strcmp(layer_names[j], s) == 0) {
|
|
fprintf(stderr, "Adding layer %s\n", s);
|
|
layers[numlayers] = available_layers[j];
|
|
if (initial_layer_name != NULL && strcmp(layer_names[j], initial_layer_name) == 0) {
|
|
currentlayer = numlayers;
|
|
}
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
fprintf(stderr, "Undefined layer: %s\n", s);
|
|
exit(3);
|
|
}
|
|
numlayers++;
|
|
s = strtok(NULL,",");
|
|
}
|
|
}
|
|
setlayer();
|
|
}
|
|
|
|
void
|
|
printdbg(const char *fmt, ...)
|
|
{
|
|
if (!debug)
|
|
return;
|
|
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
fflush(stderr);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
char *initial_layer_name = NULL;
|
|
char *layer_names_list = NULL;
|
|
char *tmp;
|
|
int i, xr, yr, bitm;
|
|
unsigned int wr, hr;
|
|
|
|
signal(SIGTERM, sigterm);
|
|
|
|
if (OVERLAYS <= 1) {
|
|
enableoverlays = 0;
|
|
} else {
|
|
if ((tmp = getenv("SVKBD_ENABLEOVERLAYS")))
|
|
enableoverlays = atoi(tmp);
|
|
}
|
|
if ((tmp = getenv("SVKBD_LAYERS")))
|
|
layer_names_list = estrdup(tmp);
|
|
|
|
if ((tmp = getenv("SVKBD_HEIGHTFACTOR")))
|
|
heightfactor = atoi(tmp);
|
|
|
|
if ((tmp = getenv("SVKBD_PRESSONRELEASE"))) /* defaults to 1 */
|
|
pressonrelease = atoi(tmp);
|
|
|
|
/* parse command line arguments */
|
|
for (i = 1; argv[i]; i++) {
|
|
if (!strcmp(argv[i], "-v")) {
|
|
die("svkbd-"VERSION);
|
|
} else if (!strcmp(argv[i], "-d")) {
|
|
isdock = True;
|
|
} else if (!strncmp(argv[i], "-g", 2)) {
|
|
if (i >= argc - 1)
|
|
usage(argv[0]);
|
|
|
|
bitm = XParseGeometry(argv[++i], &xr, &yr, &wr, &hr);
|
|
if (bitm & XValue)
|
|
wx = xr;
|
|
if (bitm & YValue)
|
|
wy = yr;
|
|
if (bitm & WidthValue)
|
|
ww = (int)wr;
|
|
if (bitm & HeightValue)
|
|
wh = (int)hr;
|
|
if (bitm & XNegative && wx == 0)
|
|
wx = -1;
|
|
if (bitm & YNegative && wy == 0)
|
|
wy = -1;
|
|
} else if (!strcmp(argv[i], "-fn")) { /* font or font set */
|
|
if (i >= argc - 1)
|
|
usage(argv[0]);
|
|
fonts[0] = estrdup(argv[++i]);
|
|
} else if (!strcmp(argv[i], "-D")) {
|
|
debug = 1;
|
|
} else if (!strcmp(argv[i], "-h")) {
|
|
usage(argv[0]);
|
|
} else if (!strcmp(argv[i], "-O")) {
|
|
enableoverlays = 0;
|
|
} else if (!strcmp(argv[i], "-o")) {
|
|
printoutput = 1;
|
|
} else if (!strcmp(argv[i], "-n")) {
|
|
simulateoutput = 0;
|
|
} else if (!strcmp(argv[i], "-R")) {
|
|
pressonrelease = 0;
|
|
} else if (!strcmp(argv[i], "-l")) {
|
|
if (i >= argc - 1)
|
|
usage(argv[0]);
|
|
free(layer_names_list);
|
|
layer_names_list = estrdup(argv[++i]);
|
|
} else if (!strcmp(argv[i], "-s")) {
|
|
if (i >= argc - 1)
|
|
usage(argv[0]);
|
|
initial_layer_name = argv[++i];
|
|
} else if (!strcmp(argv[i], "-H")) {
|
|
if (i >= argc - 1)
|
|
usage(argv[0]);
|
|
heightfactor = atoi(argv[++i]);
|
|
} else {
|
|
fprintf(stderr, "Invalid argument: %s\n", argv[i]);
|
|
usage(argv[0]);
|
|
}
|
|
}
|
|
|
|
if (printoutput)
|
|
setbuf(stdout, NULL); /* unbuffered output */
|
|
|
|
if (heightfactor <= 0)
|
|
die("height factor must be a positive integer");
|
|
|
|
init_layers(layer_names_list, initial_layer_name);
|
|
|
|
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
|
|
fprintf(stderr, "warning: no locale support");
|
|
if (!(dpy = XOpenDisplay(0)))
|
|
die("cannot open display");
|
|
setup();
|
|
run();
|
|
cleanup();
|
|
XCloseDisplay(dpy);
|
|
free(layer_names_list);
|
|
|
|
return 0;
|
|
}
|