all repos — openbox @ 19b480058e869a588ea20be5e29017ae2052e967

openbox fork - make it a bit more like ryudo

wow... this is a big commit...

all related to _NET_WM_USER_TIME and focus stealing prevention

a) add launcher startup notification. this means when you run something from
   the openbox menu or a key/mouse binding, that startup notification will go
   on in openbox and other applications like your panel or something
b) add the _NET_WM_USER_TIME property for windows
c) use the _NET_WM_USER_TIME data and startup notification to prevent focus
   stealing.
d) cookie party !! ! all are invited.
e) oh yeah, and pass around timestamps for a lot more things. like, when you
   run an action, send the timestamp for the event that is running the action.
   this is important for startup notification. this also affects menus.
f) yes.. cookies..

would it be a good idea to disable focus stealing prevention if a window takes
too long to load? i mean.. maybe after a certain length of time, a user can't be
expected to not do anything in any other windows, but would they still want the
new application to focus then? HMM. open question i guess..
Dana Jansens danakj@orodu.net
commit

19b480058e869a588ea20be5e29017ae2052e967

parent

9d6e3907650f4bc05ebf147aeec92573d7b88758

M openbox/action.copenbox/action.c

@@ -33,6 +33,7 @@ #include "event.h"

#include "dock.h" #include "config.h" #include "mainloop.h" +#include "startupnotify.h" #include <glib.h>

@@ -1005,7 +1006,7 @@ return act;

} void action_run_list(GSList *acts, ObClient *c, ObFrameContext context, - guint state, guint button, gint x, gint y, + guint state, guint button, gint x, gint y, Time time, gboolean cancel, gboolean done) { GSList *it;

@@ -1048,6 +1049,8 @@ a->data.any.y = y;

a->data.any.button = button; + a->data.any.time = time; + if (a->data.any.interactive) { a->data.inter.cancel = cancel; a->data.inter.final = done;

@@ -1068,7 +1071,7 @@ }

} } -void action_run_string(const gchar *name, struct _ObClient *c) +void action_run_string(const gchar *name, struct _ObClient *c, Time time) { ObAction *a; GSList *l;

@@ -1078,7 +1081,7 @@ g_assert(a);

l = g_slist_append(NULL, a); - action_run(l, c, 0); + action_run(l, c, 0, time); } void action_execute(union ActionData *data)

@@ -1093,13 +1096,21 @@ g_warning("failed to execute '%s': %s",

cmd, e->message); g_error_free(e); } else { - if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH | + gchar **env, *program; + + program = g_path_get_basename(argv[0]); + env = sn_get_spawn_environment(program, + data->execute.any.time); + if (!g_spawn_async(NULL, argv, env, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, NULL, &e)) { g_warning("failed to execute '%s': %s", cmd, e->message); g_error_free(e); + sn_spawn_cancel(); } + g_strfreev(env); + g_free(program); g_strfreev(argv); } g_free(cmd);

@@ -1119,7 +1130,8 @@ /* if using focus_delay, stop the timer now so that focus doesn't go

moving on us */ event_halt_focus_delay(); - client_activate(data->activate.any.c, data->activate.here, TRUE); + client_activate(data->activate.any.c, data->activate.here, TRUE, + data->activate.any.time); } }

@@ -1594,7 +1606,8 @@ event_halt_focus_delay();

focus_cycle(data->cycle.forward, data->cycle.linear, data->any.interactive, data->cycle.dialog, - data->cycle.inter.final, data->cycle.inter.cancel); + data->cycle.inter.final, data->cycle.inter.cancel, + data->cycle.inter.any.time); } void action_directional_focus(union ActionData *data)

@@ -1607,7 +1620,8 @@ focus_directional_cycle(data->interdiraction.direction,

data->any.interactive, data->interdiraction.dialog, data->interdiraction.inter.final, - data->interdiraction.inter.cancel); + data->interdiraction.inter.cancel, + data->interdiraction.inter.any.time); } void action_movetoedge(union ActionData *data)
M openbox/action.hopenbox/action.h

