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