From f4f4d782bb590e7e03302586fa3028fe785ba95d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 10 Oct 2011 11:36:21 +0100 Subject: [PATCH] implement a startup monitor, move code to src/startup.c --- include/all.h | 1 + include/startup.h | 35 ++++++++++++++++++ include/util.h | 12 ------- src/handlers.c | 11 ++++++ src/startup.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++ src/util.c | 52 --------------------------- 6 files changed, 138 insertions(+), 64 deletions(-) create mode 100644 include/startup.h create mode 100644 src/startup.c diff --git a/include/all.h b/include/all.h index 38cda89c..fd25629e 100644 --- a/include/all.h +++ b/include/all.h @@ -66,5 +66,6 @@ #include "assignments.h" #include "regex.h" #include "libi3.h" +#include "startup.h" #endif diff --git a/include/startup.h b/include/startup.h new file mode 100644 index 00000000..9e131019 --- /dev/null +++ b/include/startup.h @@ -0,0 +1,35 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * + * © 2009-2011 Michael Stapelberg and contributors + * + * See file LICENSE for license information. + * + */ +#ifndef _STARTUP_H +#define _STARTUP_H + +#define SN_API_NOT_YET_FROZEN 1 +#include + +/** + * Starts the given application by passing it through a shell. We use double + * fork to avoid zombie processes. As the started application’s parent exits + * (immediately), the application is reparented to init (process-id 1), which + * correctly handles childs, so we don’t have to do it :-). + * + * The shell is determined by looking for the SHELL environment variable. If + * it does not exist, /bin/sh is used. + * + */ +void start_application(const char *command); + +/** + * Called by libstartup-notification when something happens + * + */ +void startup_monitor_event(SnMonitorEvent *event, void *userdata); + +#endif diff --git a/include/util.h b/include/util.h index 7c7b819a..efef0bd0 100644 --- a/include/util.h +++ b/include/util.h @@ -66,18 +66,6 @@ Rect rect_add(Rect a, Rect b); */ bool update_if_necessary(uint32_t *destination, const uint32_t new_value); -/** - * Starts the given application by passing it through a shell. We use double - * fork to avoid zombie processes. As the started application’s parent exits - * (immediately), the application is reparented to init (process-id 1), which - * correctly handles childs, so we don’t have to do it :-). - * - * The shell is determined by looking for the SHELL environment variable. If - * it does not exist, /bin/sh is used. - * - */ -void start_application(const char *command); - /** * exec()s an i3 utility, for example the config file migration script or * i3-nagbar. This function first searches $PATH for the given utility named, diff --git a/src/handlers.c b/src/handlers.c index d81d7ecc..340e24aa 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -11,6 +11,9 @@ #include +#define SN_API_NOT_YET_FROZEN 1 +#include + #include "all.h" int randr_base = -1; @@ -628,6 +631,11 @@ static int handle_expose_event(xcb_expose_event_t *event) { * */ static void handle_client_message(xcb_client_message_event_t *event) { + /* If this is a startup notification ClientMessage, the library will handle + * it and call our monitor_event() callback. */ + if (sn_xcb_display_process_event(sndisplay, (xcb_generic_event_t*)event)) + return; + LOG("ClientMessage for window 0x%08x\n", event->window); if (event->type == A__NET_WM_STATE) { if (event->format != 32 || event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN) { @@ -984,6 +992,9 @@ static struct property_handler_t property_handlers[] = { * */ void property_handlers_init() { + + sn_monitor_context_new(sndisplay, conn_screen, startup_monitor_event, NULL, NULL); + property_handlers[0].atom = A__NET_WM_NAME; property_handlers[1].atom = XCB_ATOM_WM_HINTS; property_handlers[2].atom = XCB_ATOM_WM_NAME; diff --git a/src/startup.c b/src/startup.c new file mode 100644 index 00000000..dd327355 --- /dev/null +++ b/src/startup.c @@ -0,0 +1,91 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * + * © 2009-2011 Michael Stapelberg and contributors + * + * See file LICENSE for license information. + * + * startup.c: Startup notification code + * + */ +#include +#include + +#define SN_API_NOT_YET_FROZEN 1 +#include + +#include "all.h" + +/* + * Starts the given application by passing it through a shell. We use double fork + * to avoid zombie processes. As the started application’s parent exits (immediately), + * the application is reparented to init (process-id 1), which correctly handles + * childs, so we don’t have to do it :-). + * + * The shell is determined by looking for the SHELL environment variable. If it + * does not exist, /bin/sh is used. + * + */ +void start_application(const char *command) { + /* Create a startup notification context to monitor the progress of this + * startup. */ + SnLauncherContext *context; + context = sn_launcher_context_new(sndisplay, conn_screen); + sn_launcher_context_set_name(context, "i3"); + sn_launcher_context_set_description(context, "exec command in i3"); + /* Chop off everything starting from the first space (if there are any + * spaces in the command), since we don’t want the parameters. */ + char *first_word = sstrdup(command); + char *space = strchr(first_word, ' '); + if (space) + *space = '\0'; + sn_launcher_context_initiate(context, "i3", first_word, last_timestamp); + free(first_word); + + LOG("startup id = %s\n", sn_launcher_context_get_startup_id(context)); + + LOG("executing: %s\n", command); + if (fork() == 0) { + /* Child process */ + setsid(); + if (fork() == 0) { + /* Setup the environment variable(s) */ + sn_launcher_context_setup_child_process(context); + + /* Stores the path of the shell */ + static const char *shell = NULL; + + if (shell == NULL) + if ((shell = getenv("SHELL")) == NULL) + shell = "/bin/sh"; + + /* This is the child */ + execl(shell, shell, "-c", command, (void*)NULL); + /* not reached */ + } + exit(0); + } + wait(0); +} + +/* + * Called by libstartup-notification when something happens + * + */ +void startup_monitor_event(SnMonitorEvent *event, void *userdata) { + SnStartupSequence *sequence; + + DLOG("something happened\n"); + sequence = sn_monitor_event_get_startup_sequence(event); + + switch (sn_monitor_event_get_type(event)) { + case SN_MONITOR_EVENT_COMPLETED: + DLOG("startup sequence %s completed\n", sn_startup_sequence_get_id(sequence)); + break; + default: + /* ignore */ + break; + } +} diff --git a/src/util.c b/src/util.c index d0e8e947..036dce9a 100644 --- a/src/util.c +++ b/src/util.c @@ -61,58 +61,6 @@ bool update_if_necessary(uint32_t *destination, const uint32_t new_value) { return ((*destination = new_value) != old_value); } -/* - * Starts the given application by passing it through a shell. We use double fork - * to avoid zombie processes. As the started application’s parent exits (immediately), - * the application is reparented to init (process-id 1), which correctly handles - * childs, so we don’t have to do it :-). - * - * The shell is determined by looking for the SHELL environment variable. If it - * does not exist, /bin/sh is used. - * - */ -void start_application(const char *command) { - /* Create a startup notification context to monitor the progress of this - * startup. */ - SnLauncherContext *context; - context = sn_launcher_context_new(sndisplay, conn_screen); - sn_launcher_context_set_name(context, "i3"); - sn_launcher_context_set_description(context, "exec command in i3"); - /* Chop off everything starting from the first space (if there are any - * spaces in the command), since we don’t want the parameters. */ - char *first_word = sstrdup(command); - char *space = strchr(first_word, ' '); - if (space) - *space = '\0'; - sn_launcher_context_initiate(context, "i3", first_word, last_timestamp); - free(first_word); - - LOG("startup id = %s\n", sn_launcher_context_get_startup_id(context)); - - LOG("executing: %s\n", command); - if (fork() == 0) { - /* Child process */ - setsid(); - if (fork() == 0) { - /* Setup the environment variable(s) */ - sn_launcher_context_setup_child_process(context); - - /* Stores the path of the shell */ - static const char *shell = NULL; - - if (shell == NULL) - if ((shell = getenv("SHELL")) == NULL) - shell = "/bin/sh"; - - /* This is the child */ - execl(shell, shell, "-c", command, (void*)NULL); - /* not reached */ - } - exit(0); - } - wait(0); -} - /* * exec()s an i3 utility, for example the config file migration script or * i3-nagbar. This function first searches $PATH for the given utility named,