@@ -48,6 +48,7 @@ gboolean interactive;

gint x; gint y; gint button; + Time time; }; struct InteractiveAction {

@@ -209,22 +210,22 @@ @param done If the action is completing an interactive action. This only

affects interactive actions, but should generally always be FALSE. */ void action_run_list(GSList *acts, struct _ObClient *c, ObFrameContext context, - guint state, guint button, gint x, gint y, + guint state, guint button, gint x, gint y, Time time, gboolean cancel, gboolean done); -#define action_run_mouse(a, c, n, s, b, x, y) \ - action_run_list(a, c, n, s, b, x, y, FALSE, FALSE) +#define action_run_mouse(a, c, n, s, b, x, y, t) \ + action_run_list(a, c, n, s, b, x, y, t, FALSE, FALSE) -#define action_run_interactive(a, c, s, n, d) \ - action_run_list(a, c, OB_FRAME_CONTEXT_NONE, s, 0, -1, -1, n, d) +#define action_run_interactive(a, c, s, t, n, d) \ + action_run_list(a, c, OB_FRAME_CONTEXT_NONE, s, 0, -1, -1, t, n, d) -#define action_run_key(a, c, s, x, y) \ - action_run_list(a, c, OB_FRAME_CONTEXT_NONE, s, 0, x, y, FALSE, FALSE) +#define action_run_key(a, c, s, x, y, t) \ + action_run_list(a, c, OB_FRAME_CONTEXT_NONE, s, 0, x, y, t, FALSE, FALSE) -#define action_run(a, c, s) \ - action_run_list(a, c, OB_FRAME_CONTEXT_NONE, s, 0, -1, -1, FALSE, FALSE) +#define action_run(a, c, s, t) \ + action_run_list(a, c, OB_FRAME_CONTEXT_NONE, s, 0, -1, -1, t, FALSE, FALSE) -void action_run_string(const gchar *name, struct _ObClient *c); +void action_run_string(const gchar *name, struct _ObClient *c, Time time); /* Execute */ void action_execute(union ActionData *data);
M openbox/client.copenbox/client.c

@@ -296,11 +296,15 @@ self->title_count = 1;

self->wmstate = NormalState; self->layer = -1; self->desktop = screen_num_desktops; /* always an invalid value */ + self->user_time = ~0; /* maximum value, always newer than the real time */ client_get_all(self); client_restore_session_state(self); - self->user_time = sn_app_started(self->startup_id, self->class); + { + Time t = sn_app_started(self->startup_id, self->class); + if (t) self->user_time = t; + } /* update the focus lists, do this before the call to change_state or it can end up in the list twice! */

@@ -327,7 +331,7 @@

/* get and set application level settings */ settings = get_settings(self); - stacking_add(CLIENT_AS_WINDOW(self)); + stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); client_restore_session_stacking(self); if (settings) {

@@ -453,34 +457,54 @@

keyboard_grab_for_client(self, TRUE); mouse_grab_for_client(self, TRUE); - client_showhide(self); - - /* use client_focus instead of client_activate cuz client_activate does - stuff like switch desktops etc and I'm not interested in all that when - a window maps since its not based on an action from the user like - clicking a window to activate is. so keep the new window out of the way - but do focus it. */ if (activate) { /* This is focus stealing prevention, if a user_time has been set */ - if (self->user_time == CurrentTime || - self->user_time > client_last_user_time) + ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n", + self->window, self->user_time, client_last_user_time); + if (!self->user_time || self->user_time >= client_last_user_time) { - /* if using focus_delay, stop the timer now so that focus doesn't - go moving on us */ - event_halt_focus_delay(); - - client_focus(self); /* since focus can change the stacking orders, if we focus the window then the standard raise it gets is not enough, we need to queue one for after the focus change takes place */ client_raise(self); } else { - ob_debug("Focus stealing prevention activated for %s\n", - self->title); + ob_debug("Focus stealing prevention activated for %s with time %u " + "(last time %u)\n", + self->title, self->user_time, client_last_user_time); /* if the client isn't focused, then hilite it so the user knows it is there */ client_hilite(self, TRUE); + + /* don't focus it ! (focus stealing prevention) */ + activate = FALSE; } + } + else { + /* This may look rather odd. Well it's because new windows are added + to the stacking order non-intrusively. If we're not going to focus + the new window or hilite it, then we raise it to the top. This will + take affect for things that don't get focused like splash screens. + Also if you don't have focus_new enabled, then it's going to get + raised to the top. Legacy begets legacy I guess? + */ + client_raise(self); + } + + /* this has to happen before we try focus the window, but we want it to + happen after the client's stacking has been determined or it looks bad + */ + client_showhide(self); + + /* use client_focus instead of client_activate cuz client_activate does + stuff like switch desktops etc and I'm not interested in all that when + a window maps since its not based on an action from the user like + clicking a window to activate it. so keep the new window out of the way + but do focus it. */ + if (activate) { + /* if using focus_delay, stop the timer now so that focus doesn't + go moving on us */ + event_halt_focus_delay(); + client_focus(self); } /* client_activate does this but we aret using it so we have to do it

@@ -2926,46 +2950,52 @@ focus_fallback(OB_FOCUS_FALLBACK_CLOSED);

} } -void client_activate(ObClient *self, gboolean here, gboolean user) +void client_activate(ObClient *self, gboolean here, gboolean user, + Time timestamp) { /* XXX do some stuff here if user is false to determine if we really want - to activate it or not (a parent or group member is currently active) */ - - if (client_normal(self) && screen_showing_desktop) - screen_show_desktop(FALSE); - if (self->iconic) - client_iconify(self, FALSE, here); - if (self->desktop != DESKTOP_ALL && - self->desktop != screen_desktop) { - if (here) - client_set_desktop(self, screen_desktop, FALSE); - else - screen_set_desktop(self->desktop); - } else if (!self->frame->visible) - /* if its not visible for other reasons, then don't mess - with it */ - return; - if (self->shaded) - client_shade(self, FALSE); + to activate it or not (a parent or group member is currently + active)? + */ + if (!user) + client_hilite(self, TRUE); + else { + if (client_normal(self) && screen_showing_desktop) + screen_show_desktop(FALSE); + if (self->iconic) + client_iconify(self, FALSE, here); + if (self->desktop != DESKTOP_ALL && + self->desktop != screen_desktop) { + if (here) + client_set_desktop(self, screen_desktop, FALSE); + else + screen_set_desktop(self->desktop); + } else if (!self->frame->visible) + /* if its not visible for other reasons, then don't mess + with it */ + return; + if (self->shaded) + client_shade(self, FALSE); - client_focus(self); + client_focus(self); - /* we do this an action here. this is rather important. this is because - we want the results from the focus change to take place BEFORE we go - about raising the window. when a fullscreen window loses focus, we need - this or else the raise wont be able to raise above the to-lose-focus - fullscreen window. */ - client_raise(self); + /* we do this an action here. this is rather important. this is because + we want the results from the focus change to take place BEFORE we go + about raising the window. when a fullscreen window loses focus, we + need this or else the raise wont be able to raise above the + to-lose-focus fullscreen window. */ + client_raise(self); + } } void client_raise(ObClient *self) { - action_run_string("Raise", self); + action_run_string("Raise", self, CurrentTime); } void client_lower(ObClient *self) { - action_run_string("Lower", self); + action_run_string("Lower", self, CurrentTime); } gboolean client_focused(ObClient *self)
M openbox/client.hopenbox/client.h

