Add "active" and "primary" options to the <monitor> placement option for per-app...
[openbox.git] / openbox / screen.c
blobf7d39fb59990b2d768899131271a6bc03e9d12de
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 screen.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
20 #include "debug.h"
21 #include "openbox.h"
22 #include "dock.h"
23 #include "grab.h"
24 #include "startupnotify.h"
25 #include "moveresize.h"
26 #include "config.h"
27 #include "screen.h"
28 #include "client.h"
29 #include "session.h"
30 #include "frame.h"
31 #include "event.h"
32 #include "focus.h"
33 #include "focus_cycle.h"
34 #include "popup.h"
35 #include "version.h"
36 #include "obrender/render.h"
37 #include "gettext.h"
38 #include "obt/display.h"
39 #include "obt/xqueue.h"
40 #include "obt/prop.h"
42 #include <X11/Xlib.h>
43 #ifdef HAVE_UNISTD_H
44 # include <sys/types.h>
45 # include <unistd.h>
46 #endif
47 #include <assert.h>
49 /*! The event mask to grab on the root window */
50 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
51 EnterWindowMask | LeaveWindowMask | \
52 SubstructureRedirectMask | FocusChangeMask | \
53 ButtonPressMask | ButtonReleaseMask)
55 static gboolean screen_validate_layout(ObDesktopLayout *l);
56 static gboolean replace_wm(void);
57 static void screen_tell_ksplash(void);
58 static void screen_fallback_focus(void);
60 guint screen_num_desktops;
61 guint screen_num_monitors;
62 guint screen_desktop;
63 guint screen_last_desktop;
64 gboolean screen_showing_desktop;
65 ObDesktopLayout screen_desktop_layout;
66 gchar **screen_desktop_names;
67 Window screen_support_win;
68 Time screen_desktop_user_time = CurrentTime;
70 static Size screen_physical_size;
71 static guint screen_old_desktop;
72 static gboolean screen_desktop_timeout = TRUE;
73 static guint screen_desktop_timer = 0;
74 /*! An array of desktops, holding an array of areas per monitor */
75 static Rect *monitor_area = NULL;
76 /*! An array of desktops, holding an array of struts */
77 static GSList *struts_top = NULL;
78 static GSList *struts_left = NULL;
79 static GSList *struts_right = NULL;
80 static GSList *struts_bottom = NULL;
82 static ObPagerPopup *desktop_popup;
83 static guint desktop_popup_timer = 0;
84 static gboolean desktop_popup_perm;
86 /*! The number of microseconds that you need to be on a desktop before it will
87 replace the remembered "last desktop" */
88 #define REMEMBER_LAST_DESKTOP_TIME 750
90 static gboolean replace_wm(void)
92 gchar *wm_sn;
93 Atom wm_sn_atom;
94 Window current_wm_sn_owner;
95 Time timestamp;
97 wm_sn = g_strdup_printf("WM_S%d", ob_screen);
98 wm_sn_atom = XInternAtom(obt_display, wm_sn, FALSE);
99 g_free(wm_sn);
101 current_wm_sn_owner = XGetSelectionOwner(obt_display, wm_sn_atom);
102 if (current_wm_sn_owner == screen_support_win)
103 current_wm_sn_owner = None;
104 if (current_wm_sn_owner) {
105 if (!ob_replace_wm) {
106 g_message(_("A window manager is already running on screen %d"),
107 ob_screen);
108 return FALSE;
110 obt_display_ignore_errors(TRUE);
112 /* We want to find out when the current selection owner dies */
113 XSelectInput(obt_display, current_wm_sn_owner, StructureNotifyMask);
114 XSync(obt_display, FALSE);
116 obt_display_ignore_errors(FALSE);
117 if (obt_display_error_occured)
118 current_wm_sn_owner = None;
121 timestamp = event_time();
123 XSetSelectionOwner(obt_display, wm_sn_atom, screen_support_win,
124 timestamp);
126 if (XGetSelectionOwner(obt_display, wm_sn_atom) != screen_support_win) {
127 g_message(_("Could not acquire window manager selection on screen %d"),
128 ob_screen);
129 return FALSE;
132 /* Wait for old window manager to go away */
133 if (current_wm_sn_owner) {
134 gulong wait = 0;
135 const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
136 ObtXQueueWindowType wt;
138 wt.window = current_wm_sn_owner;
139 wt.type = DestroyNotify;
141 while (wait < timeout) {
142 /* Checks the local queue and incoming events for this event */
143 if (xqueue_exists_local(xqueue_match_window_type, &wt))
144 break;
145 g_usleep(G_USEC_PER_SEC / 10);
146 wait += G_USEC_PER_SEC / 10;
149 if (wait >= timeout) {
150 g_message(_("The WM on screen %d is not exiting"), ob_screen);
151 return FALSE;
155 /* Send client message indicating that we are now the WM */
156 obt_prop_message(ob_screen, obt_root(ob_screen), OBT_PROP_ATOM(MANAGER),
157 timestamp, wm_sn_atom, screen_support_win, 0, 0,
158 SubstructureNotifyMask);
160 return TRUE;
163 gboolean screen_annex(void)
165 XSetWindowAttributes attrib;
166 pid_t pid;
167 gint i, num_support;
168 gulong *supported;
170 /* create the netwm support window */
171 attrib.override_redirect = TRUE;
172 attrib.event_mask = PropertyChangeMask;
173 screen_support_win = XCreateWindow(obt_display, obt_root(ob_screen),
174 -100, -100, 1, 1, 0,
175 CopyFromParent, InputOutput,
176 CopyFromParent,
177 CWEventMask | CWOverrideRedirect,
178 &attrib);
179 XMapWindow(obt_display, screen_support_win);
180 XLowerWindow(obt_display, screen_support_win);
182 if (!replace_wm()) {
183 XDestroyWindow(obt_display, screen_support_win);
184 return FALSE;
187 obt_display_ignore_errors(TRUE);
188 XSelectInput(obt_display, obt_root(ob_screen), ROOT_EVENTMASK);
189 obt_display_ignore_errors(FALSE);
190 if (obt_display_error_occured) {
191 g_message(_("A window manager is already running on screen %d"),
192 ob_screen);
194 XDestroyWindow(obt_display, screen_support_win);
195 return FALSE;
198 screen_set_root_cursor();
200 /* set the OPENBOX_PID hint */
201 pid = getpid();
202 OBT_PROP_SET32(obt_root(ob_screen), OPENBOX_PID, CARDINAL, pid);
204 /* set supporting window */
205 OBT_PROP_SET32(obt_root(ob_screen),
206 NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win);
208 /* set properties on the supporting window */
209 OBT_PROP_SETS(screen_support_win, NET_WM_NAME, "Openbox");
210 OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK,
211 WINDOW, screen_support_win);
213 /* set the _NET_SUPPORTED_ATOMS hint */
215 /* this is all the atoms after NET_SUPPORTED in the ObtPropAtoms enum */
216 num_support = OBT_PROP_NUM_ATOMS - OBT_PROP_NET_SUPPORTED - 1;
217 i = 0;
218 supported = g_new(gulong, num_support);
219 supported[i++] = OBT_PROP_ATOM(NET_SUPPORTING_WM_CHECK);
220 supported[i++] = OBT_PROP_ATOM(NET_WM_FULL_PLACEMENT);
221 supported[i++] = OBT_PROP_ATOM(NET_CURRENT_DESKTOP);
222 supported[i++] = OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS);
223 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_GEOMETRY);
224 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_VIEWPORT);
225 supported[i++] = OBT_PROP_ATOM(NET_ACTIVE_WINDOW);
226 supported[i++] = OBT_PROP_ATOM(NET_WORKAREA);
227 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST);
228 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST_STACKING);
229 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_NAMES);
230 supported[i++] = OBT_PROP_ATOM(NET_CLOSE_WINDOW);
231 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_LAYOUT);
232 supported[i++] = OBT_PROP_ATOM(NET_SHOWING_DESKTOP);
233 supported[i++] = OBT_PROP_ATOM(NET_WM_NAME);
234 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_NAME);
235 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_NAME);
236 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_ICON_NAME);
237 supported[i++] = OBT_PROP_ATOM(NET_WM_DESKTOP);
238 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT);
239 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL);
240 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON);
241 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY);
242 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE);
243 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP);
244 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK);
245 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR);
246 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU);
247 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY);
248 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH);
249 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG);
250 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL);
251 supported[i++] = OBT_PROP_ATOM(NET_WM_ALLOWED_ACTIONS);
252 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY);
253 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
254 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
255 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
256 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
257 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
258 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
259 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
260 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
261 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
262 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
263 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
264 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE);
265 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
266 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
267 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
268 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
269 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
270 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
271 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
272 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
273 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
274 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
275 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
276 supported[i++] = OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW);
277 supported[i++] = OBT_PROP_ATOM(NET_WM_MOVERESIZE);
278 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME);
280 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME_WINDOW);
282 supported[i++] = OBT_PROP_ATOM(NET_FRAME_EXTENTS);
283 supported[i++] = OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS);
284 supported[i++] = OBT_PROP_ATOM(NET_RESTACK_WINDOW);
285 supported[i++] = OBT_PROP_ATOM(NET_STARTUP_ID);
286 #ifdef SYNC
287 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
288 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER);
289 #endif
290 supported[i++] = OBT_PROP_ATOM(NET_WM_PID);
291 supported[i++] = OBT_PROP_ATOM(NET_WM_PING);
293 supported[i++] = OBT_PROP_ATOM(KDE_WM_CHANGE_STATE);
294 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_FRAME_STRUT);
295 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
297 supported[i++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
298 supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
299 supported[i++] = OBT_PROP_ATOM(OPENBOX_PID);
300 supported[i++] = OBT_PROP_ATOM(OB_THEME);
301 supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
302 supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
303 supported[i++] = OBT_PROP_ATOM(OB_VERSION);
304 supported[i++] = OBT_PROP_ATOM(OB_APP_ROLE);
305 supported[i++] = OBT_PROP_ATOM(OB_APP_TITLE);
306 supported[i++] = OBT_PROP_ATOM(OB_APP_NAME);
307 supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS);
308 supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE);
309 g_assert(i == num_support);
311 OBT_PROP_SETA32(obt_root(ob_screen),
312 NET_SUPPORTED, ATOM, supported, num_support);
313 g_free(supported);
315 OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION,
316 OPENBOX_VERSION);
318 screen_tell_ksplash();
320 return TRUE;
323 static void screen_tell_ksplash(void)
325 XEvent e;
326 char **argv;
328 argv = g_new(gchar*, 6);
329 argv[0] = g_strdup("dcop");
330 argv[1] = g_strdup("ksplash");
331 argv[2] = g_strdup("ksplash");
332 argv[3] = g_strdup("upAndRunning(QString)");
333 argv[4] = g_strdup("wm started");
334 argv[5] = NULL;
336 /* tell ksplash through the dcop server command line interface */
337 g_spawn_async(NULL, argv, NULL,
338 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD |
339 G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
340 NULL, NULL, NULL, NULL);
341 g_strfreev(argv);
343 /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
344 hear it anyways. perhaps it is for old ksplash. or new ksplash. or
345 something. oh well. */
346 e.xclient.type = ClientMessage;
347 e.xclient.display = obt_display;
348 e.xclient.window = obt_root(ob_screen);
349 e.xclient.message_type =
350 XInternAtom(obt_display, "_KDE_SPLASH_PROGRESS", False);
351 e.xclient.format = 8;
352 strcpy(e.xclient.data.b, "wm started");
353 XSendEvent(obt_display, obt_root(ob_screen),
354 False, SubstructureNotifyMask, &e);
357 void screen_startup(gboolean reconfig)
359 gchar **names = NULL;
360 guint32 d;
361 gboolean namesexist = FALSE;
363 desktop_popup = pager_popup_new();
364 desktop_popup_perm = FALSE;
365 pager_popup_height(desktop_popup, POPUP_HEIGHT);
367 if (reconfig) {
368 /* update the pager popup's width */
369 pager_popup_text_width_to_strings(desktop_popup,
370 screen_desktop_names,
371 screen_num_desktops);
372 return;
375 /* get the initial size */
376 screen_resize();
378 /* have names already been set for the desktops? */
379 if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) {
380 g_strfreev(names);
381 namesexist = TRUE;
384 /* if names don't exist and we have session names, set those.
385 do this stuff BEFORE setting the number of desktops, because that
386 will create default names for them
388 if (!namesexist && session_desktop_names != NULL) {
389 guint i, numnames;
390 GSList *it;
392 /* get the desktop names */
393 numnames = g_slist_length(session_desktop_names);
394 names = g_new(gchar*, numnames + 1);
395 names[numnames] = NULL;
396 for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
397 names[i] = g_strdup(it->data);
399 /* set the root window property */
400 OBT_PROP_SETSS(obt_root(ob_screen),
401 NET_DESKTOP_NAMES, (const gchar*const*)names);
403 g_strfreev(names);
406 /* set the number of desktops, if it's not already set.
408 this will also set the default names from the config file up for
409 desktops that don't have names yet */
410 screen_num_desktops = 0;
411 if (OBT_PROP_GET32(obt_root(ob_screen),
412 NET_NUMBER_OF_DESKTOPS, CARDINAL, &d))
414 if (d != config_desktops_num) {
415 /* TRANSLATORS: If you need to specify a different order of the
416 arguments, you can use %1$d for the first one and %2$d for the
417 second one. For example,
418 "The current session has %2$d desktops, but Openbox is configured for %1$d ..." */
419 g_warning(ngettext("Openbox is configured for %d desktop, but the current session has %d. Overriding the Openbox configuration.", "Openbox is configured for %d desktops, but the current session has %d. Overriding the Openbox configuration.", config_desktops_num),
420 config_desktops_num, d);
422 screen_set_num_desktops(d);
424 /* restore from session if possible */
425 else if (session_num_desktops)
426 screen_set_num_desktops(session_num_desktops);
427 else
428 screen_set_num_desktops(config_desktops_num);
430 screen_desktop = screen_num_desktops; /* something invalid */
431 /* start on the current desktop when a wm was already running */
432 if (OBT_PROP_GET32(obt_root(ob_screen),
433 NET_CURRENT_DESKTOP, CARDINAL, &d) &&
434 d < screen_num_desktops)
436 screen_set_desktop(d, FALSE);
437 } else if (session_desktop >= 0)
438 screen_set_desktop(MIN((guint)session_desktop,
439 screen_num_desktops), FALSE);
440 else
441 screen_set_desktop(MIN(config_screen_firstdesk,
442 screen_num_desktops) - 1, FALSE);
443 screen_last_desktop = screen_desktop;
445 /* don't start in showing-desktop mode */
446 screen_showing_desktop = FALSE;
447 OBT_PROP_SET32(obt_root(ob_screen),
448 NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop);
450 if (session_desktop_layout_present &&
451 screen_validate_layout(&session_desktop_layout))
453 screen_desktop_layout = session_desktop_layout;
455 else
456 screen_update_layout();
459 void screen_shutdown(gboolean reconfig)
461 pager_popup_free(desktop_popup);
463 if (reconfig)
464 return;
466 XSelectInput(obt_display, obt_root(ob_screen), NoEventMask);
468 /* we're not running here no more! */
469 OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID);
470 /* not without us */
471 OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED);
472 /* don't keep this mode */
473 OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP);
475 XDestroyWindow(obt_display, screen_support_win);
477 g_strfreev(screen_desktop_names);
478 screen_desktop_names = NULL;
481 void screen_resize(void)
483 static gint oldw = 0, oldh = 0;
484 gint w, h;
485 GList *it;
486 gulong geometry[2];
488 w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
489 h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
491 if (w == oldw && h == oldh) return;
493 oldw = w; oldh = h;
495 /* Set the _NET_DESKTOP_GEOMETRY hint */
496 screen_physical_size.width = geometry[0] = w;
497 screen_physical_size.height = geometry[1] = h;
498 OBT_PROP_SETA32(obt_root(ob_screen),
499 NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2);
501 if (ob_state() != OB_STATE_RUNNING)
502 return;
504 /* this calls screen_update_areas(), which we need ! */
505 dock_configure();
507 if (oldw)
508 for (it = client_list; it; it = g_list_next(it))
509 client_move_onscreen(it->data, FALSE);
512 void screen_set_num_desktops(guint num)
514 gulong *viewport;
515 GList *it, *stacking_copy;
517 g_assert(num > 0);
519 if (screen_num_desktops == num) return;
521 screen_num_desktops = num;
522 OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
524 /* set the viewport hint */
525 viewport = g_new0(gulong, num * 2);
526 OBT_PROP_SETA32(obt_root(ob_screen),
527 NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
528 g_free(viewport);
530 /* the number of rows/columns will differ */
531 screen_update_layout();
533 /* move windows on desktops that will no longer exist!
534 make a copy of the list cuz we're changing it */
535 stacking_copy = g_list_copy(stacking_list);
536 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
537 if (WINDOW_IS_CLIENT(it->data)) {
538 ObClient *c = it->data;
539 if (c->desktop != DESKTOP_ALL && c->desktop >= num)
540 client_set_desktop(c, num - 1, FALSE, TRUE);
541 /* raise all the windows that are on the current desktop which
542 is being merged */
543 else if (screen_desktop == num - 1 &&
544 (c->desktop == DESKTOP_ALL ||
545 c->desktop == screen_desktop))
546 stacking_raise(CLIENT_AS_WINDOW(c));
549 g_list_free(stacking_copy);
551 /* change our struts/area to match (after moving windows) */
552 screen_update_areas();
554 /* may be some unnamed desktops that we need to fill in with names
555 (after updating the areas so the popup can resize) */
556 screen_update_desktop_names();
558 /* change our desktop if we're on one that no longer exists! */
559 if (screen_desktop >= screen_num_desktops)
560 screen_set_desktop(num - 1, TRUE);
563 static void screen_fallback_focus(void)
565 ObClient *c;
566 gboolean allow_omni;
568 /* only allow omnipresent windows to get focus on desktop change if
569 an omnipresent window is already focused (it'll keep focus probably, but
570 maybe not depending on mouse-focus options) */
571 allow_omni = focus_client && (client_normal(focus_client) &&
572 focus_client->desktop == DESKTOP_ALL);
574 /* the client moved there already so don't move focus. prevent flicker
575 on sendtodesktop + follow */
576 if (focus_client && focus_client->desktop == screen_desktop)
577 return;
579 /* have to try focus here because when you leave an empty desktop
580 there is no focus out to watch for. also, we have different rules
581 here. we always allow it to look under the mouse pointer if
582 config_focus_last is FALSE
584 do this before hiding the windows so if helper windows are coming
585 with us, they don't get hidden
587 if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
588 !allow_omni)))
590 /* only do the flicker reducing stuff ahead of time if we are going
591 to call xsetinputfocus on the window ourselves. otherwise there is
592 no guarantee the window will actually take focus.. */
593 if (c->can_focus) {
594 /* reduce flicker by hiliting now rather than waiting for the
595 server FocusIn event */
596 frame_adjust_focus(c->frame, TRUE);
597 /* do this here so that if you switch desktops to a window with
598 helper windows then the helper windows won't flash */
599 client_bring_helper_windows(c);
604 static gboolean last_desktop_func(gpointer data)
606 screen_desktop_timeout = TRUE;
607 screen_desktop_timer = 0;
608 return FALSE; /* don't repeat */
611 void screen_set_desktop(guint num, gboolean dofocus)
613 GList *it;
614 guint previous;
615 gulong ignore_start;
617 g_assert(num < screen_num_desktops);
619 previous = screen_desktop;
620 screen_desktop = num;
622 if (previous == num) return;
624 OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
626 /* This whole thing decides when/how to save the screen_last_desktop so
627 that it can be restored later if you want */
628 if (screen_desktop_timeout) {
629 /* If screen_desktop_timeout is true, then we've been on this desktop
630 long enough and we can save it as the last desktop. */
632 if (screen_last_desktop == previous)
633 /* this is the startup state only */
634 screen_old_desktop = screen_desktop;
635 else {
636 /* save the "last desktop" as the "old desktop" */
637 screen_old_desktop = screen_last_desktop;
638 /* save the desktop we're coming from as the "last desktop" */
639 screen_last_desktop = previous;
642 else {
643 /* If screen_desktop_timeout is false, then we just got to this desktop
644 and we are moving away again. */
646 if (screen_desktop == screen_last_desktop) {
647 /* If we are moving to the "last desktop" .. */
648 if (previous == screen_old_desktop) {
649 /* .. from the "old desktop", change the last desktop to
650 be where we are coming from */
651 screen_last_desktop = screen_old_desktop;
653 else if (screen_last_desktop == screen_old_desktop) {
654 /* .. and also to the "old desktop", change the "last
655 desktop" to be where we are coming from */
656 screen_last_desktop = previous;
658 else {
659 /* .. from some other desktop, then set the "last desktop" to
660 be the saved "old desktop", i.e. where we were before the
661 "last desktop" */
662 screen_last_desktop = screen_old_desktop;
665 else {
666 /* If we are moving to any desktop besides the "last desktop"..
667 (this is the normal case) */
668 if (screen_desktop == screen_old_desktop) {
669 /* If moving to the "old desktop", which is not the
670 "last desktop", don't save anything */
672 else if (previous == screen_old_desktop) {
673 /* If moving from the "old desktop", and not to the
674 "last desktop", don't save anything */
676 else if (screen_last_desktop == screen_old_desktop) {
677 /* If the "last desktop" is the same as "old desktop" and
678 you're not moving to the "last desktop" then save where
679 we're coming from as the "last desktop" */
680 screen_last_desktop = previous;
682 else {
683 /* If the "last desktop" is different from the "old desktop"
684 and you're not moving to the "last desktop", then don't save
685 anything */
689 screen_desktop_timeout = FALSE;
690 if (screen_desktop_timer) g_source_remove(screen_desktop_timer);
691 screen_desktop_timer = g_timeout_add(REMEMBER_LAST_DESKTOP_TIME,
692 last_desktop_func, NULL);
694 ob_debug("Moving to desktop %d", num+1);
696 if (ob_state() == OB_STATE_RUNNING)
697 screen_show_desktop_popup(screen_desktop, FALSE);
699 /* ignore enter events caused by the move */
700 ignore_start = event_start_ignore_all_enters();
702 if (moveresize_client)
703 client_set_desktop(moveresize_client, num, TRUE, FALSE);
705 /* show windows before hiding the rest to lessen the enter/leave events */
707 /* show windows from top to bottom */
708 for (it = stacking_list; it; it = g_list_next(it)) {
709 if (WINDOW_IS_CLIENT(it->data)) {
710 ObClient *c = it->data;
711 client_show(c);
715 if (dofocus) screen_fallback_focus();
717 /* hide windows from bottom to top */
718 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
719 if (WINDOW_IS_CLIENT(it->data)) {
720 ObClient *c = it->data;
721 if (client_hide(c)) {
722 if (c == focus_client) {
723 /* c was focused and we didn't do fallback clearly so make
724 sure openbox doesnt still consider the window focused.
725 this happens when using NextWindow with allDesktops,
726 since it doesnt want to move focus on desktop change,
727 but the focus is not going to stay with the current
728 window, which has now disappeared.
729 only do this if the client was actually hidden,
730 otherwise it can keep focus. */
731 focus_set_client(NULL);
737 focus_cycle_addremove(NULL, TRUE);
739 event_end_ignore_all_enters(ignore_start);
741 if (event_source_time() != CurrentTime)
742 screen_desktop_user_time = event_source_time();
745 void screen_add_desktop(gboolean current)
747 gulong ignore_start;
749 /* ignore enter events caused by this */
750 ignore_start = event_start_ignore_all_enters();
752 screen_set_num_desktops(screen_num_desktops+1);
754 /* move all the clients over */
755 if (current) {
756 GList *it;
758 for (it = client_list; it; it = g_list_next(it)) {
759 ObClient *c = it->data;
760 if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
761 /* don't move direct children, they'll be moved with their
762 parent - which will have to be on the same desktop */
763 !client_direct_parent(c))
765 ob_debug("moving window %s", c->title);
766 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
771 event_end_ignore_all_enters(ignore_start);
774 void screen_remove_desktop(gboolean current)
776 guint rmdesktop, movedesktop;
777 GList *it, *stacking_copy;
778 gulong ignore_start;
780 if (screen_num_desktops <= 1) return;
782 /* ignore enter events caused by this */
783 ignore_start = event_start_ignore_all_enters();
785 /* what desktop are we removing and moving to? */
786 if (current)
787 rmdesktop = screen_desktop;
788 else
789 rmdesktop = screen_num_desktops - 1;
790 if (rmdesktop < screen_num_desktops - 1)
791 movedesktop = rmdesktop + 1;
792 else
793 movedesktop = rmdesktop;
795 /* make a copy of the list cuz we're changing it */
796 stacking_copy = g_list_copy(stacking_list);
797 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
798 if (WINDOW_IS_CLIENT(it->data)) {
799 ObClient *c = it->data;
800 guint d = c->desktop;
801 if (d != DESKTOP_ALL && d >= movedesktop &&
802 /* don't move direct children, they'll be moved with their
803 parent - which will have to be on the same desktop */
804 !client_direct_parent(c))
806 ob_debug("moving window %s", c->title);
807 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
809 /* raise all the windows that are on the current desktop which
810 is being merged */
811 if ((screen_desktop == rmdesktop - 1 ||
812 screen_desktop == rmdesktop) &&
813 (d == DESKTOP_ALL || d == screen_desktop))
815 stacking_raise(CLIENT_AS_WINDOW(c));
816 ob_debug("raising window %s", c->title);
820 g_list_free(stacking_copy);
822 /* fallback focus like we're changing desktops */
823 if (screen_desktop < screen_num_desktops - 1) {
824 screen_fallback_focus();
825 ob_debug("fake desktop change");
828 screen_set_num_desktops(screen_num_desktops-1);
830 event_end_ignore_all_enters(ignore_start);
833 static void get_row_col(guint d, guint *r, guint *c)
835 switch (screen_desktop_layout.orientation) {
836 case OB_ORIENTATION_HORZ:
837 switch (screen_desktop_layout.start_corner) {
838 case OB_CORNER_TOPLEFT:
839 *r = d / screen_desktop_layout.columns;
840 *c = d % screen_desktop_layout.columns;
841 break;
842 case OB_CORNER_BOTTOMLEFT:
843 *r = screen_desktop_layout.rows - 1 -
844 d / screen_desktop_layout.columns;
845 *c = d % screen_desktop_layout.columns;
846 break;
847 case OB_CORNER_TOPRIGHT:
848 *r = d / screen_desktop_layout.columns;
849 *c = screen_desktop_layout.columns - 1 -
850 d % screen_desktop_layout.columns;
851 break;
852 case OB_CORNER_BOTTOMRIGHT:
853 *r = screen_desktop_layout.rows - 1 -
854 d / screen_desktop_layout.columns;
855 *c = screen_desktop_layout.columns - 1 -
856 d % screen_desktop_layout.columns;
857 break;
859 break;
860 case OB_ORIENTATION_VERT:
861 switch (screen_desktop_layout.start_corner) {
862 case OB_CORNER_TOPLEFT:
863 *r = d % screen_desktop_layout.rows;
864 *c = d / screen_desktop_layout.rows;
865 break;
866 case OB_CORNER_BOTTOMLEFT:
867 *r = screen_desktop_layout.rows - 1 -
868 d % screen_desktop_layout.rows;
869 *c = d / screen_desktop_layout.rows;
870 break;
871 case OB_CORNER_TOPRIGHT:
872 *r = d % screen_desktop_layout.rows;
873 *c = screen_desktop_layout.columns - 1 -
874 d / screen_desktop_layout.rows;
875 break;
876 case OB_CORNER_BOTTOMRIGHT:
877 *r = screen_desktop_layout.rows - 1 -
878 d % screen_desktop_layout.rows;
879 *c = screen_desktop_layout.columns - 1 -
880 d / screen_desktop_layout.rows;
881 break;
883 break;
887 static guint translate_row_col(guint r, guint c)
889 switch (screen_desktop_layout.orientation) {
890 case OB_ORIENTATION_HORZ:
891 switch (screen_desktop_layout.start_corner) {
892 case OB_CORNER_TOPLEFT:
893 return r % screen_desktop_layout.rows *
894 screen_desktop_layout.columns +
895 c % screen_desktop_layout.columns;
896 case OB_CORNER_BOTTOMLEFT:
897 return (screen_desktop_layout.rows - 1 -
898 r % screen_desktop_layout.rows) *
899 screen_desktop_layout.columns +
900 c % screen_desktop_layout.columns;
901 case OB_CORNER_TOPRIGHT:
902 return r % screen_desktop_layout.rows *
903 screen_desktop_layout.columns +
904 (screen_desktop_layout.columns - 1 -
905 c % screen_desktop_layout.columns);
906 case OB_CORNER_BOTTOMRIGHT:
907 return (screen_desktop_layout.rows - 1 -
908 r % screen_desktop_layout.rows) *
909 screen_desktop_layout.columns +
910 (screen_desktop_layout.columns - 1 -
911 c % screen_desktop_layout.columns);
913 case OB_ORIENTATION_VERT:
914 switch (screen_desktop_layout.start_corner) {
915 case OB_CORNER_TOPLEFT:
916 return c % screen_desktop_layout.columns *
917 screen_desktop_layout.rows +
918 r % screen_desktop_layout.rows;
919 case OB_CORNER_BOTTOMLEFT:
920 return c % screen_desktop_layout.columns *
921 screen_desktop_layout.rows +
922 (screen_desktop_layout.rows - 1 -
923 r % screen_desktop_layout.rows);
924 case OB_CORNER_TOPRIGHT:
925 return (screen_desktop_layout.columns - 1 -
926 c % screen_desktop_layout.columns) *
927 screen_desktop_layout.rows +
928 r % screen_desktop_layout.rows;
929 case OB_CORNER_BOTTOMRIGHT:
930 return (screen_desktop_layout.columns - 1 -
931 c % screen_desktop_layout.columns) *
932 screen_desktop_layout.rows +
933 (screen_desktop_layout.rows - 1 -
934 r % screen_desktop_layout.rows);
937 g_assert_not_reached();
938 return 0;
941 static gboolean hide_desktop_popup_func(gpointer data)
943 pager_popup_hide(desktop_popup);
944 desktop_popup_timer = 0;
945 return FALSE; /* don't repeat */
948 void screen_show_desktop_popup(guint d, gboolean perm)
950 const Rect *a;
952 /* 0 means don't show the popup */
953 if (!config_desktop_popup_time) return;
955 a = screen_physical_area_primary(FALSE);
956 pager_popup_position(desktop_popup, CenterGravity,
957 a->x + a->width / 2, a->y + a->height / 2);
958 pager_popup_icon_size_multiplier(desktop_popup,
959 (screen_desktop_layout.columns /
960 screen_desktop_layout.rows) / 2,
961 (screen_desktop_layout.rows/
962 screen_desktop_layout.columns) / 2);
963 pager_popup_max_width(desktop_popup,
964 MAX(a->width/3, POPUP_WIDTH));
965 pager_popup_show(desktop_popup, screen_desktop_names[d], d);
967 if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
968 desktop_popup_timer = 0;
969 if (!perm && !desktop_popup_perm)
970 /* only hide if its not already being show permanently */
971 desktop_popup_timer = g_timeout_add(config_desktop_popup_time,
972 hide_desktop_popup_func,
973 desktop_popup);
974 if (perm)
975 desktop_popup_perm = TRUE;
978 void screen_hide_desktop_popup(void)
980 if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
981 desktop_popup_timer = 0;
982 pager_popup_hide(desktop_popup);
983 desktop_popup_perm = FALSE;
986 guint screen_find_desktop(guint from, ObDirection dir,
987 gboolean wrap, gboolean linear)
989 guint r, c;
990 guint d;
992 d = from;
993 get_row_col(d, &r, &c);
994 if (linear) {
995 switch (dir) {
996 case OB_DIRECTION_EAST:
997 if (d < screen_num_desktops - 1)
998 ++d;
999 else if (wrap)
1000 d = 0;
1001 else
1002 return from;
1003 break;
1004 case OB_DIRECTION_WEST:
1005 if (d > 0)
1006 --d;
1007 else if (wrap)
1008 d = screen_num_desktops - 1;
1009 else
1010 return from;
1011 break;
1012 default:
1013 g_assert_not_reached();
1014 return from;
1016 } else {
1017 switch (dir) {
1018 case OB_DIRECTION_EAST:
1019 ++c;
1020 if (c >= screen_desktop_layout.columns) {
1021 if (wrap)
1022 c = 0;
1023 else
1024 return from;
1026 d = translate_row_col(r, c);
1027 if (d >= screen_num_desktops) {
1028 if (wrap)
1029 ++c;
1030 else
1031 return from;
1033 break;
1034 case OB_DIRECTION_WEST:
1035 --c;
1036 if (c >= screen_desktop_layout.columns) {
1037 if (wrap)
1038 c = screen_desktop_layout.columns - 1;
1039 else
1040 return from;
1042 d = translate_row_col(r, c);
1043 if (d >= screen_num_desktops) {
1044 if (wrap)
1045 --c;
1046 else
1047 return from;
1049 break;
1050 case OB_DIRECTION_SOUTH:
1051 ++r;
1052 if (r >= screen_desktop_layout.rows) {
1053 if (wrap)
1054 r = 0;
1055 else
1056 return from;
1058 d = translate_row_col(r, c);
1059 if (d >= screen_num_desktops) {
1060 if (wrap)
1061 ++r;
1062 else
1063 return from;
1065 break;
1066 case OB_DIRECTION_NORTH:
1067 --r;
1068 if (r >= screen_desktop_layout.rows) {
1069 if (wrap)
1070 r = screen_desktop_layout.rows - 1;
1071 else
1072 return from;
1074 d = translate_row_col(r, c);
1075 if (d >= screen_num_desktops) {
1076 if (wrap)
1077 --r;
1078 else
1079 return from;
1081 break;
1082 default:
1083 g_assert_not_reached();
1084 return from;
1087 d = translate_row_col(r, c);
1089 return d;
1092 static gboolean screen_validate_layout(ObDesktopLayout *l)
1094 if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
1095 return FALSE;
1097 /* fill in a zero rows/columns */
1098 if (l->columns == 0) {
1099 l->columns = screen_num_desktops / l->rows;
1100 if (l->rows * l->columns < screen_num_desktops)
1101 l->columns++;
1102 if (l->rows * l->columns >= screen_num_desktops + l->columns)
1103 l->rows--;
1104 } else if (l->rows == 0) {
1105 l->rows = screen_num_desktops / l->columns;
1106 if (l->columns * l->rows < screen_num_desktops)
1107 l->rows++;
1108 if (l->columns * l->rows >= screen_num_desktops + l->rows)
1109 l->columns--;
1112 /* bounds checking */
1113 if (l->orientation == OB_ORIENTATION_HORZ) {
1114 l->columns = MIN(screen_num_desktops, l->columns);
1115 l->rows = MIN(l->rows,
1116 (screen_num_desktops + l->columns - 1) / l->columns);
1117 l->columns = screen_num_desktops / l->rows +
1118 !!(screen_num_desktops % l->rows);
1119 } else {
1120 l->rows = MIN(screen_num_desktops, l->rows);
1121 l->columns = MIN(l->columns,
1122 (screen_num_desktops + l->rows - 1) / l->rows);
1123 l->rows = screen_num_desktops / l->columns +
1124 !!(screen_num_desktops % l->columns);
1126 return TRUE;
1129 void screen_update_layout(void)
1132 ObDesktopLayout l;
1133 guint32 *data;
1134 guint num;
1136 screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
1137 screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
1138 screen_desktop_layout.rows = 1;
1139 screen_desktop_layout.columns = screen_num_desktops;
1141 if (OBT_PROP_GETA32(obt_root(ob_screen),
1142 NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) {
1143 if (num == 3 || num == 4) {
1145 if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT))
1146 l.orientation = OB_ORIENTATION_VERT;
1147 else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ))
1148 l.orientation = OB_ORIENTATION_HORZ;
1149 else
1150 return;
1152 if (num < 4)
1153 l.start_corner = OB_CORNER_TOPLEFT;
1154 else {
1155 if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT))
1156 l.start_corner = OB_CORNER_TOPLEFT;
1157 else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT))
1158 l.start_corner = OB_CORNER_TOPRIGHT;
1159 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT))
1160 l.start_corner = OB_CORNER_BOTTOMRIGHT;
1161 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT))
1162 l.start_corner = OB_CORNER_BOTTOMLEFT;
1163 else
1164 return;
1167 l.columns = data[1];
1168 l.rows = data[2];
1170 if (screen_validate_layout(&l))
1171 screen_desktop_layout = l;
1173 g_free(data);
1178 void screen_update_desktop_names(void)
1180 guint i;
1182 /* empty the array */
1183 g_strfreev(screen_desktop_names);
1184 screen_desktop_names = NULL;
1186 if (OBT_PROP_GETSS(obt_root(ob_screen),
1187 NET_DESKTOP_NAMES, &screen_desktop_names))
1188 for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
1189 else
1190 i = 0;
1191 if (i < screen_num_desktops) {
1192 GSList *it;
1194 screen_desktop_names = g_renew(gchar*, screen_desktop_names,
1195 screen_num_desktops + 1);
1196 screen_desktop_names[screen_num_desktops] = NULL;
1198 it = g_slist_nth(config_desktops_names, i);
1200 for (; i < screen_num_desktops; ++i) {
1201 if (it && ((char*)it->data)[0]) /* not empty */
1202 /* use the names from the config file when possible */
1203 screen_desktop_names[i] = g_strdup(it->data);
1204 else
1205 /* make up a nice name if it's not though */
1206 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
1207 i + 1);
1208 if (it) it = g_slist_next(it);
1211 /* if we changed any names, then set the root property so we can
1212 all agree on the names */
1213 OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
1214 (const gchar*const*)screen_desktop_names);
1217 /* resize the pager for these names */
1218 pager_popup_text_width_to_strings(desktop_popup,
1219 screen_desktop_names,
1220 screen_num_desktops);
1223 void screen_show_desktop(gboolean show, ObClient *show_only)
1225 GList *it;
1227 if (show == screen_showing_desktop) return; /* no change */
1229 screen_showing_desktop = show;
1231 if (show) {
1232 /* hide windows bottom to top */
1233 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1234 if (WINDOW_IS_CLIENT(it->data)) {
1235 ObClient *client = it->data;
1236 client_showhide(client);
1240 else {
1241 /* restore windows top to bottom */
1242 for (it = stacking_list; it; it = g_list_next(it)) {
1243 if (WINDOW_IS_CLIENT(it->data)) {
1244 ObClient *client = it->data;
1245 if (client_should_show(client)) {
1246 if (!show_only || client == show_only)
1247 client_show(client);
1248 else
1249 client_iconify(client, TRUE, FALSE, TRUE);
1255 if (show) {
1256 /* focus the desktop */
1257 for (it = focus_order; it; it = g_list_next(it)) {
1258 ObClient *c = it->data;
1259 if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1260 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1261 client_focus(it->data))
1262 break;
1265 else if (!show_only) {
1266 ObClient *c;
1268 if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
1269 /* only do the flicker reducing stuff ahead of time if we are going
1270 to call xsetinputfocus on the window ourselves. otherwise there
1271 is no guarantee the window will actually take focus.. */
1272 if (c->can_focus) {
1273 /* reduce flicker by hiliting now rather than waiting for the
1274 server FocusIn event */
1275 frame_adjust_focus(c->frame, TRUE);
1280 show = !!show; /* make it boolean */
1281 OBT_PROP_SET32(obt_root(ob_screen), NET_SHOWING_DESKTOP, CARDINAL, show);
1284 void screen_install_colormap(ObClient *client, gboolean install)
1286 if (client == NULL || client->colormap == None) {
1287 if (install)
1288 XInstallColormap(obt_display, RrColormap(ob_rr_inst));
1289 else
1290 XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
1291 } else {
1292 obt_display_ignore_errors(TRUE);
1293 if (install)
1294 XInstallColormap(obt_display, client->colormap);
1295 else
1296 XUninstallColormap(obt_display, client->colormap);
1297 obt_display_ignore_errors(FALSE);
1301 typedef struct {
1302 guint desktop;
1303 StrutPartial *strut;
1304 } ObScreenStrut;
1306 #define RESET_STRUT_LIST(sl) \
1307 while (sl) { \
1308 g_slice_free(ObScreenStrut, (sl)->data); \
1309 sl = g_slist_delete_link(sl, sl); \
1312 #define ADD_STRUT_TO_LIST(sl, d, s) \
1314 ObScreenStrut *ss = g_slice_new(ObScreenStrut); \
1315 ss->desktop = d; \
1316 ss->strut = s; \
1317 sl = g_slist_prepend(sl, ss); \
1320 #define VALIDATE_STRUTS(sl, side, max) \
1322 GSList *it; \
1323 for (it = sl; it; it = g_slist_next(it)) { \
1324 ObScreenStrut *ss = it->data; \
1325 ss->strut->side = MIN(max, ss->strut->side); \
1329 static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
1331 guint i;
1332 gint n, l, r, t, b;
1333 #ifdef XINERAMA
1334 XineramaScreenInfo *info;
1335 #endif
1337 if (ob_debug_xinerama) {
1338 gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1339 gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1340 *nxin = 2;
1341 *xin_areas = g_new(Rect, *nxin + 1);
1342 RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
1343 RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
1345 #ifdef XINERAMA
1346 else if (obt_display_extension_xinerama &&
1347 (info = XineramaQueryScreens(obt_display, &n))) {
1348 *nxin = n;
1349 *xin_areas = g_new(Rect, *nxin + 1);
1350 for (i = 0; i < *nxin; ++i)
1351 RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
1352 info[i].width, info[i].height);
1353 XFree(info);
1355 #endif
1356 else {
1357 *nxin = 1;
1358 *xin_areas = g_new(Rect, *nxin + 1);
1359 RECT_SET((*xin_areas)[0], 0, 0,
1360 WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1361 HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1364 /* returns one extra with the total area in it */
1365 l = (*xin_areas)[0].x;
1366 t = (*xin_areas)[0].y;
1367 r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
1368 b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
1369 for (i = 1; i < *nxin; ++i) {
1370 l = MIN(l, (*xin_areas)[i].x);
1371 t = MIN(l, (*xin_areas)[i].y);
1372 r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
1373 b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
1375 RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
1378 void screen_update_areas(void)
1380 guint i;
1381 gulong *dims;
1382 GList *it, *onscreen;
1384 /* collect the clients that are on screen */
1385 onscreen = NULL;
1386 for (it = client_list; it; it = g_list_next(it)) {
1387 if (client_monitor(it->data) != screen_num_monitors)
1388 onscreen = g_list_prepend(onscreen, it->data);
1391 g_free(monitor_area);
1392 get_xinerama_screens(&monitor_area, &screen_num_monitors);
1394 /* set up the user-specified margins */
1395 config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1396 config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1397 config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1398 config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1399 config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
1400 config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1401 config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
1402 config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1404 RESET_STRUT_LIST(struts_left);
1405 RESET_STRUT_LIST(struts_top);
1406 RESET_STRUT_LIST(struts_right);
1407 RESET_STRUT_LIST(struts_bottom);
1409 /* collect the struts */
1410 for (it = client_list; it; it = g_list_next(it)) {
1411 ObClient *c = it->data;
1412 if (c->strut.left)
1413 ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
1414 if (c->strut.top)
1415 ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
1416 if (c->strut.right)
1417 ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
1418 if (c->strut.bottom)
1419 ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
1421 if (dock_strut.left)
1422 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
1423 if (dock_strut.top)
1424 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
1425 if (dock_strut.right)
1426 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
1427 if (dock_strut.bottom)
1428 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
1430 if (config_margins.left)
1431 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
1432 if (config_margins.top)
1433 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
1434 if (config_margins.right)
1435 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
1436 if (config_margins.bottom)
1437 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
1439 VALIDATE_STRUTS(struts_left, left,
1440 monitor_area[screen_num_monitors].width / 2);
1441 VALIDATE_STRUTS(struts_right, right,
1442 monitor_area[screen_num_monitors].width / 2);
1443 VALIDATE_STRUTS(struts_top, top,
1444 monitor_area[screen_num_monitors].height / 2);
1445 VALIDATE_STRUTS(struts_bottom, bottom,
1446 monitor_area[screen_num_monitors].height / 2);
1448 dims = g_new(gulong, 4 * screen_num_desktops);
1449 for (i = 0; i < screen_num_desktops; ++i) {
1450 Rect *area = screen_area(i, SCREEN_AREA_ALL_MONITORS, NULL);
1451 dims[i*4+0] = area->x;
1452 dims[i*4+1] = area->y;
1453 dims[i*4+2] = area->width;
1454 dims[i*4+3] = area->height;
1455 g_slice_free(Rect, area);
1458 /* set the legacy workarea hint to the union of all the monitors */
1459 OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
1460 dims, 4 * screen_num_desktops);
1462 /* the area has changed, adjust all the windows if they need it */
1463 for (it = onscreen; it; it = g_list_next(it))
1464 client_reconfigure(it->data, FALSE);
1466 g_free(dims);
1469 #if 0
1470 Rect* screen_area_all_monitors(guint desktop)
1472 guint i;
1473 Rect *a;
1475 a = screen_area_monitor(desktop, 0);
1477 /* combine all the monitors together */
1478 for (i = 1; i < screen_num_monitors; ++i) {
1479 Rect *m = screen_area_monitor(desktop, i);
1480 gint l, r, t, b;
1482 l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
1483 t = MIN(RECT_TOP(*a), RECT_TOP(*m));
1484 r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
1485 b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
1487 RECT_SET(*a, l, t, r - l + 1, b - t + 1);
1489 g_free(m);
1492 return a;
1494 #endif
1496 #define STRUT_LEFT_IN_SEARCH(s, search) \
1497 (RANGES_INTERSECT(search->y, search->height, \
1498 s->left_start, s->left_end - s->left_start + 1))
1499 #define STRUT_RIGHT_IN_SEARCH(s, search) \
1500 (RANGES_INTERSECT(search->y, search->height, \
1501 s->right_start, s->right_end - s->right_start + 1))
1502 #define STRUT_TOP_IN_SEARCH(s, search) \
1503 (RANGES_INTERSECT(search->x, search->width, \
1504 s->top_start, s->top_end - s->top_start + 1))
1505 #define STRUT_BOTTOM_IN_SEARCH(s, search) \
1506 (RANGES_INTERSECT(search->x, search->width, \
1507 s->bottom_start, s->bottom_end - s->bottom_start + 1))
1509 #define STRUT_LEFT_IGNORE(s, us, search) \
1510 (head == SCREEN_AREA_ALL_MONITORS && us && \
1511 RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
1512 #define STRUT_RIGHT_IGNORE(s, us, search) \
1513 (head == SCREEN_AREA_ALL_MONITORS && us && \
1514 RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
1515 #define STRUT_TOP_IGNORE(s, us, search) \
1516 (head == SCREEN_AREA_ALL_MONITORS && us && \
1517 RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
1518 #define STRUT_BOTTOM_IGNORE(s, us, search) \
1519 (head == SCREEN_AREA_ALL_MONITORS && us && \
1520 RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
1522 Rect* screen_area(guint desktop, guint head, Rect *search)
1524 Rect *a;
1525 GSList *it;
1526 gint l, r, t, b;
1527 guint i, d;
1528 gboolean us = search != NULL; /* user provided search */
1530 g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
1531 g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
1532 head == SCREEN_AREA_ALL_MONITORS);
1533 g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
1535 /* find any struts for this monitor
1536 which will be affecting the search area.
1539 /* search everything if search is null */
1540 if (!search) {
1541 if (head < screen_num_monitors) search = &monitor_area[head];
1542 else search = &monitor_area[screen_num_monitors];
1544 if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
1546 /* al is "all left" meaning the furthest left you can get, l is our
1547 "working left" meaning our current strut edge which we're calculating
1550 /* only include monitors which the search area lines up with */
1551 if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
1552 l = RECT_RIGHT(monitor_area[screen_num_monitors]);
1553 t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1554 r = RECT_LEFT(monitor_area[screen_num_monitors]);
1555 b = RECT_TOP(monitor_area[screen_num_monitors]);
1556 for (i = 0; i < screen_num_monitors; ++i) {
1557 /* add the monitor if applicable */
1558 if (RANGES_INTERSECT(search->x, search->width,
1559 monitor_area[i].x, monitor_area[i].width))
1561 t = MIN(t, RECT_TOP(monitor_area[i]));
1562 b = MAX(b, RECT_BOTTOM(monitor_area[i]));
1564 if (RANGES_INTERSECT(search->y, search->height,
1565 monitor_area[i].y, monitor_area[i].height))
1567 l = MIN(l, RECT_LEFT(monitor_area[i]));
1568 r = MAX(r, RECT_RIGHT(monitor_area[i]));
1571 } else {
1572 l = RECT_LEFT(monitor_area[screen_num_monitors]);
1573 t = RECT_TOP(monitor_area[screen_num_monitors]);
1574 r = RECT_RIGHT(monitor_area[screen_num_monitors]);
1575 b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1578 for (d = 0; d < screen_num_desktops; ++d) {
1579 if (d != desktop && desktop != DESKTOP_ALL) continue;
1581 for (i = 0; i < screen_num_monitors; ++i) {
1582 if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
1584 for (it = struts_left; it; it = g_slist_next(it)) {
1585 ObScreenStrut *s = it->data;
1586 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1587 STRUT_LEFT_IN_SEARCH(s->strut, search) &&
1588 !STRUT_LEFT_IGNORE(s->strut, us, search))
1589 l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors])
1590 + s->strut->left);
1592 for (it = struts_top; it; it = g_slist_next(it)) {
1593 ObScreenStrut *s = it->data;
1594 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1595 STRUT_TOP_IN_SEARCH(s->strut, search) &&
1596 !STRUT_TOP_IGNORE(s->strut, us, search))
1597 t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors])
1598 + s->strut->top);
1600 for (it = struts_right; it; it = g_slist_next(it)) {
1601 ObScreenStrut *s = it->data;
1602 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1603 STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
1604 !STRUT_RIGHT_IGNORE(s->strut, us, search))
1605 r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors])
1606 - s->strut->right);
1608 for (it = struts_bottom; it; it = g_slist_next(it)) {
1609 ObScreenStrut *s = it->data;
1610 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1611 STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
1612 !STRUT_BOTTOM_IGNORE(s->strut, us, search))
1613 b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors])
1614 - s->strut->bottom);
1617 /* limit to this monitor */
1618 if (head == i) {
1619 l = MAX(l, RECT_LEFT(monitor_area[i]));
1620 t = MAX(t, RECT_TOP(monitor_area[i]));
1621 r = MIN(r, RECT_RIGHT(monitor_area[i]));
1622 b = MIN(b, RECT_BOTTOM(monitor_area[i]));
1627 a = g_slice_new(Rect);
1628 a->x = l;
1629 a->y = t;
1630 a->width = r - l + 1;
1631 a->height = b - t + 1;
1632 return a;
1635 guint screen_find_monitor(const Rect *search)
1637 guint i;
1638 guint most = screen_num_monitors;
1639 guint mostv = 0;
1641 for (i = 0; i < screen_num_monitors; ++i) {
1642 const Rect *area = screen_physical_area_monitor(i);
1643 if (RECT_INTERSECTS_RECT(*area, *search)) {
1644 Rect r;
1645 guint v;
1647 RECT_SET_INTERSECTION(r, *area, *search);
1648 v = r.width * r.height;
1650 if (v > mostv) {
1651 mostv = v;
1652 most = i;
1656 return most < screen_num_monitors ? most : screen_monitor_primary(FALSE);
1659 const Rect* screen_physical_area_all_monitors(void)
1661 return screen_physical_area_monitor(screen_num_monitors);
1664 const Rect* screen_physical_area_monitor(guint head)
1666 g_assert(head <= screen_num_monitors);
1668 return &monitor_area[head];
1671 gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
1673 g_assert(head <= screen_num_monitors);
1674 g_assert(search);
1675 return RECT_INTERSECTS_RECT(monitor_area[head], *search);
1678 guint screen_monitor_active(void)
1680 if (moveresize_client)
1681 return client_monitor(moveresize_client);
1682 else if (focus_client)
1683 return client_monitor(focus_client);
1684 else
1685 return screen_monitor_pointer();
1688 const Rect* screen_physical_area_active(void)
1690 return screen_physical_area_monitor(screen_monitor_active());
1693 guint screen_monitor_primary(gboolean fixed)
1695 if (config_primary_monitor_index > 0) {
1696 if (config_primary_monitor_index-1 < screen_num_monitors)
1697 return config_primary_monitor_index - 1;
1698 else
1699 return 0;
1701 else if (fixed)
1702 return 0;
1703 else if (config_primary_monitor == OB_PLACE_MONITOR_ACTIVE)
1704 return screen_monitor_active();
1705 else /* config_primary_monitor == OB_PLACE_MONITOR_MOUSE */
1706 return screen_monitor_pointer();
1709 const Rect* screen_physical_area_primary(gboolean fixed)
1711 return screen_physical_area_monitor(screen_monitor_primary(fixed));
1714 void screen_set_root_cursor(void)
1716 if (sn_app_starting())
1717 XDefineCursor(obt_display, obt_root(ob_screen),
1718 ob_cursor(OB_CURSOR_BUSYPOINTER));
1719 else
1720 XDefineCursor(obt_display, obt_root(ob_screen),
1721 ob_cursor(OB_CURSOR_POINTER));
1724 guint screen_find_monitor_point(guint x, guint y)
1726 Rect mon;
1727 RECT_SET(mon, x, y, 1, 1);
1728 return screen_find_monitor(&mon);
1731 guint screen_monitor_pointer()
1733 gint x, y;
1734 if (!screen_pointer_pos(&x, &y))
1735 x = y = 0;
1736 return screen_find_monitor_point(x, y);
1739 gboolean screen_pointer_pos(gint *x, gint *y)
1741 Window w;
1742 gint i;
1743 guint u;
1744 gboolean ret;
1746 ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
1747 &w, &w, x, y, &i, &i, &u);
1748 if (!ret) {
1749 for (i = 0; i < ScreenCount(obt_display); ++i)
1750 if (i != ob_screen)
1751 if (XQueryPointer(obt_display, obt_root(i),
1752 &w, &w, x, y, &i, &i, &u))
1753 break;
1755 return ret;
1758 gboolean screen_compare_desktops(guint a, guint b)
1760 if (a == DESKTOP_ALL)
1761 a = screen_desktop;
1762 if (b == DESKTOP_ALL)
1763 b = screen_desktop;
1764 return a == b;