wmii/cmd/wmiikeys.c

475 lines
11 KiB
C
Raw Normal View History

2005-11-18 18:54:58 +03:00
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <X11/Xproto.h>
#include <X11/Xutil.h>
#include "wmii.h"
/* array indexes for file pointers */
typedef enum {
K_CTL,
K_LOOKUP,
K_GRAB_KB,
K_FONT,
K_FG_COLOR,
2005-11-18 18:54:58 +03:00
K_BG_COLOR,
K_BORDER_COLOR,
K_LAST
2005-12-05 01:45:59 +03:00
} KeyIndexes;
2005-11-18 18:54:58 +03:00
typedef struct Shortcut Shortcut;
struct Shortcut {
2005-12-05 01:45:59 +03:00
char name[MAX_BUF];
unsigned long mod;
KeyCode key;
File *cmdfile;
Shortcut *snext;
Shortcut *next;
2005-11-18 18:54:58 +03:00
};
static IXPServer *ixps = nil;
2005-11-18 18:54:58 +03:00
static Display *dpy;
2005-12-05 01:45:59 +03:00
static GC gc;
static Window win;
static Window root;
2005-11-18 18:54:58 +03:00
static XRectangle krect;
static XRectangle rect;
2005-12-05 01:45:59 +03:00
static int screen_num;
static char *sockfile = nil;
static Shortcut *shortcuts = nil;
2005-12-05 01:45:59 +03:00
static File *files[K_LAST];
static int grabkb = 0;
2005-11-18 18:54:58 +03:00
static unsigned int num_lock_mask, valid_mask;
2005-12-05 01:45:59 +03:00
static char buf[MAX_BUF];
static XFontStruct *font;
2005-11-18 18:54:58 +03:00
2005-12-05 01:45:59 +03:00
static void grab_shortcut(Shortcut * s);
static void ungrab_shortcut(Shortcut * s);
static void draw_shortcut_box(char *text);
static void quit(void *obj, char *arg);
2005-11-18 18:54:58 +03:00
2005-12-05 01:45:59 +03:00
static Action acttbl[] = {
2005-11-18 18:54:58 +03:00
{"quit", quit},
{0, 0}
};
2005-12-05 01:45:59 +03:00
static char *version[] = {
"wmiikeys - window manager improved keys - " VERSION "\n"
2005-12-05 01:45:59 +03:00
" (C)opyright MMIV-MMV Anselm R. Garbe\n", 0
2005-11-18 18:54:58 +03:00
};
2005-12-05 01:45:59 +03:00
static void usage()
2005-11-18 18:54:58 +03:00
{
fprintf(stderr, "%s",
"usage: wmiikeys [-s <socket file>] [-v]\n"
" -s socket file (default: /tmp/.ixp-$USER/wmiikeys-$WMII_IDENT)\n"
2005-12-05 01:45:59 +03:00
" -v version info\n");
2005-11-18 18:54:58 +03:00
exit(1);
}
static void center()
{
krect.x = rect.width / 2 - krect.width / 2;
krect.y = rect.height / 2 - krect.height / 2;
}
2005-11-18 18:54:58 +03:00
/* grabs shortcut on all screens */
2005-12-05 01:45:59 +03:00
static void grab_shortcut(Shortcut * s)
2005-11-18 18:54:58 +03:00
{
XGrabKey(dpy, s->key, s->mod, root,
2005-12-05 01:45:59 +03:00
True, GrabModeAsync, GrabModeAsync);
2005-11-18 18:54:58 +03:00
if (num_lock_mask) {
XGrabKey(dpy, s->key, s->mod | num_lock_mask, root,
2005-12-05 01:45:59 +03:00
True, GrabModeAsync, GrabModeAsync);
2005-11-18 18:54:58 +03:00
XGrabKey(dpy, s->key, s->mod | num_lock_mask | LockMask, root,
2005-12-05 01:45:59 +03:00
True, GrabModeAsync, GrabModeAsync);
2005-11-18 18:54:58 +03:00
}
XSync(dpy, False);
}
/*
* don't handle evil keys anymore, just define more shortcuts if you cannot
* live without evil key handling
*/
2005-12-05 01:45:59 +03:00
static void ungrab_shortcut(Shortcut * s)
2005-11-18 18:54:58 +03:00
{
XUngrabKey(dpy, s->key, s->mod, root);
if (num_lock_mask) {
XUngrabKey(dpy, s->key, s->mod | num_lock_mask, root);
2005-12-05 01:45:59 +03:00
XUngrabKey(dpy, s->key, s->mod | num_lock_mask | LockMask, root);
2005-11-18 18:54:58 +03:00
}
XSync(dpy, False);
}
2005-12-05 01:45:59 +03:00
static void create_shortcut(File * f)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
static char *chain[8];
char *k;
size_t i, toks;
Shortcut *s = 0, *r = 0;
2005-11-18 18:54:58 +03:00
cext_strlcpy(buf, f->name, sizeof(buf));
toks = cext_tokenize(chain, 8, buf, ',');
2005-11-18 18:54:58 +03:00
for (i = 0; i < toks; i++) {
if (!s)
r = s = cext_emallocz(sizeof(Shortcut));
2005-11-18 18:54:58 +03:00
else {
s->snext = cext_emallocz(sizeof(Shortcut));
s = s->snext;
2005-11-18 18:54:58 +03:00
}
cext_strlcpy(s->name, chain[i], MAX_BUF);
2005-11-18 18:54:58 +03:00
k = strrchr(chain[i], '-');
if (k)
k++;
else
k = chain[i];
s->key = XKeysymToKeycode(dpy, XStringToKeysym(k));
s->mod = blitz_strtomod(chain[i]);
}
if (r) {
s->cmdfile = f;
if (!shortcuts)
shortcuts = r;
else {
for (s = shortcuts; s && s->next; s = s->next);
s->next = r;
}
2005-11-18 18:54:58 +03:00
grab_shortcut(r);
}
}
2005-12-05 01:45:59 +03:00
static void destroy_shortcut(Shortcut * s, int ungrab)
2005-11-18 18:54:58 +03:00
{
if (s->snext)
destroy_shortcut(s->snext, 0);
2005-11-18 18:54:58 +03:00
if (ungrab)
ungrab_shortcut(s);
free(s);
}
2005-12-05 01:45:59 +03:00
static void next_keystroke(unsigned long *mod, KeyCode * key)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
XEvent e;
KeySym sym;
2005-11-18 18:54:58 +03:00
*mod = 0;
do {
XMaskEvent(dpy, KeyPressMask, &e);
*mod |= e.xkey.state & valid_mask;
*key = (KeyCode) e.xkey.keycode;
sym = XKeycodeToKeysym(dpy, e.xkey.keycode, 0);
} while (IsModifierKey(sym));
}
2005-12-05 01:45:59 +03:00
static void emulate_key_press(unsigned long mod, KeyCode key)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
XEvent e;
Window client_win;
int revert;
2005-11-18 18:54:58 +03:00
XGetInputFocus(dpy, &client_win, &revert);
e.xkey.type = KeyPress;
e.xkey.time = CurrentTime;
e.xkey.window = client_win;
e.xkey.display = dpy;
e.xkey.state = mod;
e.xkey.keycode = key;
XSendEvent(dpy, client_win, True, KeyPressMask, &e);
e.xkey.type = KeyRelease;
XSendEvent(dpy, client_win, True, KeyReleaseMask, &e);
XSync(dpy, False);
}
static void handle_shortcut_chain(Window w, Shortcut * processed, char *prefix, int grab)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
unsigned long mod;
KeyCode key;
Shortcut *s = processed->snext;
2005-11-18 18:54:58 +03:00
if (grab) {
XGrabKeyboard(dpy, w, True, GrabModeAsync, GrabModeAsync, CurrentTime);
2005-11-18 18:54:58 +03:00
XMapRaised(dpy, win);
}
draw_shortcut_box(prefix);
next_keystroke(&mod, &key);
if ((processed->mod == mod) && (processed->key == key)) {
/* double shortcut */
emulate_key_press(mod, key);
} else if ((s->mod == mod) && (s->key == key)) {
if (s->cmdfile && s->cmdfile->content)
wmii_spawn(dpy, s->cmdfile->content);
else if (s->snext) {
2005-11-18 18:54:58 +03:00
snprintf(buf, sizeof(buf), "%s/%s", prefix, s->name);
handle_shortcut_chain(w, s, buf, 0);
}
}
if (grab) {
XUngrabKeyboard(dpy, CurrentTime);
XUnmapWindow(dpy, win);
XSync(dpy, False);
}
}
2005-12-05 01:45:59 +03:00
static void handle_shortcut_gkb(Window w, unsigned long mod, KeyCode key)
2005-11-18 18:54:58 +03:00
{
Shortcut *s;
2005-11-18 18:54:58 +03:00
if (!files[K_LOOKUP]->content)
return;
2005-12-17 12:29:27 +03:00
for (s = shortcuts; s && ((s->mod != mod) || (s->key != key)); s = s->next);
if (s && s->cmdfile && s->cmdfile->content) {
wmii_spawn(dpy, s->cmdfile->content);
return;
2005-11-18 18:54:58 +03:00
}
XBell(dpy, 0);
}
2005-12-05 01:45:59 +03:00
static void handle_shortcut(Window w, unsigned long mod, KeyCode key)
2005-11-18 18:54:58 +03:00
{
Shortcut *s;
2005-11-18 18:54:58 +03:00
if (!files[K_LOOKUP]->content)
return;
2005-12-17 12:29:27 +03:00
for (s = shortcuts; s && ((s->mod != mod) || (s->key != key)); s = s->next);
if (s && s->cmdfile && s->cmdfile->content) {
wmii_spawn(dpy, s->cmdfile->content);
return;
2005-11-18 18:54:58 +03:00
}
if (s && s->snext)
2005-11-18 18:54:58 +03:00
handle_shortcut_chain(w, s, s->name, 1);
}
2005-12-05 01:45:59 +03:00
static void quit(void *obj, char *arg)
2005-11-18 18:54:58 +03:00
{
ixps->runlevel = SHUTDOWN;
}
2005-12-05 01:45:59 +03:00
static void update()
2005-11-18 18:54:58 +03:00
{
Shortcut *s;
2005-12-05 01:45:59 +03:00
File *f, *p;
2005-11-18 18:54:58 +03:00
if (!files[K_LOOKUP]->content)
return;
f = ixp_walk(ixps, files[K_LOOKUP]->content);
if (!f || !is_directory(f))
2005-12-05 01:45:59 +03:00
return; /* cannot update */
2005-11-18 18:54:58 +03:00
/* destroy existing shortcuts if any */
while ((s = shortcuts)) {
shortcuts = shortcuts->next;
destroy_shortcut(s, 1);
}
shortcuts = nil;
2005-11-18 18:54:58 +03:00
if (grabkb) {
2005-12-17 12:29:27 +03:00
XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime);
2005-11-18 18:54:58 +03:00
return;
}
/* create new shortcuts */
for (p = f->content; p; p = p->next)
create_shortcut(p);
}
/*
* Function assumes following fs-structure:
*
* /box/style/font "<value>"
* /box/style/fgcolor "#RRGGBBAA"
* /box/style/bgcolor "#RRGGBBAA"
* /box/style/bordercolor "#RRGGBBAA"
2005-11-18 18:54:58 +03:00
*/
2005-12-05 01:45:59 +03:00
static void draw_shortcut_box(char *text)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
Draw d = { 0 };
2005-11-18 18:54:58 +03:00
d.font = font;
2005-11-18 18:54:58 +03:00
krect.width = XTextWidth(d.font, text, strlen(text)) + krect.height;
krect.height = font->ascent + font->descent + 4;
2005-11-18 18:54:58 +03:00
center();
XMoveResizeWindow(dpy, win, krect.x, krect.y, krect.width, krect.height);
2005-11-18 18:54:58 +03:00
/* default stuff */
d.gc = gc;
d.drawable = win;
d.data = text;
d.rect.y = 0;
d.rect.width = krect.width;
d.rect.height = krect.height;
d.bg = blitz_loadcolor(dpy, screen_num, files[K_BG_COLOR]->content);
d.fg = blitz_loadcolor(dpy, screen_num, files[K_FG_COLOR]->content);
d.border = blitz_loadcolor(dpy, screen_num, files[K_BORDER_COLOR]->content);
2005-11-18 18:54:58 +03:00
blitz_drawlabel(dpy, &d);
}
2005-12-05 01:45:59 +03:00
static void check_event(Connection * c)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
XEvent ev;
2005-11-18 18:54:58 +03:00
while (XPending(dpy)) {
XNextEvent(dpy, &ev);
switch (ev.type) {
case KeyPress:
ev.xkey.state &= valid_mask;
if (grabkb)
handle_shortcut_gkb(root, ev.xkey.state,
2005-12-05 01:45:59 +03:00
(KeyCode) ev.xkey.keycode);
2005-11-18 18:54:58 +03:00
else
handle_shortcut(root, ev.xkey.state,
2005-12-05 01:45:59 +03:00
(KeyCode) ev.xkey.keycode);
2005-11-18 18:54:58 +03:00
break;
case KeymapNotify:
update();
break;
default:
break;
}
}
}
2005-12-05 01:45:59 +03:00
static void handle_after_write(IXPServer * s, File * f)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
int i;
size_t len;
2005-11-18 18:54:58 +03:00
if (f == files[K_CTL]) {
for (i = 0; acttbl[i].name; i++) {
len = strlen(acttbl[i].name);
if (!strncmp(acttbl[i].name, (char *) f->content, len)) {
if (strlen(f->content) > len) {
acttbl[i].func(0, &((char *) f->content)[len + 1]);
} else {
acttbl[i].func(0, 0);
}
break;
}
}
} else if (f == files[K_GRAB_KB]) {
2005-12-11 17:47:23 +03:00
grabkb = blitz_strtonum(files[K_GRAB_KB]->content, 0, 1);
2005-11-18 18:54:58 +03:00
if (!grabkb) {
XUngrabKeyboard(dpy, CurrentTime);
XUnmapWindow(dpy, win);
XSync(dpy, False);
} else
update();
} else if (f == files[K_FONT]) {
XFreeFont(dpy, font);
font = blitz_getfont(dpy, files[K_FONT]->content);
2005-12-09 01:48:32 +03:00
krect = rect;
krect.height = font->ascent + font->descent + 4;
center();
XMoveResizeWindow(dpy, win, krect.x, krect.y, krect.width, krect.height);
2005-11-18 18:54:58 +03:00
} else if (f == files[K_LOOKUP]) {
update();
}
check_event(0);
}
static int dummy_error_handler(Display * dpy, XErrorEvent * err)
{
return 0;
}
int main(int argc, char *argv[])
2005-11-18 18:54:58 +03:00
{
int i;
2005-11-18 18:54:58 +03:00
XSetWindowAttributes wa;
2005-12-05 01:45:59 +03:00
XGCValues gcv;
2005-11-18 18:54:58 +03:00
/* command line args */
for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stdout, "%s", version[0]);
exit(0);
break;
case 's':
if (i + 1 < argc)
sockfile = argv[++i];
else
usage();
break;
default:
usage();
break;
}
}
dpy = XOpenDisplay(0);
if (!dpy) {
fprintf(stderr, "%s", "wmiikeys: cannot open display\n");
exit(1);
}
XSetErrorHandler(dummy_error_handler);
screen_num = DefaultScreen(dpy);
2005-11-18 18:54:58 +03:00
/* init */
ixps = wmii_setup_server(sockfile);
2005-11-18 18:54:58 +03:00
if (!(files[K_CTL] = ixp_create(ixps, "/ctl"))) {
perror("wmiikeys: cannot connect IXP server");
2005-11-18 18:54:58 +03:00
exit(1);
}
files[K_CTL]->after_write = handle_after_write;
files[K_LOOKUP] = ixp_create(ixps, "/lookup");
files[K_LOOKUP]->after_write = handle_after_write;
files[K_GRAB_KB] = wmii_create_ixpfile(ixps, "/grabkeyb", "0");
2005-11-18 18:54:58 +03:00
files[K_GRAB_KB]->after_write = handle_after_write;
files[K_FONT] = wmii_create_ixpfile(ixps, "/box/font", BLITZ_FONT);
files[K_FONT]->after_write = handle_after_write;
font = blitz_getfont(dpy, files[K_FONT]->content);
files[K_FG_COLOR] = wmii_create_ixpfile(ixps, "/box/fgcolor", BLITZ_SEL_FG_COLOR);
files[K_BG_COLOR] = wmii_create_ixpfile(ixps, "/box/bgcolor", BLITZ_SEL_BG_COLOR);
files[K_BORDER_COLOR] = wmii_create_ixpfile(ixps, "/box/bordercolor", BLITZ_SEL_BORDER_COLOR);
2005-11-18 18:54:58 +03:00
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask =
ExposureMask | SubstructureRedirectMask | SubstructureNotifyMask;
root = RootWindow(dpy, screen_num);
rect.x = rect.y = 0;
rect.width = DisplayWidth(dpy, screen_num);
rect.height = DisplayHeight(dpy, screen_num);
krect.x = krect.y = 0;
krect.width = krect.height = 1;
2005-11-18 18:54:58 +03:00
wmii_init_lock_modifiers(dpy, &valid_mask, &num_lock_mask);
2005-11-18 18:54:58 +03:00
win = XCreateWindow(dpy, RootWindow(dpy, screen_num), krect.x, krect.y,
krect.width, krect.height, 0, DefaultDepth(dpy, screen_num),
2005-12-05 01:45:59 +03:00
CopyFromParent, DefaultVisual(dpy, screen_num),
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
2005-11-18 18:54:58 +03:00
XDefineCursor(dpy, win, XCreateFontCursor(dpy, XC_left_ptr));
XSync(dpy, False);
gcv.function = GXcopy;
gcv.graphics_exposures = False;
gc = XCreateGC(dpy, win, 0, 0);
/* main event loop */
run_server_with_fd_support(ixps, ConnectionNumber(dpy),
2005-12-05 01:45:59 +03:00
check_event, 0);
2005-11-18 18:54:58 +03:00
deinit_server(ixps);
XFreeGC(dpy, gc);
XCloseDisplay(dpy);
return 0;
}