2011-11-06 10:43:48 -05:00
|
|
|
/*
|
|
|
|
* Small C program to execute arbitrary commands or write to pipes/files
|
|
|
|
* upon keypresses of arbitrary scancodes (useful for logitech and other
|
|
|
|
* multimedia keyboards.
|
|
|
|
* (C)2005, Dan Ponte
|
|
|
|
* BSD license
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <X11/Xlib.h>
|
2020-12-15 15:22:02 -05:00
|
|
|
#include <bsd/string.h>
|
2011-11-06 10:43:48 -05:00
|
|
|
|
|
|
|
#define EV_CMD 0x1
|
|
|
|
#define EV_WRITE 0x2
|
2020-12-15 15:22:02 -05:00
|
|
|
#define CONFIG_PATH "/.logikeysrc"
|
2011-11-06 10:43:48 -05:00
|
|
|
|
|
|
|
Display *d;
|
|
|
|
Window r;
|
|
|
|
struct keyentry {
|
|
|
|
int scancode;
|
|
|
|
int type;
|
|
|
|
int fd;
|
|
|
|
void *data;
|
|
|
|
size_t dlen;
|
|
|
|
char *command;
|
|
|
|
struct keyentry *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct keyentry *head = NULL, *last = NULL;
|
|
|
|
char buf[1024];
|
|
|
|
int wquit = 0;
|
|
|
|
|
|
|
|
int udom_open(fn) /* not really a udom, but oh well...sounds cool */
|
|
|
|
char *fn;
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = open(fn, O_APPEND|O_WRONLY);
|
|
|
|
if(fd == -1) {
|
|
|
|
perror("udopen");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qsig(s)
|
|
|
|
int s;
|
|
|
|
{
|
|
|
|
wquit = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void get_keys(h)
|
|
|
|
struct keyentry *h;
|
|
|
|
{
|
|
|
|
struct keyentry *c;
|
|
|
|
|
|
|
|
for(c = h; c != NULL; c = c->next) {
|
|
|
|
XGrabKey(d, c->scancode, AnyModifier, r, False, GrabModeAsync, GrabModeAsync);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void init_x(void)
|
|
|
|
{
|
|
|
|
char *dpy;
|
|
|
|
|
|
|
|
dpy = getenv("DISPLAY");
|
|
|
|
if(dpy == NULL) {
|
|
|
|
fprintf(stderr, "error: needs $DISPLAY\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
d = XOpenDisplay(dpy);
|
|
|
|
if(d == NULL) {
|
|
|
|
fprintf(stderr, "error: cannot open display %s\n", dpy);
|
|
|
|
exit(-2);
|
|
|
|
}
|
|
|
|
r = DefaultRootWindow(d);
|
|
|
|
if(!r) {
|
|
|
|
fprintf(stderr, "error: no root window\n");
|
|
|
|
exit(-3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void free_keys(h)
|
|
|
|
struct keyentry *h;
|
|
|
|
{
|
|
|
|
struct keyentry *n, *c = h;
|
|
|
|
|
|
|
|
while(c != NULL) {
|
|
|
|
n = c->next;
|
|
|
|
XUngrabKey(d, c->scancode, AnyModifier, r);
|
|
|
|
if(c->command != NULL)
|
|
|
|
free(c->command);
|
|
|
|
if(c->data != NULL)
|
|
|
|
free(c->data);
|
|
|
|
if(c->fd != 0)
|
|
|
|
close(c->fd);
|
|
|
|
free(c);
|
|
|
|
c = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int read_config_file(file, hp, lp)
|
|
|
|
char *file;
|
|
|
|
struct keyentry **hp;
|
|
|
|
struct keyentry **lp;
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
char *kc, *cmd, *r;
|
|
|
|
struct keyentry *new;
|
|
|
|
int lnum = 0;
|
|
|
|
|
|
|
|
if((f = fopen(file, "r")) == NULL) {
|
|
|
|
perror("logikeys");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
while(!feof(f)) {
|
|
|
|
char *nlt;
|
|
|
|
fgets(buf, 1023, f);
|
|
|
|
lnum++;
|
|
|
|
if(*buf == '#' || *buf == '\n' || *buf == '\0')
|
|
|
|
continue;
|
|
|
|
if((nlt = strrchr(buf, '\n')) != NULL)
|
|
|
|
*nlt = '\0';
|
|
|
|
|
|
|
|
kc = buf;
|
|
|
|
cmd = buf;
|
|
|
|
r = strsep(&cmd, ":");
|
|
|
|
if(*r == '\0') {
|
|
|
|
fprintf(stderr, "Syntax error at line %d!\n", lnum);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
if(cmd == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
new = calloc(1, sizeof(struct keyentry));
|
|
|
|
if(*hp == NULL) {
|
|
|
|
*hp = new;
|
|
|
|
*lp = new;
|
|
|
|
} else {
|
|
|
|
(*lp)->next = new;
|
|
|
|
*lp = new;
|
|
|
|
}
|
|
|
|
new->scancode = atoi(kc);
|
|
|
|
if(*cmd == '=') {
|
|
|
|
struct keyentry *ct;
|
|
|
|
int found = 0, tsc;
|
|
|
|
char *ssrc;
|
|
|
|
char *tpipe = cmd + 1;
|
|
|
|
char *tdata = tpipe;
|
|
|
|
|
|
|
|
ssrc = strsep(&tdata, "|");
|
|
|
|
if(*ssrc == '\0') {
|
|
|
|
fprintf(stderr, "Syntax error on line %d!\n", lnum);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
tsc = atoi(tpipe);
|
|
|
|
|
|
|
|
for(ct = *hp; ct != NULL; ct = ct->next) {
|
|
|
|
if(ct->scancode == tsc && ct->fd != 0) {
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!found) {
|
|
|
|
fprintf(stderr, "No such scancode defined with pipe %d (line %d)\n", tsc, lnum);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
new->type = EV_WRITE;
|
|
|
|
new->fd = ct->fd;
|
|
|
|
new->data = strdup(tdata);
|
|
|
|
new->dlen = strlen(new->data);
|
|
|
|
} else if(*cmd == '|') {
|
|
|
|
char *pipefn, *dat, *ssret;
|
|
|
|
|
|
|
|
pipefn = cmd + 1;
|
|
|
|
dat = pipefn;
|
|
|
|
ssret = strsep(&dat, "|");
|
|
|
|
if(*ssret == '\0') {
|
|
|
|
fprintf(stderr, "Syntax error on line %d!\n", lnum);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
if((new->fd = udom_open(pipefn)) == -1) {
|
|
|
|
/* not reached? */
|
|
|
|
fprintf(stderr, "Error opening pipe %s (line %d)\n", pipefn, lnum);
|
|
|
|
exit(-2);
|
|
|
|
}
|
|
|
|
new->type = EV_WRITE;
|
|
|
|
new->data = strdup(dat);
|
|
|
|
new->dlen = strlen(new->data);
|
|
|
|
} else {
|
|
|
|
new->type = EV_CMD;
|
|
|
|
new->command = strdup(cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void doevent(e)
|
|
|
|
struct keyentry *e;
|
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
size_t tl;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
switch(e->type) {
|
|
|
|
case EV_CMD:
|
|
|
|
tl = strlen(e->command) + 4;
|
|
|
|
cmd = malloc(tl);
|
|
|
|
strlcpy(cmd, e->command, tl);
|
|
|
|
strlcat(cmd, " &", tl);
|
|
|
|
rc = system(cmd);
|
|
|
|
free(cmd);
|
|
|
|
return;
|
|
|
|
case EV_WRITE:
|
|
|
|
if(write(e->fd, e->data, e->dlen) == -1) {
|
|
|
|
perror("ev_write");
|
|
|
|
wquit = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void handle_ev(k)
|
|
|
|
int k;
|
|
|
|
{
|
|
|
|
struct keyentry *c;
|
|
|
|
|
|
|
|
for(c = head; c != NULL; c = c->next) {
|
|
|
|
if(c->scancode == k) {
|
|
|
|
doevent(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ev_loop(void)
|
|
|
|
{
|
|
|
|
long mask = KeyPressMask;
|
|
|
|
XEvent ev;
|
|
|
|
|
|
|
|
while(!wquit) {
|
|
|
|
while(XCheckMaskEvent(d, mask, &ev)) {
|
|
|
|
handle_ev(ev.xkey.keycode);
|
|
|
|
}
|
|
|
|
usleep(100000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void usage(pn)
|
|
|
|
char *pn;
|
|
|
|
{
|
|
|
|
printf("%s: usage: %s [-h] [-v] configfile\n", pn, pn);
|
|
|
|
}
|
|
|
|
|
|
|
|
void version(pn)
|
|
|
|
char *pn;
|
|
|
|
{
|
|
|
|
printf("%s: logikeys v0.1\n(C)2005, Dan Ponte\n", pn);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(argc, argv)
|
|
|
|
int argc;
|
|
|
|
char **argv;
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
char *pn = *argv;
|
|
|
|
|
|
|
|
signal(SIGINT, qsig);
|
|
|
|
signal(SIGTERM, qsig);
|
|
|
|
while((c = getopt(argc, argv, "hv")) != -1)
|
|
|
|
switch(c) {
|
|
|
|
case 'h':
|
|
|
|
usage(*argv);
|
|
|
|
return 1;
|
|
|
|
case 'v':
|
|
|
|
version(*argv);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
if(argc != 1) {
|
2020-12-15 15:22:02 -05:00
|
|
|
char *home;
|
|
|
|
char *fullpath;
|
|
|
|
size_t pathlen;
|
|
|
|
|
|
|
|
if(!(home = getenv("HOME"))) {
|
|
|
|
fprintf(stderr, "cannot get $HOME!\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
2011-11-06 10:43:48 -05:00
|
|
|
|
2020-12-15 15:22:02 -05:00
|
|
|
pathlen = (strlen(home) + sizeof(CONFIG_PATH) + 2) * sizeof(char);
|
|
|
|
if(!(fullpath = malloc(pathlen))) {
|
|
|
|
perror("malloc");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
strlcpy(fullpath, home, pathlen);
|
|
|
|
strlcat(fullpath, CONFIG_PATH, pathlen);
|
|
|
|
read_config_file(fullpath, &head, &last);
|
|
|
|
free(fullpath);
|
|
|
|
} else {
|
|
|
|
read_config_file(*argv, &head, &last);
|
|
|
|
}
|
2011-11-06 10:43:48 -05:00
|
|
|
|
|
|
|
init_x();
|
|
|
|
get_keys(head);
|
|
|
|
ev_loop();
|
|
|
|
free_keys(head);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|