@@ -484,8 +484,10 @@ @param here If true, then the client is brought to the current desktop;

otherwise, the desktop is changed to where the client lives. @param user If true, then a user action is what requested the activation; otherwise, it means an application requested it on its own + @param timestamp The time at which the activate was requested. */ -void client_activate(ObClient *self, gboolean here, gboolean user); +void client_activate(ObClient *self, gboolean here, gboolean user, + Time timestamp); /*! Calculates the stacking layer for the client window */ void client_calc_layer(ObClient *self);
M openbox/client_list_combined_menu.copenbox/client_list_combined_menu.c

@@ -84,13 +84,14 @@ }

/* executes it using the client in the actions, since we set that when we make the actions! */ -static void menu_execute(ObMenuEntry *self, guint state, gpointer data) +static void menu_execute(ObMenuEntry *self, guint state, gpointer data, + Time time) { ObAction *a; if (self->data.normal.actions) { a = self->data.normal.actions->data; - action_run(self->data.normal.actions, a->data.any.c, state); + action_run(self->data.normal.actions, a->data.any.c, state, time); } }
M openbox/client_list_menu.copenbox/client_list_menu.c

@@ -102,13 +102,14 @@ }

/* executes it using the client in the actions, since we set that when we make the actions! */ -static void desk_menu_execute(ObMenuEntry *self, guint state, gpointer data) +static void desk_menu_execute(ObMenuEntry *self, guint state, gpointer data, + Time time) { ObAction *a; if (self->data.normal.actions) { a = self->data.normal.actions->data; - action_run(self->data.normal.actions, a->data.any.c, state); + action_run(self->data.normal.actions, a->data.any.c, state, time); } }
M openbox/event.copenbox/event.c

