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