]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/screen.c
a better ordering for mapping window configure events
[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     const char *args[] = { "dcop", "ksplash", "ksplash",
315                            "upAndRunning(QString)", "wm started", NULL };
316
317     /* tell ksplash through the dcop server command line interface */
318     g_spawn_async(NULL, args, NULL,
319                   G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
320                   NULL, NULL, NULL, NULL);
321
322     /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
323        hear it anyways. perhaps it is for old ksplash. or new ksplash. or
324        something. oh well. */
325     e.xclient.type = ClientMessage;
326     e.xclient.display = ob_display;
327     e.xclient.window = RootWindow(ob_display, ob_screen);
328     e.xclient.message_type =
329         XInternAtom(ob_display, "_KDE_SPLASH_PROGRESS", False );
330     e.xclient.format = 8;
331     strcpy(e.xclient.data.b, "wm started");
332     XSendEvent(ob_display, RootWindow(ob_display, ob_screen),
333                False, SubstructureNotifyMask, &e );
334 }
335
336 void screen_startup(gboolean reconfig)
337 {
338     gchar **names = NULL;
339     guint32 d;
340     gboolean namesexist = FALSE;
341
342     desktop_cycle_popup = pager_popup_new(FALSE);
343     pager_popup_height(desktop_cycle_popup, POPUP_HEIGHT);
344
345     if (reconfig) {
346         /* update the pager popup's width */
347         pager_popup_text_width_to_strings(desktop_cycle_popup,
348                                           screen_desktop_names,
349                                           screen_num_desktops);
350         return;
351     }
352
353     /* get the initial size */
354     screen_resize();
355
356     /* have names already been set for the desktops? */
357     if (PROP_GETSS(RootWindow(ob_display, ob_screen),
358                    net_desktop_names, utf8, &names))
359     {
360         g_strfreev(names);
361         namesexist = TRUE;
362     }
363
364     /* if names don't exist and we have session names, set those.
365        do this stuff BEFORE setting the number of desktops, because that
366        will create default names for them
367     */
368     if (!namesexist && session_desktop_names != NULL) {
369         guint i, numnames;
370         GSList *it;
371
372         /* get the desktop names */
373         numnames = g_slist_length(session_desktop_names);
374         names = g_new(gchar*, numnames + 1);
375         names[numnames] = NULL;
376         for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
377             names[i] = g_strdup(it->data);
378
379         /* set the root window property */
380         PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,names);
381
382         g_strfreev(names);
383     }
384
385     /* set the number of desktops, if it's not already set.
386
387        this will also set the default names from the config file up for
388        desktops that don't have names yet */
389     screen_num_desktops = 0;
390     if (PROP_GET32(RootWindow(ob_display, ob_screen),
391                    net_number_of_desktops, cardinal, &d))
392         screen_set_num_desktops(d);
393     /* restore from session if possible */
394     else if (session_num_desktops)
395         screen_set_num_desktops(session_num_desktops);
396     else
397         screen_set_num_desktops(config_desktops_num);
398
399     screen_desktop = screen_num_desktops;  /* something invalid */
400     /* start on the current desktop when a wm was already running */
401     if (PROP_GET32(RootWindow(ob_display, ob_screen),
402                    net_current_desktop, cardinal, &d) &&
403         d < screen_num_desktops)
404     {
405         screen_set_desktop(d, FALSE);
406     } else if (session_desktop >= 0)
407         screen_set_desktop(MIN((guint)session_desktop,
408                                screen_num_desktops), FALSE);
409     else
410         screen_set_desktop(MIN(config_screen_firstdesk,
411                                screen_num_desktops) - 1, FALSE);
412     screen_last_desktop = screen_desktop;
413
414     /* don't start in showing-desktop mode */
415     screen_showing_desktop = FALSE;
416     PROP_SET32(RootWindow(ob_display, ob_screen),
417                net_showing_desktop, cardinal, screen_showing_desktop);
418
419     if (session_desktop_layout_present &&
420         screen_validate_layout(&session_desktop_layout))
421     {
422         screen_desktop_layout = session_desktop_layout;
423     }
424     else
425         screen_update_layout();
426 }
427
428 void screen_shutdown(gboolean reconfig)
429 {
430     Rect **r;
431
432     pager_popup_free(desktop_cycle_popup);
433
434     if (reconfig)
435         return;
436
437     XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
438                  NoEventMask);
439
440     /* we're not running here no more! */
441     PROP_ERASE(RootWindow(ob_display, ob_screen), openbox_pid);
442     /* not without us */
443     PROP_ERASE(RootWindow(ob_display, ob_screen), net_supported);
444     /* don't keep this mode */
445     PROP_ERASE(RootWindow(ob_display, ob_screen), net_showing_desktop);
446
447     XDestroyWindow(ob_display, screen_support_win);
448
449     g_strfreev(screen_desktop_names);
450     screen_desktop_names = NULL;
451
452     for (r = area; *r; ++r)
453         g_free(*r);
454     g_free(area);
455     area = NULL;
456 }
457
458 void screen_resize()
459 {
460     static gint oldw = 0, oldh = 0;
461     gint w, h;
462     GList *it;
463     gulong geometry[2];
464
465     w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
466     h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
467
468     if (w == oldw && h == oldh) return;
469
470     oldw = w; oldh = h;
471
472     /* Set the _NET_DESKTOP_GEOMETRY hint */
473     screen_physical_size.width = geometry[0] = w;
474     screen_physical_size.height = geometry[1] = h;
475     PROP_SETA32(RootWindow(ob_display, ob_screen),
476                 net_desktop_geometry, cardinal, geometry, 2);
477
478     if (ob_state() == OB_STATE_STARTING)
479         return;
480
481     screen_update_areas();
482     dock_configure();
483
484     for (it = client_list; it; it = g_list_next(it))
485         client_move_onscreen(it->data, FALSE);
486 }
487
488 void screen_set_num_desktops(guint num)
489 {
490     guint old;
491     gulong *viewport;
492     GList *it;
493
494     g_assert(num > 0);
495
496     if (screen_num_desktops == num) return;
497
498     old = screen_num_desktops;
499     screen_num_desktops = num;
500     PROP_SET32(RootWindow(ob_display, ob_screen),
501                net_number_of_desktops, cardinal, num);
502
503     /* set the viewport hint */
504     viewport = g_new0(gulong, num * 2);
505     PROP_SETA32(RootWindow(ob_display, ob_screen),
506                 net_desktop_viewport, cardinal, viewport, num * 2);
507     g_free(viewport);
508
509     /* the number of rows/columns will differ */
510     screen_update_layout();
511
512     /* move windows on desktops that will no longer exist! */
513     for (it = client_list; it; it = g_list_next(it)) {
514         ObClient *c = it->data;
515         if (c->desktop >= num && c->desktop != DESKTOP_ALL)
516             client_set_desktop(c, num - 1, FALSE);
517     }
518  
519     /* change our struts/area to match (after moving windows) */
520     screen_update_areas();
521
522     /* may be some unnamed desktops that we need to fill in with names
523      (after updating the areas so the popup can resize) */
524     screen_update_desktop_names();
525
526     /* change our desktop if we're on one that no longer exists! */
527     if (screen_desktop >= screen_num_desktops)
528         screen_set_desktop(num - 1, TRUE);
529 }
530
531 void screen_set_desktop(guint num, gboolean dofocus)
532 {
533     ObClient *c;
534     GList *it;
535     guint old;
536      
537     g_assert(num < screen_num_desktops);
538
539     old = screen_desktop;
540     screen_desktop = num;
541
542     if (old == num) return;
543
544     PROP_SET32(RootWindow(ob_display, ob_screen),
545                net_current_desktop, cardinal, num);
546
547     screen_last_desktop = old;
548
549     ob_debug("Moving to desktop %d\n", num+1);
550
551     if (moveresize_client)
552         client_set_desktop(moveresize_client, num, TRUE);
553
554     /* show windows before hiding the rest to lessen the enter/leave events */
555
556     /* show windows from top to bottom */
557     for (it = stacking_list; it; it = g_list_next(it)) {
558         if (WINDOW_IS_CLIENT(it->data)) {
559             ObClient *c = it->data;
560             client_show(c);
561         }
562     }
563
564     if (focus_client && ((client_normal(focus_client) &&
565                           focus_client->desktop == DESKTOP_ALL) ||
566                          focus_client->desktop == screen_desktop))
567         dofocus = FALSE;
568
569     /* have to try focus here because when you leave an empty desktop
570        there is no focus out to watch for. also, we have different rules
571        here. we always allow it to look under the mouse pointer if
572        config_focus_last is FALSE
573
574        do this before hiding the windows so if helper windows are coming
575        with us, they don't get hidden
576     */
577     if (dofocus && (c = focus_fallback(TRUE, !config_focus_last)))
578     {
579         /* only do the flicker reducing stuff ahead of time if we are going
580            to call xsetinputfocus on the window ourselves. otherwise there is
581            no guarantee the window will actually take focus.. */
582         if (c->can_focus) {
583             /* reduce flicker by hiliting now rather than waiting for the
584                server FocusIn event */
585             frame_adjust_focus(c->frame, TRUE);
586             /* do this here so that if you switch desktops to a window with
587                helper windows then the helper windows won't flash */
588             client_bring_helper_windows(c);
589         }
590     }
591
592     /* hide windows from bottom to top */
593     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
594         if (WINDOW_IS_CLIENT(it->data)) {
595             ObClient *c = it->data;
596             client_hide(c);
597         }
598     }
599
600     event_ignore_all_queued_enters();
601
602     if (event_curtime != CurrentTime)
603         screen_desktop_user_time = event_curtime;
604 }
605
606 static void get_row_col(guint d, guint *r, guint *c)
607 {
608     switch (screen_desktop_layout.orientation) {
609     case OB_ORIENTATION_HORZ:
610         switch (screen_desktop_layout.start_corner) {
611         case OB_CORNER_TOPLEFT:
612             *r = d / screen_desktop_layout.columns;
613             *c = d % screen_desktop_layout.columns;
614             break;
615         case OB_CORNER_BOTTOMLEFT:
616             *r = screen_desktop_layout.rows - 1 -
617                 d / screen_desktop_layout.columns;
618             *c = d % screen_desktop_layout.columns;
619             break;
620         case OB_CORNER_TOPRIGHT:
621             *r = d / screen_desktop_layout.columns;
622             *c = screen_desktop_layout.columns - 1 -
623                 d % screen_desktop_layout.columns;
624             break;
625         case OB_CORNER_BOTTOMRIGHT:
626             *r = screen_desktop_layout.rows - 1 -
627                 d / screen_desktop_layout.columns;
628             *c = screen_desktop_layout.columns - 1 -
629                 d % screen_desktop_layout.columns;
630             break;
631         }
632         break;
633     case OB_ORIENTATION_VERT:
634         switch (screen_desktop_layout.start_corner) {
635         case OB_CORNER_TOPLEFT:
636             *r = d % screen_desktop_layout.rows;
637             *c = d / screen_desktop_layout.rows;
638             break;
639         case OB_CORNER_BOTTOMLEFT:
640             *r = screen_desktop_layout.rows - 1 -
641                 d % screen_desktop_layout.rows;
642             *c = d / screen_desktop_layout.rows;
643             break;
644         case OB_CORNER_TOPRIGHT:
645             *r = d % screen_desktop_layout.rows;
646             *c = screen_desktop_layout.columns - 1 -
647                 d / screen_desktop_layout.rows;
648             break;
649         case OB_CORNER_BOTTOMRIGHT:
650             *r = screen_desktop_layout.rows - 1 -
651                 d % screen_desktop_layout.rows;
652             *c = screen_desktop_layout.columns - 1 -
653                 d / screen_desktop_layout.rows;
654             break;
655         }
656         break;
657     }
658 }
659
660 static guint translate_row_col(guint r, guint c)
661 {
662     switch (screen_desktop_layout.orientation) {
663     case OB_ORIENTATION_HORZ:
664         switch (screen_desktop_layout.start_corner) {
665         case OB_CORNER_TOPLEFT:
666             return r % screen_desktop_layout.rows *
667                 screen_desktop_layout.columns +
668                 c % screen_desktop_layout.columns;
669         case OB_CORNER_BOTTOMLEFT:
670             return (screen_desktop_layout.rows - 1 -
671                     r % screen_desktop_layout.rows) *
672                 screen_desktop_layout.columns +
673                 c % screen_desktop_layout.columns;
674         case OB_CORNER_TOPRIGHT:
675             return r % screen_desktop_layout.rows *
676                 screen_desktop_layout.columns +
677                 (screen_desktop_layout.columns - 1 -
678                  c % screen_desktop_layout.columns);
679         case OB_CORNER_BOTTOMRIGHT:
680             return (screen_desktop_layout.rows - 1 -
681                     r % screen_desktop_layout.rows) *
682                 screen_desktop_layout.columns +
683                 (screen_desktop_layout.columns - 1 -
684                  c % screen_desktop_layout.columns);
685         }
686     case OB_ORIENTATION_VERT:
687         switch (screen_desktop_layout.start_corner) {
688         case OB_CORNER_TOPLEFT:
689             return c % screen_desktop_layout.columns *
690                 screen_desktop_layout.rows +
691                 r % screen_desktop_layout.rows;
692         case OB_CORNER_BOTTOMLEFT:
693             return c % screen_desktop_layout.columns *
694                 screen_desktop_layout.rows +
695                 (screen_desktop_layout.rows - 1 -
696                  r % screen_desktop_layout.rows);
697         case OB_CORNER_TOPRIGHT:
698             return (screen_desktop_layout.columns - 1 -
699                     c % screen_desktop_layout.columns) *
700                 screen_desktop_layout.rows +
701                 r % screen_desktop_layout.rows;
702         case OB_CORNER_BOTTOMRIGHT:
703             return (screen_desktop_layout.columns - 1 -
704                     c % screen_desktop_layout.columns) *
705                 screen_desktop_layout.rows +
706                 (screen_desktop_layout.rows - 1 -
707                  r % screen_desktop_layout.rows);
708         }
709     }
710     g_assert_not_reached();
711     return 0;
712 }
713
714 void screen_desktop_popup(guint d, gboolean show)
715 {
716     Rect *a;
717
718     if (!show) {
719         pager_popup_hide(desktop_cycle_popup);
720     } else {
721         a = screen_physical_area_monitor(0);
722         pager_popup_position(desktop_cycle_popup, CenterGravity,
723                              a->x + a->width / 2, a->y + a->height / 2);
724         pager_popup_icon_size_multiplier(desktop_cycle_popup,
725                                          (screen_desktop_layout.columns /
726                                           screen_desktop_layout.rows) / 2,
727                                          (screen_desktop_layout.rows/
728                                           screen_desktop_layout.columns) / 2);
729         pager_popup_max_width(desktop_cycle_popup,
730                               MAX(a->width/3, POPUP_WIDTH));
731         pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d);
732     }
733 }
734
735 guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
736                            gboolean dialog, gboolean done, gboolean cancel)
737 {
738     guint r, c;
739     static guint d = (guint)-1;
740     guint ret, oldd;
741
742     if (d == (guint)-1)
743         d = screen_desktop;
744
745     if ((cancel || done) && dialog)
746         goto show_cycle_dialog;
747
748     oldd = d;
749     get_row_col(d, &r, &c);
750
751     if (linear) {
752         switch (dir) {
753         case OB_DIRECTION_EAST:
754             if (d < screen_num_desktops - 1)
755                 ++d;
756             else if (wrap)
757                 d = 0;
758             break;
759         case OB_DIRECTION_WEST:
760             if (d > 0)
761                 --d;
762             else if (wrap)
763                 d = screen_num_desktops - 1;
764             break;
765         default:
766             assert(0);
767             return screen_desktop;
768         }
769     } else {
770         switch (dir) {
771         case OB_DIRECTION_EAST:
772             ++c;
773             if (c >= screen_desktop_layout.columns) {
774                 if (wrap)
775                     c = 0;
776                 else
777                     goto show_cycle_dialog;
778             }
779             d = translate_row_col(r, c);
780             if (d >= screen_num_desktops) {
781                 if (wrap) {
782                     ++c;
783                 } else {
784                     d = oldd;
785                     goto show_cycle_dialog;
786                 }
787             }
788             break;
789         case OB_DIRECTION_WEST:
790             --c;
791             if (c >= screen_desktop_layout.columns) {
792                 if (wrap)
793                     c = screen_desktop_layout.columns - 1;
794                 else
795                     goto show_cycle_dialog;
796             }
797             d = translate_row_col(r, c);
798             if (d >= screen_num_desktops) {
799                 if (wrap) {
800                     --c;
801                 } else {
802                     d = oldd;
803                     goto show_cycle_dialog;
804                 }
805             }
806             break;
807         case OB_DIRECTION_SOUTH:
808             ++r;
809             if (r >= screen_desktop_layout.rows) {
810                 if (wrap)
811                     r = 0;
812                 else
813                     goto show_cycle_dialog;
814             }
815             d = translate_row_col(r, c);
816             if (d >= screen_num_desktops) {
817                 if (wrap) {
818                     ++r;
819                 } else {
820                     d = oldd;
821                     goto show_cycle_dialog;
822                 }
823             }
824             break;
825         case OB_DIRECTION_NORTH:
826             --r;
827             if (r >= screen_desktop_layout.rows) {
828                 if (wrap)
829                     r = screen_desktop_layout.rows - 1;
830                 else
831                     goto show_cycle_dialog;
832             }
833             d = translate_row_col(r, c);
834             if (d >= screen_num_desktops) {
835                 if (wrap) {
836                     --r;
837                 } else {
838                     d = oldd;
839                     goto show_cycle_dialog;
840                 }
841             }
842             break;
843         default:
844             assert(0);
845             return d = screen_desktop;
846         }
847
848         d = translate_row_col(r, c);
849     }
850
851 show_cycle_dialog:
852     if (dialog && !cancel && !done) {
853         screen_desktop_popup(d, TRUE);
854     } else
855         screen_desktop_popup(0, FALSE);
856     ret = d;
857
858     if (!dialog || cancel || done)
859         d = (guint)-1;
860
861     return ret;
862 }
863
864 static gboolean screen_validate_layout(ObDesktopLayout *l)
865 {
866     if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
867         return FALSE;
868
869     /* fill in a zero rows/columns */
870     if (l->columns == 0) {
871         l->columns = screen_num_desktops / l->rows;
872         if (l->rows * l->columns < screen_num_desktops)
873             l->columns++;
874         if (l->rows * l->columns >= screen_num_desktops + l->columns)
875             l->rows--;
876     } else if (l->rows == 0) {
877         l->rows = screen_num_desktops / l->columns;
878         if (l->columns * l->rows < screen_num_desktops)
879             l->rows++;
880         if (l->columns * l->rows >= screen_num_desktops + l->rows)
881             l->columns--;
882     }
883
884     /* bounds checking */
885     if (l->orientation == OB_ORIENTATION_HORZ) {
886         l->columns = MIN(screen_num_desktops, l->columns);
887         l->rows = MIN(l->rows,
888                       (screen_num_desktops + l->columns - 1) / l->columns);
889         l->columns = screen_num_desktops / l->rows +
890             !!(screen_num_desktops % l->rows);
891     } else {
892         l->rows = MIN(screen_num_desktops, l->rows);
893         l->columns = MIN(l->columns,
894                          (screen_num_desktops + l->rows - 1) / l->rows);
895         l->rows = screen_num_desktops / l->columns +
896             !!(screen_num_desktops % l->columns);
897     }
898     return TRUE;
899 }
900
901 void screen_update_layout()
902
903 {
904     ObDesktopLayout l;
905     guint32 *data;
906     guint num;
907
908     screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
909     screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
910     screen_desktop_layout.rows = 1;
911     screen_desktop_layout.columns = screen_num_desktops;
912
913     if (PROP_GETA32(RootWindow(ob_display, ob_screen),
914                     net_desktop_layout, cardinal, &data, &num)) {
915         if (num == 3 || num == 4) {
916             
917             if (data[0] == prop_atoms.net_wm_orientation_vert)
918                 l.orientation = OB_ORIENTATION_VERT;
919             else if (data[0] == prop_atoms.net_wm_orientation_horz)
920                 l.orientation = OB_ORIENTATION_HORZ;
921             else
922                 return;
923
924             if (num < 4)
925                 l.start_corner = OB_CORNER_TOPLEFT;
926             else {
927                 if (data[3] == prop_atoms.net_wm_topleft)
928                     l.start_corner = OB_CORNER_TOPLEFT;
929                 else if (data[3] == prop_atoms.net_wm_topright)
930                     l.start_corner = OB_CORNER_TOPRIGHT;
931                 else if (data[3] == prop_atoms.net_wm_bottomright)
932                     l.start_corner = OB_CORNER_BOTTOMRIGHT;
933                 else if (data[3] == prop_atoms.net_wm_bottomleft)
934                     l.start_corner = OB_CORNER_BOTTOMLEFT;
935                 else
936                     return;
937             }
938
939             l.columns = data[1];
940             l.rows = data[2];
941
942             if (screen_validate_layout(&l))
943                 screen_desktop_layout = l;
944
945             g_free(data);
946         }
947     }
948 }
949
950 void screen_update_desktop_names()
951 {
952     guint i;
953
954     /* empty the array */
955     g_strfreev(screen_desktop_names);
956     screen_desktop_names = NULL;
957
958     if (PROP_GETSS(RootWindow(ob_display, ob_screen),
959                    net_desktop_names, utf8, &screen_desktop_names))
960         for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
961     else
962         i = 0;
963     if (i < screen_num_desktops) {
964         GSList *it;
965
966         screen_desktop_names = g_renew(gchar*, screen_desktop_names,
967                                        screen_num_desktops + 1);
968         screen_desktop_names[screen_num_desktops] = NULL;
969
970         it = g_slist_nth(config_desktops_names, i);
971
972         for (; i < screen_num_desktops; ++i) {
973             if (it && ((char*)it->data)[0]) /* not empty */
974                 /* use the names from the config file when possible */
975                 screen_desktop_names[i] = g_strdup(it->data);
976             else
977                 /* make up a nice name if it's not though */
978                 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
979                                                           i + 1);
980             if (it) it = g_slist_next(it);
981         }
982
983         /* if we changed any names, then set the root property so we can
984            all agree on the names */
985         PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,
986                    screen_desktop_names);
987     }
988
989     /* resize the pager for these names */
990     pager_popup_text_width_to_strings(desktop_cycle_popup,
991                                       screen_desktop_names,
992                                       screen_num_desktops);
993 }
994
995 void screen_show_desktop(gboolean show, ObClient *show_only)
996 {
997     GList *it;
998      
999     if (show == screen_showing_desktop) return; /* no change */
1000
1001     screen_showing_desktop = show;
1002
1003     if (show) {
1004         /* hide windows bottom to top */
1005         for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1006             if (WINDOW_IS_CLIENT(it->data)) {
1007                 ObClient *client = it->data;
1008                 client_showhide(client);
1009             }
1010         }
1011     }
1012     else {
1013         /* restore windows top to bottom */
1014         for (it = stacking_list; it; it = g_list_next(it)) {
1015             if (WINDOW_IS_CLIENT(it->data)) {
1016                 ObClient *client = it->data;
1017                 if (client_should_show(client)) {
1018                     if (!show_only || client == show_only)
1019                         client_show(client);
1020                     else
1021                         client_iconify(client, TRUE, FALSE, TRUE);
1022                 }
1023             }
1024         }
1025     }
1026
1027     if (show) {
1028         /* focus the desktop */
1029         for (it = focus_order; it; it = g_list_next(it)) {
1030             ObClient *c = it->data;
1031             if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1032                 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1033                 client_focus(it->data))
1034                 break;
1035         }
1036     }
1037     else if (!show_only) {
1038         ObClient *c;
1039
1040         if ((c = focus_fallback(TRUE, FALSE))) {
1041             /* only do the flicker reducing stuff ahead of time if we are going
1042                to call xsetinputfocus on the window ourselves. otherwise there
1043                is no guarantee the window will actually take focus.. */
1044             if (c->can_focus) {
1045                 /* reduce flicker by hiliting now rather than waiting for the
1046                    server FocusIn event */
1047                 frame_adjust_focus(c->frame, TRUE);
1048             }
1049         }
1050     }
1051
1052     show = !!show; /* make it boolean */
1053     PROP_SET32(RootWindow(ob_display, ob_screen),
1054                net_showing_desktop, cardinal, show);
1055 }
1056
1057 void screen_install_colormap(ObClient *client, gboolean install)
1058 {
1059     if (client == NULL || client->colormap == None) {
1060         if (install)
1061             XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1062         else
1063             XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
1064     } else {
1065         xerror_set_ignore(TRUE);
1066         if (install)
1067             XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1068         else
1069             XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
1070         xerror_set_ignore(FALSE);
1071     }
1072 }
1073
1074 static inline void
1075 screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
1076                            gint edge, Strut *ret)
1077 {
1078     if (s->left &&
1079         ((s->left_end <= s->left_start) ||
1080          (RECT_TOP(*monitor_area) < s->left_end &&
1081           RECT_BOTTOM(*monitor_area) > s->left_start)))
1082         ret->left = MAX(ret->left, edge);
1083 }
1084
1085 static inline void
1086 screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
1087                           gint edge, Strut *ret)
1088 {
1089     if (s->top &&
1090         ((s->top_end <= s->top_start) ||
1091          (RECT_LEFT(*monitor_area) < s->top_end &&
1092           RECT_RIGHT(*monitor_area) > s->top_start)))
1093         ret->top = MAX(ret->top, edge);
1094 }
1095
1096 static inline void
1097 screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
1098                             gint edge, Strut *ret)
1099 {
1100     if (s->right &&
1101         ((s->right_end <= s->right_start) ||
1102          (RECT_TOP(*monitor_area) < s->right_end &&
1103           RECT_BOTTOM(*monitor_area) > s->right_start)))
1104         ret->right = MAX(ret->right, edge);
1105 }
1106
1107 static inline void
1108 screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
1109                              gint edge, Strut *ret)
1110 {
1111     if (s->bottom &&
1112         ((s->bottom_end <= s->bottom_start) ||
1113          (RECT_LEFT(*monitor_area) < s->bottom_end &&
1114           RECT_RIGHT(*monitor_area) > s->bottom_start)))
1115         ret->bottom = MAX(ret->bottom, edge);
1116 }
1117
1118 void screen_update_areas()
1119 {
1120     guint i, x;
1121     gulong *dims;
1122     GList *it;
1123     gint o;
1124
1125     g_free(monitor_area);
1126     extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
1127
1128     if (area) {
1129         for (i = 0; area[i]; ++i)
1130             g_free(area[i]);
1131         g_free(area);
1132     }
1133
1134     area = g_new(Rect*, screen_num_desktops + 2);
1135     for (i = 0; i < screen_num_desktops + 1; ++i)
1136         area[i] = g_new0(Rect, screen_num_monitors + 1);
1137     area[i] = NULL;
1138      
1139     dims = g_new(gulong, 4 * screen_num_desktops);
1140
1141     for (i = 0; i < screen_num_desktops + 1; ++i) {
1142         Strut *struts;
1143         gint l, r, t, b;
1144
1145         struts = g_new0(Strut, screen_num_monitors);
1146
1147         /* calc the xinerama areas */
1148         for (x = 0; x < screen_num_monitors; ++x) {
1149             area[i][x] = monitor_area[x];
1150             if (x == 0) {
1151                 l = monitor_area[x].x;
1152                 t = monitor_area[x].y;
1153                 r = monitor_area[x].x + monitor_area[x].width - 1;
1154                 b = monitor_area[x].y + monitor_area[x].height - 1;
1155             } else {
1156                 l = MIN(l, monitor_area[x].x);
1157                 t = MIN(t, monitor_area[x].y);
1158                 r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
1159                 b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
1160             }
1161         }
1162         RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
1163
1164         /* apply the struts */
1165
1166         /* find the left-most xin heads, i do this in 2 loops :| */
1167         o = area[i][0].x;
1168         for (x = 1; x < screen_num_monitors; ++x)
1169             o = MIN(o, area[i][x].x);
1170
1171         for (x = 0; x < screen_num_monitors; ++x) {
1172             for (it = client_list; it; it = g_list_next(it)) {
1173                 ObClient *c = it->data;
1174                 screen_area_add_strut_left(&c->strut,
1175                                            &monitor_area[x],
1176                                            o + c->strut.left - area[i][x].x,
1177                                            &struts[x]);
1178             }
1179             screen_area_add_strut_left(&dock_strut,
1180                                        &monitor_area[x],
1181                                        o + dock_strut.left - area[i][x].x,
1182                                        &struts[x]);
1183
1184             area[i][x].x += struts[x].left;
1185             area[i][x].width -= struts[x].left;
1186         }
1187
1188         /* find the top-most xin heads, i do this in 2 loops :| */
1189         o = area[i][0].y;
1190         for (x = 1; x < screen_num_monitors; ++x)
1191             o = MIN(o, area[i][x].y);
1192
1193         for (x = 0; x < screen_num_monitors; ++x) {
1194             for (it = client_list; it; it = g_list_next(it)) {
1195                 ObClient *c = it->data;
1196                 screen_area_add_strut_top(&c->strut,
1197                                            &monitor_area[x],
1198                                            o + c->strut.top - area[i][x].y,
1199                                            &struts[x]);
1200             }
1201             screen_area_add_strut_top(&dock_strut,
1202                                       &monitor_area[x],
1203                                       o + dock_strut.top - area[i][x].y,
1204                                       &struts[x]);
1205
1206             area[i][x].y += struts[x].top;
1207             area[i][x].height -= struts[x].top;
1208         }
1209
1210         /* find the right-most xin heads, i do this in 2 loops :| */
1211         o = area[i][0].x + area[i][0].width - 1;
1212         for (x = 1; x < screen_num_monitors; ++x)
1213             o = MAX(o, area[i][x].x + area[i][x].width - 1);
1214
1215         for (x = 0; x < screen_num_monitors; ++x) {
1216             for (it = client_list; it; it = g_list_next(it)) {
1217                 ObClient *c = it->data;
1218                 screen_area_add_strut_right(&c->strut,
1219                                            &monitor_area[x],
1220                                            (area[i][x].x +
1221                                             area[i][x].width - 1) -
1222                                             (o - c->strut.right),
1223                                             &struts[x]);
1224             }
1225             screen_area_add_strut_right(&dock_strut,
1226                                         &monitor_area[x],
1227                                         (area[i][x].x +
1228                                          area[i][x].width - 1) -
1229                                         (o - dock_strut.right),
1230                                         &struts[x]);
1231
1232             area[i][x].width -= struts[x].right;
1233         }
1234
1235         /* find the bottom-most xin heads, i do this in 2 loops :| */
1236         o = area[i][0].y + area[i][0].height - 1;
1237         for (x = 1; x < screen_num_monitors; ++x)
1238             o = MAX(o, area[i][x].y + area[i][x].height - 1);
1239
1240         for (x = 0; x < screen_num_monitors; ++x) {
1241             for (it = client_list; it; it = g_list_next(it)) {
1242                 ObClient *c = it->data;
1243                 screen_area_add_strut_bottom(&c->strut,
1244                                              &monitor_area[x],
1245                                              (area[i][x].y +
1246                                               area[i][x].height - 1) - \
1247                                              (o - c->strut.bottom),
1248                                              &struts[x]);
1249             }
1250             screen_area_add_strut_bottom(&dock_strut,
1251                                          &monitor_area[x],
1252                                          (area[i][x].y +
1253                                           area[i][x].height - 1) - \
1254                                          (o - dock_strut.bottom),
1255                                          &struts[x]);
1256
1257             area[i][x].height -= struts[x].bottom;
1258         }
1259
1260         l = RECT_LEFT(area[i][0]);
1261         t = RECT_TOP(area[i][0]);
1262         r = RECT_RIGHT(area[i][0]);
1263         b = RECT_BOTTOM(area[i][0]);
1264         for (x = 1; x < screen_num_monitors; ++x) {
1265             l = MIN(l, RECT_LEFT(area[i][x]));
1266             t = MIN(l, RECT_TOP(area[i][x]));
1267             r = MAX(r, RECT_RIGHT(area[i][x]));
1268             b = MAX(b, RECT_BOTTOM(area[i][x]));
1269         }
1270         RECT_SET(area[i][screen_num_monitors], l, t,
1271                  r - l + 1, b - t + 1);
1272
1273         /* XXX optimize when this is run? */
1274
1275         /* the area has changed, adjust all the maximized 
1276            windows */
1277         for (it = client_list; it; it = g_list_next(it)) {
1278             ObClient *c = it->data; 
1279             if (i < screen_num_desktops) {
1280                 if (c->desktop == i)
1281                     client_reconfigure(c);
1282             } else if (c->desktop == DESKTOP_ALL)
1283                 client_reconfigure(c);
1284         }
1285         if (i < screen_num_desktops) {
1286             /* don't set these for the 'all desktops' area */
1287             dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
1288             dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
1289             dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
1290             dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
1291         }
1292
1293         g_free(struts);
1294     }
1295
1296     PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
1297                 dims, 4 * screen_num_desktops);
1298
1299     g_free(dims);
1300 }
1301
1302 Rect *screen_area(guint desktop)
1303 {
1304     return screen_area_monitor(desktop, screen_num_monitors);
1305 }
1306
1307 Rect *screen_area_monitor(guint desktop, guint head)
1308 {
1309     if (head > screen_num_monitors)
1310         return NULL;
1311     if (desktop >= screen_num_desktops) {
1312         if (desktop == DESKTOP_ALL)
1313             return &area[screen_num_desktops][head];
1314         return NULL;
1315     }
1316     return &area[desktop][head];
1317 }
1318
1319 guint screen_find_monitor(Rect *search)
1320 {
1321     guint i;
1322     guint most = 0;
1323     guint mostv = 0;
1324
1325     for (i = 0; i < screen_num_monitors; ++i) {
1326         Rect *area = screen_physical_area_monitor(i);
1327         if (RECT_INTERSECTS_RECT(*area, *search)) {
1328             Rect r;
1329             guint v;
1330
1331             RECT_SET_INTERSECTION(r, *area, *search);
1332             v = r.width * r.height;
1333
1334             if (v > mostv) {
1335                 mostv = v;
1336                 most = i;
1337             }
1338         }
1339     }
1340     return most;
1341 }
1342
1343 Rect *screen_physical_area()
1344 {
1345     return screen_physical_area_monitor(screen_num_monitors);
1346 }
1347
1348 Rect *screen_physical_area_monitor(guint head)
1349 {
1350     if (head > screen_num_monitors)
1351         return NULL;
1352     return &monitor_area[head];
1353 }
1354
1355 void screen_set_root_cursor()
1356 {
1357     if (sn_app_starting())
1358         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1359                       ob_cursor(OB_CURSOR_BUSYPOINTER));
1360     else
1361         XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1362                       ob_cursor(OB_CURSOR_POINTER));
1363 }
1364
1365 gboolean screen_pointer_pos(gint *x, gint *y)
1366 {
1367     Window w;
1368     gint i;
1369     guint u;
1370     gboolean ret;
1371
1372     ret = !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen),
1373                           &w, &w, x, y, &i, &i, &u);
1374     if (!ret) {
1375         for (i = 0; i < ScreenCount(ob_display); ++i)
1376             if (i != ob_screen)
1377                 if (XQueryPointer(ob_display, RootWindow(ob_display, i),
1378                                   &w, &w, x, y, &i, &i, &u))
1379                     break;
1380     }
1381     return ret;
1382 }