@@ -895,13 +895,17 @@ if (e->xconfigurerequest.value_mask & CWStackMode) {

switch (e->xconfigurerequest.detail) { case Below: case BottomIf: - client_lower(client); + /* Apps are so rude. And this is totally disconnected from + activation/focus. Bleh. */ + /*client_lower(client);*/ break; case Above: case TopIf: default: - client_raise(client); + /* Apps are so rude. And this is totally disconnected from + activation/focus. Bleh. */ + /*client_raise(client);*/ break; } }

@@ -940,7 +944,7 @@ does, we don't want it!

it can happen now when the window is on another desktop, but we still don't want it! */ - client_activate(client, FALSE, TRUE); + client_activate(client, FALSE, TRUE, CurrentTime); break; case ClientMessage: /* validate cuz we query stuff off the client here */

@@ -1002,7 +1006,8 @@ (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));

/* XXX make use of data.l[1] and [2] ! */ client_activate(client, FALSE, (e->xclient.data.l[0] == 0 || - e->xclient.data.l[0] == 2)); + e->xclient.data.l[0] == 2), + e->xclient.data.l[1]); } else if (msgtype == prop_atoms.net_wm_moveresize) { ob_debug("net_wm_moveresize for 0x%lx\n", client->window); if ((Atom)e->xclient.data.l[2] ==

@@ -1242,7 +1247,8 @@ case ButtonRelease:

if (menu_can_hide) { if ((e = menu_entry_frame_under(ev->xbutton.x_root, ev->xbutton.y_root))) - menu_entry_frame_execute(e, ev->xbutton.state); + menu_entry_frame_execute(e, ev->xbutton.state, + ev->xbutton.time); else menu_frame_hide_all(); }

@@ -1272,7 +1278,8 @@ menu_frame_hide_all();

else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) { ObMenuFrame *f; if ((f = find_active_menu())) - menu_entry_frame_execute(f->selected, ev->xkey.state); + menu_entry_frame_execute(f->selected, ev->xkey.state, + ev->xkey.time); } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) { ObMenuFrame *f; if ((f = find_active_or_last_menu()) && f->parent)
M openbox/focus.copenbox/focus.c

@@ -56,9 +56,11 @@ static ObIconPopup *focus_cycle_popup;

static void focus_cycle_destructor(ObClient *client, gpointer data) { - /* end cycling if the target disappears */ + /* end cycling if the target disappears. CurrentTime is fine, time won't + be used + */ if (focus_cycle_target == client) - focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); + focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, CurrentTime); } static Window createWindow(Window parent, gulong mask,

@@ -185,9 +187,11 @@ event_curtime);

