]> icculus.org git repositories - dana/openbox.git/blob - openbox/screen.c
calc shadow offset when measuring a string
[dana/openbox.git] / openbox / screen.c
1 #include "openbox.h"
2 #include "dock.h"
3 #include "xerror.h"
4 #include "prop.h"
5 #include "startup.h"
6 #include "timer.h"
7 #include "config.h"
8 #include "screen.h"
9 #include "client.h"
10 #include "frame.h"
11 #include "focus.h"
12 #include "dispatch.h"
13 #include "extensions.h"
14 #include "render2/render.h"
15
16
17 #ifdef USE_LIBSN
18 #  define SN_API_NOT_YET_FROZEN
19 #  include <libsn/sn.h>
20 #endif
21
22 #include <X11/Xlib.h>
23 #ifdef HAVE_UNISTD_H
24 #  include <sys/types.h>
25 #  include <unistd.h>
26 #endif
27
28 /*! The event mask to grab on the root window */
29 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
30                         EnterWindowMask | LeaveWindowMask | \
31                         SubstructureNotifyMask | SubstructureRedirectMask | \
32                         ButtonPressMask | ButtonReleaseMask | ButtonMotionMask)
33
34 guint    screen_num_desktops    = 0;
35 guint    screen_desktop         = 0;
36 Size     screen_physical_size;
37 gboolean screen_showing_desktop;
38 DesktopLayout screen_desktop_layout;
39 char   **screen_desktop_names = NULL;
40
41 static Rect  *area = NULL;
42 static Strut *strut = NULL;
43 static Window support_window = None;
44
45 #ifdef USE_LIBSN
46 static SnMonitorContext *sn_context;
47 static int sn_busy_cnt;
48 static Timer *sn_timer = NULL;
49
50 static void sn_event_func(SnMonitorEvent *event, void *data);
51 #endif
52
53 static void screen_update_area();
54 static void set_root_cursor();
55
56 static gboolean running;
57 static int another_running(Display *d, XErrorEvent *e)
58 {
59     (void)d;(void)e;
60     g_message("A window manager is already running on screen %d",
61               ob_screen);
62     running = TRUE;
63     return -1;
64 }
65
66 gboolean screen_annex()
67 {
68     XErrorHandler old;
69     pid_t pid;
70     int i, num_support;
71     guint32 *supported;
72
73     running = FALSE;
74     old = XSetErrorHandler(another_running);
75     XSelectInput(ob_display, ob_root, ROOT_EVENTMASK);
76     XSync(ob_display, FALSE);
77     XSetErrorHandler(old);
78     if (running)
79         return FALSE;
80
81     g_message("Managing screen %d", ob_screen);
82
83     set_root_cursor();
84
85     /* set the OPENBOX_PID hint */
86     pid = getpid();
87     PROP_SET32(ob_root, openbox_pid, cardinal, pid);
88
89     /* create the netwm support window */
90     support_window = XCreateSimpleWindow(ob_display, ob_root, 0,0,1,1,0,0,0);
91
92     /* set supporting window */
93     PROP_SET32(ob_root, net_supporting_wm_check, window, support_window);
94
95     /* set properties on the supporting window */
96     PROP_SETS(support_window, net_wm_name, "Openbox3");
97     PROP_SET32(support_window, net_supporting_wm_check, window,support_window);
98
99     /* set the _NET_SUPPORTED_ATOMS hint */
100     num_support = 61;
101     i = 0;
102     supported = g_new(guint32, num_support);
103     supported[i++] = prop_atoms.net_current_desktop;
104     supported[i++] = prop_atoms.net_number_of_desktops;
105     supported[i++] = prop_atoms.net_desktop_geometry;
106     supported[i++] = prop_atoms.net_desktop_viewport;
107     supported[i++] = prop_atoms.net_active_window;
108     supported[i++] = prop_atoms.net_workarea;
109     supported[i++] = prop_atoms.net_client_list;
110     supported[i++] = prop_atoms.net_client_list_stacking;
111     supported[i++] = prop_atoms.net_desktop_names;
112     supported[i++] = prop_atoms.net_close_window;
113     supported[i++] = prop_atoms.net_desktop_layout;
114     supported[i++] = prop_atoms.net_showing_desktop;
115     supported[i++] = prop_atoms.net_wm_name;
116     supported[i++] = prop_atoms.net_wm_visible_name;
117     supported[i++] = prop_atoms.net_wm_icon_name;
118     supported[i++] = prop_atoms.net_wm_visible_icon_name;
119     supported[i++] = prop_atoms.net_wm_desktop;
120     supported[i++] = prop_atoms.net_wm_strut;
121     supported[i++] = prop_atoms.net_wm_window_type;
122     supported[i++] = prop_atoms.net_wm_window_type_desktop;
123     supported[i++] = prop_atoms.net_wm_window_type_dock;
124     supported[i++] = prop_atoms.net_wm_window_type_toolbar;
125     supported[i++] = prop_atoms.net_wm_window_type_menu;
126     supported[i++] = prop_atoms.net_wm_window_type_utility;
127     supported[i++] = prop_atoms.net_wm_window_type_splash;
128     supported[i++] = prop_atoms.net_wm_window_type_dialog;
129     supported[i++] = prop_atoms.net_wm_window_type_normal;
130     supported[i++] = prop_atoms.net_wm_allowed_actions;
131     supported[i++] = prop_atoms.net_wm_action_move;
132     supported[i++] = prop_atoms.net_wm_action_resize;
133     supported[i++] = prop_atoms.net_wm_action_minimize;
134     supported[i++] = prop_atoms.net_wm_action_shade;
135     supported[i++] = prop_atoms.net_wm_action_maximize_horz;
136     supported[i++] = prop_atoms.net_wm_action_maximize_vert;
137     supported[i++] = prop_atoms.net_wm_action_fullscreen;
138     supported[i++] = prop_atoms.net_wm_action_change_desktop;
139     supported[i++] = prop_atoms.net_wm_action_close;
140     supported[i++] = prop_atoms.net_wm_state;
141     supported[i++] = prop_atoms.net_wm_state_modal;
142     supported[i++] = prop_atoms.net_wm_state_maximized_vert;
143     supported[i++] = prop_atoms.net_wm_state_maximized_horz;
144     supported[i++] = prop_atoms.net_wm_state_shaded;
145     supported[i++] = prop_atoms.net_wm_state_skip_taskbar;
146     supported[i++] = prop_atoms.net_wm_state_skip_pager;
147     supported[i++] = prop_atoms.net_wm_state_hidden;
148     supported[i++] = prop_atoms.net_wm_state_fullscreen;
149     supported[i++] = prop_atoms.net_wm_state_above;
150     supported[i++] = prop_atoms.net_wm_state_below;
151     supported[i++] = prop_atoms.net_moveresize_window;
152     supported[i++] = prop_atoms.net_wm_moveresize;
153     supported[i++] = prop_atoms.net_wm_moveresize_size_topleft;
154     supported[i++] = prop_atoms.net_wm_moveresize_size_top;
155     supported[i++] = prop_atoms.net_wm_moveresize_size_topright;
156     supported[i++] = prop_atoms.net_wm_moveresize_size_right;
157     supported[i++] = prop_atoms.net_wm_moveresize_size_bottomright;
158     supported[i++] = prop_atoms.net_wm_moveresize_size_bottom;
159     supported[i++] = prop_atoms.net_wm_moveresize_size_bottomleft;
160     supported[i++] = prop_atoms.net_wm_moveresize_size_left;
161     supported[i++] = prop_atoms.net_wm_moveresize_move;
162     supported[i++] = prop_atoms.net_wm_moveresize_size_keyboard;
163     supported[i++] = prop_atoms.net_wm_moveresize_move_keyboard;
164     g_assert(i == num_support);
165 /*
166   supported[] = prop_atoms.net_wm_action_stick;
167 */
168
169     PROP_SETA32(ob_root, net_supported, atom, supported, num_support);
170     g_free(supported);
171
172     return TRUE;
173 }
174
175 void screen_startup()
176 {
177     GSList *it;
178     guint i;
179
180     /* get the initial size */
181     screen_resize(WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen)),
182                   HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen)));
183
184     /* set the names */
185     screen_desktop_names = g_new(char*,
186                                  g_slist_length(config_desktops_names) + 1);
187     for (i = 0, it = config_desktops_names; it; ++i, it = it->next)
188         screen_desktop_names[i] = it->data; /* dont strdup */
189     screen_desktop_names[i] = NULL;
190     PROP_SETSS(ob_root, net_desktop_names, screen_desktop_names);
191     g_free(screen_desktop_names); /* dont free the individual strings */
192     screen_desktop_names = NULL;
193
194     screen_num_desktops = 0;
195     screen_set_num_desktops(config_desktops_num);
196     if (startup_desktop >= screen_num_desktops)
197         startup_desktop = 0;
198     screen_desktop = startup_desktop;
199     screen_set_desktop(startup_desktop);
200
201     /* don't start in showing-desktop mode */
202     screen_showing_desktop = FALSE;
203     PROP_SET32(ob_root, net_showing_desktop, cardinal, screen_showing_desktop);
204
205     screen_update_layout();
206
207 #ifdef USE_LIBSN
208     sn_context = sn_monitor_context_new(ob_sn_display, ob_screen,
209                                         sn_event_func, NULL, NULL);
210     sn_busy_cnt = 0;
211 #endif
212 }
213
214 void screen_shutdown()
215 {
216     XSelectInput(ob_display, ob_root, NoEventMask);
217
218     PROP_ERASE(ob_root, openbox_pid); /* we're not running here no more! */
219     PROP_ERASE(ob_root, net_supported); /* not without us */
220     PROP_ERASE(ob_root, net_showing_desktop); /* don't keep this mode */
221
222     XDestroyWindow(ob_display, support_window);
223
224     g_strfreev(screen_desktop_names);
225     g_free(strut);
226     g_free(area);
227 }
228
229 void screen_resize(int w, int h)
230 {
231     GList *it;
232     guint32 geometry[2];
233
234     /* Set the _NET_DESKTOP_GEOMETRY hint */
235     geometry[0] = w;
236     geometry[1] = h;
237     PROP_SETA32(ob_root, net_desktop_geometry, cardinal, geometry, 2);
238     screen_physical_size.width = geometry[0];
239     screen_physical_size.height = geometry[1];
240
241     if (ob_state == State_Starting)
242         return;
243
244     dock_configure();
245     screen_update_struts();
246
247     for (it = client_list; it; it = it->next)
248         client_move_onscreen(it->data);
249 }
250
251 void screen_set_num_desktops(guint num)
252 {
253     guint i, old;
254     guint32 *viewport;
255     GList *it;
256
257     g_assert(num > 0);
258
259     old = screen_num_desktops;
260     screen_num_desktops = num;
261     PROP_SET32(ob_root, net_number_of_desktops, cardinal, num);
262
263     /* set the viewport hint */
264     viewport = g_new0(guint32, num * 2);
265     PROP_SETA32(ob_root, net_desktop_viewport, cardinal, viewport, num * 2);
266     g_free(viewport);
267
268     /* the number of rows/columns will differ */
269     screen_update_layout();
270
271     /* may be some unnamed desktops that we need to fill in with names */
272     screen_update_desktop_names();
273
274     /* update the focus lists */
275     /* free our lists for the desktops which have disappeared */
276     for (i = num; i < old; ++i)
277         g_list_free(focus_order[i]);
278     /* realloc the array */
279     focus_order = g_renew(GList*, focus_order, num);
280     /* set the new lists to be empty */
281     for (i = old; i < num; ++i)
282         focus_order[i] = NULL;
283
284     /* move windows on desktops that will no longer exist! */
285     for (it = client_list; it != NULL; it = it->next) {
286         Client *c = it->data;
287         if (c->desktop >= num && c->desktop != DESKTOP_ALL)
288             client_set_desktop(c, num - 1, FALSE);
289     }
290
291     /* change our struts/area to match (after moving windows) */
292     screen_update_struts();
293
294     dispatch_ob(Event_Ob_NumDesktops, num, old);
295
296     /* change our desktop if we're on one that no longer exists! */
297     if (screen_desktop >= screen_num_desktops)
298         screen_set_desktop(num - 1);
299 }
300
301 void screen_set_desktop(guint num)
302 {
303     GList *it;
304     guint old;
305     XEvent e;
306      
307     g_assert(num < screen_num_desktops);
308
309     old = screen_desktop;
310     screen_desktop = num;
311     PROP_SET32(ob_root, net_current_desktop, cardinal, num);
312
313     if (old == num) return;
314
315     g_message("Moving to desktop %d", num+1);
316
317     /* show windows before hiding the rest to lessen the enter/leave events */
318
319     /* show windows from top to bottom */
320     for (it = stacking_list; it != NULL; it = it->next) {
321         if (WINDOW_IS_CLIENT(it->data)) {
322             Client *c = it->data;
323             if (!c->frame->visible && client_should_show(c))
324                 frame_show(c->frame);
325         }
326     }
327
328     /* hide windows from bottom to top */
329     for (it = g_list_last(stacking_list); it != NULL; it = it->prev) {
330         if (WINDOW_IS_CLIENT(it->data)) {
331             Client *c = it->data;
332             if (c->frame->visible && !client_should_show(c))
333                 frame_hide(c->frame);
334         }
335     }
336
337     /* focus the last focused window on the desktop, and ignore enter events
338        from the switch so it doesnt mess with the focus */
339     while (XCheckTypedEvent(ob_display, EnterNotify, &e));
340     focus_fallback(Fallback_Desktop);
341
342     dispatch_ob(Event_Ob_Desktop, num, old);
343 }
344
345 void screen_update_layout()
346 {
347     guint32 *data = NULL;
348     guint num;
349
350     /* defaults */
351     screen_desktop_layout.orientation = prop_atoms.net_wm_orientation_horz;
352     screen_desktop_layout.start_corner = prop_atoms.net_wm_topleft;
353     screen_desktop_layout.rows = 1;
354     screen_desktop_layout.columns = screen_num_desktops;
355
356     if (PROP_GETA32(ob_root, net_desktop_layout, cardinal, &data, &num)) {
357         if (num == 3 || num == 4) {
358             if (data[0] == prop_atoms.net_wm_orientation_vert)
359                 screen_desktop_layout.orientation = data[0];
360             if (num == 3)
361                 screen_desktop_layout.start_corner =
362                     prop_atoms.net_wm_topright;
363             else {
364                 if (data[3] == prop_atoms.net_wm_topright)
365                     screen_desktop_layout.start_corner = data[3];
366                 else if (data[3] == prop_atoms.net_wm_bottomright)
367                     screen_desktop_layout.start_corner = data[3];
368                 else if (data[3] == prop_atoms.net_wm_bottomleft)
369                     screen_desktop_layout.start_corner = data[3];
370             }
371
372             /* fill in a zero rows/columns */
373             if (!(data[1] == 0 && data[2] == 0)) { /* both 0's is bad data.. */
374                 if (data[1] == 0) {
375                     data[1] = (screen_num_desktops +
376                                screen_num_desktops % data[2]) / data[2];
377                 } else if (data[2] == 0) {
378                     data[2] = (screen_num_desktops +
379                                screen_num_desktops % data[1]) / data[1];
380                 }
381                 screen_desktop_layout.columns = data[1];
382                 screen_desktop_layout.rows = data[2];
383             }
384
385             /* bounds checking */
386             if (screen_desktop_layout.orientation ==
387                 prop_atoms.net_wm_orientation_horz) {
388                 if (screen_desktop_layout.rows > screen_num_desktops)
389                     screen_desktop_layout.rows = screen_num_desktops;
390                 if (screen_desktop_layout.columns >
391                     ((screen_num_desktops + screen_num_desktops %
392                       screen_desktop_layout.rows) /
393                      screen_desktop_layout.rows))
394                     screen_desktop_layout.columns =
395                         (screen_num_desktops + screen_num_desktops %
396                          screen_desktop_layout.rows) /
397                         screen_desktop_layout.rows;
398             } else {
399                 if (screen_desktop_layout.columns > screen_num_desktops)
400                     screen_desktop_layout.columns = screen_num_desktops;
401                 if (screen_desktop_layout.rows >
402                     ((screen_num_desktops + screen_num_desktops %
403                       screen_desktop_layout.columns) /
404                      screen_desktop_layout.columns))
405                     screen_desktop_layout.rows =
406                         (screen_num_desktops + screen_num_desktops %
407                          screen_desktop_layout.columns) /
408                         screen_desktop_layout.columns;
409             }
410         }
411         g_free(data);
412     }
413 }
414
415 void screen_update_desktop_names()
416 {
417     guint i;
418
419     /* empty the array */
420     g_strfreev(screen_desktop_names);
421     screen_desktop_names = NULL;
422
423     if (PROP_GETSS(ob_root, net_desktop_names, utf8, &screen_desktop_names))
424         for (i = 0; screen_desktop_names[i] && i <= screen_num_desktops; ++i);
425     else
426         i = 0;
427     if (i <= screen_num_desktops) {
428         screen_desktop_names = g_renew(char*, screen_desktop_names,
429                                        screen_num_desktops + 1);
430         screen_desktop_names[screen_num_desktops] = NULL;
431         for (; i < screen_num_desktops; ++i)
432             screen_desktop_names[i] = g_strdup("Unnamed Desktop");
433     }
434 }
435
436 void screen_show_desktop(gboolean show)
437 {
438     GList *it;
439      
440     if (show == screen_showing_desktop) return; /* no change */
441
442     screen_showing_desktop = show;
443
444     if (show) {
445         /* bottom to top */
446         for (it = g_list_last(stacking_list); it != NULL; it = it->prev) {
447             if (WINDOW_IS_CLIENT(it->data)) {
448                 Client *client = it->data;
449                 if (client->frame->visible && !client_should_show(client))
450                     frame_hide(client->frame);
451             }
452         }
453     } else {
454         /* top to bottom */
455         for (it = stacking_list; it != NULL; it = it->next) {
456             if (WINDOW_IS_CLIENT(it->data)) {
457                 Client *client = it->data;
458                 if (!client->frame->visible && client_should_show(client))
459                     frame_show(client->frame);
460             }
461         }
462     }
463
464     if (show) {
465         /* focus desktop */
466         for (it = focus_order[screen_desktop]; it; it = it->next)
467             if (((Client*)it->data)->type == Type_Desktop &&
468                 client_focus(it->data))
469                 break;
470     } else {
471         focus_fallback(Fallback_NoFocus);
472     }
473
474     show = !!show; /* make it boolean */
475     PROP_SET32(ob_root, net_showing_desktop, cardinal, show);
476
477     dispatch_ob(Event_Ob_ShowDesktop, show, 0);
478 }
479
480 void screen_install_colormap(Client *client, gboolean install)
481 {
482     XWindowAttributes wa;
483
484     if (client == NULL) {
485         if (install)
486             XInstallColormap(ob_display, RrInstanceColormap(ob_render_inst));
487         else
488             XUninstallColormap(ob_display, RrInstanceColormap(ob_render_inst));
489     } else {
490         if (XGetWindowAttributes(ob_display, client->window, &wa) &&
491             wa.colormap != None) {
492             xerror_set_ignore(TRUE);
493             if (install)
494                 XInstallColormap(ob_display, wa.colormap);
495             else
496                 XUninstallColormap(ob_display, wa.colormap);
497             xerror_set_ignore(FALSE);
498         }
499     }
500 }
501
502 void screen_update_struts()
503 {
504     GList *it;
505     guint i;
506      
507     g_free(strut);
508     strut = g_new0(Strut, screen_num_desktops + 1);
509
510     for (it = client_list; it != NULL; it = it->next) {
511         Client *c = it->data;
512         if (c->iconic) continue; /* these dont count in the strut */
513     
514         if (c->desktop == 0xffffffff) {
515             for (i = 0; i < screen_num_desktops; ++i)
516                 STRUT_ADD(strut[i], c->strut);
517         } else {
518             g_assert(c->desktop < screen_num_desktops);
519             STRUT_ADD(strut[c->desktop], c->strut);
520         }
521         /* apply to the 'all desktops' strut */
522         STRUT_ADD(strut[screen_num_desktops], c->strut);
523     }
524
525     for (i = 0; i < screen_num_desktops; ++i)
526         STRUT_ADD(strut[i], dock_strut);
527
528     screen_update_area();
529 }
530
531 static void screen_update_area()
532 {
533     guint i;
534     guint32 *dims;
535
536     g_free(area);
537     area = g_new0(Rect, screen_num_desktops + 1);
538      
539     dims = g_new(guint32, 4 * screen_num_desktops);
540     for (i = 0; i < screen_num_desktops + 1; ++i) {
541         Rect old_area = area[i];
542 /*
543   #ifdef    XINERAMA
544   // reset to the full areas
545   if (isXineramaActive())
546   xineramaUsableArea = getXineramaAreas();
547   #endif // XINERAMA
548 */
549
550         RECT_SET(area[i], strut[i].left, strut[i].top,
551                  screen_physical_size.width - (strut[i].left +
552                                                strut[i].right),
553                  screen_physical_size.height - (strut[i].top +
554                                                 strut[i].bottom));
555     
556 /*
557   #ifdef    XINERAMA
558   if (isXineramaActive()) {
559   // keep each of the ximerama-defined areas inside the strut
560   RectList::iterator xit, xend = xineramaUsableArea.end();
561   for (xit = xineramaUsableArea.begin(); xit != xend; ++xit) {
562   if (xit->x() < usableArea.x()) {
563   xit->setX(usableArea.x());
564   xit->setWidth(xit->width() - usableArea.x());
565   }
566   if (xit->y() < usableArea.y()) {
567   xit->setY(usableArea.y());
568   xit->setHeight(xit->height() - usableArea.y());
569   }
570   if (xit->x() + xit->width() > usableArea.width())
571   xit->setWidth(usableArea.width() - xit->x());
572   if (xit->y() + xit->height() > usableArea.height())
573   xit->setHeight(usableArea.height() - xit->y());
574   }
575   }
576   #endif // XINERAMA
577 */
578         if (!RECT_EQUAL(old_area, area[i])) {
579             /* the area has changed, adjust all the maximized windows */
580             GList *it;
581             for (it = client_list; it; it = it->next) {
582                 Client *c = it->data;
583                 if (i < screen_num_desktops) {
584                     if (c->desktop == i)
585                         client_remaximize(c);
586                 } else {
587                     /* the 'all desktops' size */
588                     if (c->desktop == DESKTOP_ALL)
589                         client_remaximize(c);
590                 }
591             }
592         }
593
594         /* don't set these for the 'all desktops' area */
595         if (i < screen_num_desktops) {
596             dims[(i * 4) + 0] = area[i].x;
597             dims[(i * 4) + 1] = area[i].y;
598             dims[(i * 4) + 2] = area[i].width;
599             dims[(i * 4) + 3] = area[i].height;
600         }
601     }
602     PROP_SETA32(ob_root, net_workarea, cardinal,
603                 dims, 4 * screen_num_desktops);
604     g_free(dims);
605 }
606
607 Rect *screen_area(guint desktop)
608 {
609     if (desktop >= screen_num_desktops) {
610         if (desktop == DESKTOP_ALL)
611             return &area[screen_num_desktops];
612         return NULL;
613     }
614     return &area[desktop];
615 }
616
617 Strut *screen_strut(guint desktop)
618 {
619     if (desktop >= screen_num_desktops) {
620         if (desktop == DESKTOP_ALL)
621             return &strut[screen_num_desktops];
622         return NULL;
623     }
624     return &strut[desktop];
625 }
626
627 static void set_root_cursor()
628 {
629 #ifdef USE_LIBSN
630         if (sn_busy_cnt)
631             XDefineCursor(ob_display, ob_root, ob_cursors.busy);
632         else
633 #endif
634             XDefineCursor(ob_display, ob_root, ob_cursors.ptr);
635 }
636
637 #ifdef USE_LIBSN
638 static void sn_timeout(void *data)
639 {
640     timer_stop(sn_timer);
641     sn_timer = NULL;
642     sn_busy_cnt = 0;
643
644     set_root_cursor();
645 }
646
647 static void sn_event_func(SnMonitorEvent *ev, void *data)
648 {
649     SnStartupSequence *seq;
650     const char *seq_id, *bin_name;
651     int cnt = sn_busy_cnt;
652
653     if (!(seq = sn_monitor_event_get_startup_sequence(ev)))
654         return;
655
656     seq_id = sn_startup_sequence_get_id(seq);
657     bin_name = sn_startup_sequence_get_binary_name(seq);
658     
659     if (!(seq_id && bin_name))
660         return;
661
662     switch (sn_monitor_event_get_type(ev)) {
663     case SN_MONITOR_EVENT_INITIATED:
664         ++sn_busy_cnt;
665         if (sn_timer)
666             timer_stop(sn_timer);
667         /* 30 second timeout for apps to start */
668         sn_timer = timer_start(30 * 1000000, sn_timeout, NULL);
669         break;
670     case SN_MONITOR_EVENT_CHANGED:
671         break;
672     case SN_MONITOR_EVENT_COMPLETED:
673         if (sn_busy_cnt) --sn_busy_cnt;
674         if (sn_timer) {
675             timer_stop(sn_timer);
676             sn_timer = NULL;
677         }
678         break;
679     case SN_MONITOR_EVENT_CANCELED:
680         if (sn_busy_cnt) --sn_busy_cnt;
681         if (sn_timer) {
682             timer_stop(sn_timer);
683             sn_timer = NULL;
684         }
685     };
686
687     if (sn_busy_cnt != cnt)
688         set_root_cursor();
689 }
690 #endif