From 9f4ea8618bcef101ab4eaa6f202bde00f009ec8d Mon Sep 17 00:00:00 2001 From: Johannes Witzig Date: Thu, 29 Mar 2018 20:43:12 +0200 Subject: [PATCH 1/5] fix cmd_behave events focus/blur The effective situation was: focus and blur were synomnyms and both got fired by both window events. This commit fixes that bug. --- cmd_behave.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/cmd_behave.c b/cmd_behave.c index 6ddead0c..bf24d148 100644 --- a/cmd_behave.c +++ b/cmd_behave.c @@ -4,13 +4,14 @@ struct events { const char * const name; int mask; + int type } events[] = { - { "mouse-enter", EnterWindowMask }, - { "mouse-leave", LeaveWindowMask }, - { "focus", FocusChangeMask }, - { "blur", FocusChangeMask }, - { "mouse-click", ButtonReleaseMask }, - { NULL, 0 }, + { "mouse-enter", EnterWindowMask, EnterNotify }, + { "mouse-leave", LeaveWindowMask, LeaveNotify }, + { "focus", FocusChangeMask, FocusIn }, + { "blur", FocusChangeMask, FocusOut }, + { "mouse-click", ButtonReleaseMask, ButtonRelease }, + { NULL, 0, 0 }, }; /* So we can invoke xdotool from within this command */ @@ -75,12 +76,14 @@ int cmd_behave(context_t *context) { /* The remainder of args are supposed to be what to run on the action */ long selectmask = 0; + int eventtype = 0; int i; for (i = 0; events[i].name != NULL; i++) { //printf("%s vs %s\n", events[i].name, event); if (!strcmp(events[i].name, event)) { xdotool_debug(context, "Adding mask for event '%s': 0x%lx", event, events[i].mask); selectmask |= events[i].mask; + eventtype = events[i].type; } } @@ -137,8 +140,16 @@ int cmd_behave(context_t *context) { break; case FocusIn: case FocusOut: - tmpcontext.windows = &(e.xfocus.window); - ret = context_execute(&tmpcontext); + /* As both events are selected by the same mask, + * we have to make sure we only fire on the type + * that was requested. */ + if (e.type == eventtype) { + tmpcontext.windows = &(e.xfocus.window); + ret = context_execute(&tmpcontext); + } else { + /* Set to success to avoid "Command failed." */ + ret = XDO_SUCCESS; + } break; case ButtonRelease: tmpcontext.windows = &(e.xbutton.window); From b15347b4d1bd3706911ad79092935a2e3bd8f968 Mon Sep 17 00:00:00 2001 From: Johannes Witzig Date: Wed, 4 Apr 2018 21:11:54 +0200 Subject: [PATCH 2/5] fix memory/pointer problem with cmd_behave The previous code would abort at runtime on constructs like xdotool selectwindow behave "%1" mouse-enter getactivewindow because context->windows gets freed (e.g. in window_save) ... --- cmd_behave.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd_behave.c b/cmd_behave.c index bf24d148..da4b8bf9 100644 --- a/cmd_behave.c +++ b/cmd_behave.c @@ -112,6 +112,7 @@ int cmd_behave(context_t *context) { // Copy context context_t tmpcontext = *context; + tmpcontext.windows = calloc(1, sizeof(Window)); tmpcontext.nwindows = 1; Window hover; /* for LeaveNotify */ switch (e.type) { @@ -135,7 +136,7 @@ int cmd_behave(context_t *context) { /* fall through */ case EnterNotify: - tmpcontext.windows = &(e.xcrossing.window); + *tmpcontext.windows = e.xcrossing.window; ret = context_execute(&tmpcontext); break; case FocusIn: @@ -144,7 +145,7 @@ int cmd_behave(context_t *context) { * we have to make sure we only fire on the type * that was requested. */ if (e.type == eventtype) { - tmpcontext.windows = &(e.xfocus.window); + *tmpcontext.windows = e.xfocus.window; ret = context_execute(&tmpcontext); } else { /* Set to success to avoid "Command failed." */ @@ -152,7 +153,7 @@ int cmd_behave(context_t *context) { } break; case ButtonRelease: - tmpcontext.windows = &(e.xbutton.window); + *tmpcontext.windows = e.xbutton.window; ret = context_execute(&tmpcontext); break; default: @@ -163,6 +164,10 @@ int cmd_behave(context_t *context) { if (ret != XDO_SUCCESS) { xdotool_output(context, "Command failed."); } + + if(tmpcontext.windows != NULL) { + free(tmpcontext.windows); + } } return ret; } From 9134c1ec88f924848ebf08224fb16ca515d86d2d Mon Sep 17 00:00:00 2001 From: Johannes Witzig Date: Thu, 5 Apr 2018 21:51:22 +0200 Subject: [PATCH 3/5] add destroy, map and unmap events to cmd_behave --- cmd_behave.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/cmd_behave.c b/cmd_behave.c index da4b8bf9..ee80479d 100644 --- a/cmd_behave.c +++ b/cmd_behave.c @@ -11,6 +11,9 @@ struct events { { "focus", FocusChangeMask, FocusIn }, { "blur", FocusChangeMask, FocusOut }, { "mouse-click", ButtonReleaseMask, ButtonRelease }, + { "destroy", StructureNotifyMask, DestroyNotify }, + { "map", StructureNotifyMask, MapNotify }, + { "unmap", StructureNotifyMask, UnmapNotify }, { NULL, 0, 0 }, }; @@ -39,7 +42,10 @@ int cmd_behave(context_t *context) { " mouse-leave - When the mouse leaves a window\n" " mouse-click - Fired when the mouse button is released\n" " focus - When the window gets focus\n" - " blur - When the window loses focus\n"; + " blur - When the window loses focus\n" + " destroy - When the window gets destroyed\n" + " map - When the window is mapped\n" + " unmap - When the window is unmapped\n"; int option_index; while ((c = getopt_long_only(context->argc, context->argv, "+h", @@ -156,6 +162,21 @@ int cmd_behave(context_t *context) { *tmpcontext.windows = e.xbutton.window; ret = context_execute(&tmpcontext); break; + case DestroyNotify: + case MapNotify: + case UnmapNotify: + case CirculateNotify: /* these four*/ + case ConfigureNotify: /* are also */ + case GravityNotify: /* caught by */ + case ReparentNotify: /* StructureNotifyMask */ + if (e.type == eventtype) { + *tmpcontext.windows = e.xany.window; + ret = context_execute(&tmpcontext); + } else { + /* Set to success to avoid "Command failed." */ + ret = XDO_SUCCESS; + } + break; default: printf("Unexpected event: %d\n", e.type); break; From 42aa05a9ac2b94f030adf89403d47f41e8b5862b Mon Sep 17 00:00:00 2001 From: Johannes Witzig Date: Tue, 10 Apr 2018 12:06:52 +0200 Subject: [PATCH 4/5] Add possibility to exit cmd_behave Also convert some messages to debug output. --- cmd_behave.c | 54 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/cmd_behave.c b/cmd_behave.c index ee80479d..d71dc1a5 100644 --- a/cmd_behave.c +++ b/cmd_behave.c @@ -23,17 +23,26 @@ extern int context_execute(context_t *context); int cmd_behave(context_t *context) { int ret = 0; char *cmd = *context->argv; + int exit_on = -1; int c; typedef enum { - opt_unused, opt_help + opt_unused, opt_help, opt_exiton } optlist_t; static struct option longopts[] = { { "help", no_argument, NULL, opt_help }, + { "exit-on", required_argument, NULL, opt_exiton }, { 0, 0, 0, 0 }, }; static const char *usage = - "Usage: %s window event action [args...]\n" + "Usage: %s [options] window event action [args...]\n" + "--exit-on CODE - Stop behave whenever the action returns with CODE,\n" + " where the latter is one of the following:\n" + " success - success code returned by internal commands\n" + " failure - error code returned by internal commands\n" + " NUM - a numerical exit code (useful in conjunction\n" + " with the exec command)\n" + "\n" "The event is a window event, such as mouse-enter, resize, etc.\n" "The action is any valid xdotool command (chains OK here)\n" "\n" @@ -57,6 +66,16 @@ int cmd_behave(context_t *context) { consume_args(context, context->argc); return EXIT_SUCCESS; break; + case opt_exiton: + if (!strcmp(optarg, "success")) { + exit_on = XDO_SUCCESS; + } else if (!strcmp(optarg, "failure")) { + exit_on = XDO_ERROR; + } else { + exit_on = atoi(optarg); + /* XXX: do we want error handling here? */ + } + break; default: fprintf(stderr, usage, cmd); return EXIT_FAILURE; @@ -121,6 +140,7 @@ int cmd_behave(context_t *context) { tmpcontext.windows = calloc(1, sizeof(Window)); tmpcontext.nwindows = 1; Window hover; /* for LeaveNotify */ + int ran_action = True; switch (e.type) { case LeaveNotify: /* LeaveNotify is confusing. @@ -135,6 +155,7 @@ int cmd_behave(context_t *context) { xdo_get_window_at_mouse(context->xdo, &hover); if (hover == e.xcrossing.window) { //printf("Ignoring Leave, we're still in the window\n"); + ran_action = False; break; } //printf("Window: %ld\n", e.xcrossing.window); @@ -154,8 +175,7 @@ int cmd_behave(context_t *context) { *tmpcontext.windows = e.xfocus.window; ret = context_execute(&tmpcontext); } else { - /* Set to success to avoid "Command failed." */ - ret = XDO_SUCCESS; + ran_action = False; } break; case ButtonRelease: @@ -173,23 +193,35 @@ int cmd_behave(context_t *context) { *tmpcontext.windows = e.xany.window; ret = context_execute(&tmpcontext); } else { - /* Set to success to avoid "Command failed." */ - ret = XDO_SUCCESS; + ran_action = False; } break; default: - printf("Unexpected event: %d\n", e.type); + /* There are a couple of events that we receive + * regardless of the chosen selection mask (e.g. + * MappingNotify) and we should not treat those + * as errors. */ + xdotool_debug("Unexpected event: %d\n", e.type); + ran_action = False; break; } - if (ret != XDO_SUCCESS) { - xdotool_output(context, "Command failed."); - } - if(tmpcontext.windows != NULL) { free(tmpcontext.windows); } + + if (ran_action == True) { + if (exit_on == ret) { + break; + } + + if (ret != XDO_SUCCESS) { + xdotool_debug(context, "Command failed."); + } + } } + + consume_args(context, context->argc); return ret; } From 7bd751d73ccfbbf9fb6a02de197b2ef0d1cee896 Mon Sep 17 00:00:00 2001 From: Johannes Witzig Date: Tue, 10 Apr 2018 12:14:45 +0200 Subject: [PATCH 5/5] remove superfluous indention --- cmd_behave.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd_behave.c b/cmd_behave.c index d71dc1a5..86419274 100644 --- a/cmd_behave.c +++ b/cmd_behave.c @@ -172,10 +172,10 @@ int cmd_behave(context_t *context) { * we have to make sure we only fire on the type * that was requested. */ if (e.type == eventtype) { - *tmpcontext.windows = e.xfocus.window; - ret = context_execute(&tmpcontext); + *tmpcontext.windows = e.xfocus.window; + ret = context_execute(&tmpcontext); } else { - ran_action = False; + ran_action = False; } break; case ButtonRelease: @@ -190,10 +190,10 @@ int cmd_behave(context_t *context) { case GravityNotify: /* caught by */ case ReparentNotify: /* StructureNotifyMask */ if (e.type == eventtype) { - *tmpcontext.windows = e.xany.window; - ret = context_execute(&tmpcontext); + *tmpcontext.windows = e.xany.window; + ret = context_execute(&tmpcontext); } else { - ran_action = False; + ran_action = False; } break; default: @@ -207,7 +207,7 @@ int cmd_behave(context_t *context) { } if(tmpcontext.windows != NULL) { - free(tmpcontext.windows); + free(tmpcontext.windows); } if (ran_action == True) {