XSync(ob_display, FALSE); } - /* in the middle of cycling..? kill it. */ + /* in the middle of cycling..? kill it. CurrentTime is fine, time won't + be used. + */ if (focus_cycle_target) - focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); + focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, CurrentTime); old = focus_client; focus_client = client;

@@ -547,7 +551,7 @@ return FALSE;

} void focus_cycle(gboolean forward, gboolean linear, gboolean interactive, - gboolean dialog, gboolean done, gboolean cancel) + gboolean dialog, gboolean done, gboolean cancel, Time time) { static ObClient *first = NULL; static ObClient *t = NULL;

@@ -608,7 +612,7 @@ } while (it != start);

done_cycle: if (done && focus_cycle_target) - client_activate(focus_cycle_target, FALSE, TRUE); + client_activate(focus_cycle_target, FALSE, TRUE, time); t = NULL; first = NULL;

@@ -625,7 +629,8 @@ return;

} void focus_directional_cycle(ObDirection dir, gboolean interactive, - gboolean dialog, gboolean done, gboolean cancel) + gboolean dialog, gboolean done, gboolean cancel, + Time time) { static ObClient *first = NULL; ObClient *ft = NULL;

@@ -670,7 +675,7 @@

done_cycle: if (done && focus_cycle_target) - client_activate(focus_cycle_target, FALSE, TRUE); + client_activate(focus_cycle_target, FALSE, TRUE, time); first = NULL; focus_cycle_target = NULL;
M openbox/focus.hopenbox/focus.h

@@ -60,9 +60,10 @@ void focus_fallback(ObFocusFallbackType type);

/*! Cycle focus amongst windows. */ void focus_cycle(gboolean forward, gboolean linear, gboolean interactive, - gboolean dialog, gboolean done, gboolean cancel); + gboolean dialog, gboolean done, gboolean cancel, Time time); void focus_directional_cycle(ObDirection dir, gboolean interactive, - gboolean dialog, gboolean done, gboolean cancel); + gboolean dialog, gboolean done, gboolean cancel, + Time time); void focus_cycle_draw_indicator(); /*! Add a new client into the focus order */
M openbox/keyboard.copenbox/keyboard.c

@@ -184,9 +184,9 @@ return TRUE;

} void keyboard_interactive_end(ObInteractiveState *s, - guint state, gboolean cancel) + guint state, gboolean cancel, Time time) { - action_run_interactive(s->actions, s->client, state, cancel, TRUE); + action_run_interactive(s->actions, s->client, state, time, cancel, TRUE); g_slist_free(s->actions); g_free(s);

@@ -236,7 +236,7 @@ else */if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))

cancel = done = TRUE; } if (done) { - keyboard_interactive_end(s, e->xkey.state, cancel); + keyboard_interactive_end(s, e->xkey.state, cancel, e->xkey.time); handled = TRUE; } else

@@ -280,7 +280,8 @@

keyboard_reset_chains(); action_run_key(p->actions, client, e->xkey.state, - e->xkey.x_root, e->xkey.y_root); + e->xkey.x_root, e->xkey.y_root, + e->xkey.time); } break; }
M openbox/menu.hopenbox/menu.h

@@ -39,7 +39,7 @@ typedef struct _ObSeparatorMenuEntry ObSeparatorMenuEntry;

typedef void (*ObMenuUpdateFunc)(struct _ObMenuFrame *frame, gpointer data); typedef void (*ObMenuExecuteFunc)(struct _ObMenuEntry *entry, - guint state, gpointer data); + guint state, gpointer data, Time time); typedef void (*ObMenuDestroyFunc)(struct _ObMenu *menu, gpointer data); struct _ObMenu
M openbox/menuframe.copenbox/menuframe.c

@@ -803,7 +803,7 @@ + (config_menu_middle ? 1 : ob_rr_theme->menu_overlap));

menu_frame_show(f, self->frame); } -void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state) +void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state, Time time) { if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL && self->entry->data.normal.enabled)

