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