From da51d3732292a19fbc17e50e920b49adb9443d60 Mon Sep 17 00:00:00 2001 From: o9000 Date: Mon, 8 Aug 2016 12:04:31 +0200 Subject: [PATCH] Execp: force update after custom command execution (issue #586) --- src/execplugin/execplugin.c | 49 ++++++++++++++++----- src/execplugin/execplugin.h | 3 ++ src/server.c | 5 +++ src/tint.c | 87 +++++++++++++++++-------------------- src/util/common.c | 10 +++++ src/util/common.h | 3 ++ 6 files changed, 100 insertions(+), 57 deletions(-) diff --git a/src/execplugin/execplugin.c b/src/execplugin/execplugin.c index d04b513..86259aa 100644 --- a/src/execplugin/execplugin.c +++ b/src/execplugin/execplugin.c @@ -32,12 +32,11 @@ Execp *create_execp() Execp *execp = calloc(1, sizeof(Execp)); execp->backend = calloc(1, sizeof(ExecpBackend)); execp->backend->child_pipe = -1; - + execp->backend->cmd_pids = g_tree_new(cmp_ptr); execp->backend->interval = 30; execp->backend->cache_icon = TRUE; execp->backend->centered = TRUE; execp->backend->font_color.alpha = 0.5; - return execp; } @@ -83,6 +82,10 @@ void destroy_execp(void *obj) close(execp->backend->child_pipe); execp->backend->child_pipe = -1; } + if (execp->backend->cmd_pids) { + g_tree_destroy(execp->backend->cmd_pids); + execp->backend->cmd_pids = NULL; + } execp->backend->bg = NULL; pango_font_description_free(execp->backend->font_desc); @@ -467,6 +470,18 @@ void execp_dump_geometry(void *obj, int indent) execp->backend->text); } +void execp_force_update(Execp *execp) +{ + if (execp->backend->child_pipe > 0) { + // Command currently running, nothing to do + } else { + if (execp->backend->timer) + stop_timeout(execp->backend->timer); + // Run command right away + execp->backend->timer = add_timeout(10, 0, execp_timer_callback, execp, &execp->backend->timer); + } +} + void execp_action(void *obj, int button, int x, int y) { Execp *execp = obj; @@ -498,20 +513,32 @@ void execp_action(void *obj, int button, int x, int y) execp->area.width, execp->area.height, command); - tint_exec(full_cmd); + pid_t pid = fork(); + if (pid < 0) { + fprintf(stderr, "Could not fork\n"); + } else if (pid == 0) { + // Child process + // Allow children to exist after parent destruction + setsid(); + // Run the command + execl("/bin/sh", "/bin/sh", "-c", full_cmd, NULL); + fprintf(stderr, "Failed to execlp %s\n", full_cmd); + exit(1); + } + // Parent process + g_tree_insert(execp->backend->cmd_pids, GINT_TO_POINTER(pid), GINT_TO_POINTER(1)); g_free(full_cmd); } else { - if (execp->backend->child_pipe > 0) { - // Command currently running, nothing to do - } else { - if (execp->backend->timer) - stop_timeout(execp->backend->timer); - // Run command right away - execp->backend->timer = add_timeout(10, 0, execp_timer_callback, execp, &execp->backend->timer); - } + execp_force_update(execp); } } +void execp_cmd_completed(Execp *execp, pid_t pid) +{ + g_tree_remove(execp->backend->cmd_pids, GINT_TO_POINTER(pid)); + execp_force_update(execp); +} + void execp_timer_callback(void *arg) { Execp *execp = arg; diff --git a/src/execplugin/execplugin.h b/src/execplugin/execplugin.h index 4227642..0656348 100644 --- a/src/execplugin/execplugin.h +++ b/src/execplugin/execplugin.h @@ -70,6 +70,7 @@ typedef struct ExecpBackend { // List of Execp which are frontends for this backend, one for each panel GList *instances; + GTree *cmd_pids; } ExecpBackend; typedef struct ExecpFrontend { @@ -130,6 +131,8 @@ gboolean resize_execp(void *obj); // Called on mouse click event. void execp_action(void *obj, int button, int x, int y); +void execp_cmd_completed(Execp *obj, pid_t pid); + // Called to check if new output from the command can be read. // No command might be running. // Returns 1 if the output has been updated and a redraw is needed. diff --git a/src/server.c b/src/server.c index c1691a4..d95a444 100644 --- a/src/server.c +++ b/src/server.c @@ -140,6 +140,11 @@ void cleanup_server() XFreeGC(server.display, server.gc); server.gc = NULL; server.disable_transparency = FALSE; +#ifdef HAVE_SN + if (server.pids) + g_tree_destroy(server.pids); + server.pids = NULL; +#endif } void send_event32(Window win, Atom at, long data1, long data2, long data3) diff --git a/src/tint.c b/src/tint.c index 7a4610a..1cd1d5e 100644 --- a/src/tint.c +++ b/src/tint.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **************************************************************************/ +#include #include #include #include @@ -376,8 +377,8 @@ void init(int argc, char *argv[]) debug_geometry = getenv("DEBUG_GEOMETRY") != NULL; } -static int sn_pipe_valid = 0; -static int sn_pipe[2]; +static int sigchild_pipe_valid = FALSE; +static int sigchild_pipe[2]; #ifdef HAVE_SN static int error_trap_depth = 0; @@ -397,50 +398,41 @@ static void error_trap_pop(SnDisplay *display, Display *xdisplay) XSync(xdisplay, False); /* get all errors out of the queue */ --error_trap_depth; } +#endif // HAVE_SN static void sigchld_handler(int sig) { - if (!startup_notifications) + if (!sigchild_pipe_valid) return; - if (!sn_pipe_valid) - return; - ssize_t wur = write(sn_pipe[1], "x", 1); - (void)wur; - fsync(sn_pipe[1]); + int savedErrno = errno; + ssize_t unused = write(sigchild_pipe[1], "x", 1); + (void)unused; + fsync(sigchild_pipe[1]); + errno = savedErrno; } static void sigchld_handler_async() { - if (!startup_notifications) - return; // Wait for all dead processes pid_t pid; - while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) { - SnLauncherContext *ctx; - ctx = (SnLauncherContext *)g_tree_lookup(server.pids, GINT_TO_POINTER(pid)); + int status; + while ((pid = waitpid(-1, &status, WNOHANG)) != -1) { +#ifdef HAVE_SN + SnLauncherContext *ctx = (SnLauncherContext *)g_tree_lookup(server.pids, GINT_TO_POINTER(pid)); if (ctx) { g_tree_remove(server.pids, GINT_TO_POINTER(pid)); sn_launcher_context_complete(ctx); sn_launcher_context_unref(ctx); } +#endif + for (GList *l = panel_config.execp_list; l; l = l->next) { + Execp *execp = (Execp *)l->data; + if (g_tree_lookup(execp->backend->cmd_pids, GINT_TO_POINTER(pid))) + execp_cmd_completed(execp, pid); + } } } -static gint cmp_ptr(gconstpointer a, gconstpointer b) -{ - if (a < b) - return -1; - else if (a == b) - return 0; - else - return 1; -} -#else -static void sigchld_handler_async() -{ -} -#endif // HAVE_SN - void init_X11_pre_config() { server.display = XOpenDisplay(NULL); @@ -475,25 +467,32 @@ void init_X11_post_config() { server_init_visual(); + gboolean need_sigchld = FALSE; #ifdef HAVE_SN // Initialize startup-notification if (startup_notifications) { server.sn_display = sn_display_new(server.display, error_trap_push, error_trap_pop); server.pids = g_tree_new(cmp_ptr); + need_sigchld = TRUE; + } +#endif // HAVE_SN + if (panel_config.execp_list) + need_sigchld = TRUE; + + if (need_sigchld) { // Setup a handler for child termination - if (pipe(sn_pipe) != 0) { + if (pipe(sigchild_pipe) != 0) { fprintf(stderr, "Creating pipe failed.\n"); } else { - fcntl(sn_pipe[0], F_SETFL, O_NONBLOCK | fcntl(sn_pipe[0], F_GETFL)); - fcntl(sn_pipe[1], F_SETFL, O_NONBLOCK | fcntl(sn_pipe[1], F_GETFL)); - sn_pipe_valid = 1; + fcntl(sigchild_pipe[0], F_SETFL, O_NONBLOCK | fcntl(sigchild_pipe[0], F_GETFL)); + fcntl(sigchild_pipe[1], F_SETFL, O_NONBLOCK | fcntl(sigchild_pipe[1], F_GETFL)); + sigchild_pipe_valid = 1; struct sigaction act = {.sa_handler = sigchld_handler, .sa_flags = SA_RESTART}; if (sigaction(SIGCHLD, &act, 0)) { perror("sigaction"); } } } -#endif // HAVE_SN imlib_context_set_display(server.display); imlib_context_set_visual(server.visual); @@ -543,15 +542,11 @@ void cleanup() XCloseDisplay(server.display); server.display = NULL; -#ifdef HAVE_SN - if (startup_notifications) { - if (sn_pipe_valid) { - sn_pipe_valid = 0; - close(sn_pipe[1]); - close(sn_pipe[0]); - } + if (sigchild_pipe_valid) { + sigchild_pipe_valid = FALSE; + close(sigchild_pipe[1]); + close(sigchild_pipe[0]); } -#endif uevent_cleanup(); } @@ -1641,9 +1636,9 @@ start: FD_ZERO(&fdset); FD_SET(x11_fd, &fdset); int maxfd = x11_fd; - if (sn_pipe_valid) { - FD_SET(sn_pipe[0], &fdset); - maxfd = maxfd < sn_pipe[0] ? sn_pipe[0] : maxfd; + if (sigchild_pipe_valid) { + FD_SET(sigchild_pipe[0], &fdset); + maxfd = maxfd < sigchild_pipe[0] ? sigchild_pipe[0] : maxfd; } for (GList *l = panel_config.execp_list; l; l = l->next) { Execp *execp = (Execp *)l->data; @@ -1664,9 +1659,9 @@ start: if (XPending(server.display) > 0 || select(maxfd + 1, &fdset, 0, 0, select_timeout) >= 0) { uevent_handler(); - if (sn_pipe_valid) { + if (sigchild_pipe_valid) { char buffer[1]; - while (read(sn_pipe[0], buffer, sizeof(buffer)) > 0) { + while (read(sigchild_pipe[0], buffer, sizeof(buffer)) > 0) { sigchld_handler_async(); } } diff --git a/src/util/common.c b/src/util/common.c index bfefee3..c45b996 100644 --- a/src/util/common.c +++ b/src/util/common.c @@ -673,3 +673,13 @@ GSList *slist_remove_duplicates(GSList *list, GCompareFunc eq, GDestroyNotify fr return new_list; } + +gint cmp_ptr(gconstpointer a, gconstpointer b) +{ + if (a < b) + return -1; + else if (a == b) + return 0; + else + return 1; +} diff --git a/src/util/common.h b/src/util/common.h index 1bc51bf..44a8590 100644 --- a/src/util/common.h +++ b/src/util/common.h @@ -120,6 +120,9 @@ GSList *load_locations_from_env(GSList *locations, const char *var, ...); GSList *slist_remove_duplicates(GSList *list, GCompareFunc eq, GDestroyNotify fr); +// A trivial pointer comparator. +gint cmp_ptr(gconstpointer a, gconstpointer b); + #define free_and_null(p) \ { \ free(p); \