@@ -821,9 +821,9 @@ if (!(state & ControlMask))

menu_frame_hide_all(); if (func) - func(entry, state, data); + func(entry, state, data, time); else - action_run(acts, client, state); + action_run(acts, client, state, time); } }
M openbox/menuframe.hopenbox/menuframe.h

@@ -121,6 +121,6 @@ ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y);

void menu_entry_frame_show_submenu(ObMenuEntryFrame *self); -void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state); +void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state, Time time); #endif
M openbox/mouse.copenbox/mouse.c

@@ -155,7 +155,7 @@ }

static gboolean fire_binding(ObMouseAction a, ObFrameContext context, ObClient *c, guint state, - guint button, gint x, gint y) + guint button, gint x, gint y, Time time) { GSList *it; ObMouseBinding *b;

@@ -168,7 +168,7 @@ }

/* if not bound, then nothing to do! */ if (it == NULL) return FALSE; - action_run_mouse(b->actions[a], c, context, state, button, x, y); + action_run_mouse(b->actions[a], c, context, state, button, x, y, time); return TRUE; }

@@ -196,7 +196,8 @@

fire_binding(OB_MOUSE_ACTION_PRESS, context, client, e->xbutton.state, e->xbutton.button, - e->xbutton.x_root, e->xbutton.y_root); + e->xbutton.x_root, e->xbutton.y_root, + e->xbutton.time); if (CLIENT_CONTEXT(context, client)) { /* Replay the event, so it goes to the client*/

@@ -249,19 +250,22 @@ }

fire_binding(OB_MOUSE_ACTION_RELEASE, context, client, e->xbutton.state, e->xbutton.button, - e->xbutton.x_root, e->xbutton.y_root); + e->xbutton.x_root, e->xbutton.y_root, + e->xbutton.time); if (click) fire_binding(OB_MOUSE_ACTION_CLICK, context, client, e->xbutton.state, e->xbutton.button, e->xbutton.x_root, - e->xbutton.y_root); + e->xbutton.y_root, + e->xbutton.time); if (dclick) fire_binding(OB_MOUSE_ACTION_DOUBLE_CLICK, context, client, e->xbutton.state, e->xbutton.button, e->xbutton.x_root, - e->xbutton.y_root); + e->xbutton.y_root, + e->xbutton.time); break; case MotionNotify:

@@ -284,7 +288,7 @@ context == OB_FRAME_CONTEXT_CLOSE)

break; fire_binding(OB_MOUSE_ACTION_MOTION, context, - client, state, button, px, py); + client, state, button, px, py, e->xmotion.time); button = 0; state = 0; }
M openbox/startupnotify.copenbox/startupnotify.c

@@ -18,6 +18,9 @@ See the COPYING file for a copy of the GNU General Public License.

*/ #include "startupnotify.h" +#include "gettext.h" + +extern gchar **environ; #ifndef USE_LIBSN

@@ -29,6 +32,11 @@ {

return CurrentTime; } gboolean sn_get_desktop(gchar *id, guint *desktop) { return FALSE; } +gchar **sn_get_spawn_environment(char *program, Time time) +{ + return g_strdupv(environ); +} +void sn_spawn_cancel() {} #else

@@ -39,18 +47,12 @@

#define SN_API_NOT_YET_FROZEN #include <libsn/sn.h> -typedef struct { - SnStartupSequence *seq; - gboolean feedback; -} ObWaitData; - static SnDisplay *sn_display; static SnMonitorContext *sn_context; -static GSList *sn_waits; /* list of ObWaitDatas */ +static SnLauncherContext *sn_launcher; +static GSList *sn_waits; /* list of SnStartupSequences we're waiting on */ -static ObWaitData* wait_data_new(SnStartupSequence *seq); -static void wait_data_free(ObWaitData *d); -static ObWaitData* wait_find(const gchar *id); +static SnStartupSequence* sequence_find(const gchar *id); static void sn_handler(const XEvent *e, gpointer data); static void sn_event_func(SnMonitorEvent *event, gpointer data);

