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..
This commit is contained in:
Dana Jansens 2007-03-11 04:44:15 +00:00
parent 9d6e390765
commit 19b480058e
16 changed files with 264 additions and 169 deletions

View file

@ -33,6 +33,7 @@
#include "dock.h"
#include "config.h"
#include "mainloop.h"
#include "startupnotify.h"
#include <glib.h>
@ -1005,7 +1006,7 @@ ObAction *action_parse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
}
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 @@ void action_run_list(GSList *acts, ObClient *c, ObFrameContext context,
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_list(GSList *acts, ObClient *c, ObFrameContext context,
}
}
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 @@ void action_run_string(const gchar *name, struct _ObClient *c)
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 @@ void action_execute(union ActionData *data)
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 @@ void action_activate(union ActionData *data)
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 @@ void action_cycle_windows(union ActionData *data)
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 @@ void action_directional_focus(union ActionData *data)
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)

View file

@ -48,6 +48,7 @@ struct AnyAction {
gint x;
gint y;
gint button;
Time time;
};
struct InteractiveAction {
@ -209,22 +210,22 @@ ObAction* action_copy(const ObAction *a);
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);

View file

@ -296,11 +296,15 @@ void client_manage(Window window)
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 @@ void client_manage(Window window)
/* 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,35 +457,55 @@ void client_manage(Window window)
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
here as well */
@ -2926,11 +2950,16 @@ void client_unfocus(ObClient *self)
}
}
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) */
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)
@ -2952,20 +2981,21 @@ void client_activate(ObClient *self, gboolean here, gboolean user)
/* 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. */
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)

View file

@ -484,8 +484,10 @@ void client_unfocus(ObClient *self);
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);

View file

@ -84,13 +84,14 @@ static void self_update(ObMenuFrame *frame, gpointer data)
/* 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);
}
}

View file

@ -102,13 +102,14 @@ static void desk_menu_update(ObMenuFrame *frame, gpointer data)
/* 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);
}
}

View file

@ -895,13 +895,17 @@ static void event_handle_client(ObClient *client, XEvent *e)
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 @@ static void event_handle_client(ObClient *client, XEvent *e)
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 @@ static void event_handle_client(ObClient *client, XEvent *e)
/* 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 @@ static void event_handle_menu(XEvent *ev)
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 @@ static void event_handle_menu(XEvent *ev)
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)

View file

@ -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 @@ void focus_set_client(ObClient *client)
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 @@ static gboolean valid_focus_target(ObClient *ft)
}
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 @@ void focus_cycle(gboolean forward, gboolean linear, gboolean interactive,
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 @@ done_cycle:
}
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 @@ void focus_directional_cycle(ObDirection dir, gboolean interactive,
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;

View file

@ -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 */

View file

@ -184,9 +184,9 @@ gboolean keyboard_interactive_grab(guint state, ObClient *client,
}
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 @@ gboolean keyboard_process_interactive_grab(const XEvent *e, ObClient **client)
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 @@ void keyboard_event(ObClient *client, const XEvent *e)
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;
}

View file

@ -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

View file

@ -803,7 +803,7 @@ void menu_entry_frame_show_submenu(ObMenuEntryFrame *self)
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 @@ void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state)
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);
}
}

View file

@ -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

View file

@ -155,7 +155,7 @@ void mouse_unbind_all()
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 @@ static gboolean fire_binding(ObMouseAction a, ObFrameContext context,
/* 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 @@ void mouse_event(ObClient *client, XEvent *e)
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 @@ void mouse_event(ObClient *client, XEvent *e)
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 @@ void mouse_event(ObClient *client, XEvent *e)
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;
}

View file

@ -18,6 +18,9 @@
*/
#include "startupnotify.h"
#include "gettext.h"
extern gchar **environ;
#ifndef USE_LIBSN
@ -29,6 +32,11 @@ Time sn_app_started(const gchar *id, const gchar *wmclass)
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 @@ gboolean sn_get_desktop(gchar *id, guint *desktop) { return FALSE; }
#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 @@ void sn_startup(gboolean reconfig)
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 @@ void sn_shutdown(gboolean reconfig)
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)
static SnStartupSequence* sequence_find(const gchar *id)
{
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)
{
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 @@ static ObWaitData* wait_find(const gchar *id)
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 @@ static void sn_event_func(SnMonitorEvent *ev, gpointer data)
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 @@ Time sn_app_started(const gchar *id, const gchar *wmclass)
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,10 +185,10 @@ Time sn_app_started(const gchar *id, const gchar *wmclass)
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;
@ -226,4 +197,53 @@ gboolean sn_get_desktop(gchar *id, guint *desktop)
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

View file

@ -34,4 +34,12 @@ Time sn_app_started(const gchar *id, const gchar *wmclass);
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