sxhkd-rc

[fork] simple X hotkey daemon (but for the rc shell)
Log | Files | Refs | README | LICENSE

commit 07595089fee6aac5953af8acb2f2e98682fa03d5
parent 1b0df27723ffc9e2238028ce0e5fb4d4be8888e2
Author: Bastien Dejean <nihilhill@gmail.com>
Date:   Sun,  5 Apr 2015 17:27:23 +0200

Relay motion events as messages on a socket

Spawning shouldn't be used for motion events:
- It might generate hundreds of parallel processes per second.
- It randomly alters the order of the pointer coordinates.

Diffstat:
Mdoc/sxhkd.1 | 20++++++++++++++------
Mdoc/sxhkd.1.txt | 12++++++++----
Mgrab.c | 2+-
Mparse.c | 3---
Mparse.h | 1-
Msxhkd.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msxhkd.h | 8+++++++-
7 files changed, 88 insertions(+), 34 deletions(-)

diff --git a/doc/sxhkd.1 b/doc/sxhkd.1 @@ -2,12 +2,12 @@ .\" Title: sxhkd .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> -.\" Date: 03/20/2015 +.\" Date: 04/05/2015 .\" Manual: Sxhkd Manual .\" Source: Sxhkd 0.5.5 .\" Language: English .\" -.TH "SXHKD" "1" "03/20/2015" "Sxhkd 0\&.5\&.5" "Sxhkd Manual" +.TH "SXHKD" "1" "04/05/2015" "Sxhkd 0\&.5\&.5" "Sxhkd Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -69,6 +69,16 @@ Read the main configuration from the given file\&. Redirect the commands output to the given file\&. .RE .PP +\fB\-o\fR \fIMOTION_SOCKET\fR +.RS 4 +Write motion messages to the given SOCKET\&. +.RE +.PP +\fB\-g\fR \fIMOTION_MSG_TPL\fR +.RS 4 +Motion messages template\&. +.RE +.PP \fB\-s\fR \fISTATUS_FIFO\fR .RS 4 Output status information to the given FIFO\&. @@ -162,8 +172,6 @@ The \fIEscape\fR key can be used to abort a chord chain\&. .sp If \fB@\fR is added at the beginning of the keysym, the command will be run on key release events, otherwise on key press events\&. .sp -If \fB!\fR is added at the beginning of the keysym, the command will be run on motion notify events and must contain two integer conversion specifications which will be replaced by the \fIx\fR and \fIy\fR coordinates of the pointer relative to the root window referential (the only valid button keysyms for this type of hotkeys are: \fIbutton1\fR, \&..., \fIbutton5\fR)\&. -.sp If \fB~\fR is added at the beginning of the keysym, the captured event will be replayed for the other clients\&. .sp Mouse hotkeys can be defined by using one of the following special keysym names: \fIbutton1\fR, \fIbutton2\fR, \fIbutton3\fR, \&..., \fIbutton24\fR\&. @@ -206,8 +214,8 @@ super + {alt,ctrl,alt + ctrl} + XF86Eject super + button{1\-3} bspc pointer \-g {move,resize_side,resize_corner} -super + !button{1\-3} - bspc pointer \-t %i %i +super + @button{1\-3} + bspc pointer \-u super + {_,shift + }{h,j,k,l} bspc window {\-f,\-s} {left,down,up,right} diff --git a/doc/sxhkd.1.txt b/doc/sxhkd.1.txt @@ -42,6 +42,12 @@ Options *-r* _REDIR_FILE_:: Redirect the commands output to the given file. +*-o* _MOTION_SOCKET_:: + Write motion messages to the given SOCKET. + +*-g* _MOTION_MSG_TPL_:: + Motion messages template. + *-s* _STATUS_FIFO_:: Output status information to the given FIFO. @@ -100,8 +106,6 @@ The _Escape_ key can be used to abort a chord chain. If *@* is added at the beginning of the keysym, the command will be run on key release events, otherwise on key press events. -If *!* is added at the beginning of the keysym, the command will be run on motion notify events and must contain two integer conversion specifications which will be replaced by the _x_ and _y_ coordinates of the pointer relative to the root window referential (the only valid button keysyms for this type of hotkeys are: _button1_, …, _button5_). - If *~* is added at the beginning of the keysym, the captured event will be replayed for the other clients. Mouse hotkeys can be defined by using one of the following special keysym names: _button1_, _button2_, _button3_, …, _button24_. @@ -144,8 +148,8 @@ super + {alt,ctrl,alt + ctrl} + XF86Eject super + button{1-3} bspc pointer -g {move,resize_side,resize_corner} -super + !button{1-3} - bspc pointer -t %i %i +super + @button{1-3} + bspc pointer -u super + {_,shift + }{h,j,k,l} bspc window {-f,-s} {left,down,up,right} diff --git a/grab.c b/grab.c @@ -75,7 +75,7 @@ void grab_key_button_checked(xcb_keycode_t keycode, xcb_button_t button, uint16_ if (button == XCB_NONE) err = xcb_request_check(dpy, xcb_grab_key_checked(dpy, true, root, modfield, keycode, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_SYNC)); else - err = xcb_request_check(dpy, xcb_grab_button_checked(dpy, true, root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, button, modfield)); + err = xcb_request_check(dpy, xcb_grab_button_checked(dpy, true, root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, button, modfield)); unsigned int value = (button == XCB_NONE ? keycode : button); char *type = (button == XCB_NONE ? "key" : "button"); if (err != NULL) { diff --git a/parse.c b/parse.c @@ -2661,9 +2661,6 @@ bool parse_chain(char *string, chain_t *chain) if (name[0] == RELEASE_PREFIX) { event_type = XCB_KEY_RELEASE; offset++; - } else if (name[0] == MOTION_PREFIX) { - event_type = XCB_MOTION_NOTIFY; - offset++; } else if (name[0] == REPLAY_PREFIX) { replay_event = true; offset++; diff --git a/parse.h b/parse.h @@ -28,7 +28,6 @@ #include "sxhkd.h" #define RELEASE_PREFIX '@' -#define MOTION_PREFIX '!' #define REPLAY_PREFIX '~' #define START_COMMENT '#' #define MAGIC_INHIBIT '\\' diff --git a/sxhkd.c b/sxhkd.c @@ -29,6 +29,7 @@ #include <unistd.h> #include <sys/stat.h> #include <sys/select.h> +#include <sys/socket.h> #include <fcntl.h> #include <signal.h> #include <stdbool.h> @@ -40,23 +41,27 @@ int main(int argc, char *argv[]) { char opt; char *fifo_path = NULL; + char *socket_path = NULL; status_fifo = NULL; config_path = NULL; mapping_count = 0; timeout = TIMEOUT; + sock_address.sun_family = AF_UNIX; + sock_address.sun_path[0] = 0; + snprintf(motion_msg_tpl, sizeof(motion_msg_tpl), "%s", MOTION_MSG_TPL); unsigned int max_freq = 0; motion_interval = 0; last_motion_time = 0; redir_fd = -1; - while ((opt = getopt(argc, argv, "vhm:t:c:r:s:f:")) != (char)-1) { + while ((opt = getopt(argc, argv, "vhm:t:c:r:s:f:o:g:")) != (char)-1) { switch (opt) { case 'v': printf("%s\n", VERSION); exit(EXIT_SUCCESS); break; case 'h': - printf("sxhkd [-h|-v|-m COUNT|-t TIMEOUT|-c CONFIG_FILE|-r REDIR_FILE|-s STATUS_FIFO] [EXTRA_CONFIG ...]\n"); + printf("sxhkd [-h|-v|-m COUNT|-t TIMEOUT|-c CONFIG_FILE|-r REDIR_FILE|-s STATUS_FIFO|-o MOTION_SOCKET|-g MOTION_MSG_TPL] [EXTRA_CONFIG ...]\n"); exit(EXIT_SUCCESS); break; case 'm': @@ -77,6 +82,12 @@ int main(int argc, char *argv[]) case 's': fifo_path = optarg; break; + case 'o': + socket_path = optarg; + break; + case 'g': + snprintf(motion_msg_tpl, sizeof(motion_msg_tpl), "%s", optarg); + break; case 'f': if (sscanf(optarg, "%u", &max_freq) != 1) warn("Can't parse maximum pointer frequency.\n"); @@ -97,6 +108,23 @@ int main(int argc, char *argv[]) snprintf(config_file, sizeof(config_file), "%s", config_path); } + if (socket_path == NULL) { + socket_path = getenv(SOCKET_ENV); + } + + if (socket_path == NULL) { + char *host = NULL; + int dn = 0, sn = 0; + if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) { + snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), SOCKET_PATH_TPL, host, dn, sn); + } else { + warn("Failed to set motion socket address."); + } + free(host); + } else { + snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", socket_path); + } + if (fifo_path != NULL) { int fifo_fd = open(fifo_path, O_RDWR | O_NONBLOCK); if (fifo_fd != -1) @@ -148,7 +176,7 @@ int main(int argc, char *argv[]) key_button_event(evt, event_type); break; case XCB_MOTION_NOTIFY: - motion_notify(evt, event_type); + motion_notify(evt); break; case XCB_MAPPING_NOTIFY: mapping_notify(evt); @@ -229,30 +257,42 @@ void key_button_event(xcb_generic_event_t *evt, uint8_t event_type) xcb_flush(dpy); } -void motion_notify(xcb_generic_event_t *evt, uint8_t event_type) +void motion_notify(xcb_generic_event_t *evt) { xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt; - /* PRINTF("motion notify %X %X %u\n", e->child, e->detail, e->state); */ + PRINTF("motion notify %X %X %u %i %i\n", e->child, e->detail, e->state, e->root_x, e->root_y); if (motion_interval > 0 && (e->time - last_motion_time) < motion_interval) return; last_motion_time = e->time; - uint16_t lockfield = num_lock | caps_lock | scroll_lock; - uint16_t buttonfield = e->state >> 8; - uint16_t modfield = e->state & ~lockfield & MOD_STATE_FIELD; - xcb_button_t button = 1; - while (~buttonfield & 1 && button < 5) { - buttonfield = buttonfield >> 1; - button++; + char msg[MAXLEN]; + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + return; } - hotkey_t *hk = find_hotkey(XCB_NO_SYMBOL, button, modfield, event_type, NULL); - if (hk != NULL) { - char command[2 * MAXLEN]; - snprintf(command, sizeof(command), hk->command, e->root_x, e->root_y); - run(command); + if (connect(fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) { + close(fd); + return; } + int len = sizeof(msg), i = 0; + for (int j = 0, c = motion_msg_tpl[j]; c && i < len; j++, c = motion_msg_tpl[j]) { + if (c == ' ') { + msg[i++] = 0; + } else if (c == 'X') { + i += snprintf(msg+i, len-i, "%i", e->root_x); + } else if (c == 'Y') { + i += snprintf(msg+i, len-i, "%i", e->root_y); + } else { + msg[i++] = c; + } + } + if (i >= len) { + i--; + } + msg[i] = 0; + send(fd, msg, i+1, 0); + close(fd); } - void mapping_notify(xcb_generic_event_t *evt) { if (!mapping_count) diff --git a/sxhkd.h b/sxhkd.h @@ -28,6 +28,7 @@ #include <xcb/xcb_keysyms.h> #include <stdio.h> #include <stdbool.h> +#include <sys/un.h> #include "types.h" #include "helpers.h" @@ -35,6 +36,9 @@ #define SXHKD_SHELL_ENV "SXHKD_SHELL" #define SHELL_ENV "SHELL" #define CONFIG_PATH "sxhkd/sxhkdrc" +#define SOCKET_ENV "BSPWM_SOCKET" +#define SOCKET_PATH_TPL "/tmp/bspwm%s_%i_%i-socket" +#define MOTION_MSG_TPL "pointer -t X Y" #define HOTKEY_PREFIX 'H' #define COMMAND_PREFIX 'C' #define TIMEOUT_PREFIX 'T' @@ -51,6 +55,8 @@ char **extra_confs; int num_extra_confs; int redir_fd; FILE *status_fifo; +struct sockaddr_un sock_address; +char motion_msg_tpl[MAXLEN]; char progress[3 * MAXLEN]; int mapping_count; int timeout; @@ -66,7 +72,7 @@ uint16_t caps_lock; uint16_t scroll_lock; void key_button_event(xcb_generic_event_t *evt, uint8_t event_type); -void motion_notify(xcb_generic_event_t *evt, uint8_t event_type); +void motion_notify(xcb_generic_event_t *evt); void mapping_notify(xcb_generic_event_t *evt); void setup(void); void cleanup(void);