@@ -62,6 +64,7 @@

sn_display = sn_display_new(ob_display, NULL, NULL); sn_context = sn_monitor_context_new(sn_display, ob_screen, sn_event_func, NULL, NULL); + sn_launcher = sn_launcher_context_new(sn_display, ob_screen); ob_main_loop_x_add(ob_main_loop, sn_handler, NULL, NULL); }

@@ -75,45 +78,26 @@

ob_main_loop_x_remove(ob_main_loop, sn_handler); for (it = sn_waits; it; it = g_slist_next(it)) - wait_data_free(it->data); + sn_startup_sequence_unref((SnStartupSequence*)it->data); g_slist_free(sn_waits); sn_waits = NULL; screen_set_root_cursor(); + sn_launcher_context_unref(sn_launcher); sn_monitor_context_unref(sn_context); sn_display_unref(sn_display); } -static ObWaitData* wait_data_new(SnStartupSequence *seq) -{ - ObWaitData *d = g_new(ObWaitData, 1); - d->seq = seq; - d->feedback = TRUE; - - sn_startup_sequence_ref(d->seq); - - return d; -} - -static void wait_data_free(ObWaitData *d) +static SnStartupSequence* sequence_find(const gchar *id) { - if (d) { - sn_startup_sequence_unref(d->seq); - - g_free(d); - } -} - -static ObWaitData* wait_find(const gchar *id) -{ - ObWaitData *ret = NULL; + SnStartupSequence*ret = NULL; GSList *it; for (it = sn_waits; it; it = g_slist_next(it)) { - ObWaitData *d = it->data; - if (!strcmp(id, sn_startup_sequence_get_id(d->seq))) { - ret = d; + SnStartupSequence *seq = it->data; + if (!strcmp(id, sn_startup_sequence_get_id(seq))) { + ret = seq; break; } }

@@ -122,31 +106,17 @@ }

gboolean sn_app_starting() { - GSList *it; - - for (it = sn_waits; it; it = g_slist_next(it)) { - ObWaitData *d = it->data; - if (d->feedback) - return TRUE; - } - return FALSE; + return sn_waits != NULL; } static gboolean sn_wait_timeout(gpointer data) { - ObWaitData *d = data; - d->feedback = FALSE; + SnStartupSequence *seq = data; + sn_waits = g_slist_remove(sn_waits, seq); screen_set_root_cursor(); return FALSE; /* don't repeat */ } -static void sn_wait_destroy(gpointer data) -{ - ObWaitData *d = data; - sn_waits = g_slist_remove(sn_waits, d); - wait_data_free(d); -} - static void sn_handler(const XEvent *e, gpointer data) { XEvent ec;

@@ -158,18 +128,19 @@ static void sn_event_func(SnMonitorEvent *ev, gpointer data)

{ SnStartupSequence *seq; gboolean change = FALSE; - ObWaitData *d; if (!(seq = sn_monitor_event_get_startup_sequence(ev))) return; switch (sn_monitor_event_get_type(ev)) { case SN_MONITOR_EVENT_INITIATED: - d = wait_data_new(seq); - sn_waits = g_slist_prepend(sn_waits, d); - /* 15 second timeout for apps to start */ - ob_main_loop_timeout_add(ob_main_loop, 15 * G_USEC_PER_SEC, - sn_wait_timeout, d, sn_wait_destroy); + sn_startup_sequence_ref(seq); + sn_waits = g_slist_prepend(sn_waits, seq); + /* 30 second timeout for apps to start if the launcher doesn't + have a timeout */ + ob_main_loop_timeout_add(ob_main_loop, 30 * G_USEC_PER_SEC, + sn_wait_timeout, seq, + (GDestroyNotify)sn_startup_sequence_unref); change = TRUE; break; case SN_MONITOR_EVENT_CHANGED:

@@ -178,10 +149,10 @@ change = TRUE;

break; case SN_MONITOR_EVENT_COMPLETED: case SN_MONITOR_EVENT_CANCELED: - if ((d = wait_find(sn_startup_sequence_get_id(seq)))) { - d->feedback = FALSE; + if ((seq = sequence_find(sn_startup_sequence_get_id(seq)))) { + sn_waits = g_slist_remove(sn_waits, seq); ob_main_loop_timeout_remove_data(ob_main_loop, sn_wait_timeout, - d, FALSE); + seq, FALSE); change = TRUE; } break;

@@ -197,15 +168,15 @@ GSList *it;

Time t = CurrentTime; for (it = sn_waits; it; it = g_slist_next(it)) { - ObWaitData *d = it->data; + SnStartupSequence *seq = it->data; const gchar *seqid, *seqclass; - seqid = sn_startup_sequence_get_id(d->seq); - seqclass = sn_startup_sequence_get_wmclass(d->seq); + seqid = sn_startup_sequence_get_id(seq); + seqclass = sn_startup_sequence_get_wmclass(seq); if ((seqid && id && !strcmp(seqid, id)) || (seqclass && wmclass && !strcmp(seqclass, wmclass))) { - sn_startup_sequence_complete(d->seq); - t = sn_startup_sequence_get_timestamp(d->seq); + sn_startup_sequence_complete(seq); + t = sn_startup_sequence_get_timestamp(seq); break; } }

@@ -214,16 +185,65 @@ }

gboolean sn_get_desktop(gchar *id, guint *desktop) { - ObWaitData *d; + SnStartupSequence *seq; - if (id && (d = wait_find(id))) { - gint desk = sn_startup_sequence_get_workspace(d->seq); + if (id && (seq = sequence_find(id))) { + gint desk = sn_startup_sequence_get_workspace(seq); if (desk != -1) { *desktop = desk; return TRUE; } } return FALSE; +} + +static gboolean sn_launch_wait_timeout(gpointer data) +{ + SnLauncherContext *sn = data; + sn_launcher_context_complete(sn); + return FALSE; /* don't repeat */ +} + +gchar **sn_get_spawn_environment(char *program, Time time) +{ + gchar **env, *desc; + guint len; + const char *id; + + desc = g_strdup_printf(_("Running %s\n"), program); + + if (sn_launcher_context_get_initiated(sn_launcher)) { + sn_launcher_context_unref(sn_launcher); + sn_launcher = sn_launcher_context_new(sn_display, ob_screen); + } + + sn_launcher_context_set_name(sn_launcher, program); + sn_launcher_context_set_description(sn_launcher, desc); + sn_launcher_context_set_icon_name(sn_launcher, program); + sn_launcher_context_set_binary_name(sn_launcher, program); + sn_launcher_context_initiate(sn_launcher, "openbox", program, time); + id = sn_launcher_context_get_startup_id(sn_launcher); + + /* 30 second timeout for apps to start */ + sn_launcher_context_ref(sn_launcher); + ob_main_loop_timeout_add(ob_main_loop, 30 * G_USEC_PER_SEC, + sn_launch_wait_timeout, sn_launcher, + (GDestroyNotify)sn_launcher_context_unref); + + env = g_strdupv(environ); + len = g_strv_length(env); /* includes last null */ + env = g_renew(gchar*, env, ++len); /* add one spot */ + env[len-2] = g_strdup_printf("DESKTOP_STARTUP_ID=%s", id); + env[len-1] = NULL; + + g_free(desc); + + return env; +} + +void sn_spawn_cancel() +{ + sn_launcher_context_complete(sn_launcher); } #endif
M openbox/startupnotify.hopenbox/startupnotify.h

@@ -34,4 +34,12 @@ /*! Get the desktop requested via the startup-notiication protocol if one

was requested */ gboolean sn_get_desktop(gchar *id, guint *desktop); +/* Get the environment to run the program in, with startup notification */ +gchar **sn_get_spawn_environment(char *program, Time time); + +/* Tell startup notification we're not actually running the program we + told it we were +*/ +void sn_spawn_cancel(); + #endif