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