From 5f151761c3065c661403f8f2d6a33a42d1199da6 Mon Sep 17 00:00:00 2001 From: aszlig Date: Fri, 24 Feb 2012 18:03:52 +0100 Subject: [PATCH 01/52] Actually include playback in _real_ example config. --- keynavrc | 1 + 1 file changed, 1 insertion(+) diff --git a/keynavrc b/keynavrc index d76499f..c7ddf8a 100644 --- a/keynavrc +++ b/keynavrc @@ -67,6 +67,7 @@ ctrl+n cut-right,cut-down # Record keynav actions q record +shift+at playback ### Example using the 'sh' command. # Make firefox the active window From d1cfcd2e9adce4b51aaff142b1733c9e4c54752b Mon Sep 17 00:00:00 2001 From: Shou Ya Date: Fri, 21 Jun 2013 13:51:01 +0800 Subject: [PATCH 02/52] bugfix: failed when the last char in the config is not a newline --- keynav.c | 69 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/keynav.c b/keynav.c index d5edc3e..15cc30f 100644 --- a/keynav.c +++ b/keynav.c @@ -1,5 +1,5 @@ /* - * keynav - Keyboard navigation tool. + * keynav - Keyboard navigation tool. * * XXX: Merge 'wininfo' and 'wininfo_history'. The latest history entry is the * same as wininfo, so use that instead. @@ -216,7 +216,7 @@ dispatch_t dispatch[] = { // Mouse activity "warp", cmd_warp, - "click", cmd_click, + "click", cmd_click, "doubleclick", cmd_doubleclick, "drag", cmd_drag, @@ -225,7 +225,7 @@ dispatch_t dispatch[] = { "daemonize", cmd_daemonize, "sh", cmd_shell, "start", cmd_start, - "end", cmd_end, + "end", cmd_end, "history-back", cmd_history_back, "quit", cmd_quit, "restart", cmd_restart, @@ -238,7 +238,7 @@ typedef struct keybinding { char *commands; int keycode; int mods; -} keybinding_t; +} keybinding_t; GPtrArray *keybindings = NULL; @@ -379,7 +379,7 @@ void addbinding(int keycode, int mods, char *commands) { if (recordings_filename != NULL && strcmp(recordings_filename, newrecordingpath)) { free(newrecordingpath); - fprintf(stderr, + fprintf(stderr, "Recordings file already set to '%s', you tried to\n" "set it to '%s'. Keeping original value.\n", recordings_filename, path); @@ -407,7 +407,7 @@ void parse_config_file(const char* file) { free(rcfile); return; } else { - fprintf(stderr, + fprintf(stderr, "No HOME set in environment. Can't expand '%s' (fatal error)\n", file); /* This is fatal. */ @@ -426,8 +426,12 @@ void parse_config_file(const char* file) { /* fopen succeeded */ while (fgets(line, LINEBUF_SIZE, fp) != NULL) { lineno++; + /* Kill the newline */ - *(line + strlen(line) - 1) = '\0'; + while (line[strlen(line) - 1] == '\n' || + line[strlen(line) - 1] == '\r') + *(line + strlen(line) - 1) = '\0'; + if (parse_config_line(line) != 0) { fprintf(stderr, "Error with config %s:%d: %s\n", file, lineno, line); } @@ -506,7 +510,7 @@ int parse_config_line(char *orig_line) { /* syntax: * keysequence cmd1,cmd2,cmd3 * - * ex: + * ex: * ctrl+semicolon start * space warp * semicolon warp,click @@ -554,7 +558,7 @@ int parse_config_line(char *orig_line) { mods = parse_mods(keyseq); /* FreeBSD sets 'tokctx' to NULL at end of string. - * glibc sets 'tokctx' to the next character (the '\0') + * glibc sets 'tokctx' to the next character (the '\0') * Reported by Richard Kolkovich */ if (tokctx == NULL || *tokctx == '\0') { fprintf(stderr, "Incomplete configuration line. Missing commands: '%s'\n", line); @@ -609,7 +613,7 @@ void updategrid(Window win, struct wininfo *info, int apply_clip, int draw) { updatecliprects(info, &clip_rectangles, &nclip_rectangles); memset(clip_rectangles, 0, nclip_rectangles * sizeof(XRectangle)); } - + #ifdef PROFILE_THINGS struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); @@ -700,7 +704,7 @@ void updategridtext(Window win, struct wininfo *info, int apply_clip, int draw) int row, col; int rect = (info->grid_cols + 1 + info->grid_rows + 1); /* start at end of grid lines */ - + x_off = info->border_thickness / 2; y_off = info->border_thickness / 2; @@ -736,13 +740,13 @@ void updategridtext(Window win, struct wininfo *info, int apply_clip, int draw) int xpos = cell_width * col + x_off + (cell_width / 2); int ypos = cell_height * row + y_off + (cell_height / 2); - row_selected = (appstate.grid_nav && appstate.grid_nav_row == row + row_selected = (appstate.grid_nav && appstate.grid_nav_row == row && appstate.grid_nav_state == GRID_NAV_COL); //printf("Grid: %c%c\n", label[0], label[1]); /* If the current column is the one selected by grid nav, use * a different color */ - //printf("Grid geom: %fx%f @ %d,%d\n", + //printf("Grid geom: %fx%f @ %d,%d\n", //xpos - rectwidth / 2 + te.x_bearing / 2, //ypos - rectheight / 2 + te.y_bearing / 2, //rectwidth, rectheight); @@ -759,7 +763,7 @@ void updategridtext(Window win, struct wininfo *info, int apply_clip, int draw) cairo_set_source_rgb(canvas_cairo, 0, .3, .3); } else { cairo_set_source_rgb(canvas_cairo, 0, .2, 0); - } + } cairo_fill(canvas_cairo); cairo_append_path(canvas_cairo, pathcopy); cairo_set_source_rgb(canvas_cairo, .8, .8, 0); @@ -770,7 +774,7 @@ void updategridtext(Window win, struct wininfo *info, int apply_clip, int draw) cairo_set_source_rgb(canvas_cairo, 1, 1, 1); } else { cairo_set_source_rgb(canvas_cairo, .8, .8, .8); - } + } cairo_fill(canvas_cairo); cairo_move_to(canvas_cairo, xpos - te.width / 2, ypos); cairo_show_text(canvas_cairo, label); @@ -804,7 +808,7 @@ void cmd_start(char *args) { wininfo.y = viewports[wininfo.curviewport].y; wininfo.w = viewports[wininfo.curviewport].w; wininfo.h = viewports[wininfo.curviewport].h; - + /* Default start with 4 cells, 2x2 */ wininfo.grid_rows = 2; wininfo.grid_cols = 2; @@ -857,7 +861,7 @@ void cmd_start(char *args) { if (zone == 0) { /* Create our window for the first time */ viewport_t *viewport = &(viewports[wininfo.curviewport]); - + depth = viewports[wininfo.curviewport].screen->root_depth; wininfo_history_cursor = 0; @@ -889,7 +893,7 @@ void cmd_start(char *args) { winattr.override_redirect = 1; XChangeWindowAttributes(dpy, zone, CWOverrideRedirect, &winattr); - XSelectInput(dpy, zone, StructureNotifyMask | ExposureMask + XSelectInput(dpy, zone, StructureNotifyMask | ExposureMask | PointerMotionMask | LeaveWindowMask ); } /* if zone == 0 */ } @@ -1044,7 +1048,7 @@ void cmd_windowzoom(char *args) { xdo_get_active_window(xdo, &curwin); XGetGeometry(xdo->xdpy, curwin, &rootwin, &x, &y, &width, &height, &border_width, &depth); - XTranslateCoordinates(xdo->xdpy, curwin, rootwin, + XTranslateCoordinates(xdo->xdpy, curwin, rootwin, -border_width, -border_width, &x, &y, &dummy_win); wininfo.x = x; @@ -1059,7 +1063,7 @@ void cmd_warp(char *args) { int x, y; x = wininfo.x + wininfo.w / 2; y = wininfo.y + wininfo.h / 2; - + if (mouseinfo.x != -1 && mouseinfo.y != -1) { closepixel(dpy, zone, &mouseinfo); } @@ -1302,7 +1306,7 @@ void update() { //clip = 0; if (((clip || draw) + (move || resize)) > 1) { - /* more than one action to perform, unmap to hide move/draws + /* more than one action to perform, unmap to hide move/draws * to reduce flickering */ XUnmapWindow(dpy, zone); } @@ -1318,7 +1322,7 @@ void update() { XCopyArea(dpy, canvas, zone, canvas_gc, 0, 0, wininfo.w, wininfo.h, 0, 0); } if (clip) { - XShapeCombineRectangles(dpy, zone, ShapeBounding, 0, 0, + XShapeCombineRectangles(dpy, zone, ShapeBounding, 0, 0, clip_rectangles, nclip_rectangles, ShapeSet, 0); } } @@ -1356,7 +1360,7 @@ void correct_overflow() { if (wininfo.x < 0) { wininfo.x = 0; } - if (wininfo.x + wininfo.w > + if (wininfo.x + wininfo.w > viewports[wininfo.curviewport].x + viewports[wininfo.curviewport].w) wininfo.x = viewports[wininfo.curviewport].x + viewports[wininfo.curviewport].w - wininfo.w; @@ -1364,7 +1368,7 @@ void correct_overflow() { * vertically stacked. */ if (wininfo.y < 0) wininfo.y = 0; - if (wininfo.y + wininfo.h > + if (wininfo.y + wininfo.h > viewports[wininfo.curviewport].y + viewports[wininfo.curviewport].h) wininfo.y = viewports[wininfo.curviewport].h - wininfo.h; } @@ -1425,7 +1429,7 @@ void viewport_left() { void handle_keypress(XKeyEvent *e) { int i; /* If a mouse button is pressed (like, when we're dragging), - * then the 'mods' will include values like Button1Mask. + * then the 'mods' will include values like Button1Mask. * Let's remove those, as they cause breakage */ e->state &= ~(Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask); @@ -1576,7 +1580,7 @@ void handle_commands(char *commands) { for (i = 0; dispatch[i].command; i++) { /* XXX: This approach means we can't have one command be a subset of * another. For example, 'grid' and 'grid-foo' will fail because when you - * use 'grid-foo' it'll match 'grid' first. + * use 'grid-foo' it'll match 'grid' first. * This hasn't been a problem yet... */ @@ -1585,7 +1589,7 @@ void handle_commands(char *commands) { if (!strncmp(tok, dispatch[i].command, cmdlen)) { /* tok + len + 1 is * "command arg1 arg2" - * ^^^^^^^^^ <-- this + * ^^^^^^^^^ <-- this */ char *args = tok + cmdlen; if (*args == '\0') @@ -1706,7 +1710,7 @@ int query_current_screen() { int i; if (xinerama) { return query_current_screen_xinerama(); - } else { + } else { return query_current_screen_normal(); } } @@ -1794,7 +1798,7 @@ void recordings_save(const char *filename) { fprintf(output, "%d ", rec->keycode); for (j = 0; j < rec->commands->len; j++) { - fprintf(output, "%s%s", + fprintf(output, "%s%s", (char *) g_ptr_array_index(rec->commands, j), (j + 1 < rec->commands->len ? ", " : "")); } @@ -1880,8 +1884,8 @@ int main(int argc, char **argv) { return EXIT_SUCCESS; } - if (argc > 1 && (!strcmp(argv[1], "version") - || !strcmp(argv[1], "-v") + if (argc > 1 && (!strcmp(argv[1], "version") + || !strcmp(argv[1], "-v") || !strcmp(argv[1], "--version"))) { printf("keynav %s\n", KEYNAV_VERSION); return EXIT_SUCCESS; @@ -1951,7 +1955,7 @@ int main(int argc, char **argv) { // Ignorable events. case GraphicsExpose: - case NoExpose: + case NoExpose: case LeaveNotify: // Mouse left the window case KeyRelease: // key was released case DestroyNotify: // window was destroyed @@ -1966,4 +1970,3 @@ int main(int argc, char **argv) { xdo_free(xdo); } /* int main */ - From 7999bf46afea84b506911d7c0ebda3ebe23290dd Mon Sep 17 00:00:00 2001 From: Denis Kasak Date: Fri, 27 Feb 2015 14:06:44 +0100 Subject: [PATCH 03/52] Some support for XDG Base Directory. --- keynav.c | 13 +++++++++++++ keynav.pod | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/keynav.c b/keynav.c index d5edc3e..5bdd342 100644 --- a/keynav.c +++ b/keynav.c @@ -444,6 +444,19 @@ void parse_config() { defaults(); parse_config_file(GLOBAL_CONFIG_FILE); parse_config_file("~/.keynavrc"); + + // support XDG Base Directory + char *config_home = getenv("XDG_CONFIG_HOME"); + char *user_config_file; + + if (config_home && + asprintf(&user_config_file, "%s/keynav/keynavrc", config_home) != -1) { + parse_config_file(user_config_file); + free(user_config_file); + } else { + // standard default if XDG_CONFIG_HOME is not set + parse_config_file("~/.config/keynav/keynavrc"); + } } void defaults() { diff --git a/keynav.pod b/keynav.pod index 43313aa..0f1ff63 100644 --- a/keynav.pod +++ b/keynav.pod @@ -27,7 +27,9 @@ Another example: daemonize on startup: =head1 CONFIGURATION keynav is configured by default from a config file in your home directory -"~/.keynavrc" +"~/.keynavrc" or "$XDG_CONFIG_HOME/keynav/keynavrc" per the XDG Base Directory +standard (defaulting to "~/.config/keynav/keynavrc" if XDG_CONFIG_HOME is not +set). The default configuration can be found in the L section. From 16210d5326bd172da9fa1ecbb34eaa3d52eadbb9 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Mon, 2 Mar 2015 21:11:29 +0600 Subject: [PATCH 04/52] make keynav work in different layout --- keynav.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/keynav.c b/keynav.c index d5edc3e..c9d8d50 100644 --- a/keynav.c +++ b/keynav.c @@ -1432,6 +1432,9 @@ void handle_keypress(XKeyEvent *e) { /* Ignore LockMask (Numlock, etc) and Mod2Mask (shift, etc) */ e->state &= ~(LockMask | Mod2Mask); + /* Ignore different keyboard layouts (e.g. russian) */ + e->state &= ~(1<<13); + if (appstate.recording == record_getkey) { if (handle_recording(e) == HANDLE_STOP) { return; From 82d8bb7c2691678d87fce0b59a1bdaa2c66a8c78 Mon Sep 17 00:00:00 2001 From: Dabo Ross Date: Tue, 7 Apr 2015 12:59:14 -0700 Subject: [PATCH 05/52] Enable sleeping after move+resize request --- keynav.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/keynav.c b/keynav.c index d5edc3e..5d32714 100644 --- a/keynav.c +++ b/keynav.c @@ -1331,8 +1331,8 @@ void update() { /* Under Gnome3/GnomeShell, it seems to ignore this move+resize request * unless we sync and sleep here. Sigh. Gnome is retarded. */ - //XSync(dpy, 0); - //usleep(5000); + XSync(dpy, 0); + usleep(5000); } else if (resize) { XResizeWindow(dpy, zone, wininfo.w, wininfo.h); } else if (move) { From e6d03b0da5597460138f0c8414251536121efb68 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Mon, 20 Apr 2015 23:08:24 +0600 Subject: [PATCH 06/52] beautify grid * make grid black and white with (almost) pixel-perfect align; * do not show grid if area too small (< 4x4); --- keynav.c | 89 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 27 deletions(-) diff --git a/keynav.c b/keynav.c index c9d8d50..b198abf 100644 --- a/keynav.c +++ b/keynav.c @@ -601,7 +601,6 @@ void updategrid(Window win, struct wininfo *info, int apply_clip, int draw) { double h = info->h; double cell_width; double cell_height; - double x_off, y_off; int i; int rect = 0; @@ -615,46 +614,84 @@ void updategrid(Window win, struct wininfo *info, int apply_clip, int draw) { clock_gettime(CLOCK_MONOTONIC, &start); #endif - //printf("updategrid: clip:%d, draw:%d\n", apply_clip, draw); + if (w <= 4 || h <= 4) { + cairo_new_path(canvas_cairo); + cairo_fill(canvas_cairo); + return; + } - x_off = info->border_thickness / 2; - y_off = info->border_thickness / 2; + //printf("updategrid: clip:%d, draw:%d\n", apply_clip, draw); if (draw) { cairo_new_path(canvas_cairo); cairo_set_source_rgb(canvas_cairo, 1, 1, 1); cairo_rectangle(canvas_cairo, 0, 0, w, h); - cairo_fill(canvas_cairo); cairo_set_line_width(canvas_cairo, wininfo.border_thickness); + cairo_fill(canvas_cairo); } - w -= info->border_thickness; - h -= info->border_thickness; cell_width = (w / info->grid_cols); cell_height = (h / info->grid_rows); + int x_total_offset = 0; + /* clip vertically */ for (i = 0; i <= info->grid_cols; i++) { - cairo_move_to(canvas_cairo, cell_width * i + x_off, y_off); - cairo_line_to(canvas_cairo, cell_width * i + x_off, h + 1); + int x_off = 0; + if (i > 0) { + x_off = -info->border_thickness / 2; + } + + if (i == info->grid_cols) { + x_total_offset = info->w - 1; + } + + int x_w_off = 0; + if (i == 0 || i == info->grid_cols) { + x_w_off = info->border_thickness / 2; + } - clip_rectangles[rect].x = cell_width * i; + cairo_move_to(canvas_cairo, x_total_offset + 1, 0); + cairo_line_to(canvas_cairo, x_total_offset + 1, info->h); + + clip_rectangles[rect].x = x_total_offset + x_off; clip_rectangles[rect].y = 0; - clip_rectangles[rect].width = info->border_thickness; + clip_rectangles[rect].width = info->border_thickness - x_w_off; clip_rectangles[rect].height = info->h; rect++; + + x_total_offset += cell_width; } + int y_total_offset = 0; + /* clip horizontally */ for (i = 0; i <= info->grid_rows; i++) { - cairo_move_to(canvas_cairo, x_off, cell_height * i + y_off); - cairo_line_to(canvas_cairo, w + 1, cell_height * i + y_off); + int y_off = 0; + if (i > 0) { + y_off = -info->border_thickness / 2; + } + + if (i == info->grid_rows) { + y_total_offset = info->h - 1; + } + + int y_w_off = 0; + if (i == 0 || i == info->grid_rows) { + y_w_off = info->border_thickness / 2; + } + + cairo_move_to(canvas_cairo, 0, y_total_offset + 1); + cairo_line_to(canvas_cairo, info->w, y_total_offset + 1); clip_rectangles[rect].x = 0; - clip_rectangles[rect].y = cell_height * i; + clip_rectangles[rect].y = y_total_offset + y_off; + clip_rectangles[rect].width = info->w; - clip_rectangles[rect].height = info->border_thickness; + clip_rectangles[rect].height = info->border_thickness - y_w_off; rect++; + + y_total_offset += cell_height; } cairo_path_t *path = cairo_copy_path(canvas_cairo); @@ -668,14 +705,8 @@ void updategrid(Window win, struct wininfo *info, int apply_clip, int draw) { #endif if (draw) { - cairo_set_source_rgb(canvas_cairo, 0.5, 0, 0); - cairo_set_line_width(canvas_cairo, 3); - cairo_stroke(canvas_cairo); - - /* cairo_stroke clears the current path, put it back */ - cairo_append_path(canvas_cairo, path); + cairo_set_source_rgba(canvas_cairo, 0, 0, 0, 1.0); cairo_set_line_width(canvas_cairo, 1); - cairo_set_source_rgba(canvas_cairo, 1, 1, 1, .7); cairo_stroke(canvas_cairo); #ifdef PROFILE_THINGS @@ -857,7 +888,7 @@ void cmd_start(char *args) { if (zone == 0) { /* Create our window for the first time */ viewport_t *viewport = &(viewports[wininfo.curviewport]); - + depth = viewports[wininfo.curviewport].screen->root_depth; wininfo_history_cursor = 0; @@ -909,9 +940,11 @@ void cmd_end(char *args) { appstate.active = False; - //XDestroyWindow(dpy, zone); XUnmapWindow(dpy, zone); + XDestroyWindow(dpy, zone); XUngrabKeyboard(dpy, CurrentTime); + + zone = 0; } void cmd_history_back(char *args) { @@ -1938,9 +1971,11 @@ int main(int argc, char **argv) { break; case Expose: - XCopyArea(dpy, canvas, zone, canvas_gc, e.xexpose.x, e.xexpose.y, - e.xexpose.width, e.xexpose.height, - e.xexpose.x, e.xexpose.y); + if (zone) { + XCopyArea(dpy, canvas, zone, canvas_gc, e.xexpose.x, e.xexpose.y, + e.xexpose.width, e.xexpose.height, + e.xexpose.x, e.xexpose.y); + } break; case MotionNotify: From f37c3f2fc5409bdc65d8cc3aa65c96431b85f37a Mon Sep 17 00:00:00 2001 From: Tyler Akins Date: Wed, 20 May 2015 14:35:05 -0500 Subject: [PATCH 07/52] Documentation improvements --- README | 1 - README.md | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) delete mode 100644 README create mode 100644 README.md diff --git a/README b/README deleted file mode 100644 index 9f23d53..0000000 --- a/README +++ /dev/null @@ -1 +0,0 @@ -Please see http://www.semicomplete.com/projects/keynav diff --git a/README.md b/README.md new file mode 100644 index 0000000..b36f7a9 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +keynav +====== + +Control the mouse with the keyboard. + +Please see http://www.semicomplete.com/projects/keynav + + +Compiling +--------- + +You may need some extra libraries to compile keynav. On Debian and Ubuntu you can install these packages: + + sudo apt-get install libcairo2-dev libxinerama-dev libxdo-dev + +Next you simply run make: + + make From 80f21b4ab20840052b599b11e4bc25a11abf65e2 Mon Sep 17 00:00:00 2001 From: Tyler Akins Date: Wed, 20 May 2015 14:35:35 -0500 Subject: [PATCH 08/52] Split up the libraries This way when one is not found, it doesn't stop the inclusion of the rest. --- Makefile | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 2b3f8ef..9519443 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,17 @@ -CFLAGS+=$(shell pkg-config --cflags cairo-xlib xinerama glib-2.0 xext x11 xtst 2> /dev/null || echo -I/usr/X11R6/include -I/usr/local/include) -LDFLAGS+=$(shell pkg-config --libs cairo-xlib xinerama glib-2.0 xext x11 xtst 2> /dev/null || echo -L/usr/X11R6/lib -L/usr/local/lib -lX11 -lXtst -lXinerama -lXext -lglib) -LDFLAGS+=$(shell pkg-config --libs glib-2.0) +CFLAGS+=$(shell pkg-config --cflags cairo-xlib 2> /dev/null) +CFLAGS+=$(shell pkg-config --cflags xinerama 2> /dev/null) +CFLAGS+=$(shell pkg-config --cflags glib-2.0 2> /dev/null) +CFLAGS+=$(shell pkg-config --cflags xext 2> /dev/null) +CFLAGS+=$(shell pkg-config --cflags x11 2> /dev/null) +CFLAGS+=$(shell pkg-config --cflags xtst 2> /dev/null) + +LDFLAGS+=$(shell pkg-config --libs cairo-xlib 2> /dev/null) +LDFLAGS+=$(shell pkg-config --libs xinerama 2> /dev/null) +LDFLAGS+=$(shell pkg-config --libs glib-2.0 2> /dev/null) +LDFLAGS+=$(shell pkg-config --libs xext 2> /dev/null) +LDFLAGS+=$(shell pkg-config --libs x11 2> /dev/null) +LDFLAGS+=$(shell pkg-config --libs xtst 2> /dev/null) +LDFLAGS+=-g OTHERFILES=README CHANGELIST COPYRIGHT \ keynavrc Makefile version.sh VERSION From b014951f7e5287024b9a2e3ea8c7da4186fd8ce2 Mon Sep 17 00:00:00 2001 From: Tyler Akins Date: Wed, 20 May 2015 14:36:07 -0500 Subject: [PATCH 09/52] Fixing return values --- keynav.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/keynav.c b/keynav.c index b4076c1..2dfb056 100644 --- a/keynav.c +++ b/keynav.c @@ -411,7 +411,7 @@ void parse_config_file(const char* file) { "No HOME set in environment. Can't expand '%s' (fatal error)\n", file); /* This is fatal. */ - exit(1); + exit(EXIT_FAILURE); } } /* if file[0] == '~' */ @@ -1921,12 +1921,12 @@ int main(int argc, char **argv) { if ((pcDisplay = getenv("DISPLAY")) == NULL) { fprintf(stderr, "Error: DISPLAY environment variable not set\n"); - return EXIT_SUCCESS; + return EXIT_FAILURE; } if ((dpy = XOpenDisplay(pcDisplay)) == NULL) { fprintf(stderr, "Error: Can't open display: %s\n", pcDisplay); - return EXIT_SUCCESS; + return EXIT_FAILURE; } if (argc > 1 && (!strcmp(argv[1], "version") From a862cfcbadb402765af95fb0a2f88607903a346a Mon Sep 17 00:00:00 2001 From: Tyler Akins Date: Wed, 20 May 2015 14:36:18 -0500 Subject: [PATCH 10/52] Mentioning new path of config file The old path still works. --- keynavrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keynavrc b/keynavrc index c7ddf8a..802b450 100644 --- a/keynavrc +++ b/keynavrc @@ -1,5 +1,5 @@ # This is a keynavrc file. Yours should live in -# $HOME/.keynavrc +# $HOME/.config/keynav/keynavrc # # Lines beginning with '#' are comments. # Format is: From 3d3d266e2c3017208ee0c1f07b518b933f3f8454 Mon Sep 17 00:00:00 2001 From: Tyler Akins Date: Wed, 20 May 2015 14:36:42 -0500 Subject: [PATCH 11/52] Adding arrow keys --- keynavrc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/keynavrc b/keynavrc index 802b450..4f52a39 100644 --- a/keynavrc +++ b/keynavrc @@ -65,6 +65,24 @@ ctrl+u cut-right,cut-up ctrl+b cut-left,cut-down ctrl+n cut-right,cut-down +# Arrow keys can move the grid as welll +Left cut-left +Down cut-down +Up cut-up +Right cut-right +shift+Left move-left +shift+Down move-down +shift+Up move-up +shift+Right move-right +ctrl+Left cut-left +ctrl+Down cut-down +ctrl+Up cut-up +ctrl+Right cut-right +ctrl+shift+Left move-left +ctrl+shift+Down move-down +ctrl+shift+Up move-up +ctrl+shift+Right move-right + # Record keynav actions q record shift+at playback From 1341fa6c3bcc1999131390bc59ed386e24869a9c Mon Sep 17 00:00:00 2001 From: Tyler Akins Date: Wed, 20 May 2015 14:36:52 -0500 Subject: [PATCH 12/52] Additional drag examples --- keynavrc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/keynavrc b/keynavrc index 4f52a39..69c1fb0 100644 --- a/keynavrc +++ b/keynavrc @@ -100,6 +100,11 @@ v sh "xdotool key shift+Insert" #q drag 1 # Start drag holding middle mouse + control and shift #w drag 2 ctrl+shift +# Dragging with modifiers +#q drag 1 +#ctrl+q drag 1 ctrl +#shift+q drag 1 shift +#shift+ctrl+q drag 1 shift+ctrl ### History a history-back From 29c8205764081f850230b770b3c071a2353124c2 Mon Sep 17 00:00:00 2001 From: Tyler Akins Date: Wed, 20 May 2015 14:37:01 -0500 Subject: [PATCH 13/52] Fixing grid commands --- keynavrc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/keynavrc b/keynavrc index 69c1fb0..78c0c09 100644 --- a/keynavrc +++ b/keynavrc @@ -131,12 +131,12 @@ a history-back ### Example using a 3x3 grid with nethack-vi keys #ctrl+semicolon start, grid 3x3 -#h cell-select 2x1 # left -#j cell-select 3x2 # down -#k cell-select 1x2 # up -#l cell-select 2x3 # right +#h cell-select 1x2 # left +#j cell-select 2x3 # down +#k cell-select 2x1 # up +#l cell-select 3x2 # right #y cell-select 1x1 # up-left -#u cell-select 1x3 # up-right -#b cell-select 3x1 # down-left +#u cell-select 3x1 # up-right +#b cell-select 1x3 # down-left #n cell-select 3x3 # down-right #period cell-select 2x2 # center From 2abb32d9f1afae02b029fad2f1551192378ce871 Mon Sep 17 00:00:00 2001 From: Marek Marecki Date: Tue, 1 Dec 2015 09:49:14 +0100 Subject: [PATCH 14/52] Update .gitignore rules --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 230a2f7..0473a06 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ /keynav /keynav.1 +/keynav.1.gz /keynav_version.h *.o + +.* +!.gitignore From 0ac6ec9ec061fee692fb2e6b805aeb571b995cbe Mon Sep 17 00:00:00 2001 From: Marek Marecki Date: Tue, 1 Dec 2015 09:52:20 +0100 Subject: [PATCH 15/52] Use rm -f instead of || true --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2b3f8ef..2aba7bb 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ VERSION=$(shell sh version.sh) all: keynav clean: - rm *.o keynav keynav_version.h || true; + rm -f *.o keynav keynav_version.h keynav.1.gz keynav.o: keynav_version.h keynav_version.h: version.sh From 4c49348848e93c2aebd1b6f0f5c75fc55597ae3a Mon Sep 17 00:00:00 2001 From: Marek Marecki Date: Tue, 1 Dec 2015 09:52:39 +0100 Subject: [PATCH 16/52] Add PREFIX variable to Makefile --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 2aba7bb..670dfb9 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,8 @@ CFLAGS+=$(shell pkg-config --cflags cairo-xlib xinerama glib-2.0 xext x11 xtst 2 LDFLAGS+=$(shell pkg-config --libs cairo-xlib xinerama glib-2.0 xext x11 xtst 2> /dev/null || echo -L/usr/X11R6/lib -L/usr/local/lib -lX11 -lXtst -lXinerama -lXext -lglib) LDFLAGS+=$(shell pkg-config --libs glib-2.0) +PREFIX=/usr + OTHERFILES=README CHANGELIST COPYRIGHT \ keynavrc Makefile version.sh VERSION #CFLAGS+=-DPROFILE_THINGS From 3bf5750682ae8b11366cb1355091b595fd2332d2 Mon Sep 17 00:00:00 2001 From: Marek Marecki Date: Tue, 1 Dec 2015 09:55:43 +0100 Subject: [PATCH 17/52] Add install and uninstall targets --- Makefile | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 670dfb9..71b6975 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ VERSION=$(shell sh version.sh) #CFLAGS+=-DPROFILE_THINGS #LDFLAGS+=-lrt -.PHONY: all +.PHONY: all uninstall all: keynav @@ -64,3 +64,14 @@ test-package-build: create-package keynav.1: keynav.pod pod2man -c "" -r "" $< > $@ + +install: keynav keynav.1 + install ./keynav $(PREFIX)/bin/keynav + rm -f keynav.1.gz + gzip keynav.1 + mkdir -p $(PREFIX)/share/man/man1 + install ./keynav.1.gz $(PREFIX)/share/man/man1/ + +uninstall: + rm -f $(PREFIX)/bin/keynav + rm -f $(PREFIX)/share/man/man1/keynav.1.gz From 410b6a00ce95834d4c0cbc65b3208b83e2184fc4 Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 10 Mar 2016 01:01:46 +0600 Subject: [PATCH 18/52] allow binded keys used as macros trigger --- keynav.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keynav.c b/keynav.c index b198abf..79b2ab3 100644 --- a/keynav.c +++ b/keynav.c @@ -1526,7 +1526,7 @@ handler_info_t handle_recording(XKeyEvent *e) { //printf("Recording as keycode:%d\n", e->keycode); active_recording->keycode = e->keycode; - return HANDLE_CONTINUE; + return HANDLE_STOP; } handler_info_t handle_gridnav(XKeyEvent *e) { From cbd1d9ed0c398dd7eb49d2edf81af4720fd4a9ce Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Thu, 10 Mar 2016 16:18:45 +0600 Subject: [PATCH 19/52] config parser support for commas in sh --- keynav.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/keynav.c b/keynav.c index 79b2ab3..e9e98b4 100644 --- a/keynav.c +++ b/keynav.c @@ -1589,12 +1589,41 @@ handler_info_t handle_gridnav(XKeyEvent *e) { void handle_commands(char *commands) { char *cmdcopy; - char *tokctx, *tok, *strptr; + char *tokctx, *tok, *strptr, *copyptr; + int is_quoted, is_escaped; - //printf("Commands; %s\n", commands); cmdcopy = strdup(commands); - strptr = cmdcopy; - while ((tok = strtok_r(strptr, ",", &tokctx)) != NULL) { + copyptr = cmdcopy; + while (*copyptr != '\0') { + /* Parse with knowledge of quotes and escaping */ + is_quoted = is_escaped = FALSE; + strptr = tok = copyptr; + while (*copyptr != '\0' && (is_quoted == TRUE || *copyptr != ',')) { + if (is_escaped == TRUE) { + is_escaped = FALSE; + } + + if (*copyptr == '"' && is_escaped == FALSE) { + is_quoted = !is_quoted; + } + + if (*copyptr == '\\' && is_escaped == FALSE) { + is_escaped = TRUE; + copyptr++; + } + + *strptr = *copyptr; + + strptr++; + copyptr++; + } + + if (*strptr != '\0') { + *strptr = '\0'; + + copyptr++; + } + int i; int found = 0; strptr = NULL; @@ -1624,6 +1653,7 @@ void handle_commands(char *commands) { * ^^^^^^^^^ <-- this */ char *args = tok + cmdlen; + if (*args == '\0') args = ""; else From 166b05886d462269c006d5b48018356d3b4fd36a Mon Sep 17 00:00:00 2001 From: Stanislav Seletskiy Date: Mon, 29 Aug 2016 19:59:11 +0700 Subject: [PATCH 20/52] cleanup resources and do not leak xorg pixmaps --- keynav.c | 67 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/keynav.c b/keynav.c index e9e98b4..86bc32b 100644 --- a/keynav.c +++ b/keynav.c @@ -820,33 +820,7 @@ void updategridtext(Window win, struct wininfo *info, int apply_clip, int draw) } /* Draw rectangles and text */ } /* void updategridtext */ -void cmd_start(char *args) { - XSetWindowAttributes winattr; - int i; - int screen; - - screen = query_current_screen(); - wininfo.curviewport = screen; - - appstate.grid_nav_row = -1; - appstate.grid_nav_col = -1; - - wininfo.x = viewports[wininfo.curviewport].x; - wininfo.y = viewports[wininfo.curviewport].y; - wininfo.w = viewports[wininfo.curviewport].w; - wininfo.h = viewports[wininfo.curviewport].h; - - /* Default start with 4 cells, 2x2 */ - wininfo.grid_rows = 2; - wininfo.grid_cols = 2; - - wininfo.border_thickness = 3; - wininfo.center_cut_size = 3; - - if (ISACTIVE) - return; - - int depth; +void grab_keyboard() { int grabstate; int grabtries = 0; @@ -881,6 +855,37 @@ void cmd_start(char *args) { } } //printf("Got grab!\n"); +} + +void cmd_start(char *args) { + XSetWindowAttributes winattr; + int i; + int screen; + + screen = query_current_screen(); + wininfo.curviewport = screen; + + appstate.grid_nav_row = -1; + appstate.grid_nav_col = -1; + + wininfo.x = viewports[wininfo.curviewport].x; + wininfo.y = viewports[wininfo.curviewport].y; + wininfo.w = viewports[wininfo.curviewport].w; + wininfo.h = viewports[wininfo.curviewport].h; + + grab_keyboard(); + + /* Default start with 4 cells, 2x2 */ + wininfo.grid_rows = 2; + wininfo.grid_cols = 2; + + wininfo.border_thickness = 3; + wininfo.center_cut_size = 3; + + if (ISACTIVE) + return; + + int depth; appstate.active = True; appstate.need_draw = 1; @@ -941,6 +946,13 @@ void cmd_end(char *args) { appstate.active = False; XUnmapWindow(dpy, zone); + cairo_destroy(shape_cairo); + cairo_surface_destroy(shape_surface); + cairo_destroy(canvas_cairo); + cairo_surface_destroy(canvas_surface); + XFreePixmap(dpy, shape); + XFreePixmap(dpy, canvas); + XFreeGC(dpy, canvas_gc); XDestroyWindow(dpy, zone); XUngrabKeyboard(dpy, CurrentTime); @@ -2034,4 +2046,3 @@ int main(int argc, char **argv) { xdo_free(xdo); } /* int main */ - From 5bfaaa83fc35324e70c2049d221b8bd524139221 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Sat, 4 Mar 2017 12:48:29 -0500 Subject: [PATCH 21/52] update keynav.pod to list all authors listed in git commits --- keynav.pod | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/keynav.pod b/keynav.pod index 0f1ff63..271a381 100644 --- a/keynav.pod +++ b/keynav.pod @@ -390,8 +390,22 @@ L Alternately, if you really prefer email, feel free to file bugs by emailing the list. What works for you :) -=head1 AUTHOR +=head1 AUTHORS + +keynav was originally written by Jordan Sissel. +This version includes changes, enhancements, and fixes from at least the following (based on git logs): + +Brian Cole +Dabo Ross +Denis Kasak +Jordan Sissel +Krister Svanlund +Marek Marecki +Shou Ya +Stanislav Seletskiy +Tyler Akins +aszlig +lilydjwg -keynav was written by Jordan Sissel. =cut From 272f931e975cd44aad0d49d8c4eb03c6835572af Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Sat, 4 Mar 2017 14:26:43 -0500 Subject: [PATCH 22/52] README -> README.md in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e55d475..d9a4d01 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ LDFLAGS+=-g PREFIX=/usr -OTHERFILES=README CHANGELIST COPYRIGHT \ +OTHERFILES=README.md CHANGELIST COPYRIGHT \ keynavrc Makefile version.sh VERSION #CFLAGS+=-DPROFILE_THINGS #LDFLAGS+=-lrt From 39e93a50aa3037625a0216bbbdd44795baaee992 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Sat, 4 Mar 2017 14:29:21 -0500 Subject: [PATCH 23/52] Also include keynav.pod in tarball --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d9a4d01..3793c7e 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ LDFLAGS+=-g PREFIX=/usr -OTHERFILES=README.md CHANGELIST COPYRIGHT \ +OTHERFILES=README.md CHANGELIST COPYRIGHT keynav.pod \ keynavrc Makefile version.sh VERSION #CFLAGS+=-DPROFILE_THINGS #LDFLAGS+=-lrt From d97dd0e8d5325c3615e46bc2508abdda13ae3df1 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Mon, 6 Mar 2017 17:55:25 -0500 Subject: [PATCH 24/52] remove apparently unneeded libraries --- Makefile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Makefile b/Makefile index 3793c7e..654da4d 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,12 @@ CFLAGS+=$(shell pkg-config --cflags cairo-xlib 2> /dev/null) CFLAGS+=$(shell pkg-config --cflags xinerama 2> /dev/null) CFLAGS+=$(shell pkg-config --cflags glib-2.0 2> /dev/null) -CFLAGS+=$(shell pkg-config --cflags xext 2> /dev/null) CFLAGS+=$(shell pkg-config --cflags x11 2> /dev/null) -CFLAGS+=$(shell pkg-config --cflags xtst 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs cairo-xlib 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs xinerama 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs glib-2.0 2> /dev/null) -LDFLAGS+=$(shell pkg-config --libs xext 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs x11 2> /dev/null) -LDFLAGS+=$(shell pkg-config --libs xtst 2> /dev/null) LDFLAGS+=-g PREFIX=/usr From 2f74096a9d55b892d62c5892426d22be82cacee9 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Mon, 6 Mar 2017 17:56:58 -0500 Subject: [PATCH 25/52] remove -g from LDFLAGS Literally the only platform I could find where ld even has a -g flag is ULTRIX. --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 654da4d..fd14c31 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,6 @@ LDFLAGS+=$(shell pkg-config --libs cairo-xlib 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs xinerama 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs glib-2.0 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs x11 2> /dev/null) -LDFLAGS+=-g PREFIX=/usr From 798f044fc47ffe8b6856278854b82a83d60e8968 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Mon, 6 Mar 2017 18:03:16 -0500 Subject: [PATCH 26/52] clean up Makefile --- Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Makefile b/Makefile index fd14c31..2a41765 100644 --- a/Makefile +++ b/Makefile @@ -12,14 +12,11 @@ PREFIX=/usr OTHERFILES=README.md CHANGELIST COPYRIGHT keynav.pod \ keynavrc Makefile version.sh VERSION -#CFLAGS+=-DPROFILE_THINGS -#LDFLAGS+=-lrt VERSION=$(shell sh version.sh) #CFLAGS+=-pg -g #LDFLAGS+=-pg -g -#LDFLAGS+=-L/usr/lib/debug/usr/lib/ -lcairo -lX11 -lXinerama -LXtst -lXext #CFLAGS+=-O2 #CFLAGS+=-DPROFILE_THINGS From 2434a163a1c10a70df8dd84dbebb7d12ef819649 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Mon, 6 Mar 2017 18:05:01 -0500 Subject: [PATCH 27/52] add gitignore for VERSION and generated tarballs --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 0473a06..873091a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,10 @@ /keynav.1 /keynav.1.gz /keynav_version.h +/VERSION *.o +/keynav-*.tar.gz + .* !.gitignore From 3815a41630d3ca0082f5ebd3a51779bd6b4d8043 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Mon, 13 Mar 2017 23:27:33 -0400 Subject: [PATCH 28/52] brush up build/install instructions in readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b36f7a9..f749cb9 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,7 @@ You may need some extra libraries to compile keynav. On Debian and Ubuntu you c Next you simply run make: make + +This will produce an executable `./keynav` which may be run directly (or copied +somewhere in your path). You can also install (by default directly to `/usr`) +via `make install`. From 68e697b0081ba0ec89cb8a10678e59ecc69ae8ee Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Fri, 19 May 2017 22:11:06 -0400 Subject: [PATCH 29/52] change Makefile to explicitly use CFLAGS not actually sure how it worked at all before (implicit env?), but it broke on FreeBSD --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2a41765..f7ae4b5 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ keynav_version.h: version.sh keynav: LDFLAGS+=-Xlinker -rpath=/usr/local/lib keynav: keynav.o - $(CC) keynav.o -o $@ $(LDFLAGS) -lxdo; \ + $(CC) keynav.o -o keynav $(CFLAGS) $(LDFLAGS) -lxdo keynav_version.h: sh version.sh --header > $@ From a06d9b988c969259e06e91912d42a9af3ef4b96a Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Fri, 19 May 2017 22:18:51 -0400 Subject: [PATCH 30/52] note in README that FreeBSD users should use gmake --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f749cb9..3279ead 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,5 @@ Next you simply run make: This will produce an executable `./keynav` which may be run directly (or copied somewhere in your path). You can also install (by default directly to `/usr`) via `make install`. + +On FreeBSD (and, I expect, other non-GNU platforms), you will want to use gmake. From 529a644e172477fa8db657a3eb15d3c2233878fe Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Sun, 25 Jun 2017 22:09:00 -0400 Subject: [PATCH 31/52] have `make install` create $PREFIX/bin if needed --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index f7ae4b5..aaa2d19 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,7 @@ keynav.1: keynav.pod pod2man -c "" -r "" $< > $@ install: keynav keynav.1 + mkdir -p $(PREFIX)/bin install ./keynav $(PREFIX)/bin/keynav rm -f keynav.1.gz gzip keynav.1 From 4feea2abe15d38df5fbadbd77425ac93e93657af Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Mon, 3 Jul 2017 22:16:17 -0400 Subject: [PATCH 32/52] add FAQ --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index 3279ead..a6e3a51 100644 --- a/README.md +++ b/README.md @@ -22,3 +22,35 @@ somewhere in your path). You can also install (by default directly to `/usr`) via `make install`. On FreeBSD (and, I expect, other non-GNU platforms), you will want to use gmake. + + +FAQ +--- + +Q: What platforms are supportded? +A: keynav should work on nearly any Unix-like that runs X11. It has been +confirmed to work on extremely varied GNU/Linux systems (incuding RPM-based, +Debian derivatives, musl-based systems, and Arch), and FreeBSD. If you get it to +run elsewhere, please let me know so I can add it to the list. If you try to run +it on another Unix-like and have trouble, please get in touch and I'll try to +help. If attempting to run elsewhere, note that we currently have a dependency +on GNU Make (gmake), and it hasn't been tested with many compilers yet. + +Q: Does it work on Android/Windows/Wayland/iOS/...? +A: Sadly, no; keynav is totally dependent on X11, and porting it to any other +graphical system would really be a clone/rewrite. Although I am aware of no +exact analogues on other systems, I suggest looking into Tasker (Android), +AutoHotKey (Windows), and AppleScript (macOS). If you find something that works, +let me know and I'll consider adding it to this list. + +Q: Can I use keynav to scroll? +A: Yes! X11 represents mouse scrolling as key presses, so you just add the +relevant stanza to your keynavrc. Mouse buttons are +1=left, 2=middle, 3=right, 4=scroll-up, 5=scroll-down. So for example to scroll +up with i and down with e: + i click 4,end + e click 5,end +or to keep scrolling without having to re-invoke keynav: + i click 4 + e click 5 + From 00181fbb7a46afa07c785329712d6f688e798c31 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Mon, 3 Jul 2017 22:18:37 -0400 Subject: [PATCH 33/52] fix readme formatting --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a6e3a51..216494c 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,12 @@ A: Yes! X11 represents mouse scrolling as key presses, so you just add the relevant stanza to your keynavrc. Mouse buttons are 1=left, 2=middle, 3=right, 4=scroll-up, 5=scroll-down. So for example to scroll up with i and down with e: - i click 4,end - e click 5,end +``` +i click 4,end +e click 5,end +``` or to keep scrolling without having to re-invoke keynav: - i click 4 - e click 5 - +``` +i click 4 +e click 5 +``` From 2a990844e6ce0b9feb577d63884a4d8aa8426bcb Mon Sep 17 00:00:00 2001 From: SRGOM Date: Tue, 4 Jul 2017 14:31:00 +0530 Subject: [PATCH 34/52] Update README.md Added horizontal scrolls. --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 216494c..2f225fb 100644 --- a/README.md +++ b/README.md @@ -46,14 +46,12 @@ let me know and I'll consider adding it to this list. Q: Can I use keynav to scroll? A: Yes! X11 represents mouse scrolling as key presses, so you just add the relevant stanza to your keynavrc. Mouse buttons are -1=left, 2=middle, 3=right, 4=scroll-up, 5=scroll-down. So for example to scroll -up with i and down with e: +1=left, 2=middle, 3=right, 4=scroll-up, 5=scroll-down, 6=scroll-left, 7=scroll-right. So for example to scroll up with i and down with e: ``` i click 4,end e click 5,end ``` -or to keep scrolling without having to re-invoke keynav: +or to keep scrolling without having to re-invoke keynav, remove the end command from the bindings, like this: ``` i click 4 -e click 5 ``` From 903f80838165b88a3babbbd540f21f9250b70e53 Mon Sep 17 00:00:00 2001 From: Aleks-Daniel Jakimenko-Aleksejev Date: Sat, 15 Jul 2017 05:18:39 +0300 Subject: [PATCH 35/52] Direct support for mod1-5 (resolves issue #19) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I hope there is a better (without copy-paste) way to write this, but for now this will do. Basically, we need to support at least mod3 and mod4 so that people can start keynav using Hyper and Super keys. Given that there is no universally fixed modifier bit for these keys, we can't just make “hyper” and “super” strings work. Other modifiers (1,2,5) are added for consistency, and also because somebody may actually need them. --- keynav.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/keynav.c b/keynav.c index 380712f..6fca202 100644 --- a/keynav.c +++ b/keynav.c @@ -319,6 +319,16 @@ int parse_mods(char *keyseq) { if ((keysym == XK_Super_L) || (keysym == XK_Super_R) || (keysym == XK_Hyper_L) || (keysym == XK_Hyper_R)) modmask |= Mod4Mask; + if (!strcasecmp(mod, "mod1")) + modmask |= Mod1Mask; + if (!strcasecmp(mod, "mod2")) + modmask |= Mod2Mask; + if (!strcasecmp(mod, "mod3")) + modmask |= Mod3Mask; + if (!strcasecmp(mod, "mod4")) + modmask |= Mod4Mask; + if (!strcasecmp(mod, "mod5")) + modmask |= Mod5Mask; /* 'xmodmap' will output the current modN:KeySym mappings */ } From 1e0877ce72ba7244db4b97ca4f3b4f09ad4d21d2 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Fri, 25 Aug 2017 16:30:21 -0700 Subject: [PATCH 36/52] Add Alex Daniel to contributors list --- keynav.pod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keynav.pod b/keynav.pod index 271a381..59a8632 100644 --- a/keynav.pod +++ b/keynav.pod @@ -406,6 +406,6 @@ Stanislav Seletskiy Tyler Akins aszlig lilydjwg - +Alex Daniel =cut From afd19891f7e3306a1a3888933c85456054e87ae6 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Fri, 25 Aug 2017 17:10:40 -0700 Subject: [PATCH 37/52] Fix ungrabbing on clear when there are multiple bindings to 'start' --- keynav.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/keynav.c b/keynav.c index 152794a..d5c5ba9 100644 --- a/keynav.c +++ b/keynav.c @@ -241,8 +241,13 @@ typedef struct keybinding { } keybinding_t; GPtrArray *keybindings = NULL; -int startKeycode = 0; -int startKeymods = 0; + +typedef struct startkey { + int keycode; + int mods; +} startkey_t; + +GPtrArray *startkeys = NULL; int parse_keycode(char *keyseq) { char *tokctx; @@ -360,8 +365,10 @@ void addbinding(int keycode, int mods, char *commands) { if (!strncmp(commands, "start", 5)) { int i = 0; - startKeycode = keycode; - startKeymods = mods; + startkey_t *startkey = calloc(sizeof(startkey_t), 1); + startkey->keycode = keycode; + startkey->mods = mods; + g_ptr_array_add(startkeys, startkey); /* Grab on all screen root windows */ for (i = 0; i < ScreenCount(dpy); i++) { Window root = RootWindow(dpy, i); @@ -457,6 +464,7 @@ void parse_config() { char *homedir; keybindings = g_ptr_array_new(); + startkeys = g_ptr_array_new(); recordings = g_ptr_array_new(); defaults(); @@ -572,16 +580,21 @@ int parse_config_line(char *orig_line) { /* Reset keybindings */ g_ptr_array_free(keybindings, TRUE); keybindings = g_ptr_array_new(); - if(startKeycode != 0){ - int i; - for (i = 0; i < ScreenCount(dpy); i++) { + + /* ungrab keybindings associated with start */ + if (startkeys->len > 0) { + for (int i = 0; i < ScreenCount(dpy); i++) { Window root = RootWindow(dpy, i); - XUngrabKey(dpy, startKeycode, startKeymods, root); - XUngrabKey(dpy, startKeycode, startKeymods | LockMask, root); - XUngrabKey(dpy, startKeycode, startKeymods | Mod2Mask, root); - XUngrabKey(dpy, startKeycode, startKeymods | LockMask | Mod2Mask, root); + for (int j = 0; j < startkeys->len; j++) { + startkey_t *sk = g_ptr_array_index(startkeys, j); + XUngrabKey(dpy, sk->keycode, sk->mods, root); + XUngrabKey(dpy, sk->keycode, sk->mods | LockMask, root); + XUngrabKey(dpy, sk->keycode, sk->mods | Mod2Mask, root); + XUngrabKey(dpy, sk->keycode, sk->mods | LockMask | Mod2Mask, root); + } } - startKeycode = startKeymods = 0; + g_ptr_array_free(startkeys, TRUE); + startkeys = g_ptr_array_new(); } } else if (strcmp(keyseq, "daemonize") == 0) { handle_commands(keyseq); From d2354208978316ff2d683285cce235983f5fbb56 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Fri, 25 Aug 2017 17:30:56 -0700 Subject: [PATCH 38/52] Remove trailing whitespace --- keynav.c | 61 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/keynav.c b/keynav.c index d5c5ba9..034cbc6 100644 --- a/keynav.c +++ b/keynav.c @@ -1,5 +1,5 @@ /* - * keynav - Keyboard navigation tool. + * keynav - Keyboard navigation tool. * * XXX: Merge 'wininfo' and 'wininfo_history'. The latest history entry is the * same as wininfo, so use that instead. @@ -216,7 +216,7 @@ dispatch_t dispatch[] = { // Mouse activity "warp", cmd_warp, - "click", cmd_click, + "click", cmd_click, "doubleclick", cmd_doubleclick, "drag", cmd_drag, @@ -225,7 +225,7 @@ dispatch_t dispatch[] = { "daemonize", cmd_daemonize, "sh", cmd_shell, "start", cmd_start, - "end", cmd_end, + "end", cmd_end, "history-back", cmd_history_back, "quit", cmd_quit, "restart", cmd_restart, @@ -238,7 +238,7 @@ typedef struct keybinding { char *commands; int keycode; int mods; -} keybinding_t; +} keybinding_t; GPtrArray *keybindings = NULL; @@ -400,7 +400,7 @@ void addbinding(int keycode, int mods, char *commands) { if (recordings_filename != NULL && strcmp(recordings_filename, newrecordingpath)) { free(newrecordingpath); - fprintf(stderr, + fprintf(stderr, "Recordings file already set to '%s', you tried to\n" "set it to '%s'. Keeping original value.\n", recordings_filename, path); @@ -428,7 +428,7 @@ void parse_config_file(const char* file) { free(rcfile); return; } else { - fprintf(stderr, + fprintf(stderr, "No HOME set in environment. Can't expand '%s' (fatal error)\n", file); /* This is fatal. */ @@ -545,7 +545,7 @@ int parse_config_line(char *orig_line) { /* syntax: * keysequence cmd1,cmd2,cmd3 * - * ex: + * ex: * ctrl+semicolon start * space warp * semicolon warp,click @@ -609,7 +609,7 @@ int parse_config_line(char *orig_line) { mods = parse_mods(keyseq); /* FreeBSD sets 'tokctx' to NULL at end of string. - * glibc sets 'tokctx' to the next character (the '\0') + * glibc sets 'tokctx' to the next character (the '\0') * Reported by Richard Kolkovich */ if (tokctx == NULL || *tokctx == '\0') { fprintf(stderr, "Incomplete configuration line. Missing commands: '%s'\n", line); @@ -663,7 +663,7 @@ void updategrid(Window win, struct wininfo *info, int apply_clip, int draw) { updatecliprects(info, &clip_rectangles, &nclip_rectangles); memset(clip_rectangles, 0, nclip_rectangles * sizeof(XRectangle)); } - + #ifdef PROFILE_THINGS struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); @@ -786,7 +786,7 @@ void updategridtext(Window win, struct wininfo *info, int apply_clip, int draw) int row, col; int rect = (info->grid_cols + 1 + info->grid_rows + 1); /* start at end of grid lines */ - + x_off = info->border_thickness / 2; y_off = info->border_thickness / 2; @@ -822,13 +822,13 @@ void updategridtext(Window win, struct wininfo *info, int apply_clip, int draw) int xpos = cell_width * col + x_off + (cell_width / 2); int ypos = cell_height * row + y_off + (cell_height / 2); - row_selected = (appstate.grid_nav && appstate.grid_nav_row == row + row_selected = (appstate.grid_nav && appstate.grid_nav_row == row && appstate.grid_nav_state == GRID_NAV_COL); //printf("Grid: %c%c\n", label[0], label[1]); /* If the current column is the one selected by grid nav, use * a different color */ - //printf("Grid geom: %fx%f @ %d,%d\n", + //printf("Grid geom: %fx%f @ %d,%d\n", //xpos - rectwidth / 2 + te.x_bearing / 2, //ypos - rectheight / 2 + te.y_bearing / 2, //rectwidth, rectheight); @@ -845,7 +845,7 @@ void updategridtext(Window win, struct wininfo *info, int apply_clip, int draw) cairo_set_source_rgb(canvas_cairo, 0, .3, .3); } else { cairo_set_source_rgb(canvas_cairo, 0, .2, 0); - } + } cairo_fill(canvas_cairo); cairo_append_path(canvas_cairo, pathcopy); cairo_set_source_rgb(canvas_cairo, .8, .8, 0); @@ -856,7 +856,7 @@ void updategridtext(Window win, struct wininfo *info, int apply_clip, int draw) cairo_set_source_rgb(canvas_cairo, 1, 1, 1); } else { cairo_set_source_rgb(canvas_cairo, .8, .8, .8); - } + } cairo_fill(canvas_cairo); cairo_move_to(canvas_cairo, xpos - te.width / 2, ypos); cairo_show_text(canvas_cairo, label); @@ -948,7 +948,7 @@ void cmd_start(char *args) { if (zone == 0) { /* Create our window for the first time */ viewport_t *viewport = &(viewports[wininfo.curviewport]); - + depth = viewports[wininfo.curviewport].screen->root_depth; wininfo_history_cursor = 0; @@ -980,7 +980,7 @@ void cmd_start(char *args) { winattr.override_redirect = 1; XChangeWindowAttributes(dpy, zone, CWOverrideRedirect, &winattr); - XSelectInput(dpy, zone, StructureNotifyMask | ExposureMask + XSelectInput(dpy, zone, StructureNotifyMask | ExposureMask | PointerMotionMask | LeaveWindowMask ); } /* if zone == 0 */ } @@ -1145,7 +1145,7 @@ void cmd_windowzoom(char *args) { xdo_get_active_window(xdo, &curwin); XGetGeometry(xdo->xdpy, curwin, &rootwin, &x, &y, &width, &height, &border_width, &depth); - XTranslateCoordinates(xdo->xdpy, curwin, rootwin, + XTranslateCoordinates(xdo->xdpy, curwin, rootwin, -border_width, -border_width, &x, &y, &dummy_win); wininfo.x = x; @@ -1160,7 +1160,7 @@ void cmd_warp(char *args) { int x, y; x = wininfo.x + wininfo.w / 2; y = wininfo.y + wininfo.h / 2; - + if (mouseinfo.x != -1 && mouseinfo.y != -1) { closepixel(dpy, zone, &mouseinfo); } @@ -1403,7 +1403,7 @@ void update() { //clip = 0; if (((clip || draw) + (move || resize)) > 1) { - /* more than one action to perform, unmap to hide move/draws + /* more than one action to perform, unmap to hide move/draws * to reduce flickering */ XUnmapWindow(dpy, zone); } @@ -1419,7 +1419,7 @@ void update() { XCopyArea(dpy, canvas, zone, canvas_gc, 0, 0, wininfo.w, wininfo.h, 0, 0); } if (clip) { - XShapeCombineRectangles(dpy, zone, ShapeBounding, 0, 0, + XShapeCombineRectangles(dpy, zone, ShapeBounding, 0, 0, clip_rectangles, nclip_rectangles, ShapeSet, 0); } } @@ -1457,7 +1457,7 @@ void correct_overflow() { if (wininfo.x < 0) { wininfo.x = 0; } - if (wininfo.x + wininfo.w > + if (wininfo.x + wininfo.w > viewports[wininfo.curviewport].x + viewports[wininfo.curviewport].w) wininfo.x = viewports[wininfo.curviewport].x + viewports[wininfo.curviewport].w - wininfo.w; @@ -1465,7 +1465,7 @@ void correct_overflow() { * vertically stacked. */ if (wininfo.y < 0) wininfo.y = 0; - if (wininfo.y + wininfo.h > + if (wininfo.y + wininfo.h > viewports[wininfo.curviewport].y + viewports[wininfo.curviewport].h) wininfo.y = viewports[wininfo.curviewport].h - wininfo.h; } @@ -1526,7 +1526,7 @@ void viewport_left() { void handle_keypress(XKeyEvent *e) { int i; /* If a mouse button is pressed (like, when we're dragging), - * then the 'mods' will include values like Button1Mask. + * then the 'mods' will include values like Button1Mask. * Let's remove those, as they cause breakage */ e->state &= ~(Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask); @@ -1710,7 +1710,7 @@ void handle_commands(char *commands) { for (i = 0; dispatch[i].command; i++) { /* XXX: This approach means we can't have one command be a subset of * another. For example, 'grid' and 'grid-foo' will fail because when you - * use 'grid-foo' it'll match 'grid' first. + * use 'grid-foo' it'll match 'grid' first. * This hasn't been a problem yet... */ @@ -1719,7 +1719,7 @@ void handle_commands(char *commands) { if (!strncmp(tok, dispatch[i].command, cmdlen)) { /* tok + len + 1 is * "command arg1 arg2" - * ^^^^^^^^^ <-- this + * ^^^^^^^^^ <-- this */ char *args = tok + cmdlen; @@ -1841,7 +1841,7 @@ int query_current_screen() { int i; if (xinerama) { return query_current_screen_xinerama(); - } else { + } else { return query_current_screen_normal(); } } @@ -1929,7 +1929,7 @@ void recordings_save(const char *filename) { fprintf(output, "%d ", rec->keycode); for (j = 0; j < rec->commands->len; j++) { - fprintf(output, "%s%s", + fprintf(output, "%s%s", (char *) g_ptr_array_index(rec->commands, j), (j + 1 < rec->commands->len ? ", " : "")); } @@ -2015,8 +2015,8 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } - if (argc > 1 && (!strcmp(argv[1], "version") - || !strcmp(argv[1], "-v") + if (argc > 1 && (!strcmp(argv[1], "version") + || !strcmp(argv[1], "-v") || !strcmp(argv[1], "--version"))) { printf("keynav %s\n", KEYNAV_VERSION); return EXIT_SUCCESS; @@ -2088,7 +2088,7 @@ int main(int argc, char **argv) { // Ignorable events. case GraphicsExpose: - case NoExpose: + case NoExpose: case LeaveNotify: // Mouse left the window case KeyRelease: // key was released case DestroyNotify: // window was destroyed @@ -2103,4 +2103,3 @@ int main(int argc, char **argv) { xdo_free(xdo); } /* int main */ - From 011ad58ce3cbff221d8c1605e2efd30d5fb3d2d8 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Fri, 25 Aug 2017 17:50:05 -0700 Subject: [PATCH 39/52] Fix bug where command dispatch only matches prefix + execs multiple Without this fix, keynav doesn't print an error message if you specify a command that matches the prefixes of some commands, like "c", it will execute all matching commands. For example, "c" causes cut-up, cut-down, cut-left, cut-right, cursorzoom, etc to all be executed. --- keynav.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/keynav.c b/keynav.c index 034cbc6..9988b04 100644 --- a/keynav.c +++ b/keynav.c @@ -1715,8 +1715,9 @@ void handle_commands(char *commands) { */ /* If this command starts with a dispatch function, call it */ - size_t cmdlen = strcspn(tok, " \t"); - if (!strncmp(tok, dispatch[i].command, cmdlen)) { + size_t cmdlen = strlen(dispatch[i].command); + size_t tokcmdlen = strcspn(tok, " \t"); + if (cmdlen == tokcmdlen && !strncmp(tok, dispatch[i].command, cmdlen)) { /* tok + len + 1 is * "command arg1 arg2" * ^^^^^^^^^ <-- this @@ -1730,7 +1731,7 @@ void handle_commands(char *commands) { found = 1; dispatch[i].func(args); - + break; } } From 14522d1c55a6059a5de1d34ca96e63ecaecd41b3 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Fri, 25 Aug 2017 17:55:58 -0700 Subject: [PATCH 40/52] Add toggle-start, to allow a key to be both start and end --- keynav.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/keynav.c b/keynav.c index 9988b04..742cb9a 100644 --- a/keynav.c +++ b/keynav.c @@ -144,6 +144,7 @@ void cmd_daemonize(char *args); void cmd_doubleclick(char *args); void cmd_drag(char *args); void cmd_end(char *args); +void cmd_toggle_start(char *args); void cmd_grid(char *args); void cmd_grid_nav(char *args); void cmd_history_back(char *args); @@ -226,6 +227,7 @@ dispatch_t dispatch[] = { "sh", cmd_shell, "start", cmd_start, "end", cmd_end, + "toggle-start", cmd_toggle_start, "history-back", cmd_history_back, "quit", cmd_quit, "restart", cmd_restart, @@ -363,7 +365,7 @@ void addbinding(int keycode, int mods, char *commands) { keybinding->mods = mods; g_ptr_array_add(keybindings, keybinding); - if (!strncmp(commands, "start", 5)) { + if (!strncmp(commands, "start", 5) || !strncmp(commands, "toggle-start", 12)) { int i = 0; startkey_t *startkey = calloc(sizeof(startkey_t), 1); startkey->keycode = keycode; @@ -1015,6 +1017,14 @@ void cmd_end(char *args) { zone = 0; } +void cmd_toggle_start(char *args) { + if (ISACTIVE) { + cmd_end(args); + } else { + cmd_start(args); + } +} + void cmd_history_back(char *args) { if (!ISACTIVE) return; From bd079a529e1690fbc6d8be04d1b164261a6aad53 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Fri, 25 Aug 2017 18:03:30 -0700 Subject: [PATCH 41/52] Add myself to the contributors list --- keynav.pod | 1 + 1 file changed, 1 insertion(+) diff --git a/keynav.pod b/keynav.pod index 59a8632..8cfee60 100644 --- a/keynav.pod +++ b/keynav.pod @@ -407,5 +407,6 @@ Tyler Akins aszlig lilydjwg Alex Daniel +Michael Sloan =cut From 43be74545da09f5e554a62b6aa75e8b46a8ef749 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Tue, 29 Aug 2017 20:43:49 -0700 Subject: [PATCH 42/52] Add docs for toggle-start command --- keynav.pod | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/keynav.pod b/keynav.pod index 8cfee60..22f46fc 100644 --- a/keynav.pod +++ b/keynav.pod @@ -266,6 +266,11 @@ Start keynav. Any keys with this binding will operate as global bindings to acti End keynav. This hides the keynav window and otherwise makes keynav inactive until the next 'start' command. +=item B + +Toggle keynav. If keynav is active, this makes it inactive. If keynav is +inactive, this makes it active. + =item B Go backwards in command history. All activity is tracked in history, so if you From 4e7697d0afecda665fe93bb64166e15d86164973 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Sun, 10 Sep 2017 18:37:38 -0400 Subject: [PATCH 43/52] README typo: supportded -> supported --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f225fb..bba2e26 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ On FreeBSD (and, I expect, other non-GNU platforms), you will want to use gmake. FAQ --- -Q: What platforms are supportded? +Q: What platforms are supported? A: keynav should work on nearly any Unix-like that runs X11. It has been confirmed to work on extremely varied GNU/Linux systems (incuding RPM-based, Debian derivatives, musl-based systems, and Arch), and FreeBSD. If you get it to From bcf25531838df6eddf5d6bf79f50c2f711f875ea Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Sun, 10 Sep 2017 19:24:29 -0400 Subject: [PATCH 44/52] add "debug" build and make "keynav" release build --- Makefile | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index aaa2d19..b71ea91 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ LDFLAGS+=$(shell pkg-config --libs cairo-xlib 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs xinerama 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs glib-2.0 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs x11 2> /dev/null) +LDFLAGS+=-Xlinker -rpath=/usr/local/lib PREFIX=/usr @@ -15,13 +16,6 @@ OTHERFILES=README.md CHANGELIST COPYRIGHT keynav.pod \ VERSION=$(shell sh version.sh) -#CFLAGS+=-pg -g -#LDFLAGS+=-pg -g -#CFLAGS+=-O2 - -#CFLAGS+=-DPROFILE_THINGS -#LDFLAGS+=-lrt - .PHONY: all uninstall all: keynav @@ -32,9 +26,17 @@ clean: keynav.o: keynav_version.h keynav_version.h: version.sh -keynav: LDFLAGS+=-Xlinker -rpath=/usr/local/lib +debug:CFLAGS+=-DPROFILE_THINGS +#debug:CFLAGS+=-pg +debug:CFLAGS+=-g +#debug:LDFLAGS+=-lrt +debug: keynav.o + $(CC) keynav.o -o keynav $(CFLAGS) $(LDFLAGS) -lxdo + +keynav:CFLAGS+=-O2 keynav: keynav.o $(CC) keynav.o -o keynav $(CFLAGS) $(LDFLAGS) -lxdo + strip keynav keynav_version.h: sh version.sh --header > $@ From 46b9572a98e89da7367bd233c0a85137605815a5 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Sat, 30 Sep 2017 20:19:51 -0400 Subject: [PATCH 45/52] add test.sh, an early prototype of automated testing --- test.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100755 test.sh diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..d20ded5 --- /dev/null +++ b/test.sh @@ -0,0 +1,22 @@ +#!/bin/sh +set -e + +Xvfb :4 & +PID_XVFB=$! +sleep 1 + +export DISPLAY=:4 + +./keynav 2>&1 >keynav.log & +PID_KEYNAV=$! + +sleep 1 +xdotool getmouselocation +sleep 1 +xdotool key ctrl+space j l k h space +sleep 1 +xdotool getmouselocation +sleep 1 + +kill -9 $PID_XVFB +kill -9 $PID_KEYNAV From ff07abe4deb4862380c4fb8a2ea45c987f387457 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Wed, 1 Nov 2017 02:29:16 -0400 Subject: [PATCH 46/52] take variable declarations out of for-loops This is a bit inconsistent with other functions, and apparently (I was surprised, too) breaks on the version of gcc shipped with CentOS, which will only accept it if you explicitly declare -std=c99. --- keynav.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/keynav.c b/keynav.c index 742cb9a..e6dfe18 100644 --- a/keynav.c +++ b/keynav.c @@ -557,7 +557,7 @@ int parse_config_line(char *orig_line) { char *tokctx; char *keyseq; int keycode, mods; - + int i, j; char *comment; /* Ignore everything after a '#' */ @@ -585,9 +585,9 @@ int parse_config_line(char *orig_line) { /* ungrab keybindings associated with start */ if (startkeys->len > 0) { - for (int i = 0; i < ScreenCount(dpy); i++) { + for (i = 0; i < ScreenCount(dpy); i++) { Window root = RootWindow(dpy, i); - for (int j = 0; j < startkeys->len; j++) { + for (j = 0; j < startkeys->len; j++) { startkey_t *sk = g_ptr_array_index(startkeys, j); XUngrabKey(dpy, sk->keycode, sk->mods, root); XUngrabKey(dpy, sk->keycode, sk->mods | LockMask, root); From a5308f7f439b565c81c85d8e917a5a75a2c4cd28 Mon Sep 17 00:00:00 2001 From: Brian Cole Date: Sat, 4 Nov 2017 11:23:46 -0400 Subject: [PATCH 47/52] Fix issue 11 (docs list old repo, site) https://github.com/yjftsjthsd-g/keynav/issues/11 --- keynav.pod | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/keynav.pod b/keynav.pod index 22f46fc..a57b2d2 100644 --- a/keynav.pod +++ b/keynav.pod @@ -373,27 +373,23 @@ at the edge. # Make 'v' paste things: v sh "xdotool key shift+Insert",end -See the author's keynavrc as an example, here: +See the original author's keynavrc as an example, here: -http://semicomplete.googlecode.com/svn/keynav/examples/keynavrc.jordan +https://raw.githubusercontent.com/yjftsjthsd-g/keynav/master/examples/keynavrc.jordan =head1 SEE ALSO Related: L -Project site: L +Original project site: L -Google Code: L +Github: L =head1 CONTACT -Please send questions and comments to keynav-users@googlegroups.com. File -bugs and feature requests at the following URL: +For all questions, comments, bugs, and feature requests, please open an issue at: -L - -Alternately, if you really prefer email, feel free to file bugs by emailing the -list. What works for you :) +L =head1 AUTHORS From a8e3d990db0b0afc1e841377c0ca9a78acf945ad Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Sat, 23 Sep 2017 21:32:02 -0700 Subject: [PATCH 48/52] Mask wanted keyboard modifiers instead of masking out unwanted modifiers --- keynav.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/keynav.c b/keynav.c index e6dfe18..6ca11ac 100644 --- a/keynav.c +++ b/keynav.c @@ -328,8 +328,9 @@ int parse_mods(char *keyseq) { modmask |= Mod4Mask; if (!strcasecmp(mod, "mod1")) modmask |= Mod1Mask; + // See masking of state in handle_keypress if (!strcasecmp(mod, "mod2")) - modmask |= Mod2Mask; + printf("Error in configuration: keynav does not support mod2 modifier, but other modifiers are supported."); if (!strcasecmp(mod, "mod3")) modmask |= Mod3Mask; if (!strcasecmp(mod, "mod4")) @@ -1535,16 +1536,10 @@ void viewport_left() { void handle_keypress(XKeyEvent *e) { int i; - /* If a mouse button is pressed (like, when we're dragging), - * then the 'mods' will include values like Button1Mask. - * Let's remove those, as they cause breakage */ - e->state &= ~(Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask); - /* Ignore LockMask (Numlock, etc) and Mod2Mask (shift, etc) */ - e->state &= ~(LockMask | Mod2Mask); - - /* Ignore different keyboard layouts (e.g. russian) */ - e->state &= ~(1<<13); + /* Only pay attention to shift. In particular, things not included here are + * mouse buttons (active when dragging), numlock (including Mod2Mask) */ + e->state &= (ShiftMask | ControlMask | Mod1Mask | Mod3Mask | Mod4Mask | Mod4Mask); if (appstate.recording == record_getkey) { if (handle_recording(e) == HANDLE_STOP) { From a3acc1a1728efeef0acb11eefaceda9c431b25ad Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Sat, 23 Sep 2017 21:36:27 -0700 Subject: [PATCH 49/52] Use 'found' variable instead of break, as suggested in review --- keynav.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/keynav.c b/keynav.c index 6ca11ac..93d3d05 100644 --- a/keynav.c +++ b/keynav.c @@ -1712,7 +1712,7 @@ void handle_commands(char *commands) { g_ptr_array_add(active_recording->commands, (gpointer) strdup(tok)); } - for (i = 0; dispatch[i].command; i++) { + for (i = 0; dispatch[i].command && !found; i++) { /* XXX: This approach means we can't have one command be a subset of * another. For example, 'grid' and 'grid-foo' will fail because when you * use 'grid-foo' it'll match 'grid' first. @@ -1736,7 +1736,6 @@ void handle_commands(char *commands) { found = 1; dispatch[i].func(args); - break; } } From f8790fa4ad75779003f0be9cea2cbb4d6340adac Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Sat, 23 Sep 2017 22:31:27 -0700 Subject: [PATCH 50/52] Re-query viewports info when xrandr configuration changes Without this change, when a new display is added or an existing display changes size, keynav doesn't use correct starting bounds for its rectangle. I also sometimes get "BadWindow (invalid Window parameter)" from a call of X_GrabKeyboard, consistent with using an invalid root window. --- Makefile | 2 ++ keynav.c | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b71ea91..c0178c6 100644 --- a/Makefile +++ b/Makefile @@ -2,11 +2,13 @@ CFLAGS+=$(shell pkg-config --cflags cairo-xlib 2> /dev/null) CFLAGS+=$(shell pkg-config --cflags xinerama 2> /dev/null) CFLAGS+=$(shell pkg-config --cflags glib-2.0 2> /dev/null) CFLAGS+=$(shell pkg-config --cflags x11 2> /dev/null) +CFLAGS+=$(shell pkg-config --cflags xrandr 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs cairo-xlib 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs xinerama 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs glib-2.0 2> /dev/null) LDFLAGS+=$(shell pkg-config --libs x11 2> /dev/null) +LDFLAGS+=$(shell pkg-config --libs xrandr 2> /dev/null) LDFLAGS+=-Xlinker -rpath=/usr/local/lib PREFIX=/usr diff --git a/keynav.c b/keynav.c index 93d3d05..4e120be 100644 --- a/keynav.c +++ b/keynav.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -1812,6 +1813,7 @@ void query_screen_xinerama() { XineramaScreenInfo *screeninfo; screeninfo = XineramaQueryScreens(dpy, &nviewports); + free(viewports); viewports = calloc(nviewports, sizeof(viewport_t)); for (i = 0; i < nviewports; i++) { viewports[i].x = screeninfo[i].x_org; @@ -1829,6 +1831,7 @@ void query_screen_normal() { int i; Screen *s; nviewports = ScreenCount(dpy); + free(viewports); viewports = calloc(nviewports, sizeof(viewport_t)); for (i = 0; i < nviewports; i++) { @@ -2049,6 +2052,15 @@ int main(int argc, char **argv) { * before we try to daemonize */ XSync(dpy, 0); + /* If xrandr is enabled, ask to receive events for screen configuration + * changes. */ + int xrandr_event_base = 0; + int xrandr_error_base = 0; + int xrandr = XRRQueryExtension (dpy, &xrandr_event_base, &xrandr_error_base); + if (xrandr) { + XRRSelectInput(dpy, DefaultRootWindow(dpy), RRScreenChangeNotifyMask); + } + if (daemonize) { printf("Daemonizing now...\n"); daemon(0, 0); @@ -2101,7 +2113,11 @@ int main(int argc, char **argv) { case MappingNotify: // when keyboard mapping changes break; default: - printf("Unexpected X11 event: %d\n", e.type); + if (e.type == xrandr_event_base + RRScreenChangeNotify) { + query_screens(); + } else { + printf("Unexpected X11 event: %d\n", e.type); + } break; } } From a5785ba6b6c345d83fd23867ab398d567468894e Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Sat, 23 Sep 2017 22:42:03 -0700 Subject: [PATCH 51/52] Make it so that windowzoom doesn't crash when no window is active With xmonad (tiling window manager), I can make it so that no window is selected, just by making an empty workspace active. Running a keynav command involving "windowzoom" in this context results in: X Error of failed request: BadDrawable (invalid Pixmap or Window parameter) Major opcode of failed request: 14 (X_GetGeometry) This fixes that such that keynav no longer crashes in this case, but it does output an error message: XGetWindowProperty[_NET_ACTIVE_WINDOW] failed (code=1) Looks like this is coming from xdotool - https://github.com/jordansissel/xdotool/blob/a70547cf14ab31b3a2900f8bd1e8648ad3633b38/xdo.c#L712 I don't really mind the extra output. --- keynav.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/keynav.c b/keynav.c index 4e120be..74e67fd 100644 --- a/keynav.c +++ b/keynav.c @@ -1155,15 +1155,17 @@ void cmd_windowzoom(char *args) { unsigned int width, height, border_width, depth; xdo_get_active_window(xdo, &curwin); - XGetGeometry(xdo->xdpy, curwin, &rootwin, &x, &y, &width, &height, - &border_width, &depth); - XTranslateCoordinates(xdo->xdpy, curwin, rootwin, - -border_width, -border_width, &x, &y, &dummy_win); + if (curwin) { + XGetGeometry(xdo->xdpy, curwin, &rootwin, &x, &y, &width, &height, + &border_width, &depth); + XTranslateCoordinates(xdo->xdpy, curwin, rootwin, + -border_width, -border_width, &x, &y, &dummy_win); - wininfo.x = x; - wininfo.y = y; - wininfo.w = width; - wininfo.h = height; + wininfo.x = x; + wininfo.y = y; + wininfo.w = width; + wininfo.h = height; + } } void cmd_warp(char *args) { From 982994fbae5aac26f1ac446f12867bcc15b05f56 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Thu, 14 Jun 2018 00:26:46 -0700 Subject: [PATCH 52/52] Check if 'zone' is non-null before using it This seems to fix the following X11 error Error of failed request: BadWindow (invalid Window parameter) Major opcode of failed request: 129 (SHAPE) Minor opcode of failed request: 1 (X_ShapeRectangles) Resource id in failed request: 0x0 --- keynav.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/keynav.c b/keynav.c index 74e67fd..65abe7c 100644 --- a/keynav.c +++ b/keynav.c @@ -2097,12 +2097,14 @@ int main(int argc, char **argv) { break; case MotionNotify: + if (zone) { if (mouseinfo.x != -1 && mouseinfo.y != -1) { closepixel(dpy, zone, &mouseinfo); } mouseinfo.x = e.xmotion.x; mouseinfo.y = e.xmotion.y; openpixel(dpy, zone, &mouseinfo); + } break; // Ignorable events.