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.
24 #include "startupnotify.h"
25 #include "moveresize.h"
34 #include "render/render.h"
36 #include "obt/display.h"
38 #include "obt/mainloop.h"
42 # include <sys/types.h>
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)
53 static gboolean screen_validate_layout(ObDesktopLayout *l);
54 static gboolean replace_wm(void);
55 //static void screen_tell_ksplash(void);
56 static void screen_fallback_focus(void);
58 guint screen_num_desktops;
59 guint screen_num_monitors;
61 guint screen_last_desktop;
62 gboolean screen_showing_desktop;
63 ObDesktopLayout screen_desktop_layout;
64 gchar **screen_desktop_names;
65 Window screen_support_win;
66 Time screen_desktop_user_time = CurrentTime;
68 static Size screen_physical_size;
69 static guint screen_old_desktop;
70 static gboolean screen_desktop_timeout = TRUE;
71 /*! An array of desktops, holding array of areas per monitor */
72 static Rect *monitor_area = NULL;
73 /*! An array of desktops, holding an array of struts */
74 static GSList *struts_top = NULL;
75 static GSList *struts_left = NULL;
76 static GSList *struts_right = NULL;
77 static GSList *struts_bottom = NULL;
79 static ObPagerPopup *desktop_popup;
81 /*! The number of microseconds that you need to be on a desktop before it will
82 replace the remembered "last desktop" */
83 #define REMEMBER_LAST_DESKTOP_TIME 750000
85 static gboolean replace_wm(void)
89 Window current_wm_sn_owner;
92 wm_sn = g_strdup_printf("WM_S%d", ob_screen);
93 wm_sn_atom = XInternAtom(obt_display, wm_sn, FALSE);
96 current_wm_sn_owner = XGetSelectionOwner(obt_display, wm_sn_atom);
97 if (current_wm_sn_owner == screen_support_win)
98 current_wm_sn_owner = None;
99 if (current_wm_sn_owner) {
100 if (!ob_replace_wm) {
101 g_message(_("A window manager is already running on screen %d"),
105 obt_display_ignore_errors(TRUE);
107 /* We want to find out when the current selection owner dies */
108 XSelectInput(obt_display, current_wm_sn_owner, StructureNotifyMask);
109 XSync(obt_display, FALSE);
111 obt_display_ignore_errors(FALSE);
112 if (obt_display_error_occured)
113 current_wm_sn_owner = None;
116 timestamp = event_get_server_time();
118 XSetSelectionOwner(obt_display, wm_sn_atom, screen_support_win,
121 if (XGetSelectionOwner(obt_display, wm_sn_atom) != screen_support_win) {
122 g_message(_("Could not acquire window manager selection on screen %d"),
127 /* Wait for old window manager to go away */
128 if (current_wm_sn_owner) {
131 const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
133 while (wait < timeout) {
134 if (XCheckWindowEvent(obt_display, current_wm_sn_owner,
135 StructureNotifyMask, &event) &&
136 event.type == DestroyNotify)
138 g_usleep(G_USEC_PER_SEC / 10);
139 wait += G_USEC_PER_SEC / 10;
142 if (wait >= timeout) {
143 g_message(_("The WM on screen %d is not exiting"), ob_screen);
148 /* Send client message indicating that we are now the WM */
149 obt_prop_message(ob_screen, obt_root(ob_screen), OBT_PROP_ATOM(MANAGER),
150 timestamp, wm_sn_atom, screen_support_win, 0, 0,
151 SubstructureNotifyMask);
156 gboolean screen_annex(void)
158 XSetWindowAttributes attrib;
163 /* create the netwm support window */
164 attrib.override_redirect = TRUE;
165 attrib.event_mask = PropertyChangeMask;
166 screen_support_win = XCreateWindow(obt_display, obt_root(ob_screen),
168 CopyFromParent, InputOutput,
170 CWEventMask | CWOverrideRedirect,
172 XMapWindow(obt_display, screen_support_win);
173 XLowerWindow(obt_display, screen_support_win);
176 XDestroyWindow(obt_display, screen_support_win);
180 obt_display_ignore_errors(TRUE);
181 XSelectInput(obt_display, obt_root(ob_screen), ROOT_EVENTMASK);
182 obt_display_ignore_errors(FALSE);
183 if (obt_display_error_occured) {
184 g_message(_("A window manager is already running on screen %d"),
187 XDestroyWindow(obt_display, screen_support_win);
191 screen_set_root_cursor();
193 /* set the OPENBOX_PID hint */
195 OBT_PROP_SET32(obt_root(ob_screen), OPENBOX_PID, CARDINAL, pid);
197 /* set supporting window */
198 OBT_PROP_SET32(obt_root(ob_screen),
199 NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win);
201 /* set properties on the supporting window */
202 OBT_PROP_SETS(screen_support_win, NET_WM_NAME, utf8, "Openbox");
203 OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK,
204 WINDOW, screen_support_win);
206 /* set the _NET_SUPPORTED_ATOMS hint */
208 /* this is all the atoms after NET_SUPPORTED in the ObtPropAtoms enum */
209 num_support = OBT_PROP_NUM_ATOMS - OBT_PROP_NET_SUPPORTED - 1;
211 supported = g_new(gulong, num_support);
212 supported[i++] = OBT_PROP_ATOM(NET_SUPPORTING_WM_CHECK);
213 supported[i++] = OBT_PROP_ATOM(NET_WM_FULL_PLACEMENT);
214 supported[i++] = OBT_PROP_ATOM(NET_CURRENT_DESKTOP);
215 supported[i++] = OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS);
216 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_GEOMETRY);
217 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_VIEWPORT);
218 supported[i++] = OBT_PROP_ATOM(NET_ACTIVE_WINDOW);
219 supported[i++] = OBT_PROP_ATOM(NET_WORKAREA);
220 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST);
221 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST_STACKING);
222 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_NAMES);
223 supported[i++] = OBT_PROP_ATOM(NET_CLOSE_WINDOW);
224 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_LAYOUT);
225 supported[i++] = OBT_PROP_ATOM(NET_SHOWING_DESKTOP);
226 supported[i++] = OBT_PROP_ATOM(NET_WM_NAME);
227 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_NAME);
228 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_NAME);
229 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_ICON_NAME);
230 supported[i++] = OBT_PROP_ATOM(NET_WM_DESKTOP);
231 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT);
232 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL);
233 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON);
234 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY);
235 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE);
236 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP);
237 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK);
238 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR);
239 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU);
240 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY);
241 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH);
242 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG);
243 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL);
244 supported[i++] = OBT_PROP_ATOM(NET_WM_ALLOWED_ACTIONS);
245 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
246 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
247 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
248 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
249 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
250 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
251 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
252 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
253 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
254 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
255 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
256 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE);
257 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
258 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
259 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
260 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
261 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
262 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
263 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
264 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
265 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
266 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
267 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
268 supported[i++] = OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW);
269 supported[i++] = OBT_PROP_ATOM(NET_WM_MOVERESIZE);
270 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME);
272 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME_WINDOW);
274 supported[i++] = OBT_PROP_ATOM(NET_FRAME_EXTENTS);
275 supported[i++] = OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS);
276 supported[i++] = OBT_PROP_ATOM(NET_RESTACK_WINDOW);
277 supported[i++] = OBT_PROP_ATOM(NET_STARTUP_ID);
279 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
280 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER);
282 supported[i++] = OBT_PROP_ATOM(NET_WM_PID);
283 supported[i++] = OBT_PROP_ATOM(NET_WM_PING);
285 supported[i++] = OBT_PROP_ATOM(KDE_WM_CHANGE_STATE);
286 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_FRAME_STRUT);
287 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
289 supported[i++] = OBT_PROP_ATOM(OB_FOCUS);
290 supported[i++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
291 supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
292 supported[i++] = OBT_PROP_ATOM(OPENBOX_PID);
293 supported[i++] = OBT_PROP_ATOM(OB_THEME);
294 supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
295 supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
296 g_assert(i == num_support);
298 OBT_PROP_SETA32(obt_root(ob_screen),
299 NET_SUPPORTED, ATOM, supported, num_support);
302 // screen_tell_ksplash();
307 static void screen_tell_ksplash(void)
312 argv = g_new(gchar*, 6);
313 argv[0] = g_strdup("dcop");
314 argv[1] = g_strdup("ksplash");
315 argv[2] = g_strdup("ksplash");
316 argv[3] = g_strdup("upAndRunning(QString)");
317 argv[4] = g_strdup("wm started");
320 /* tell ksplash through the dcop server command line interface */
321 g_spawn_async(NULL, argv, NULL,
322 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD |
323 G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
324 NULL, NULL, NULL, NULL);
327 /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
328 hear it anyways. perhaps it is for old ksplash. or new ksplash. or
329 something. oh well. */
330 e.xclient.type = ClientMessage;
331 e.xclient.display = obt_display;
332 e.xclient.window = obt_root(ob_screen);
333 e.xclient.message_type =
334 XInternAtom(obt_display, "_KDE_SPLASH_PROGRESS", False );
335 e.xclient.format = 8;
336 strcpy(e.xclient.data.b, "wm started");
337 XSendEvent(obt_display, obt_root(ob_screen),
338 False, SubstructureNotifyMask, &e);
341 void screen_startup(gboolean reconfig)
343 gchar **names = NULL;
345 gboolean namesexist = FALSE;
347 desktop_popup = pager_popup_new();
348 pager_popup_height(desktop_popup, POPUP_HEIGHT);
351 /* update the pager popup's width */
352 pager_popup_text_width_to_strings(desktop_popup,
353 screen_desktop_names,
354 screen_num_desktops);
358 /* get the initial size */
361 /* have names already been set for the desktops? */
362 if (OBT_PROP_GETSS(obt_root(ob_screen), NET_DESKTOP_NAMES, utf8, &names)) {
367 /* if names don't exist and we have session names, set those.
368 do this stuff BEFORE setting the number of desktops, because that
369 will create default names for them
371 if (!namesexist && session_desktop_names != NULL) {
375 /* get the desktop names */
376 numnames = g_slist_length(session_desktop_names);
377 names = g_new(gchar*, numnames + 1);
378 names[numnames] = NULL;
379 for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
380 names[i] = g_strdup(it->data);
382 /* set the root window property */
383 OBT_PROP_SETSS(obt_root(ob_screen),
384 NET_DESKTOP_NAMES, utf8, (const gchar**)names);
389 /* set the number of desktops, if it's not already set.
391 this will also set the default names from the config file up for
392 desktops that don't have names yet */
393 screen_num_desktops = 0;
394 if (OBT_PROP_GET32(obt_root(ob_screen),
395 NET_NUMBER_OF_DESKTOPS, CARDINAL, &d))
397 if (d != config_desktops_num) {
398 /* TRANSLATORS: If you need to specify a different order of the
399 arguments, you can use %1$d for the first one and %2$d for the
400 second one. For example,
401 "The current session has %2$d desktops, but Openbox is configured for %1$d ..." */
402 g_warning(_("Openbox is configured for %d desktops, but the current session has %d. Overriding the Openbox configuration."),
403 config_desktops_num, d);
405 screen_set_num_desktops(d);
407 /* restore from session if possible */
408 else if (session_num_desktops)
409 screen_set_num_desktops(session_num_desktops);
411 screen_set_num_desktops(config_desktops_num);
413 screen_desktop = screen_num_desktops; /* something invalid */
414 /* start on the current desktop when a wm was already running */
415 if (OBT_PROP_GET32(obt_root(ob_screen),
416 NET_CURRENT_DESKTOP, CARDINAL, &d) &&
417 d < screen_num_desktops)
419 screen_set_desktop(d, FALSE);
420 } else if (session_desktop >= 0)
421 screen_set_desktop(MIN((guint)session_desktop,
422 screen_num_desktops), FALSE);
424 screen_set_desktop(MIN(config_screen_firstdesk,
425 screen_num_desktops) - 1, FALSE);
426 OBT_PROP_GET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, &screen_last_desktop);
427 if (screen_last_desktop < 0 || screen_last_desktop >= screen_num_desktops) {
428 screen_last_desktop = screen_desktop;
429 OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
432 /* don't start in showing-desktop mode */
433 screen_showing_desktop = FALSE;
434 OBT_PROP_SET32(obt_root(ob_screen),
435 NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop);
437 if (session_desktop_layout_present &&
438 screen_validate_layout(&session_desktop_layout))
440 screen_desktop_layout = session_desktop_layout;
443 screen_update_layout();
446 void screen_shutdown(gboolean reconfig)
448 pager_popup_free(desktop_popup);
453 XSelectInput(obt_display, obt_root(ob_screen), NoEventMask);
455 /* we're not running here no more! */
456 OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID);
458 OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED);
459 /* don't keep this mode */
460 OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP);
462 XDestroyWindow(obt_display, screen_support_win);
464 g_strfreev(screen_desktop_names);
465 screen_desktop_names = NULL;
468 void screen_resize(void)
470 static gint oldw = 0, oldh = 0;
475 w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
476 h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
478 if (w == oldw && h == oldh) return;
482 /* Set the _NET_DESKTOP_GEOMETRY hint */
483 screen_physical_size.width = geometry[0] = w;
484 screen_physical_size.height = geometry[1] = h;
485 OBT_PROP_SETA32(obt_root(ob_screen),
486 NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2);
488 if (ob_state() == OB_STATE_STARTING)
491 screen_update_areas();
494 for (it = client_list; it; it = g_list_next(it))
495 client_move_onscreen(it->data, FALSE);
498 void screen_set_num_desktops(guint num)
502 GList *it, *stacking_copy;
506 if (screen_num_desktops == num) return;
508 old = screen_num_desktops;
509 screen_num_desktops = num;
510 OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
512 /* set the viewport hint */
513 viewport = g_new0(gulong, num * 2);
514 OBT_PROP_SETA32(obt_root(ob_screen),
515 NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
518 /* the number of rows/columns will differ */
519 screen_update_layout();
521 /* move windows on desktops that will no longer exist!
522 make a copy of the list cuz we're changing it */
523 stacking_copy = g_list_copy(stacking_list);
524 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
525 if (WINDOW_IS_CLIENT(it->data)) {
526 ObClient *c = it->data;
527 if (c->desktop != DESKTOP_ALL && c->desktop >= num)
528 client_set_desktop(c, num - 1, FALSE, TRUE);
529 /* raise all the windows that are on the current desktop which
531 else if (screen_desktop == num - 1 &&
532 (c->desktop == DESKTOP_ALL ||
533 c->desktop == screen_desktop))
534 stacking_raise(CLIENT_AS_WINDOW(c));
538 /* change our struts/area to match (after moving windows) */
539 screen_update_areas();
541 /* may be some unnamed desktops that we need to fill in with names
542 (after updating the areas so the popup can resize) */
543 screen_update_desktop_names();
545 /* change our desktop if we're on one that no longer exists! */
546 if (screen_desktop >= screen_num_desktops)
547 screen_set_desktop(num - 1, TRUE);
550 static void screen_fallback_focus(void)
555 /* only allow omnipresent windows to get focus on desktop change if
556 an omnipresent window is already focused (it'll keep focus probably, but
557 maybe not depending on mouse-focus options) */
558 allow_omni = focus_client && (client_normal(focus_client) &&
559 focus_client->desktop == DESKTOP_ALL);
561 /* the client moved there already so don't move focus. prevent flicker
562 on sendtodesktop + follow */
563 if (focus_client && focus_client->desktop == screen_desktop)
566 /* have to try focus here because when you leave an empty desktop
567 there is no focus out to watch for. also, we have different rules
568 here. we always allow it to look under the mouse pointer if
569 config_focus_last is FALSE
571 do this before hiding the windows so if helper windows are coming
572 with us, they don't get hidden
574 if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
577 /* only do the flicker reducing stuff ahead of time if we are going
578 to call xsetinputfocus on the window ourselves. otherwise there is
579 no guarantee the window will actually take focus.. */
581 /* reduce flicker by hiliting now rather than waiting for the
582 server FocusIn event */
583 frame_adjust_focus(c->frame, TRUE);
584 /* do this here so that if you switch desktops to a window with
585 helper windows then the helper windows won't flash */
586 client_bring_helper_windows(c);
591 static gboolean last_desktop_func(gpointer data)
593 screen_desktop_timeout = TRUE;
594 OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
598 void screen_set_desktop(guint num, gboolean dofocus)
604 g_assert(num < screen_num_desktops);
606 previous = screen_desktop;
607 screen_desktop = num;
609 if (previous == num) return;
611 OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
613 /* This whole thing decides when/how to save the screen_last_desktop so
614 that it can be restored later if you want */
615 if (screen_desktop_timeout) {
616 /* If screen_desktop_timeout is true, then we've been on this desktop
617 long enough and we can save it as the last desktop. */
619 if (screen_last_desktop == previous)
620 /* this is the startup state only */
621 screen_old_desktop = screen_desktop;
623 /* save the "last desktop" as the "old desktop" */
624 screen_old_desktop = screen_last_desktop;
625 /* save the desktop we're coming from as the "last desktop" */
626 screen_last_desktop = previous;
630 /* If screen_desktop_timeout is false, then we just got to this desktop
631 and we are moving away again. */
633 if (screen_desktop == screen_last_desktop) {
634 /* If we are moving to the "last desktop" .. */
635 if (previous == screen_old_desktop) {
636 /* .. from the "old desktop", change the last desktop to
637 be where we are coming from */
638 screen_last_desktop = screen_old_desktop;
640 else if (screen_last_desktop == screen_old_desktop) {
641 /* .. and also to the "old desktop", change the "last
642 desktop" to be where we are coming from */
643 screen_last_desktop = previous;
646 /* .. from some other desktop, then set the "last desktop" to
647 be the saved "old desktop", i.e. where we were before the
649 screen_last_desktop = screen_old_desktop;
653 /* If we are moving to any desktop besides the "last desktop"..
654 (this is the normal case) */
655 if (screen_desktop == screen_old_desktop) {
656 /* If moving to the "old desktop", which is not the
657 "last desktop", don't save anything */
659 else if (previous == screen_old_desktop) {
660 /* If moving from the "old desktop", and not to the
661 "last desktop", don't save anything */
663 else if (screen_last_desktop == screen_old_desktop) {
664 /* If the "last desktop" is the same as "old desktop" and
665 you're not moving to the "last desktop" then save where
666 we're coming from as the "last desktop" */
667 screen_last_desktop = previous;
670 /* If the "last desktop" is different from the "old desktop"
671 and you're not moving to the "last desktop", then don't save
676 screen_desktop_timeout = FALSE;
677 obt_main_loop_timeout_remove(ob_main_loop, last_desktop_func);
678 obt_main_loop_timeout_add(ob_main_loop, REMEMBER_LAST_DESKTOP_TIME,
679 last_desktop_func, NULL, NULL, NULL);
681 ob_debug("Moving to desktop %d", num+1);
683 if (ob_state() == OB_STATE_RUNNING)
684 screen_show_desktop_popup(screen_desktop);
686 /* ignore enter events caused by the move */
687 ignore_start = event_start_ignore_all_enters();
689 if (moveresize_client)
690 client_set_desktop(moveresize_client, num, TRUE, FALSE);
692 /* show windows before hiding the rest to lessen the enter/leave events */
694 /* show windows from top to bottom */
695 for (it = stacking_list; it; it = g_list_next(it)) {
696 if (WINDOW_IS_CLIENT(it->data)) {
697 ObClient *c = it->data;
702 if (dofocus) screen_fallback_focus();
704 /* hide windows from bottom to top */
705 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
706 if (WINDOW_IS_CLIENT(it->data)) {
707 ObClient *c = it->data;
712 event_end_ignore_all_enters(ignore_start);
714 if (event_curtime != CurrentTime)
715 screen_desktop_user_time = event_curtime;
718 void screen_add_desktop(gboolean current)
722 /* ignore enter events caused by this */
723 ignore_start = event_start_ignore_all_enters();
725 screen_set_num_desktops(screen_num_desktops+1);
727 /* move all the clients over */
731 for (it = client_list; it; it = g_list_next(it)) {
732 ObClient *c = it->data;
733 if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
734 /* don't move direct children, they'll be moved with their
735 parent - which will have to be on the same desktop */
736 !client_direct_parent(c))
738 ob_debug("moving window %s", c->title);
739 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
744 event_end_ignore_all_enters(ignore_start);
747 void screen_remove_desktop(gboolean current)
749 guint rmdesktop, movedesktop;
750 GList *it, *stacking_copy;
753 if (screen_num_desktops <= 1) return;
755 /* ignore enter events caused by this */
756 ignore_start = event_start_ignore_all_enters();
758 /* what desktop are we removing and moving to? */
760 rmdesktop = screen_desktop;
762 rmdesktop = screen_num_desktops - 1;
763 if (rmdesktop < screen_num_desktops - 1)
764 movedesktop = rmdesktop + 1;
766 movedesktop = rmdesktop;
768 /* make a copy of the list cuz we're changing it */
769 stacking_copy = g_list_copy(stacking_list);
770 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
771 if (WINDOW_IS_CLIENT(it->data)) {
772 ObClient *c = it->data;
773 guint d = c->desktop;
774 if (d != DESKTOP_ALL && d >= movedesktop &&
775 /* don't move direct children, they'll be moved with their
776 parent - which will have to be on the same desktop */
777 !client_direct_parent(c))
779 ob_debug("moving window %s", c->title);
780 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
782 /* raise all the windows that are on the current desktop which
784 if ((screen_desktop == rmdesktop - 1 ||
785 screen_desktop == rmdesktop) &&
786 (d == DESKTOP_ALL || d == screen_desktop))
788 stacking_raise(CLIENT_AS_WINDOW(c));
789 ob_debug("raising window %s", c->title);
794 /* fallback focus like we're changing desktops */
795 if (screen_desktop < screen_num_desktops - 1) {
796 screen_fallback_focus();
797 ob_debug("fake desktop change");
800 screen_set_num_desktops(screen_num_desktops-1);
802 event_end_ignore_all_enters(ignore_start);
805 static void get_row_col(guint d, guint *r, guint *c)
807 switch (screen_desktop_layout.orientation) {
808 case OB_ORIENTATION_HORZ:
809 switch (screen_desktop_layout.start_corner) {
810 case OB_CORNER_TOPLEFT:
811 *r = d / screen_desktop_layout.columns;
812 *c = d % screen_desktop_layout.columns;
814 case OB_CORNER_BOTTOMLEFT:
815 *r = screen_desktop_layout.rows - 1 -
816 d / screen_desktop_layout.columns;
817 *c = d % screen_desktop_layout.columns;
819 case OB_CORNER_TOPRIGHT:
820 *r = d / screen_desktop_layout.columns;
821 *c = screen_desktop_layout.columns - 1 -
822 d % screen_desktop_layout.columns;
824 case OB_CORNER_BOTTOMRIGHT:
825 *r = screen_desktop_layout.rows - 1 -
826 d / screen_desktop_layout.columns;
827 *c = screen_desktop_layout.columns - 1 -
828 d % screen_desktop_layout.columns;
832 case OB_ORIENTATION_VERT:
833 switch (screen_desktop_layout.start_corner) {
834 case OB_CORNER_TOPLEFT:
835 *r = d % screen_desktop_layout.rows;
836 *c = d / screen_desktop_layout.rows;
838 case OB_CORNER_BOTTOMLEFT:
839 *r = screen_desktop_layout.rows - 1 -
840 d % screen_desktop_layout.rows;
841 *c = d / screen_desktop_layout.rows;
843 case OB_CORNER_TOPRIGHT:
844 *r = d % screen_desktop_layout.rows;
845 *c = screen_desktop_layout.columns - 1 -
846 d / screen_desktop_layout.rows;
848 case OB_CORNER_BOTTOMRIGHT:
849 *r = screen_desktop_layout.rows - 1 -
850 d % screen_desktop_layout.rows;
851 *c = screen_desktop_layout.columns - 1 -
852 d / screen_desktop_layout.rows;
859 static guint translate_row_col(guint r, guint c)
861 switch (screen_desktop_layout.orientation) {
862 case OB_ORIENTATION_HORZ:
863 switch (screen_desktop_layout.start_corner) {
864 case OB_CORNER_TOPLEFT:
865 return r % screen_desktop_layout.rows *
866 screen_desktop_layout.columns +
867 c % screen_desktop_layout.columns;
868 case OB_CORNER_BOTTOMLEFT:
869 return (screen_desktop_layout.rows - 1 -
870 r % screen_desktop_layout.rows) *
871 screen_desktop_layout.columns +
872 c % screen_desktop_layout.columns;
873 case OB_CORNER_TOPRIGHT:
874 return r % screen_desktop_layout.rows *
875 screen_desktop_layout.columns +
876 (screen_desktop_layout.columns - 1 -
877 c % screen_desktop_layout.columns);
878 case OB_CORNER_BOTTOMRIGHT:
879 return (screen_desktop_layout.rows - 1 -
880 r % screen_desktop_layout.rows) *
881 screen_desktop_layout.columns +
882 (screen_desktop_layout.columns - 1 -
883 c % screen_desktop_layout.columns);
885 case OB_ORIENTATION_VERT:
886 switch (screen_desktop_layout.start_corner) {
887 case OB_CORNER_TOPLEFT:
888 return c % screen_desktop_layout.columns *
889 screen_desktop_layout.rows +
890 r % screen_desktop_layout.rows;
891 case OB_CORNER_BOTTOMLEFT:
892 return c % screen_desktop_layout.columns *
893 screen_desktop_layout.rows +
894 (screen_desktop_layout.rows - 1 -
895 r % screen_desktop_layout.rows);
896 case OB_CORNER_TOPRIGHT:
897 return (screen_desktop_layout.columns - 1 -
898 c % screen_desktop_layout.columns) *
899 screen_desktop_layout.rows +
900 r % screen_desktop_layout.rows;
901 case OB_CORNER_BOTTOMRIGHT:
902 return (screen_desktop_layout.columns - 1 -
903 c % screen_desktop_layout.columns) *
904 screen_desktop_layout.rows +
905 (screen_desktop_layout.rows - 1 -
906 r % screen_desktop_layout.rows);
909 g_assert_not_reached();
913 static gboolean hide_desktop_popup_func(gpointer data)
915 pager_popup_hide(desktop_popup);
916 return FALSE; /* don't repeat */
919 void screen_show_desktop_popup(guint d)
923 /* 0 means don't show the popup */
924 if (!config_desktop_popup_time) return;
926 a = screen_physical_area_active();
927 pager_popup_position(desktop_popup, CenterGravity,
928 a->x + a->width / 2, a->y + a->height / 2);
929 pager_popup_icon_size_multiplier(desktop_popup,
930 (screen_desktop_layout.columns /
931 screen_desktop_layout.rows) / 2,
932 (screen_desktop_layout.rows/
933 screen_desktop_layout.columns) / 2);
934 pager_popup_max_width(desktop_popup,
935 MAX(a->width/3, POPUP_WIDTH));
936 pager_popup_show(desktop_popup, screen_desktop_names[d], d);
938 obt_main_loop_timeout_remove(ob_main_loop, hide_desktop_popup_func);
939 obt_main_loop_timeout_add(ob_main_loop, config_desktop_popup_time * 1000,
940 hide_desktop_popup_func, NULL, NULL, NULL);
944 void screen_hide_desktop_popup(void)
946 obt_main_loop_timeout_remove(ob_main_loop, hide_desktop_popup_func);
947 pager_popup_hide(desktop_popup);
950 guint screen_find_desktop(guint from, ObDirection dir,
951 gboolean wrap, gboolean linear)
957 get_row_col(d, &r, &c);
960 case OB_DIRECTION_EAST:
961 if (d < screen_num_desktops - 1)
968 case OB_DIRECTION_WEST:
972 d = screen_num_desktops - 1;
977 g_assert_not_reached();
982 case OB_DIRECTION_EAST:
984 if (c >= screen_desktop_layout.columns) {
990 d = translate_row_col(r, c);
991 if (d >= screen_num_desktops) {
998 case OB_DIRECTION_WEST:
1000 if (c >= screen_desktop_layout.columns) {
1002 c = screen_desktop_layout.columns - 1;
1006 d = translate_row_col(r, c);
1007 if (d >= screen_num_desktops) {
1014 case OB_DIRECTION_SOUTH:
1016 if (r >= screen_desktop_layout.rows) {
1022 d = translate_row_col(r, c);
1023 if (d >= screen_num_desktops) {
1030 case OB_DIRECTION_NORTH:
1032 if (r >= screen_desktop_layout.rows) {
1034 r = screen_desktop_layout.rows - 1;
1038 d = translate_row_col(r, c);
1039 if (d >= screen_num_desktops) {
1047 g_assert_not_reached();
1051 d = translate_row_col(r, c);
1056 static gboolean screen_validate_layout(ObDesktopLayout *l)
1058 if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
1061 /* fill in a zero rows/columns */
1062 if (l->columns == 0) {
1063 l->columns = screen_num_desktops / l->rows;
1064 if (l->rows * l->columns < screen_num_desktops)
1066 if (l->rows * l->columns >= screen_num_desktops + l->columns)
1068 } else if (l->rows == 0) {
1069 l->rows = screen_num_desktops / l->columns;
1070 if (l->columns * l->rows < screen_num_desktops)
1072 if (l->columns * l->rows >= screen_num_desktops + l->rows)
1076 /* bounds checking */
1077 if (l->orientation == OB_ORIENTATION_HORZ) {
1078 l->columns = MIN(screen_num_desktops, l->columns);
1079 l->rows = MIN(l->rows,
1080 (screen_num_desktops + l->columns - 1) / l->columns);
1081 l->columns = screen_num_desktops / l->rows +
1082 !!(screen_num_desktops % l->rows);
1084 l->rows = MIN(screen_num_desktops, l->rows);
1085 l->columns = MIN(l->columns,
1086 (screen_num_desktops + l->rows - 1) / l->rows);
1087 l->rows = screen_num_desktops / l->columns +
1088 !!(screen_num_desktops % l->columns);
1093 void screen_update_layout(void)
1100 screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
1101 screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
1102 screen_desktop_layout.rows = 1;
1103 screen_desktop_layout.columns = screen_num_desktops;
1105 if (OBT_PROP_GETA32(obt_root(ob_screen),
1106 NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) {
1107 if (num == 3 || num == 4) {
1109 if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT))
1110 l.orientation = OB_ORIENTATION_VERT;
1111 else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ))
1112 l.orientation = OB_ORIENTATION_HORZ;
1117 l.start_corner = OB_CORNER_TOPLEFT;
1119 if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT))
1120 l.start_corner = OB_CORNER_TOPLEFT;
1121 else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT))
1122 l.start_corner = OB_CORNER_TOPRIGHT;
1123 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT))
1124 l.start_corner = OB_CORNER_BOTTOMRIGHT;
1125 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT))
1126 l.start_corner = OB_CORNER_BOTTOMLEFT;
1131 l.columns = data[1];
1134 if (screen_validate_layout(&l))
1135 screen_desktop_layout = l;
1142 void screen_update_desktop_names(void)
1146 /* empty the array */
1147 g_strfreev(screen_desktop_names);
1148 screen_desktop_names = NULL;
1150 if (OBT_PROP_GETSS(obt_root(ob_screen),
1151 NET_DESKTOP_NAMES, utf8, &screen_desktop_names))
1152 for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
1155 if (i < screen_num_desktops) {
1158 screen_desktop_names = g_renew(gchar*, screen_desktop_names,
1159 screen_num_desktops + 1);
1160 screen_desktop_names[screen_num_desktops] = NULL;
1162 it = g_slist_nth(config_desktops_names, i);
1164 for (; i < screen_num_desktops; ++i) {
1165 if (it && ((char*)it->data)[0]) /* not empty */
1166 /* use the names from the config file when possible */
1167 screen_desktop_names[i] = g_strdup(it->data);
1169 /* make up a nice name if it's not though */
1170 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
1172 if (it) it = g_slist_next(it);
1175 /* if we changed any names, then set the root property so we can
1176 all agree on the names */
1177 OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
1178 utf8, (const gchar**)screen_desktop_names);
1181 /* resize the pager for these names */
1182 pager_popup_text_width_to_strings(desktop_popup,
1183 screen_desktop_names,
1184 screen_num_desktops);
1187 void screen_show_desktop(gboolean show, ObClient *show_only)
1191 if (show == screen_showing_desktop) return; /* no change */
1193 screen_showing_desktop = show;
1196 /* hide windows bottom to top */
1197 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1198 if (WINDOW_IS_CLIENT(it->data)) {
1199 ObClient *client = it->data;
1200 client_showhide(client);
1205 /* restore windows top to bottom */
1206 for (it = stacking_list; it; it = g_list_next(it)) {
1207 if (WINDOW_IS_CLIENT(it->data)) {
1208 ObClient *client = it->data;
1209 if (client_should_show(client)) {
1210 if (!show_only || client == show_only)
1211 client_show(client);
1213 client_iconify(client, TRUE, FALSE, TRUE);
1220 /* focus the desktop */
1221 for (it = focus_order; it; it = g_list_next(it)) {
1222 ObClient *c = it->data;
1223 if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1224 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1225 client_focus(it->data))
1229 else if (!show_only) {
1232 if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
1233 /* only do the flicker reducing stuff ahead of time if we are going
1234 to call xsetinputfocus on the window ourselves. otherwise there
1235 is no guarantee the window will actually take focus.. */
1237 /* reduce flicker by hiliting now rather than waiting for the
1238 server FocusIn event */
1239 frame_adjust_focus(c->frame, TRUE);
1244 show = !!show; /* make it boolean */
1245 OBT_PROP_SET32(obt_root(ob_screen), NET_SHOWING_DESKTOP, CARDINAL, show);
1248 void screen_install_colormap(ObClient *client, gboolean install)
1250 if (client == NULL || client->colormap == None) {
1252 XInstallColormap(obt_display, RrColormap(ob_rr_inst));
1254 XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
1256 obt_display_ignore_errors(TRUE);
1258 XInstallColormap(obt_display, client->colormap);
1260 XUninstallColormap(obt_display, client->colormap);
1261 obt_display_ignore_errors(FALSE);
1265 #define STRUT_LEFT_ON_MONITOR(s, i) \
1266 (RANGES_INTERSECT(s->left_start, s->left_end - s->left_start + 1, \
1267 monitor_area[i].y, monitor_area[i].height))
1268 #define STRUT_RIGHT_ON_MONITOR(s, i) \
1269 (RANGES_INTERSECT(s->right_start, s->right_end - s->right_start + 1, \
1270 monitor_area[i].y, monitor_area[i].height))
1271 #define STRUT_TOP_ON_MONITOR(s, i) \
1272 (RANGES_INTERSECT(s->top_start, s->top_end - s->top_start + 1, \
1273 monitor_area[i].x, monitor_area[i].width))
1274 #define STRUT_BOTTOM_ON_MONITOR(s, i) \
1275 (RANGES_INTERSECT(s->bottom_start, s->bottom_end - s->bottom_start + 1, \
1276 monitor_area[i].x, monitor_area[i].width))
1280 StrutPartial *strut;
1283 #define RESET_STRUT_LIST(sl) \
1284 (g_slist_free(sl), sl = NULL)
1286 #define ADD_STRUT_TO_LIST(sl, d, s) \
1288 ObScreenStrut *ss = g_new(ObScreenStrut, 1); \
1291 sl = g_slist_prepend(sl, ss); \
1294 #define VALIDATE_STRUTS(sl, side, max) \
1297 for (it = sl; it; it = g_slist_next(it)) { \
1298 ObScreenStrut *ss = it->data; \
1299 ss->strut->side = MIN(max, ss->strut->side); \
1303 static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
1308 if (ob_debug_xinerama) {
1309 gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1310 gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1312 *xin_areas = g_new(Rect, *nxin + 1);
1313 RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
1314 RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
1317 else if (obt_display_extension_xinerama) {
1320 XineramaScreenInfo *info = XineramaQueryScreens(obt_display, &n);
1322 *xin_areas = g_new(Rect, *nxin + 1);
1323 for (i = 0; i < *nxin; ++i)
1324 RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
1325 info[i].width, info[i].height);
1331 *xin_areas = g_new(Rect, *nxin + 1);
1332 RECT_SET((*xin_areas)[0], 0, 0,
1333 WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1334 HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1337 /* returns one extra with the total area in it */
1338 l = (*xin_areas)[0].x;
1339 t = (*xin_areas)[0].y;
1340 r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
1341 b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
1342 for (i = 1; i < *nxin; ++i) {
1343 l = MIN(l, (*xin_areas)[i].x);
1344 t = MIN(l, (*xin_areas)[i].y);
1345 r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
1346 b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
1348 RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
1351 void screen_update_areas(void)
1358 g_free(monitor_area);
1359 get_xinerama_screens(&monitor_area, &screen_num_monitors);
1361 /* set up the user-specified margins */
1362 config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1363 config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1364 config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1365 config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1366 config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
1367 config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1368 config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
1369 config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1371 dims = g_new(gulong, 4 * screen_num_desktops * screen_num_monitors);
1373 RESET_STRUT_LIST(struts_left);
1374 RESET_STRUT_LIST(struts_top);
1375 RESET_STRUT_LIST(struts_right);
1376 RESET_STRUT_LIST(struts_bottom);
1378 /* collect the struts */
1379 for (it = client_list; it; it = g_list_next(it)) {
1380 ObClient *c = it->data;
1382 ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
1384 ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
1386 ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
1387 if (c->strut.bottom)
1388 ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
1390 if (dock_strut.left)
1391 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
1393 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
1394 if (dock_strut.right)
1395 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
1396 if (dock_strut.bottom)
1397 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
1399 if (config_margins.left)
1400 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
1401 if (config_margins.top)
1402 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
1403 if (config_margins.right)
1404 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
1405 if (config_margins.bottom)
1406 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
1408 VALIDATE_STRUTS(struts_left, left,
1409 monitor_area[screen_num_monitors].width / 2);
1410 VALIDATE_STRUTS(struts_right, right,
1411 monitor_area[screen_num_monitors].width / 2);
1412 VALIDATE_STRUTS(struts_top, top,
1413 monitor_area[screen_num_monitors].height / 2);
1414 VALIDATE_STRUTS(struts_bottom, bottom,
1415 monitor_area[screen_num_monitors].height / 2);
1417 /* set up the work areas to be full screen */
1418 for (i = 0; i < screen_num_monitors; ++i)
1419 for (j = 0; j < screen_num_desktops; ++j) {
1420 dims[(i * screen_num_desktops + j) * 4+0] = monitor_area[i].x;
1421 dims[(i * screen_num_desktops + j) * 4+1] = monitor_area[i].y;
1422 dims[(i * screen_num_desktops + j) * 4+2] = monitor_area[i].width;
1423 dims[(i * screen_num_desktops + j) * 4+3] = monitor_area[i].height;
1426 /* calculate the work areas from the struts */
1427 for (i = 0; i < screen_num_monitors; ++i)
1428 for (j = 0; j < screen_num_desktops; ++j) {
1429 gint l = 0, r = 0, t = 0, b = 0;
1431 /* only add the strut to the area if it touches the monitor */
1433 for (sit = struts_left; sit; sit = g_slist_next(sit)) {
1434 ObScreenStrut *s = sit->data;
1435 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1436 STRUT_LEFT_ON_MONITOR(s->strut, i))
1437 l = MAX(l, s->strut->left);
1439 for (sit = struts_top; sit; sit = g_slist_next(sit)) {
1440 ObScreenStrut *s = sit->data;
1441 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1442 STRUT_TOP_ON_MONITOR(s->strut, i))
1443 t = MAX(t, s->strut->top);
1445 for (sit = struts_right; sit; sit = g_slist_next(sit)) {
1446 ObScreenStrut *s = sit->data;
1447 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1448 STRUT_RIGHT_ON_MONITOR(s->strut, i))
1449 r = MAX(r, s->strut->right);
1451 for (sit = struts_bottom; sit; sit = g_slist_next(sit)) {
1452 ObScreenStrut *s = sit->data;
1453 if ((s->desktop == j || s->desktop == DESKTOP_ALL) &&
1454 STRUT_BOTTOM_ON_MONITOR(s->strut, i))
1455 b = MAX(b, s->strut->bottom);
1458 /* based on these margins, set the work area for the
1460 dims[(i * screen_num_desktops + j) * 4 + 0] += l;
1461 dims[(i * screen_num_desktops + j) * 4 + 1] += t;
1462 dims[(i * screen_num_desktops + j) * 4 + 2] -= l + r;
1463 dims[(i * screen_num_desktops + j) * 4 + 3] -= t + b;
1466 /* all the work areas are not used here, only the ones for the first
1468 OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
1469 dims, 4 * screen_num_desktops);
1471 /* the area has changed, adjust all the windows if they need it */
1472 for (it = client_list; it; it = g_list_next(it))
1473 client_reconfigure(it->data, FALSE);
1479 Rect* screen_area_all_monitors(guint desktop)
1484 a = screen_area_monitor(desktop, 0);
1486 /* combine all the monitors together */
1487 for (i = 1; i < screen_num_monitors; ++i) {
1488 Rect *m = screen_area_monitor(desktop, i);
1491 l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
1492 t = MIN(RECT_TOP(*a), RECT_TOP(*m));
1493 r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
1494 b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
1496 RECT_SET(*a, l, t, r - l + 1, b - t + 1);
1505 #define STRUT_LEFT_IN_SEARCH(s, search) \
1506 (RANGES_INTERSECT(search->y, search->height, \
1507 s->left_start, s->left_end - s->left_start + 1))
1508 #define STRUT_RIGHT_IN_SEARCH(s, search) \
1509 (RANGES_INTERSECT(search->y, search->height, \
1510 s->right_start, s->right_end - s->right_start + 1))
1511 #define STRUT_TOP_IN_SEARCH(s, search) \
1512 (RANGES_INTERSECT(search->x, search->width, \
1513 s->top_start, s->top_end - s->top_start + 1))
1514 #define STRUT_BOTTOM_IN_SEARCH(s, search) \
1515 (RANGES_INTERSECT(search->x, search->width, \
1516 s->bottom_start, s->bottom_end - s->bottom_start + 1))
1518 #define STRUT_LEFT_IGNORE(s, us, search) \
1519 (head == SCREEN_AREA_ALL_MONITORS && us && \
1520 RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
1521 #define STRUT_RIGHT_IGNORE(s, us, search) \
1522 (head == SCREEN_AREA_ALL_MONITORS && us && \
1523 RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
1524 #define STRUT_TOP_IGNORE(s, us, search) \
1525 (head == SCREEN_AREA_ALL_MONITORS && us && \
1526 RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
1527 #define STRUT_BOTTOM_IGNORE(s, us, search) \
1528 (head == SCREEN_AREA_ALL_MONITORS && us && \
1529 RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
1531 Rect* screen_area(guint desktop, guint head, Rect *search)
1535 gint l, r, t, b, al, ar, at, ab;
1537 gboolean us = search != NULL; /* user provided search */
1539 g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
1540 g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
1541 head == SCREEN_AREA_ALL_MONITORS);
1542 g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
1544 /* find any struts for this monitor
1545 which will be affecting the search area.
1548 /* search everything if search is null */
1550 if (head < screen_num_monitors) search = &monitor_area[head];
1551 else search = &monitor_area[screen_num_monitors];
1553 if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
1555 /* al is "all left" meaning the furthest left you can get, l is our
1556 "working left" meaning our current strut edge which we're calculating
1559 /* only include monitors which the search area lines up with */
1560 if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
1561 al = l = RECT_RIGHT(monitor_area[screen_num_monitors]);
1562 at = t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1563 ar = r = RECT_LEFT(monitor_area[screen_num_monitors]);
1564 ab = b = RECT_TOP(monitor_area[screen_num_monitors]);
1565 for (i = 0; i < screen_num_monitors; ++i) {
1566 /* add the monitor if applicable */
1567 if (RANGES_INTERSECT(search->x, search->width,
1568 monitor_area[i].x, monitor_area[i].width))
1570 at = t = MIN(t, RECT_TOP(monitor_area[i]));
1571 ab = b = MAX(b, RECT_BOTTOM(monitor_area[i]));
1573 if (RANGES_INTERSECT(search->y, search->height,
1574 monitor_area[i].y, monitor_area[i].height))
1576 al = l = MIN(l, RECT_LEFT(monitor_area[i]));
1577 ar = r = MAX(r, RECT_RIGHT(monitor_area[i]));
1581 al = l = RECT_LEFT(monitor_area[screen_num_monitors]);
1582 at = t = RECT_TOP(monitor_area[screen_num_monitors]);
1583 ar = r = RECT_RIGHT(monitor_area[screen_num_monitors]);
1584 ab = b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1587 for (d = 0; d < screen_num_desktops; ++d) {
1588 if (d != desktop && desktop != DESKTOP_ALL) continue;
1590 for (i = 0; i < screen_num_monitors; ++i) {
1591 if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
1593 for (it = struts_left; it; it = g_slist_next(it)) {
1594 ObScreenStrut *s = it->data;
1595 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1596 STRUT_LEFT_IN_SEARCH(s->strut, search) &&
1597 !STRUT_LEFT_IGNORE(s->strut, us, search))
1598 l = MAX(l, al + s->strut->left);
1600 for (it = struts_top; it; it = g_slist_next(it)) {
1601 ObScreenStrut *s = it->data;
1602 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1603 STRUT_TOP_IN_SEARCH(s->strut, search) &&
1604 !STRUT_TOP_IGNORE(s->strut, us, search))
1605 t = MAX(t, at + s->strut->top);
1607 for (it = struts_right; it; it = g_slist_next(it)) {
1608 ObScreenStrut *s = it->data;
1609 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1610 STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
1611 !STRUT_RIGHT_IGNORE(s->strut, us, search))
1612 r = MIN(r, ar - s->strut->right);
1614 for (it = struts_bottom; it; it = g_slist_next(it)) {
1615 ObScreenStrut *s = it->data;
1616 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1617 STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
1618 !STRUT_BOTTOM_IGNORE(s->strut, us, search))
1619 b = MIN(b, ab - s->strut->bottom);
1622 /* limit to this monitor */
1624 l = MAX(l, RECT_LEFT(monitor_area[i]));
1625 t = MAX(t, RECT_TOP(monitor_area[i]));
1626 r = MIN(r, RECT_RIGHT(monitor_area[i]));
1627 b = MIN(b, RECT_BOTTOM(monitor_area[i]));
1635 a->width = r - l + 1;
1636 a->height = b - t + 1;
1640 guint screen_find_monitor(Rect *search)
1643 guint most = screen_num_monitors;
1646 for (i = 0; i < screen_num_monitors; ++i) {
1647 Rect *area = screen_physical_area_monitor(i);
1648 if (RECT_INTERSECTS_RECT(*area, *search)) {
1652 RECT_SET_INTERSECTION(r, *area, *search);
1653 v = r.width * r.height;
1665 Rect* screen_physical_area_all_monitors(void)
1667 return screen_physical_area_monitor(screen_num_monitors);
1670 Rect* screen_physical_area_monitor(guint head)
1673 g_assert(head <= screen_num_monitors);
1676 *a = monitor_area[head];
1680 gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
1682 g_assert(head <= screen_num_monitors);
1684 return RECT_INTERSECTS_RECT(monitor_area[head], *search);
1687 Rect* screen_physical_area_active(void)
1692 if (moveresize_client)
1693 a = screen_physical_area_monitor(client_monitor(focus_client));
1694 else if (focus_client)
1695 a = screen_physical_area_monitor(client_monitor(focus_client));
1698 if (screen_pointer_pos(&x, &y))
1699 RECT_SET(mon, x, y, 1, 1);
1701 RECT_SET(mon, 0, 0, 1, 1);
1702 a = screen_physical_area_monitor(screen_find_monitor(&mon));
1707 void screen_set_root_cursor(void)
1709 if (sn_app_starting())
1710 XDefineCursor(obt_display, obt_root(ob_screen),
1711 ob_cursor(OB_CURSOR_BUSY));
1713 XDefineCursor(obt_display, obt_root(ob_screen),
1714 ob_cursor(OB_CURSOR_POINTER));
1717 gboolean screen_pointer_pos(gint *x, gint *y)
1724 ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
1725 &w, &w, x, y, &i, &i, &u);
1727 for (i = 0; i < ScreenCount(obt_display); ++i)
1729 if (XQueryPointer(obt_display, obt_root(i),
1730 &w, &w, x, y, &i, &i, &u))