g_spawn_async wants a char**, not const
[mikachu/openbox.git] / openbox / screen.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    screen.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "debug.h"
21 #include "openbox.h"
22 #include "dock.h"
23 #include "xerror.h"
24 #include "prop.h"
25 #include "grab.h"
26 #include "startupnotify.h"
27 #include "moveresize.h"
28 #include "config.h"
29 #include "screen.h"
30 #include "client.h"
31 #include "session.h"
32 #include "frame.h"
33 #include "event.h"
34 #include "focus.h"
35 #include "popup.h"
36 #include "extensions.h"
37 #include "render/render.h"
38 #include "gettext.h"
39
40 #include <X11/Xlib.h>
41 #ifdef HAVE_UNISTD_H
42 #  include <sys/types.h>
43 #  include <unistd.h>
44 #endif
45 #include <assert.h>
46
47 /*! The event mask to grab on the root window */
48 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
49                         EnterWindowMask | LeaveWindowMask | \
50                         SubstructureRedirectMask | FocusChangeMask | \
51                         ButtonPressMask | ButtonReleaseMask | ButtonMotionMask)
52
53 static gboolean screen_validate_layout(ObDesktopLayout *l);
54 static gboolean replace_wm();
55 static void     screen_tell_ksplash();
56
57 guint    screen_num_desktops;
58 guint    screen_num_monitors;
59 guint    screen_desktop;
60 guint    screen_last_desktop;
61 Size     screen_physical_size;
62 gboolean screen_showing_desktop;
63 ObDesktopLayout screen_desktop_layout;
64 gchar  **screen_desktop_names;
65 Window   screen_support_win;
66 Time     screen_desktop_user_time = CurrentTime;
67
68 static Rect  **area; /* array of desktop holding array of xinerama areas */
69 static Rect  *monitor_area;
70
71 static ObPagerPopup *desktop_cycle_popup;
72
73 static gboolean replace_wm()
74 {
75     gchar *wm_sn;
76     Atom wm_sn_atom;
77     Window current_wm_sn_owner;
78     Time timestamp;
79
80     wm_sn = g_strdup_printf("WM_S%d", ob_screen);
81     wm_sn_atom = XInternAtom(ob_display, wm_sn, FALSE);
82     g_free(wm_sn);
83
84     current_wm_sn_owner = XGetSelectionOwner(ob_display, wm_sn_atom);
85     if (current_wm_sn_owner == screen_support_win)
86         current_wm_sn_owner = None;
87     if (current_wm_sn_owner) {
88         if (!ob_replace_wm) {
89             g_message(_("A window manager is already running on screen %d"),
90                       ob_screen);
91             return FALSE;
92         }
93         xerror_set_ignore(TRUE);
94         xerror_occured = FALSE;
95
96         /* We want to find out when the current selection owner dies */
97         XSelectInput(ob_display, current_wm_sn_owner, StructureNotifyMask);
98         XSync(ob_display, FALSE);
99
100         xerror_set_ignore(FALSE);
101         if (xerror_occured)
102             current_wm_sn_owner = None;
103     }
104
105     {
106         /* Generate a timestamp */
107         XEvent event;
108
109         XSelectInput(ob_display, screen_support_win, PropertyChangeMask);
110
111         XChangeProperty(ob_display, screen_support_win,
112                         prop_atoms.wm_class, prop_atoms.string,
113                         8, PropModeAppend, NULL, 0);
114         XWindowEvent(ob_display, screen_support_win,
115                      PropertyChangeMask, &event);
116
117         XSelectInput(ob_display, screen_support_win, NoEventMask);
118
119         timestamp = event.xproperty.time;
120     }
121
122     XSetSelectionOwner(ob_display, wm_sn_atom, screen_support_win,
123                        timestamp);
124
125     if (XGetSelectionOwner(ob_display, wm_sn_atom) != screen_support_win) {
126         g_message(_("Could not acquire window manager selection on screen %d"),
127                   ob_screen);
128         return FALSE;
129     }
130
131     /* Wait for old window manager to go away */
132     if (current_wm_sn_owner) {
133       XEvent event;
134       gulong wait = 0;
135       const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
136
137       while (wait < timeout) {
138           if (XCheckWindowEvent(ob_display, current_wm_sn_owner,
139                                 StructureNotifyMask, &event) &&
140               event.type == DestroyNotify)
141               break;
142           g_usleep(G_USEC_PER_SEC / 10);
143           wait += G_USEC_PER_SEC / 10;
144       }
145
146       if (wait >= timeout) {
147           g_message(_("The WM on screen %d is not exiting"), ob_screen);
148           return FALSE;
149       }
150     }
151
152     /* Send client message indicating that we are now the WM */
153     prop_message(RootWindow(ob_display, ob_screen), prop_atoms.manager,
154                  timestamp, wm_sn_atom, screen_support_win, 0,
155                  SubstructureNotifyMask);
156
157     return TRUE;
158 }
159
160 gboolean screen_annex()
161 {
162     XSetWindowAttributes attrib;
163     pid_t pid;
164     gint i, num_support;
165     Atom *prop_atoms_start, *wm_supported_pos;
166     gulong *supported;
167
168     /* create the netwm support window */
169     attrib.override_redirect = TRUE;
170     screen_support_win = XCreateWindow(ob_display,
171                                        RootWindow(ob_display, ob_screen),
172                                        -100, -100, 1, 1, 0,
173                                        CopyFromParent, InputOutput,
174                                        CopyFromParent,
175                                        CWOverrideRedirect, &attrib);
176     XMapWindow(ob_display, screen_support_win);
177     XLowerWindow(ob_display, screen_support_win);
178
179     if (!replace_wm()) {
180         XDestroyWindow(ob_display, screen_support_win);
181         return FALSE;
182     }
183
184     xerror_set_ignore(TRUE);
185     xerror_occured = FALSE;
186     XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
187                  ROOT_EVENTMASK);
188     xerror_set_ignore(FALSE);
189     if (xerror_occured) {
190         g_message(_("A window manager is already running on screen %d"),
191                   ob_screen);
192
193         XDestroyWindow(ob_display, screen_support_win);
194         return FALSE;
195     }
196
197     screen_set_root_cursor();
198
199     /* set the OPENBOX_PID hint */
200     pid = getpid();
201     PROP_SET32(RootWindow(ob_display, ob_screen),
202                openbox_pid, cardinal, pid);
203
204     /* set supporting window */
205     PROP_SET32(RootWindow(ob_display, ob_screen),
206                net_supporting_wm_check, window, screen_support_win);
207
208     /* set properties on the supporting window */
209     PROP_SETS(screen_support_win, net_wm_name, "Openbox");
210     PROP_SET32(screen_support_win, net_supporting_wm_check,
211                window, screen_support_win);
212
213     /* set the _NET_SUPPORTED_ATOMS hint */
214
215     /* this is all the atoms after net_supported in the prop_atoms struct */
216     prop_atoms_start = (Atom*)&prop_atoms;
217     wm_supported_pos = (Atom*)&(prop_atoms.net_supported);
218     num_support = sizeof(prop_atoms) / sizeof(Atom) -
219         (wm_supported_pos - prop_atoms_start) - 1;
220     i = 0;
221     supported = g_new(gulong, num_support);
222     supported[i++] = prop_atoms.net_supporting_wm_check;
223     supported[i++] = prop_atoms.net_wm_full_placement;
224     supported[i++] = prop_atoms.net_current_desktop;
225     supported[i++] = prop_atoms.net_number_of_desktops;
226     supported[i++] = prop_atoms.net_desktop_geometry;
227     supported[i++] = prop_atoms.net_desktop_viewport;
228     supported[i++] = prop_atoms.net_active_window;
229     supported[i++] = prop_atoms.net_workarea;
230     supported[i++] = prop_atoms.net_client_list;
231     supported[i++] = prop_atoms.net_client_list_stacking;
232     supported[i++] = prop_atoms.net_desktop_names;
233     supported[i++] = prop_atoms.net_close_window;
234     supported[i++] = prop_atoms.net_desktop_layout;
235     supported[i++] = prop_atoms.net_showing_desktop;
236     supported[i++] = prop_atoms.net_wm_name;
237     supported[i++] = prop_atoms.net_wm_visible_name;
238     supported[i++] = prop_atoms.net_wm_icon_name;
239     supported[i++] = prop_atoms.net_wm_visible_icon_name;
240     supported[i++] = prop_atoms.net_wm_desktop;
241     supported[i++] = prop_atoms.net_wm_strut;
242     supported[i++] = prop_atoms.net_wm_strut_partial;
243     supported[i++] = prop_atoms.net_wm_icon;
244     supported[i++] = prop_atoms.net_wm_icon_geometry;
245     supported[i++] = prop_atoms.net_wm_window_type;
246     supported[i++] = prop_atoms.net_wm_window_type_desktop;
247     supported[i++] = prop_atoms.net_wm_window_type_dock;
248     supported[i++] = prop_atoms.net_wm_window_type_toolbar;
249     supported[i++] = prop_atoms.net_wm_window_type_menu;
250     supported[i++] = prop_atoms.net_wm_window_type_utility;
251     supported[i++] = prop_atoms.net_wm_window_type_splash;
252     supported[i++] = prop_atoms.net_wm_window_type_dialog;
253     supported[i++] = prop_atoms.net_wm_window_type_normal;
254     supported[i++] = prop_atoms.net_wm_allowed_actions;
255     supported[i++] = prop_atoms.net_wm_action_move;
256     supported[i++] = prop_atoms.net_wm_action_resize;
257     supported[i++] = prop_atoms.net_wm_action_minimize;
258     supported[i++] = prop_atoms.net_wm_action_shade;
259     supported[i++] = prop_atoms.net_wm_action_maximize_horz;
260     supported[i++] = prop_atoms.net_wm_action_maximize_vert;
261     supported[i++] = prop_atoms.net_wm_action_fullscreen;
262     supported[i++] = prop_atoms.net_wm_action_change_desktop;
263     supported[i++] = prop_atoms.net_wm_action_close;
264     supported[i++] = prop_atoms.net_wm_action_above;
265     supported[i++] = prop_atoms.net_wm_action_below;
266     supported[i++] = prop_atoms.net_wm_state;
267     supported[i++] = prop_atoms.net_wm_state_modal;
268     supported[i++] = prop_atoms.net_wm_state_maximized_vert;
269     supported[i++] = prop_atoms.net_wm_state_maximized_horz;
270     supported[i++] = prop_atoms.net_wm_state_shaded;
271     supported[i++] = prop_atoms.net_wm_state_skip_taskbar;
272     supported[i++] = prop_atoms.net_wm_state_skip_pager;
273     supported[i++] = prop_atoms.net_wm_state_hidden;
274     supported[i++] = prop_atoms.net_wm_state_fullscreen;
275     supported[i++] = prop_atoms.net_wm_state_above;
276     supported[i++] = prop_atoms.net_wm_state_below;
277     supported[i++] = prop_atoms.net_wm_state_demands_attention;
278     supported[i++] = prop_atoms.net_moveresize_window;
279     supported[i++] = prop_atoms.net_wm_moveresize;
280     supported[i++] = prop_atoms.net_wm_user_time;
281     supported[i++] = prop_atoms.net_wm_user_time_window;
282     supported[i++] = prop_atoms.net_frame_extents;
283     supported[i++] = prop_atoms.net_request_frame_extents;
284     supported[i++] = prop_atoms.net_restack_window;
285     supported[i++] = prop_atoms.net_startup_id;
286 #ifdef SYNC
287     supported[i++] = prop_atoms.net_wm_sync_request;
288     supported[i++] = prop_atoms.net_wm_sync_request_counter;
289 #endif
290
291     supported[i++] = prop_atoms.kde_wm_change_state;
292     supported[i++] = prop_atoms.kde_net_wm_frame_strut;
293     supported[i++] = prop_atoms.kde_net_wm_window_type_override;
294
295     supported[i++] = prop_atoms.ob_wm_action_undecorate;
296     supported[i++] = prop_atoms.ob_wm_state_undecorated;
297     supported[i++] = prop_atoms.openbox_pid;
298     supported[i++] = prop_atoms.ob_theme;
299     supported[i++] = prop_atoms.ob_control;
300     g_assert(i == num_support);
301
302     PROP_SETA32(RootWindow(ob_display, ob_screen),
303                 net_supported, atom, supported, num_support);
304     g_free(supported);
305
306     screen_tell_ksplash();
307
308     return TRUE;
309 }
310
311 static void screen_tell_ksplash()
312 {
313     XEvent e;
314     char **argv;
315
316     argv = g_new(gchar*, 6);
317     argv[0] = g_strdup("dcop");
318     argv[1] = g_strdup("ksplash");
319     argv[2] = g_strdup("ksplash");
320     argv[3] = g_strdup("upAndRunning(QString)");
321     argv[4] = g_strdup("wm started");
322     argv[5] = NULL;
323
324     /* tell ksplash through the dcop server command line interface */
325     g_spawn_async(NULL, argv, NULL,
326                   G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
327                   NULL, NULL, NULL, NULL);
328     g_strfreev(argv);
329
330     /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
331        hear it anyways. perhaps it is for old ksplash. or new ksplash. or
332        something. oh well. */
333     e.xclient.type = ClientMessage;
334     e.xclient.display = ob_display;
335     e.xclient.window = RootWindow(ob_display, ob_screen);
336     e.xclient.message_type =
337         XInternAtom(ob_display, "_KDE_SPLASH_PROGRESS", False );
338     e.xclient.format = 8;
339     strcpy(e.xclient.data.b, "wm started");
340     XSendEvent(ob_display, RootWindow(ob_display, ob_screen),
341                False, SubstructureNotifyMask, &e );
342 }
343
344 void screen_startup(gboolean reconfig)
345 {
346     gchar **names = NULL;
347     guint32 d;
348     gboolean namesexist = FALSE;
349
350     desktop_cycle_popup = pager_popup_new(FALSE);
351     pager_popup_height(desktop_cycle_popup, POPUP_HEIGHT);
352
353     if (reconfig) {
354         /* update the pager popup's width */
355         pager_popup_text_width_to_strings(desktop_cycle_popup,
356                                           screen_desktop_names,
357                                           screen_num_desktops);
358         return;
359     }
360
361     /* get the initial size */
362     screen_resize();
363
364     /* have names already been set for the desktops? */
365     if (PROP_GETSS(RootWindow(ob_display, ob_screen),
366                    net_desktop_names, utf8, &names))
367     {
368         g_strfreev(names);
369         namesexist = TRUE;
370     }
371
372     /* if names don't exist and we have session names, set those.
373        do this stuff BEFORE setting the number of desktops, because that
374        will create default names for them
375     */
376     if (!namesexist && session_desktop_names != NULL) {
377         guint i, numnames;
378         GSList *it;
379
380         /* get the desktop names */
381         numnames = g_slist_length(session_desktop_names);
382         names = g_new(gchar*, numnames + 1);
383         names[numnames] = NULL;
384         for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
385             names[i] = g_strdup(it->data);
386
387         /* set the root window property */
388         PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,names);
389
390         g_strfreev(names);
391     }
392
393     /* set the number of desktops, if it's not already set.
394
395        this will also set the default names from the config file up for
396        desktops that don't have names yet */
397     screen_num_desktops = 0;
398     if (PROP_GET32(RootWindow(ob_display, ob_screen),
399                    net_number_of_desktops, cardinal, &d))
400         screen_set_num_desktops(d);
401     /* restore from session if possible */
402     else if (session_num_desktops)
403         screen_set_num_desktops(session_num_desktops);
404     else
405         screen_set_num_desktops(config_desktops_num);
406
407     screen_desktop = screen_num_desktops;  /* something invalid */
408     /* start on the current desktop when a wm was already running */
409     if (PROP_GET32(RootWindow(ob_display, ob_screen),
410                    net_current_desktop, cardinal, &d) &&
411         d < screen_num_desktops)
412     {
413         screen_set_desktop(d, FALSE);
414     } else if (session_desktop >= 0)
415         screen_set_desktop(MIN((guint)session_desktop,
416                                screen_num_desktops), FALSE);
417     else
418         screen_set_desktop(MIN(config_screen_firstdesk,
419                                screen_num_desktops) - 1, FALSE);
420     screen_last_desktop = screen_desktop;
421
422     /* don't start in showing-desktop mode */
423     screen_showing_desktop = FALSE;
424     PROP_SET32(RootWindow(ob_display, ob_screen),
425                net_showing_desktop, cardinal, screen_showing_desktop);
426
427     if (session_desktop_layout_present &&
428         screen_validate_layout(&session_desktop_layout))
429     {
430         screen_desktop_layout = session_desktop_layout;
431     }
432     else
433         screen_update_layout();
434 }
435
436 void screen_shutdown(gboolean reconfig)
437 {
438     Rect **r;
439
440     pager_popup_free(desktop_cycle_popup);
441
442     if (reconfig)
443         return;
444
445     XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
446                  NoEventMask);
447
448     /* we're not running here no more! */
449     PROP_ERASE(RootWindow(ob_display, ob_screen), openbox_pid);
450     /* not without us */
451     PROP_ERASE(RootWindow(ob_display, ob_screen), net_supported);
452     /* don't keep this mode */
453     PROP_ERASE(RootWindow(ob_display, ob_screen), net_showing_desktop);
454
455     XDestroyWindow(ob_display, screen_support_win);
456
457     g_strfreev(screen_desktop_names);
458     screen_desktop_names = NULL;
459
460     for (r = area; *r; ++r)
461         g_free(*r);
462     g_free(area);
463     area = NULL;
464 }
465
466 void screen_resize()
467 {
468     static gint oldw = 0, oldh = 0;
469     gint w, h;
470     GList *it;
471     gulong geometry[2];
472
473     w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
474     h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
475
476     if (w == oldw && h == oldh) return;
477
478     oldw = w; oldh = h;
479
480     /* Set the _NET_DESKTOP_GEOMETRY hint */
481     screen_physical_size.width = geometry[0] = w;
482     screen_physical_size.height = geometry[1] = h;
483     PROP_SETA32(RootWindow(ob_display, ob_screen),
484                 net_desktop_geometry, cardinal, geometry, 2);
485
486     if (ob_state() == OB_STATE_STARTING)
487         return;
488
489     screen_update_areas();
490     dock_configure();
491
492     for (it = client_list; it; it = g_list_next(it))
493         client_move_onscreen(it->data, FALSE);
494 }
495
496 void screen_set_num_desktops(guint num)
497 {
498     guint old;
499     gulong *viewport;
500     GList *it;
501
502     g_assert(num > 0);
503
504     if (screen_num_desktops == num) return;
505
506     old = screen_num_desktops;
507     screen_num_desktops = num;
508     PROP_SET32(RootWindow(ob_display, ob_screen),
509                net_number_of_desktops, cardinal, num);
510
511     /* set the viewport hint */
512     viewport = g_new0(gulong, num * 2);
513     PROP_SETA32(RootWindow(ob_display, ob_screen),
514                 net_desktop_viewport, cardinal, viewport, num * 2);
515     g_free(viewport);
516
517     /* the number of rows/columns will differ */
518     screen_update_layout();
519
520     /* move windows on desktops that will no longer exist! */
521     for (it = client_list; it; it = g_list_next(it)) {
522         ObClient *c = it->data;
523         if (c->desktop >= num && c->desktop != DESKTOP_ALL)
524             client_set_desktop(c, num - 1, FALSE);
525     }
526  
527     /* change our struts/area to match (after moving windows) */
528     screen_update_areas();
529
530     /* may be some unnamed desktops that we need to fill in with names
531      (after updating the areas so the popup can resize) */
532     screen_update_desktop_names();
533
534     /* change our desktop if we're on one that no longer exists! */
535     if (screen_desktop >= screen_num_desktops)
536         screen_set_desktop(num - 1, TRUE);
537 }
538
539 void screen_set_desktop(guint num, gboolean dofocus)
540 {
541     ObClient *c;
542     GList *it;
543     guint old;
544      
545     g_assert(num < screen_num_desktops);
546
547     old = screen_desktop;
548     screen_desktop = num;
549
550     if (old == num) return;
551
552     PROP_SET32(RootWindow(ob_display, ob_screen),
553                net_current_desktop, cardinal, num);
554
555     screen_last_desktop = old;
556
557     ob_debug("Moving to desktop %d\n", num+1);
558
559     if (moveresize_client)
560         client_set_desktop(moveresize_client, num, TRUE);
561
562     /* show windows before hiding the rest to lessen the enter/leave events */
563
564     /* show windows from top to bottom */
565     for (it = stacking_list; it; it = g_list_next(it)) {
566         if (WINDOW_IS_CLIENT(it->data)) {
567             ObClient *c = it->data;
568             client_show(c);
569         }
570     }
571
572     if (focus_client && ((client_normal(focus_client) &&
573                           focus_client->desktop == DESKTOP_ALL) ||
574                          focus_client->desktop == screen_desktop))
575         dofocus = FALSE;
576
577     /* have to try focus here because when you leave an empty desktop
578        there is no focus out to watch for. also, we have different rules
579        here. we always allow it to look under the mouse pointer if
580        config_focus_last is FALSE
581
582        do this before hiding the windows so if helper windows are coming
583        with us, they don't get hidden
584     */
585     if (dofocus && (c = focus_fallback(TRUE, !config_focus_last)))
586     {
587         /* only do the flicker reducing stuff ahead of time if we are going
588            to call xsetinputfocus on the window ourselves. otherwise there is
589            no guarantee the window will actually take focus.. */
590         if (c->can_focus) {
591             /* reduce flicker by hiliting now rather than waiting for the
592                server FocusIn event */
593             frame_adjust_focus(c->frame, TRUE);
594             /* do this here so that if you switch desktops to a window with
595                helper windows then the helper windows won't flash */
596             client_bring_helper_windows(c);
597         }
598     }
599
600     /* hide windows from bottom to top */
601     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
602         if (WINDOW_IS_CLIENT(it->data)) {
603             ObClient *c = it->data;
604             client_hide(c);
605         }
606     }
607
608     event_ignore_all_queued_enters();
609
610     if (event_curtime != CurrentTime)
611         screen_desktop_user_time = event_curtime;
612 }
613
614 static void get_row_col(guint d, guint *r, guint *c)
615 {
616     switch (screen_desktop_layout.orientation) {
617     case OB_ORIENTATION_HORZ:
618         switch (screen_desktop_layout.start_corner) {
619         case OB_CORNER_TOPLEFT:
620             *r = d / screen_desktop_layout.columns;
621             *c = d % screen_desktop_layout.columns;
622             break;
623         case OB_CORNER_BOTTOMLEFT:
624             *r = screen_desktop_layout.rows - 1 -
625                 d / screen_desktop_layout.columns;
626             *c = d % screen_desktop_layout.columns;
627             break;
628         case OB_CORNER_TOPRIGHT:
629             *r = d / screen_desktop_layout.columns;
630             *c = screen_desktop_layout.columns - 1 -
631                 d % screen_desktop_layout.columns;
632             break;
633         case OB_CORNER_BOTTOMRIGHT:
634             *r = screen_desktop_layout.rows - 1 -
635                 d / screen_desktop_layout.columns;
636             *c = screen_desktop_layout.columns - 1 -
637                 d % screen_desktop_layout.columns;
638             break;
639         }
640         break;
641     case OB_ORIENTATION_VERT:
642         switch (screen_desktop_layout.start_corner) {
643         case OB_CORNER_TOPLEFT:
644             *r = d % screen_desktop_layout.rows;
645             *c = d / screen_desktop_layout.rows;
646             break;
647         case OB_CORNER_BOTTOMLEFT:
648             *r = screen_desktop_layout.rows - 1 -
649                 d % screen_desktop_layout.rows;
650             *c = d / screen_desktop_layout.rows;
651             break;
652         case OB_CORNER_TOPRIGHT:
653             *r = d % screen_desktop_layout.rows;
654             *c = screen_desktop_layout.columns - 1 -
655                 d / screen_desktop_layout.rows;
656             break;
657         case OB_CORNER_BOTTOMRIGHT:
658             *r = screen_desktop_layout.rows - 1 -
659                 d % screen_desktop_layout.rows;
660             *c = screen_desktop_layout.columns - 1 -
661                 d / screen_desktop_layout.rows;
662             break;
663         }
664         break;
665     }
666 }
667
668 static guint translate_row_col(guint r, guint c)
669 {
670     switch (screen_desktop_layout.orientation) {
671     case OB_ORIENTATION_HORZ:
672         switch (screen_desktop_layout.start_corner) {
673         case OB_CORNER_TOPLEFT:
674             return r % screen_desktop_layout.rows *
675                 screen_desktop_layout.columns +
676                 c % screen_desktop_layout.columns;
677         case OB_CORNER_BOTTOMLEFT:
678             return (screen_desktop_layout.rows - 1 -
679                     r % screen_desktop_layout.rows) *
680                 screen_desktop_layout.columns +
681                 c % screen_desktop_layout.columns;
682         case OB_CORNER_TOPRIGHT:
683             return r % screen_desktop_layout.rows *
684                 screen_desktop_layout.columns +
685                 (screen_desktop_layout.columns - 1 -
686                  c % screen_desktop_layout.columns);
687         case OB_CORNER_BOTTOMRIGHT:
688             return (screen_desktop_layout.rows - 1 -
689                     r % screen_desktop_layout.rows) *
690                 screen_desktop_layout.columns +
691                 (screen_desktop_layout.columns - 1 -
692                  c % screen_desktop_layout.columns);
693         }
694     case OB_ORIENTATION_VERT:
695         switch (screen_desktop_layout.start_corner) {
696         case OB_CORNER_TOPLEFT:
697             return c % screen_desktop_layout.columns *
698                 screen_desktop_layout.rows +
699                 r % screen_desktop_layout.rows;
700         case OB_CORNER_BOTTOMLEFT:
701             return c % screen_desktop_layout.columns *
702                 screen_desktop_layout.rows +
703                 (screen_desktop_layout.rows - 1 -
704                  r % screen_desktop_layout.rows);
705         case OB_CORNER_TOPRIGHT:
706             return (screen_desktop_layout.columns - 1 -
707                     c % screen_desktop_layout.columns) *
708                 screen_desktop_layout.rows +
709                 r % screen_desktop_layout.rows;
710         case OB_CORNER_BOTTOMRIGHT:
711             return (screen_desktop_layout.columns - 1 -
712                     c % screen_desktop_layout.columns) *
713                 screen_desktop_layout.rows +
714                 (screen_desktop_layout.rows - 1 -
715                  r % screen_desktop_layout.rows);
716         }
717     }
718     g_assert_not_reached();
719     return 0;
720 }
721
722 void screen_desktop_popup(guint d, gboolean show)
723 {
724     Rect *a;
725
726     if (!show) {
727         pager_popup_hide(desktop_cycle_popup);
728     } else {
729         a = screen_physical_area_monitor(0);
730         pager_popup_position(desktop_cycle_popup, CenterGravity,
731                              a->x + a->width / 2, a->y + a->height / 2);
732         pager_popup_icon_size_multiplier(desktop_cycle_popup,
733                                          (screen_desktop_layout.columns /
734                                           screen_desktop_layout.rows) / 2,
735                                          (screen_desktop_layout.rows/
736                                           screen_desktop_layout.columns) / 2);
737         pager_popup_max_width(desktop_cycle_popup,
738                               MAX(a->width/3, POPUP_WIDTH));
739         pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d);
740     }
741 }
742
743 guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
744                            gboolean dialog, gboolean done, gboolean cancel)
745 {
746     guint r, c;
747     static guint d = (guint)-1;
748     guint ret, oldd;
749
750     if (d == (guint)-1)
751         d = screen_desktop;
752
753     if ((cancel || done) && dialog)
754         goto show_cycle_dialog;
755
756     oldd = d;
757     get_row_col(d, &r, &c);
758
759     if (linear) {
760         switch (dir) {
761         case OB_DIRECTION_EAST:
762             if (d < screen_num_desktops - 1)
763                 ++d;
764             else if (wrap)
765                 d = 0;
766             break;
767         case OB_DIRECTION_WEST:
768             if (d > 0)
769                 --d;
770             else if (wrap)
771                 d = screen_num_desktops - 1;
772             break;
773         default:
774             assert(0);
775             return screen_desktop;
776         }
777     } else {
778         switch (dir) {
779         case OB_DIRECTION_EAST:
780             ++c;
781             if (c >= screen_desktop_layout.columns) {
782                 if (wrap)
783                     c = 0;
784                 else
785                     goto show_cycle_dialog;
786             }
787             d = translate_row_col(r, c);
788             if (d >= screen_num_desktops) {
789                 if (wrap) {
790                     ++c;
791                 } else {
792                     d = oldd;
793                     goto show_cycle_dialog;
794                 }
795             }
796             break;
797         case OB_DIRECTION_WEST:
798             --c;
799             if (c >= screen_desktop_layout.columns) {
800                 if (wrap)
801                     c = screen_desktop_layout.columns - 1;
802                 else
803                     goto show_cycle_dialog;
804             }
805             d = translate_row_col(r, c);
806             if (d >= screen_num_desktops) {
807                 if (wrap) {
808                     --c;
809                 } else {
810                     d = oldd;
811                     goto show_cycle_dialog;
812                 }
813             }
814             break;
815         case OB_DIRECTION_SOUTH:
816             ++r;
817             if (r >= screen_desktop_layout.rows) {
818                 if (wrap)
819                     r = 0;
820                 else
821                     goto show_cycle_dialog;
822             }
823             d = translate_row_col(r, c);
824             if (d >= screen_num_desktops) {
825                 if (wrap) {
826                     ++r;
827                 } else {
828                     d = oldd;
829                     goto show_cycle_dialog;
830                 }
831             }
832             break;
833         case OB_DIRECTION_NORTH:
834             --r;
835             if (r >= screen_desktop_layout.rows) {
836                 if (wrap)
837                     r = screen_desktop_layout.rows - 1;
838                 else
839                     goto show_cycle_dialog;
840             }
841             d = translate_row_col(r, c);
842             if (d >= screen_num_desktops) {
843                 if (wrap) {
844                     --r;
845                 } else {
846                     d = oldd;
847                     goto show_cycle_dialog;
848                 }
849             }
850             break;
851         default:
852             assert(0);
853             return d = screen_desktop;
854         }
855
856         d = translate_row_col(r, c);
857     }
858
859 show_cycle_dialog:
860     if (dialog && !cancel && !done) {
861         screen_desktop_popup(d, TRUE);
862     } else
863         screen_desktop_popup(0, FALSE);
864     ret = d;
865
866     if (!dialog || cancel || done)
867         d = (guint)-1;
868
869     return ret;
870 }
871
872 static gboolean screen_validate_layout(ObDesktopLayout *l)
873 {
874     if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
875         return FALSE;
876
877     /* fill in a zero rows/columns */
878     if (l->columns == 0) {
879         l->columns = screen_num_desktops / l->rows;
880         if (l->rows * l->columns < screen_num_desktops)
881             l->columns++;
882         if (l->rows * l->columns >= screen_num_desktops + l->columns)
883             l->rows--;
884     } else if (l->rows == 0) {
885         l->rows = screen_num_desktops / l->columns;
886         if (l->columns * l->rows < screen_num_desktops)
887             l->rows++;
888         if (l->columns * l->rows >= screen_num_desktops + l->rows)
889             l->columns--;
890     }
891
892     /* bounds checking */
893     if (l->orientation == OB_ORIENTATION_HORZ) {
894         l->columns = MIN(screen_num_desktops, l->columns);
895         l->rows = MIN(l->rows,
896                       (screen_num_desktops + l->columns - 1) / l->columns);
897         l->columns = screen_num_desktops / l->rows +
898             !!(screen_num_desktops % l->rows);
899     } else {
900         l->rows = MIN(screen_num_desktops, l->rows);
901         l->columns = MIN(l->columns,
902                          (screen_num_desktops + l->rows - 1) / l->rows);
903         l->rows = screen_num_desktops / l->columns +
904             !!(screen_num_desktops % l->columns);
905     }
906     return TRUE;
907 }
908
909 void screen_update_layout()
910
911 {
912     ObDesktopLayout l;
913     guint32 *data;
914     guint num;
915
916     screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
917     screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
918     screen_desktop_layout.rows = 1;
919     screen_desktop_layout.columns = screen_num_desktops;
920
921     if (PROP_GETA32(RootWindow(ob_display, ob_screen),
922                     net_desktop_layout, cardinal, &data, &num)) {
923         if (num == 3 || num == 4) {
924             
925             if (data[0] == prop_atoms.net_wm_orientation_vert)
926                 l.orientation = OB_ORIENTATION_VERT;
927             else if (data[0] == prop_atoms.net_wm_orientation_horz)
928                 l.orientation = OB_ORIENTATION_HORZ;
929             else
930                 return;
931
932             if (num < 4)
933                 l.start_corner = OB_CORNER_TOPLEFT;
934             else {
935                 if (data[3] == prop_atoms.net_wm_topleft)
936                     l.start_corner = OB_CORNER_TOPLEFT;
937                 else if (data[3] == prop_atoms.net_wm_topright)
938                     l.start_corner = OB_CORNER_TOPRIGHT;
939                 else if (data[3] == prop_atoms.net_wm_bottomright)
940                     l.start_corner = OB_CORNER_BOTTOMRIGHT;
941                 else if (data[3] == prop_atoms.net_wm_bottomleft)
942                     l.start_corner = OB_CORNER_BOTTOMLEFT;
943                 else
944                     return;
945             }
946
947             l.columns = data[1];
948             l.rows = data[2];
949
950             if (screen_validate_layout(&l))
951                 screen_desktop_layout = l;
952
953             g_free(data);
954         }
955     }
956 }
957
958 void screen_update_desktop_names()
959 {
960     guint i;
961
962     /* empty the array */
963     g_strfreev(screen_desktop_names);
964     screen_desktop_names = NULL;
965
966     if (PROP_GETSS(RootWindow(ob_display, ob_screen),
967                    net_desktop_names, utf8, &screen_desktop_names))
968         for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
969     else
970         i = 0;
971     if (i < screen_num_desktops) {
972         GSList *it;
973
974         screen_desktop_names = g_renew(gchar*, screen_desktop_names,
975                                        screen_num_desktops + 1);
976         screen_desktop_names[screen_num_desktops] = NULL;
977
978         it = g_slist_nth(config_desktops_names, i);
979
980         for (; i < screen_num_desktops; ++i) {
981             if (it && ((char*)it->data)[0]) /* not empty */
982                 /* use the names from the config file when possible */
983                 screen_desktop_names[i] = g_strdup(it->data);
984             else
985                 /* make up a nice name if it's not though */
986                 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
987                                                           i + 1);
988             if (it) it = g_slist_next(it);
989         }
990
991         /* if we changed any names, then set the root property so we can
992            all agree on the names */
993         PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,
994                    screen_desktop_names);
995     }
996
997     /* resize the pager for these names */
998     pager_popup_text_width_to_strings(desktop_cycle_popup,
999                                       screen_desktop_names,
1000                                       screen_num_desktops);
1001 }
1002
1003 void screen_show_desktop(gboolean show, ObClient *show_only)
1004 {
1005     GList *it;
1006      
1007     if (show == screen_showing_desktop) return; /* no change */
1008
1009     screen_showing_desktop = show;
1010
1011     if (show) {
1012         /* hide windows bottom to top */
1013         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1014             if (WINDOW_IS_CLIENT(it->data)) {
1015                 ObClient *client = it->data;
1016                 client_showhide(client);
1017             }
1018         }
1019     }
1020     else {
1021         /* restore windows top to bottom */
1022         for (it = stacking_list; it; it = g_list_next(it)) {
1023             if (WINDOW_IS_CLIENT(it->data)) {
1024                 ObClient *client = it->data;
1025                 if (client_should_show(client)) {
1026                     if (!show_only || client == show_only)
1027                         client_show(client);
1028                     else
1029                         client_iconify(client, TRUE, FALSE, TRUE);
1030                 }
1031             }
1032         }
1033     }
1034
1035     if (show) {
1036         /* focus the desktop */
1037         for (it = focus_order; it; it = g_list_next(it)) {
1038             ObClient *c = it->data;
1039             if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1040                 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1041                 client_focus(it->data))
1042                 break;
1043         }
1044     }
1045     else if (!show_only) {
1046         ObClient *c;
1047
1048         if ((c = focus_fallback(TRUE, FALSE))) {
1049             /* only do the flicker reducing stuff ahead of time if we are going
1050                to call xsetinputfocus on the window ourselves. otherwise there
1051                is no guarantee the window will actually take focus.. */
1052             if (c->can_focus) {
1053                 /* reduce flicker by hiliting now rather than waiting for the
1054                    server FocusIn event */
1055                 frame_adjust_focus(c->frame, TRUE);
1056             }
1057         }
1058     }
1059
1060     show = !!show; /* make it boolean */
1061     PROP_SET32(RootWindow(ob_display, ob_screen),
1062                net_showing_desktop, cardinal, show);
1063 }
1064
1065 void screen_install_colormap(ObClient *client, gboolean install)
1066 {
1067     if (client == NULL || client->colormap == None) {
1068         if (install)
1069             XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1070         else
1071             XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1072     } else {
1073         xerror_set_ignore(TRUE);
1074         if (install)
1075             XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1076         else
1077             XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1078         xerror_set_ignore(FALSE);
1079     }
1080 }
1081
1082 static inline void
1083 screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
1084                            gint edge, Strut *ret)
1085 {
1086     if (s->left &&
1087         ((s->left_end <= s->left_start) ||
1088          (RECT_TOP(*monitor_area) < s->left_end &&
1089           RECT_BOTTOM(*monitor_area) > s->left_start)))
1090         ret->left = MAX(ret->left, edge);
1091 }
1092
1093 static inline void
1094 screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
1095                           gint edge, Strut *ret)
1096 {
1097     if (s->top &&
1098         ((s->top_end <= s->top_start) ||
1099          (RECT_LEFT(*monitor_area) < s->top_end &&
1100           RECT_RIGHT(*monitor_area) > s->top_start)))
1101         ret->top = MAX(ret->top, edge);
1102 }
1103
1104 static inline void
1105 screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
1106                             gint edge, Strut *ret)
1107 {
1108     if (s->right &&
1109         ((s->right_end <= s->right_start) ||
1110          (RECT_TOP(*monitor_area) < s->right_end &&
1111           RECT_BOTTOM(*monitor_area) > s->right_start)))
1112         ret->right = MAX(ret->right, edge);
1113 }
1114
1115 static inline void
1116 screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
1117                              gint edge, Strut *ret)
1118 {
1119     if (s->bottom &&
1120         ((s->bottom_end <= s->bottom_start) ||
1121          (RECT_LEFT(*monitor_area) < s->bottom_end &&
1122           RECT_RIGHT(*monitor_area) > s->bottom_start)))
1123         ret->bottom = MAX(ret->bottom, edge);
1124 }
1125
1126 void screen_update_areas()
1127 {
1128     guint i, x;
1129     gulong *dims;
1130     GList *it;
1131     gint o;
1132
1133     g_free(monitor_area);
1134     extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
1135
1136     if (area) {
1137         for (i = 0; area[i]; ++i)
1138             g_free(area[i]);
1139         g_free(area);
1140     }
1141
1142     area = g_new(Rect*, screen_num_desktops + 2);
1143     for (i = 0; i < screen_num_desktops + 1; ++i)
1144         area[i] = g_new0(Rect, screen_num_monitors + 1);
1145     area[i] = NULL;
1146      
1147     dims = g_new(gulong, 4 * screen_num_desktops);
1148
1149     for (i = 0; i < screen_num_desktops + 1; ++i) {
1150         Strut *struts;
1151         gint l, r, t, b;
1152
1153         struts = g_new0(Strut, screen_num_monitors);
1154
1155         /* calc the xinerama areas */
1156         for (x = 0; x < screen_num_monitors; ++x) {
1157             area[i][x] = monitor_area[x];
1158             if (x == 0) {
1159                 l = monitor_area[x].x;
1160                 t = monitor_area[x].y;
1161                 r = monitor_area[x].x + monitor_area[x].width - 1;
1162                 b = monitor_area[x].y + monitor_area[x].height - 1;
1163             } else {
1164                 l = MIN(l, monitor_area[x].x);
1165                 t = MIN(t, monitor_area[x].y);
1166                 r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
1167                 b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
1168             }
1169         }
1170         RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
1171
1172         /* apply the struts */
1173
1174         /* find the left-most xin heads, i do this in 2 loops :| */
1175         o = area[i][0].x;
1176         for (x = 1; x < screen_num_monitors; ++x)
1177             o = MIN(o, area[i][x].x);
1178
1179         for (x = 0; x < screen_num_monitors; ++x) {
1180             for (it = client_list; it; it = g_list_next(it)) {
1181                 ObClient *c = it->data;
1182                 screen_area_add_strut_left(&c->strut,
1183                                            &monitor_area[x],
1184                                            o + c->strut.left - area[i][x].x,
1185                                            &struts[x]);
1186             }
1187             screen_area_add_strut_left(&dock_strut,
1188                                        &monitor_area[x],
1189                                        o + dock_strut.left - area[i][x].x,
1190                                        &struts[x]);
1191
1192             area[i][x].x += struts[x].left;
1193             area[i][x].width -= struts[x].left;
1194         }
1195
1196         /* find the top-most xin heads, i do this in 2 loops :| */
1197         o = area[i][0].y;
1198         for (x = 1; x < screen_num_monitors; ++x)
1199             o = MIN(o, area[i][x].y);
1200
1201         for (x = 0; x < screen_num_monitors; ++x) {
1202             for (it = client_list; it; it = g_list_next(it)) {
1203                 ObClient *c = it->data;
1204                 screen_area_add_strut_top(&c->strut,
1205                                            &monitor_area[x],
1206                                            o + c->strut.top - area[i][x].y,
1207                                            &struts[x]);
1208             }
1209             screen_area_add_strut_top(&dock_strut,
1210                                       &monitor_area[x],
1211                                       o + dock_strut.top - area[i][x].y,
1212                                       &struts[x]);
1213
1214             area[i][x].y += struts[x].top;
1215             area[i][x].height -= struts[x].top;
1216         }
1217
1218         /* find the right-most xin heads, i do this in 2 loops :| */
1219         o = area[i][0].x + area[i][0].width - 1;
1220         for (x = 1; x < screen_num_monitors; ++x)
1221             o = MAX(o, area[i][x].x + area[i][x].width - 1);
1222
1223         for (x = 0; x < screen_num_monitors; ++x) {
1224             for (it = client_list; it; it = g_list_next(it)) {
1225                 ObClient *c = it->data;
1226                 screen_area_add_strut_right(&c->strut,
1227                                            &monitor_area[x],
1228                                            (area[i][x].x +
1229                                             area[i][x].width - 1) -
1230                                             (o - c->strut.right),
1231                                             &struts[x]);
1232             }
1233             screen_area_add_strut_right(&dock_strut,
1234                                         &monitor_area[x],
1235                                         (area[i][x].x +
1236                                          area[i][x].width - 1) -
1237                                         (o - dock_strut.right),
1238                                         &struts[x]);
1239
1240             area[i][x].width -= struts[x].right;
1241         }
1242
1243         /* find the bottom-most xin heads, i do this in 2 loops :| */
1244         o = area[i][0].y + area[i][0].height - 1;
1245         for (x = 1; x < screen_num_monitors; ++x)
1246             o = MAX(o, area[i][x].y + area[i][x].height - 1);
1247
1248         for (x = 0; x < screen_num_monitors; ++x) {
1249             for (it = client_list; it; it = g_list_next(it)) {
1250                 ObClient *c = it->data;
1251                 screen_area_add_strut_bottom(&c->strut,
1252                                              &monitor_area[x],
1253                                              (area[i][x].y +
1254                                               area[i][x].height - 1) - \
1255                                              (o - c->strut.bottom),
1256                                              &struts[x]);
1257             }
1258             screen_area_add_strut_bottom(&dock_strut,
1259                                          &monitor_area[x],
1260                                          (area[i][x].y +
1261                                           area[i][x].height - 1) - \
1262                                          (o - dock_strut.bottom),
1263                                          &struts[x]);
1264
1265             area[i][x].height -= struts[x].bottom;
1266         }
1267
1268         l = RECT_LEFT(area[i][0]);
1269         t = RECT_TOP(area[i][0]);
1270         r = RECT_RIGHT(area[i][0]);
1271         b = RECT_BOTTOM(area[i][0]);
1272         for (x = 1; x < screen_num_monitors; ++x) {
1273             l = MIN(l, RECT_LEFT(area[i][x]));
1274             t = MIN(l, RECT_TOP(area[i][x]));
1275             r = MAX(r, RECT_RIGHT(area[i][x]));
1276             b = MAX(b, RECT_BOTTOM(area[i][x]));
1277         }
1278         RECT_SET(area[i][screen_num_monitors], l, t,
1279                  r - l + 1, b - t + 1);
1280
1281         /* XXX optimize when this is run? */
1282
1283         /* the area has changed, adjust all the maximized 
1284            windows */
1285         for (it = client_list; it; it = g_list_next(it)) {
1286             ObClient *c = it->data; 
1287             if (i < screen_num_desktops) {
1288                 if (c->desktop == i)
1289                     client_reconfigure(c);
1290             } else if (c->desktop == DESKTOP_ALL)
1291                 client_reconfigure(c);
1292         }
1293         if (i < screen_num_desktops) {
1294             /* don't set these for the 'all desktops' area */
1295             dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
1296             dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
1297             dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
1298             dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
1299         }
1300
1301         g_free(struts);
1302     }
1303
1304     PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
1305                 dims, 4 * screen_num_desktops);
1306
1307     g_free(dims);
1308 }
1309
1310 Rect *screen_area(guint desktop)
1311 {
1312     return screen_area_monitor(desktop, screen_num_monitors);
1313 }
1314
1315 Rect *screen_area_monitor(guint desktop, guint head)
1316 {
1317     if (head > screen_num_monitors)
1318         return NULL;
1319     if (desktop >= screen_num_desktops) {
1320         if (desktop == DESKTOP_ALL)
1321             return &area[screen_num_desktops][head];
1322         return NULL;
1323     }
1324     return &area[desktop][head];
1325 }
1326
1327 guint screen_find_monitor(Rect *search)
1328 {
1329     guint i;
1330     guint most = 0;
1331     guint mostv = 0;
1332
1333     for (i = 0; i < screen_num_monitors; ++i) {
1334         Rect *area = screen_physical_area_monitor(i);
1335         if (RECT_INTERSECTS_RECT(*area, *search)) {
1336             Rect r;
1337             guint v;
1338
1339             RECT_SET_INTERSECTION(r, *area, *search);
1340             v = r.width * r.height;
1341
1342             if (v > mostv) {
1343                 mostv = v;
1344                 most = i;
1345             }
1346         }
1347     }
1348     return most;
1349 }
1350
1351 Rect *screen_physical_area()
1352 {
1353     return screen_physical_area_monitor(screen_num_monitors);
1354 }
1355
1356 Rect *screen_physical_area_monitor(guint head)
1357 {
1358     if (head > screen_num_monitors)
1359         return NULL;
1360     return &monitor_area[head];
1361 }
1362
1363 void screen_set_root_cursor()
1364 {
1365     if (sn_app_starting())
1366         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1367                       ob_cursor(OB_CURSOR_BUSYPOINTER));
1368     else
1369         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1370                       ob_cursor(OB_CURSOR_POINTER));
1371 }
1372
1373 gboolean screen_pointer_pos(gint *x, gint *y)
1374 {
1375     Window w;
1376     gint i;
1377     guint u;
1378     gboolean ret;
1379
1380     ret = !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen),
1381                           &w, &w, x, y, &i, &i, &u);
1382     if (!ret) {
1383         for (i = 0; i < ScreenCount(ob_display); ++i)
1384             if (i != ob_screen)
1385                 if (XQueryPointer(ob_display, RootWindow(ob_display, i),
1386                                   &w, &w, x, y, &i, &i, &u))
1387                     break;
1388     }
1389     return ret;
1390 }