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.
25 #include "startupnotify.h"
26 #include "moveresize.h"
34 #include "focus_cycle.h"
37 #include "obrender/render.h"
39 #include "obt/display.h"
40 #include "obt/xqueue.h"
45 # include <sys/types.h>
50 /*! The event mask to grab on the root window */
51 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
52 EnterWindowMask | LeaveWindowMask | \
53 SubstructureRedirectMask | FocusChangeMask | \
54 ButtonPressMask | ButtonReleaseMask)
56 static gboolean screen_validate_layout(ObDesktopLayout *l);
57 static gboolean replace_wm(void);
58 //static void screen_tell_ksplash(void);
59 static void screen_fallback_focus(void);
61 guint screen_num_desktops;
62 guint screen_num_monitors;
64 guint screen_last_desktop;
65 ObScreenShowDestopMode screen_show_desktop_mode;
66 ObDesktopLayout screen_desktop_layout;
67 gchar **screen_desktop_names;
68 Window screen_support_win;
69 Time screen_desktop_user_time = CurrentTime;
71 static Size screen_physical_size;
72 static guint screen_old_desktop;
73 static gboolean screen_desktop_timeout = TRUE;
74 static guint screen_desktop_timer = 0;
75 /*! An array of desktops, holding an array of areas per monitor */
76 static Rect *monitor_area = NULL;
77 /*! An array of desktops, holding an array of struts */
78 static GSList *struts_top = NULL;
79 static GSList *struts_left = NULL;
80 static GSList *struts_right = NULL;
81 static GSList *struts_bottom = NULL;
83 static ObPagerPopup **desktop_popup;
84 static guint desktop_popup_timer = 0;
85 static gboolean desktop_popup_perm;
87 /*! The number of microseconds that you need to be on a desktop before it will
88 replace the remembered "last desktop" */
89 #define REMEMBER_LAST_DESKTOP_TIME 750
91 static gboolean replace_wm(void)
95 Window current_wm_sn_owner;
98 wm_sn = g_strdup_printf("WM_S%d", ob_screen);
99 wm_sn_atom = XInternAtom(obt_display, wm_sn, FALSE);
102 current_wm_sn_owner = XGetSelectionOwner(obt_display, wm_sn_atom);
103 if (current_wm_sn_owner == screen_support_win)
104 current_wm_sn_owner = None;
105 if (current_wm_sn_owner) {
106 if (!ob_replace_wm) {
107 g_message(_("A window manager is already running on screen %d"),
111 obt_display_ignore_errors(TRUE);
113 /* We want to find out when the current selection owner dies */
114 XSelectInput(obt_display, current_wm_sn_owner, StructureNotifyMask);
115 XSync(obt_display, FALSE);
117 obt_display_ignore_errors(FALSE);
118 if (obt_display_error_occured)
119 current_wm_sn_owner = None;
122 timestamp = event_time();
124 XSetSelectionOwner(obt_display, wm_sn_atom, screen_support_win,
127 if (XGetSelectionOwner(obt_display, wm_sn_atom) != screen_support_win) {
128 g_message(_("Could not acquire window manager selection on screen %d"),
133 /* Wait for old window manager to go away */
134 if (current_wm_sn_owner) {
136 const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
137 ObtXQueueWindowType wt;
139 wt.window = current_wm_sn_owner;
140 wt.type = DestroyNotify;
142 while (wait < timeout) {
143 /* Checks the local queue and incoming events for this event */
144 if (xqueue_exists_local(xqueue_match_window_type, &wt))
146 g_usleep(G_USEC_PER_SEC / 10);
147 wait += G_USEC_PER_SEC / 10;
150 if (wait >= timeout) {
151 g_message(_("The WM on screen %d is not exiting"), ob_screen);
156 /* Send client message indicating that we are now the WM */
157 obt_prop_message(ob_screen, obt_root(ob_screen), OBT_PROP_ATOM(MANAGER),
158 timestamp, wm_sn_atom, screen_support_win, 0, 0,
159 SubstructureNotifyMask);
164 gboolean screen_annex(void)
166 XSetWindowAttributes attrib;
171 /* create the netwm support window */
172 attrib.override_redirect = TRUE;
173 attrib.event_mask = PropertyChangeMask | KeyPressMask | KeyReleaseMask;
174 screen_support_win = XCreateWindow(obt_display, obt_root(ob_screen),
176 CopyFromParent, InputOutput,
178 CWEventMask | CWOverrideRedirect,
180 XMapWindow(obt_display, screen_support_win);
181 XLowerWindow(obt_display, screen_support_win);
184 XDestroyWindow(obt_display, screen_support_win);
188 obt_display_ignore_errors(TRUE);
189 XSelectInput(obt_display, obt_root(ob_screen), ROOT_EVENTMASK);
190 obt_display_ignore_errors(FALSE);
191 if (obt_display_error_occured) {
192 g_message(_("A window manager is already running on screen %d"),
195 XDestroyWindow(obt_display, screen_support_win);
199 screen_set_root_cursor();
201 /* set the OPENBOX_PID hint */
203 OBT_PROP_SET32(obt_root(ob_screen), OPENBOX_PID, CARDINAL, pid);
205 /* set supporting window */
206 OBT_PROP_SET32(obt_root(ob_screen),
207 NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win);
209 /* set properties on the supporting window */
210 OBT_PROP_SETS(screen_support_win, NET_WM_NAME, "Openbox");
211 OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK,
212 WINDOW, screen_support_win);
214 /* set the _NET_SUPPORTED_ATOMS hint */
216 /* this is all the atoms after NET_SUPPORTED in the ObtPropAtoms enum */
217 num_support = OBT_PROP_NUM_ATOMS - OBT_PROP_NET_SUPPORTED - 1;
219 supported = g_new(gulong, num_support);
220 supported[i++] = OBT_PROP_ATOM(NET_SUPPORTING_WM_CHECK);
221 supported[i++] = OBT_PROP_ATOM(NET_WM_FULL_PLACEMENT);
222 supported[i++] = OBT_PROP_ATOM(NET_CURRENT_DESKTOP);
223 supported[i++] = OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS);
224 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_GEOMETRY);
225 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_VIEWPORT);
226 supported[i++] = OBT_PROP_ATOM(NET_ACTIVE_WINDOW);
227 supported[i++] = OBT_PROP_ATOM(NET_WORKAREA);
228 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST);
229 supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST_STACKING);
230 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_NAMES);
231 supported[i++] = OBT_PROP_ATOM(NET_CLOSE_WINDOW);
232 supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_LAYOUT);
233 supported[i++] = OBT_PROP_ATOM(NET_SHOWING_DESKTOP);
234 supported[i++] = OBT_PROP_ATOM(NET_WM_NAME);
235 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_NAME);
236 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_NAME);
237 supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_ICON_NAME);
238 supported[i++] = OBT_PROP_ATOM(NET_WM_DESKTOP);
239 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT);
240 supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL);
241 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON);
242 supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY);
243 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE);
244 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP);
245 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK);
246 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR);
247 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU);
248 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY);
249 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH);
250 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG);
251 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL);
252 supported[i++] = OBT_PROP_ATOM(NET_WM_ALLOWED_ACTIONS);
253 supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY);
254 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
255 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
256 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
257 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
258 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
259 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
260 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
261 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
262 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
263 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
264 supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
265 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE);
266 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
267 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
268 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
269 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
270 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
271 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
272 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
273 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
274 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
275 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
276 supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
277 supported[i++] = OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW);
278 supported[i++] = OBT_PROP_ATOM(NET_WM_MOVERESIZE);
279 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME);
281 supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME_WINDOW);
283 supported[i++] = OBT_PROP_ATOM(NET_FRAME_EXTENTS);
284 supported[i++] = OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS);
285 supported[i++] = OBT_PROP_ATOM(NET_RESTACK_WINDOW);
286 supported[i++] = OBT_PROP_ATOM(NET_STARTUP_ID);
288 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST);
289 supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER);
291 supported[i++] = OBT_PROP_ATOM(NET_WM_PID);
292 supported[i++] = OBT_PROP_ATOM(NET_WM_PING);
294 supported[i++] = OBT_PROP_ATOM(KDE_WM_CHANGE_STATE);
295 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_FRAME_STRUT);
296 supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE);
298 supported[i++] = OBT_PROP_ATOM(OB_FOCUS);
299 supported[i++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
300 supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
301 supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_LOCKED);
302 supported[i++] = OBT_PROP_ATOM(OPENBOX_PID);
303 supported[i++] = OBT_PROP_ATOM(OB_THEME);
304 supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
305 supported[i++] = OBT_PROP_ATOM(OB_LAST_DESKTOP);
306 supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
307 supported[i++] = OBT_PROP_ATOM(OB_VERSION);
308 supported[i++] = OBT_PROP_ATOM(OB_APP_ROLE);
309 supported[i++] = OBT_PROP_ATOM(OB_APP_TITLE);
310 supported[i++] = OBT_PROP_ATOM(OB_APP_NAME);
311 supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS);
312 supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_NAME);
313 supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_CLASS);
314 supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE);
315 supported[i++] = OBT_PROP_ATOM(OB_TARGET_WINDOW);
316 g_assert(i == num_support);
318 OBT_PROP_SETA32(obt_root(ob_screen),
319 NET_SUPPORTED, ATOM, supported, num_support);
322 OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION,
325 //screen_tell_ksplash();
330 static void screen_tell_ksplash(void)
335 argv = g_new(gchar*, 6);
336 argv[0] = g_strdup("dcop");
337 argv[1] = g_strdup("ksplash");
338 argv[2] = g_strdup("ksplash");
339 argv[3] = g_strdup("upAndRunning(QString)");
340 argv[4] = g_strdup("wm started");
343 /* tell ksplash through the dcop server command line interface */
344 g_spawn_async(NULL, argv, NULL,
345 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD |
346 G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL,
347 NULL, NULL, NULL, NULL);
350 /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to
351 hear it anyways. perhaps it is for old ksplash. or new ksplash. or
352 something. oh well. */
353 e.xclient.type = ClientMessage;
354 e.xclient.display = obt_display;
355 e.xclient.window = obt_root(ob_screen);
356 e.xclient.message_type =
357 XInternAtom(obt_display, "_KDE_SPLASH_PROGRESS", False);
358 e.xclient.format = 8;
359 strcpy(e.xclient.data.b, "wm started");
360 XSendEvent(obt_display, obt_root(ob_screen),
361 False, SubstructureNotifyMask, &e);
364 void screen_startup(gboolean reconfig)
366 gchar **names = NULL;
368 gboolean namesexist = FALSE;
370 desktop_popup_perm = FALSE;
374 desktop_popup = g_new(ObPagerPopup*, screen_num_monitors);
375 for (i = 0; i < screen_num_monitors; i++) {
376 desktop_popup[i] = pager_popup_new();
377 desktop_popup[i]->popup->a_text->texture[0].data.text.font = ob_rr_theme->menu_title_font;
378 pager_popup_height(desktop_popup[i], POPUP_HEIGHT);
380 /* update the pager popup's width */
381 pager_popup_text_width_to_strings(desktop_popup[i],
382 screen_desktop_names,
383 screen_num_desktops);
388 desktop_popup = NULL;
391 /* get the initial size */
394 /* have names already been set for the desktops? */
395 if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) {
400 /* if names don't exist and we have session names, set those.
401 do this stuff BEFORE setting the number of desktops, because that
402 will create default names for them
404 if (!namesexist && session_desktop_names != NULL) {
408 /* get the desktop names */
409 numnames = g_slist_length(session_desktop_names);
410 names = g_new(gchar*, numnames + 1);
411 names[numnames] = NULL;
412 for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it))
413 names[i] = g_strdup(it->data);
415 /* set the root window property */
416 OBT_PROP_SETSS(obt_root(ob_screen),
417 NET_DESKTOP_NAMES, (const gchar*const*)names);
422 /* set the number of desktops, if it's not already set.
424 this will also set the default names from the config file up for
425 desktops that don't have names yet */
426 screen_num_desktops = 0;
427 if (OBT_PROP_GET32(obt_root(ob_screen),
428 NET_NUMBER_OF_DESKTOPS, CARDINAL, &d))
430 if (d != config_desktops_num) {
431 /* TRANSLATORS: If you need to specify a different order of the
432 arguments, you can use %1$d for the first one and %2$d for the
433 second one. For example,
434 "The current session has %2$d desktops, but Openbox is configured for %1$d ..." */
435 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),
436 config_desktops_num, d);
438 screen_set_num_desktops(d);
440 /* restore from session if possible */
441 else if (session_num_desktops)
442 screen_set_num_desktops(session_num_desktops);
444 screen_set_num_desktops(config_desktops_num);
446 screen_desktop = screen_num_desktops; /* something invalid */
447 /* start on the current desktop when a wm was already running */
448 if (OBT_PROP_GET32(obt_root(ob_screen),
449 NET_CURRENT_DESKTOP, CARDINAL, &d) &&
450 d < screen_num_desktops)
452 screen_set_desktop(d, FALSE);
453 } else if (session_desktop >= 0)
454 screen_set_desktop(MIN((guint)session_desktop,
455 screen_num_desktops), FALSE);
457 screen_set_desktop(MIN(config_screen_firstdesk,
458 screen_num_desktops) - 1, FALSE);
459 OBT_PROP_GET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, &screen_last_desktop);
460 if (screen_last_desktop < 0 || screen_last_desktop >= screen_num_desktops) {
461 screen_last_desktop = screen_desktop;
462 OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
465 /* don't start in showing-desktop mode */
466 screen_show_desktop_mode = SCREEN_SHOW_DESKTOP_NO;
467 OBT_PROP_SET32(obt_root(ob_screen),
468 NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop());
470 if (session_desktop_layout_present &&
471 screen_validate_layout(&session_desktop_layout))
473 screen_desktop_layout = session_desktop_layout;
476 screen_update_layout();
479 void screen_shutdown(gboolean reconfig)
483 for (i = 0; i < screen_num_monitors; i++) {
484 pager_popup_free(desktop_popup[i]);
486 g_free(desktop_popup);
491 XSelectInput(obt_display, obt_root(ob_screen), NoEventMask);
493 /* we're not running here no more! */
494 OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID);
496 OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED);
497 /* don't keep this mode */
498 OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP);
500 XDestroyWindow(obt_display, screen_support_win);
502 g_strfreev(screen_desktop_names);
503 screen_desktop_names = NULL;
506 void screen_resize(void)
512 w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
513 h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
515 /* Set the _NET_DESKTOP_GEOMETRY hint */
516 screen_physical_size.width = geometry[0] = w;
517 screen_physical_size.height = geometry[1] = h;
518 OBT_PROP_SETA32(obt_root(ob_screen),
519 NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2);
521 if (ob_state() != OB_STATE_RUNNING)
524 /* this calls screen_update_areas(), which we need ! */
528 // bug: this is done in screen_update_areas() already
529 // for (it = client_list; it; it = g_list_next(it)) {
530 // client_move_onscreen(it->data, FALSE);
531 // client_reconfigure(it->data, FALSE);
535 void screen_set_num_desktops(guint num)
538 GList *it, *stacking_copy;
542 if (screen_num_desktops == num) return;
544 screen_num_desktops = num;
545 OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num);
547 /* set the viewport hint */
548 viewport = g_new0(gulong, num * 2);
549 OBT_PROP_SETA32(obt_root(ob_screen),
550 NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2);
553 /* the number of rows/columns will differ */
554 screen_update_layout();
556 /* move windows on desktops that will no longer exist!
557 make a copy of the list cuz we're changing it */
558 stacking_copy = g_list_copy(stacking_list);
559 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
560 if (WINDOW_IS_CLIENT(it->data)) {
561 ObClient *c = it->data;
562 if (c->desktop != DESKTOP_ALL && c->desktop >= num)
563 client_set_desktop(c, num - 1, FALSE, TRUE);
564 /* raise all the windows that are on the current desktop which
566 else if (screen_desktop == num - 1 &&
567 (c->desktop == DESKTOP_ALL ||
568 c->desktop == screen_desktop))
569 stacking_raise(CLIENT_AS_WINDOW(c));
572 g_list_free(stacking_copy);
574 /* change our struts/area to match (after moving windows) */
575 screen_update_areas();
577 /* may be some unnamed desktops that we need to fill in with names
578 (after updating the areas so the popup can resize) */
579 screen_update_desktop_names();
581 /* change our desktop if we're on one that no longer exists! */
582 if (screen_desktop >= screen_num_desktops)
583 screen_set_desktop(num - 1, TRUE);
586 static void screen_fallback_focus(void)
591 /* only allow omnipresent windows to get focus on desktop change if
592 an omnipresent window is already focused (it'll keep focus probably, but
593 maybe not depending on mouse-focus options) */
594 allow_omni = focus_client && (client_normal(focus_client) &&
595 focus_client->desktop == DESKTOP_ALL);
597 /* the client moved there already so don't move focus. prevent flicker
598 on sendtodesktop + follow */
599 if (focus_client && focus_client->desktop == screen_desktop)
602 /* have to try focus here because when you leave an empty desktop
603 there is no focus out to watch for. also, we have different rules
604 here. we always allow it to look under the mouse pointer if
605 config_focus_last is FALSE
607 do this before hiding the windows so if helper windows are coming
608 with us, they don't get hidden
610 if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni,
613 /* only do the flicker reducing stuff ahead of time if we are going
614 to call xsetinputfocus on the window ourselves. otherwise there is
615 no guarantee the window will actually take focus.. */
617 /* reduce flicker by hiliting now rather than waiting for the
618 server FocusIn event */
619 frame_adjust_focus(c->frame, TRUE);
620 /* do this here so that if you switch desktops to a window with
621 helper windows then the helper windows won't flash */
622 client_bring_helper_windows(c);
627 static gboolean last_desktop_func(gpointer data)
629 screen_desktop_timeout = TRUE;
630 OBT_PROP_SET32(obt_root(ob_screen), OB_LAST_DESKTOP, CARDINAL, screen_last_desktop);
631 screen_desktop_timer = 0;
632 return FALSE; /* don't repeat */
635 void screen_set_desktop(guint num, gboolean dofocus)
641 g_assert(num < screen_num_desktops);
643 previous = screen_desktop;
644 screen_desktop = num;
646 if (previous == num) return;
648 OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num);
650 /* This whole thing decides when/how to save the screen_last_desktop so
651 that it can be restored later if you want */
652 if (screen_desktop_timeout) {
653 /* If screen_desktop_timeout is true, then we've been on this desktop
654 long enough and we can save it as the last desktop. */
656 if (screen_last_desktop == previous)
657 /* this is the startup state only */
658 screen_old_desktop = screen_desktop;
660 /* save the "last desktop" as the "old desktop" */
661 screen_old_desktop = screen_last_desktop;
662 /* save the desktop we're coming from as the "last desktop" */
663 screen_last_desktop = previous;
667 /* If screen_desktop_timeout is false, then we just got to this desktop
668 and we are moving away again. */
670 if (screen_desktop == screen_last_desktop) {
671 /* If we are moving to the "last desktop" .. */
672 if (previous == screen_old_desktop) {
673 /* .. from the "old desktop", change the last desktop to
674 be where we are coming from */
675 screen_last_desktop = screen_old_desktop;
677 else if (screen_last_desktop == screen_old_desktop) {
678 /* .. and also to the "old desktop", change the "last
679 desktop" to be where we are coming from */
680 screen_last_desktop = previous;
683 /* .. from some other desktop, then set the "last desktop" to
684 be the saved "old desktop", i.e. where we were before the
686 screen_last_desktop = screen_old_desktop;
690 /* If we are moving to any desktop besides the "last desktop"..
691 (this is the normal case) */
692 if (screen_desktop == screen_old_desktop) {
693 /* If moving to the "old desktop", which is not the
694 "last desktop", don't save anything */
696 else if (previous == screen_old_desktop) {
697 /* If moving from the "old desktop", and not to the
698 "last desktop", don't save anything */
700 else if (screen_last_desktop == screen_old_desktop) {
701 /* If the "last desktop" is the same as "old desktop" and
702 you're not moving to the "last desktop" then save where
703 we're coming from as the "last desktop" */
704 screen_last_desktop = previous;
707 /* If the "last desktop" is different from the "old desktop"
708 and you're not moving to the "last desktop", then don't save
713 screen_desktop_timeout = FALSE;
714 if (screen_desktop_timer) g_source_remove(screen_desktop_timer);
715 screen_desktop_timer = g_timeout_add(REMEMBER_LAST_DESKTOP_TIME,
716 last_desktop_func, NULL);
718 ob_debug("Moving to desktop %d", num+1);
720 if (ob_state() == OB_STATE_RUNNING)
721 screen_show_desktop_popup(screen_desktop, FALSE);
723 /* ignore enter events caused by the move */
724 ignore_start = event_start_ignore_all_enters();
726 if (moveresize_client)
727 client_set_desktop(moveresize_client, num, TRUE, FALSE);
729 /* show windows before hiding the rest to lessen the enter/leave events */
731 /* show windows from top to bottom */
732 for (it = stacking_list; it; it = g_list_next(it)) {
733 if (WINDOW_IS_CLIENT(it->data)) {
734 ObClient *c = it->data;
739 if (dofocus) screen_fallback_focus();
741 /* hide windows from bottom to top */
742 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
743 if (WINDOW_IS_CLIENT(it->data)) {
744 ObClient *c = it->data;
745 if (client_hide(c)) {
746 if (c == focus_client) {
747 /* c was focused and we didn't do fallback clearly so make
748 sure openbox doesnt still consider the window focused.
749 this happens when using NextWindow with allDesktops,
750 since it doesnt want to move focus on desktop change,
751 but the focus is not going to stay with the current
752 window, which has now disappeared.
753 only do this if the client was actually hidden,
754 otherwise it can keep focus. */
755 focus_set_client(NULL);
761 focus_cycle_addremove(NULL, TRUE);
763 event_end_ignore_all_enters(ignore_start);
765 if (event_source_time() != CurrentTime)
766 screen_desktop_user_time = event_source_time();
769 void screen_add_desktop(gboolean current)
773 /* ignore enter events caused by this */
774 ignore_start = event_start_ignore_all_enters();
776 screen_set_num_desktops(screen_num_desktops+1);
778 /* move all the clients over */
782 for (it = client_list; it; it = g_list_next(it)) {
783 ObClient *c = it->data;
784 if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop &&
785 /* don't move direct children, they'll be moved with their
786 parent - which will have to be on the same desktop */
787 !client_direct_parent(c))
789 ob_debug("moving window %s", c->title);
790 client_set_desktop(c, c->desktop+1, FALSE, TRUE);
795 event_end_ignore_all_enters(ignore_start);
798 void screen_remove_desktop(gboolean current)
800 guint rmdesktop, movedesktop;
801 GList *it, *stacking_copy;
804 if (screen_num_desktops <= 1) return;
806 /* ignore enter events caused by this */
807 ignore_start = event_start_ignore_all_enters();
809 /* what desktop are we removing and moving to? */
811 rmdesktop = screen_desktop;
813 rmdesktop = screen_num_desktops - 1;
814 if (rmdesktop < screen_num_desktops - 1)
815 movedesktop = rmdesktop + 1;
817 movedesktop = rmdesktop;
819 /* make a copy of the list cuz we're changing it */
820 stacking_copy = g_list_copy(stacking_list);
821 for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) {
822 if (WINDOW_IS_CLIENT(it->data)) {
823 ObClient *c = it->data;
824 guint d = c->desktop;
825 if (d != DESKTOP_ALL && d >= movedesktop &&
826 /* don't move direct children, they'll be moved with their
827 parent - which will have to be on the same desktop */
828 !client_direct_parent(c))
830 ob_debug("moving window %s", c->title);
831 client_set_desktop(c, c->desktop - 1, TRUE, TRUE);
833 /* raise all the windows that are on the current desktop which
835 if ((screen_desktop == rmdesktop - 1 ||
836 screen_desktop == rmdesktop) &&
837 (d == DESKTOP_ALL || d == screen_desktop))
839 stacking_raise(CLIENT_AS_WINDOW(c));
840 ob_debug("raising window %s", c->title);
844 g_list_free(stacking_copy);
846 /* fallback focus like we're changing desktops */
847 if (screen_desktop < screen_num_desktops - 1) {
848 screen_fallback_focus();
849 ob_debug("fake desktop change");
852 screen_set_num_desktops(screen_num_desktops-1);
854 event_end_ignore_all_enters(ignore_start);
857 static void get_row_col(guint d, guint *r, guint *c)
859 switch (screen_desktop_layout.orientation) {
860 case OB_ORIENTATION_HORZ:
861 switch (screen_desktop_layout.start_corner) {
862 case OB_CORNER_TOPLEFT:
863 *r = d / screen_desktop_layout.columns;
864 *c = d % screen_desktop_layout.columns;
866 case OB_CORNER_BOTTOMLEFT:
867 *r = screen_desktop_layout.rows - 1 -
868 d / screen_desktop_layout.columns;
869 *c = d % screen_desktop_layout.columns;
871 case OB_CORNER_TOPRIGHT:
872 *r = d / screen_desktop_layout.columns;
873 *c = screen_desktop_layout.columns - 1 -
874 d % screen_desktop_layout.columns;
876 case OB_CORNER_BOTTOMRIGHT:
877 *r = screen_desktop_layout.rows - 1 -
878 d / screen_desktop_layout.columns;
879 *c = screen_desktop_layout.columns - 1 -
880 d % screen_desktop_layout.columns;
884 case OB_ORIENTATION_VERT:
885 switch (screen_desktop_layout.start_corner) {
886 case OB_CORNER_TOPLEFT:
887 *r = d % screen_desktop_layout.rows;
888 *c = d / screen_desktop_layout.rows;
890 case OB_CORNER_BOTTOMLEFT:
891 *r = screen_desktop_layout.rows - 1 -
892 d % screen_desktop_layout.rows;
893 *c = d / screen_desktop_layout.rows;
895 case OB_CORNER_TOPRIGHT:
896 *r = d % screen_desktop_layout.rows;
897 *c = screen_desktop_layout.columns - 1 -
898 d / screen_desktop_layout.rows;
900 case OB_CORNER_BOTTOMRIGHT:
901 *r = screen_desktop_layout.rows - 1 -
902 d % screen_desktop_layout.rows;
903 *c = screen_desktop_layout.columns - 1 -
904 d / screen_desktop_layout.rows;
911 static guint translate_row_col(guint r, guint c)
913 switch (screen_desktop_layout.orientation) {
914 case OB_ORIENTATION_HORZ:
915 switch (screen_desktop_layout.start_corner) {
916 case OB_CORNER_TOPLEFT:
917 return r % screen_desktop_layout.rows *
918 screen_desktop_layout.columns +
919 c % screen_desktop_layout.columns;
920 case OB_CORNER_BOTTOMLEFT:
921 return (screen_desktop_layout.rows - 1 -
922 r % screen_desktop_layout.rows) *
923 screen_desktop_layout.columns +
924 c % screen_desktop_layout.columns;
925 case OB_CORNER_TOPRIGHT:
926 return r % screen_desktop_layout.rows *
927 screen_desktop_layout.columns +
928 (screen_desktop_layout.columns - 1 -
929 c % screen_desktop_layout.columns);
930 case OB_CORNER_BOTTOMRIGHT:
931 return (screen_desktop_layout.rows - 1 -
932 r % screen_desktop_layout.rows) *
933 screen_desktop_layout.columns +
934 (screen_desktop_layout.columns - 1 -
935 c % screen_desktop_layout.columns);
937 case OB_ORIENTATION_VERT:
938 switch (screen_desktop_layout.start_corner) {
939 case OB_CORNER_TOPLEFT:
940 return c % screen_desktop_layout.columns *
941 screen_desktop_layout.rows +
942 r % screen_desktop_layout.rows;
943 case OB_CORNER_BOTTOMLEFT:
944 return c % screen_desktop_layout.columns *
945 screen_desktop_layout.rows +
946 (screen_desktop_layout.rows - 1 -
947 r % screen_desktop_layout.rows);
948 case OB_CORNER_TOPRIGHT:
949 return (screen_desktop_layout.columns - 1 -
950 c % screen_desktop_layout.columns) *
951 screen_desktop_layout.rows +
952 r % screen_desktop_layout.rows;
953 case OB_CORNER_BOTTOMRIGHT:
954 return (screen_desktop_layout.columns - 1 -
955 c % screen_desktop_layout.columns) *
956 screen_desktop_layout.rows +
957 (screen_desktop_layout.rows - 1 -
958 r % screen_desktop_layout.rows);
961 g_assert_not_reached();
965 static gboolean hide_desktop_popup_func(gpointer data)
969 desktop_popup_timer = 0;
971 for (i = 0; i < screen_num_monitors; i++) {
972 pager_popup_hide(desktop_popup[i]);
974 return FALSE; /* don't repeat */
977 void screen_show_desktop_popup(guint d, gboolean perm)
982 /* 0 means don't show the popup */
983 if (!config_desktop_popup_time) return;
985 for (i = 0; i < screen_num_monitors; i++) {
986 a = screen_physical_area_monitor(i);
987 pager_popup_position(desktop_popup[i], CenterGravity,
988 a->x + a->width / 2, a->y + a->height / 2);
989 pager_popup_icon_size_multiplier(desktop_popup[i],
990 (screen_desktop_layout.columns /
991 screen_desktop_layout.rows) / 2,
992 (screen_desktop_layout.rows/
993 screen_desktop_layout.columns) / 2);
994 pager_popup_max_width(desktop_popup[i],
995 MAX(a->width/3, POPUP_WIDTH));
996 pager_popup_show(desktop_popup[i], screen_desktop_names[d], d);
998 if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
999 desktop_popup_timer = 0;
1000 if (!perm && !desktop_popup_perm)
1001 /* only hide if its not already being show permanently */
1002 desktop_popup_timer = g_timeout_add(config_desktop_popup_time,
1003 hide_desktop_popup_func,
1006 desktop_popup_perm = TRUE;
1009 void screen_hide_desktop_popup(void)
1011 if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
1012 desktop_popup_timer = 0;
1013 desktop_popup_perm = FALSE;
1016 for (i = 0; i < screen_num_monitors; i++) {
1017 pager_popup_hide(desktop_popup[i]);
1021 guint screen_find_desktop(guint from, ObDirection dir,
1022 gboolean wrap, gboolean linear)
1028 get_row_col(d, &r, &c);
1031 case OB_DIRECTION_EAST:
1032 if (d < screen_num_desktops - 1)
1039 case OB_DIRECTION_WEST:
1043 d = screen_num_desktops - 1;
1048 g_assert_not_reached();
1053 case OB_DIRECTION_EAST:
1055 if (c >= screen_desktop_layout.columns) {
1061 d = translate_row_col(r, c);
1062 if (d >= screen_num_desktops) {
1069 case OB_DIRECTION_WEST:
1071 if (c >= screen_desktop_layout.columns) {
1073 c = screen_desktop_layout.columns - 1;
1077 d = translate_row_col(r, c);
1078 if (d >= screen_num_desktops) {
1085 case OB_DIRECTION_SOUTH:
1087 if (r >= screen_desktop_layout.rows) {
1093 d = translate_row_col(r, c);
1094 if (d >= screen_num_desktops) {
1101 case OB_DIRECTION_NORTH:
1103 if (r >= screen_desktop_layout.rows) {
1105 r = screen_desktop_layout.rows - 1;
1109 d = translate_row_col(r, c);
1110 if (d >= screen_num_desktops) {
1118 g_assert_not_reached();
1122 d = translate_row_col(r, c);
1127 static gboolean screen_validate_layout(ObDesktopLayout *l)
1129 if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */
1132 /* fill in a zero rows/columns */
1133 if (l->columns == 0) {
1134 l->columns = screen_num_desktops / l->rows;
1135 if (l->rows * l->columns < screen_num_desktops)
1137 if (l->rows * l->columns >= screen_num_desktops + l->columns)
1139 } else if (l->rows == 0) {
1140 l->rows = screen_num_desktops / l->columns;
1141 if (l->columns * l->rows < screen_num_desktops)
1143 if (l->columns * l->rows >= screen_num_desktops + l->rows)
1147 /* bounds checking */
1148 if (l->orientation == OB_ORIENTATION_HORZ) {
1149 l->columns = MIN(screen_num_desktops, l->columns);
1150 l->rows = MIN(l->rows,
1151 (screen_num_desktops + l->columns - 1) / l->columns);
1152 l->columns = screen_num_desktops / l->rows +
1153 !!(screen_num_desktops % l->rows);
1155 l->rows = MIN(screen_num_desktops, l->rows);
1156 l->columns = MIN(l->columns,
1157 (screen_num_desktops + l->rows - 1) / l->rows);
1158 l->rows = screen_num_desktops / l->columns +
1159 !!(screen_num_desktops % l->columns);
1164 void screen_update_layout(void)
1171 screen_desktop_layout.orientation = OB_ORIENTATION_HORZ;
1172 screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT;
1173 screen_desktop_layout.rows = 1;
1174 screen_desktop_layout.columns = screen_num_desktops;
1176 if (OBT_PROP_GETA32(obt_root(ob_screen),
1177 NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) {
1178 if (num == 3 || num == 4) {
1180 if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT))
1181 l.orientation = OB_ORIENTATION_VERT;
1182 else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ))
1183 l.orientation = OB_ORIENTATION_HORZ;
1188 l.start_corner = OB_CORNER_TOPLEFT;
1190 if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT))
1191 l.start_corner = OB_CORNER_TOPLEFT;
1192 else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT))
1193 l.start_corner = OB_CORNER_TOPRIGHT;
1194 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT))
1195 l.start_corner = OB_CORNER_BOTTOMRIGHT;
1196 else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT))
1197 l.start_corner = OB_CORNER_BOTTOMLEFT;
1202 l.columns = data[1];
1205 if (screen_validate_layout(&l))
1206 screen_desktop_layout = l;
1213 void screen_update_desktop_names(void)
1217 /* empty the array */
1218 g_strfreev(screen_desktop_names);
1219 screen_desktop_names = NULL;
1221 if (OBT_PROP_GETSS(obt_root(ob_screen),
1222 NET_DESKTOP_NAMES, &screen_desktop_names))
1223 for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
1226 if (i < screen_num_desktops) {
1229 screen_desktop_names = g_renew(gchar*, screen_desktop_names,
1230 screen_num_desktops + 1);
1231 screen_desktop_names[screen_num_desktops] = NULL;
1233 it = g_slist_nth(config_desktops_names, i);
1235 for (; i < screen_num_desktops; ++i) {
1236 if (it && ((char*)it->data)[0]) /* not empty */
1237 /* use the names from the config file when possible */
1238 screen_desktop_names[i] = g_strdup(it->data);
1240 /* make up a nice name if it's not though */
1241 screen_desktop_names[i] = g_strdup_printf(_("desktop %i"),
1243 if (it) it = g_slist_next(it);
1246 /* if we changed any names, then set the root property so we can
1247 all agree on the names */
1248 OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
1249 (const gchar*const*)screen_desktop_names);
1252 /* resize the pager for these names */
1253 for (i = 0; i < screen_num_monitors; i++) {
1254 pager_popup_text_width_to_strings(desktop_popup[i],
1255 screen_desktop_names,
1256 screen_num_desktops);
1260 void screen_show_desktop(ObScreenShowDestopMode show_mode, ObClient *show_only)
1264 ObScreenShowDestopMode before_mode = screen_show_desktop_mode;
1266 gboolean showing_before = screen_showing_desktop();
1267 screen_show_desktop_mode = show_mode;
1268 gboolean showing_after = screen_showing_desktop();
1270 if (showing_before == showing_after) {
1272 screen_show_desktop_mode = before_mode;
1276 if (screen_show_desktop_mode == SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE &&
1279 /* If we're showing the desktop until the show-mode is toggled, we
1280 don't allow breaking out of showing-desktop mode unless we're
1281 showing all the windows again. */
1282 screen_show_desktop_mode = before_mode;
1286 if (showing_after) {
1287 /* hide windows bottom to top */
1288 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
1289 if (WINDOW_IS_CLIENT(it->data)) {
1290 ObClient *client = it->data;
1291 client_showhide(client);
1296 /* restore windows top to bottom */
1297 for (it = stacking_list; it; it = g_list_next(it)) {
1298 if (WINDOW_IS_CLIENT(it->data)) {
1299 ObClient *client = it->data;
1300 if (client_should_show(client)) {
1301 if (!show_only || client == show_only)
1302 client_show(client);
1304 client_iconify(client, TRUE, FALSE, TRUE);
1310 if (showing_after) {
1311 /* focus the desktop */
1312 for (it = focus_order; it; it = g_list_next(it)) {
1313 ObClient *c = it->data;
1314 if (c->type == OB_CLIENT_TYPE_DESKTOP &&
1315 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
1316 client_focus(it->data))
1320 else if (!show_only) {
1323 if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) {
1324 /* only do the flicker reducing stuff ahead of time if we are going
1325 to call xsetinputfocus on the window ourselves. otherwise there
1326 is no guarantee the window will actually take focus.. */
1328 /* reduce flicker by hiliting now rather than waiting for the
1329 server FocusIn event */
1330 frame_adjust_focus(c->frame, TRUE);
1335 OBT_PROP_SET32(obt_root(ob_screen),
1336 NET_SHOWING_DESKTOP,
1341 gboolean screen_showing_desktop()
1343 switch (screen_show_desktop_mode) {
1344 case SCREEN_SHOW_DESKTOP_NO:
1346 case SCREEN_SHOW_DESKTOP_UNTIL_WINDOW:
1347 case SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE:
1350 g_assert_not_reached();
1354 void screen_install_colormap(ObClient *client, gboolean install)
1356 if (client == NULL || client->colormap == None) {
1358 XInstallColormap(obt_display, RrColormap(ob_rr_inst));
1360 XUninstallColormap(obt_display, RrColormap(ob_rr_inst));
1362 obt_display_ignore_errors(TRUE);
1364 XInstallColormap(obt_display, client->colormap);
1366 XUninstallColormap(obt_display, client->colormap);
1367 obt_display_ignore_errors(FALSE);
1373 StrutPartial *strut;
1376 #define RESET_STRUT_LIST(sl) \
1378 g_slice_free(ObScreenStrut, (sl)->data); \
1379 sl = g_slist_delete_link(sl, sl); \
1382 #define ADD_STRUT_TO_LIST(sl, d, s) \
1384 ObScreenStrut *ss = g_slice_new(ObScreenStrut); \
1387 sl = g_slist_prepend(sl, ss); \
1390 #define VALIDATE_STRUTS(sl, side, max) \
1393 for (it = sl; it; it = g_slist_next(it)) { \
1394 ObScreenStrut *ss = it->data; \
1395 ss->strut->side = MIN(max, ss->strut->side); \
1399 static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
1405 XineramaScreenInfo *info;
1408 if (ob_debug_xinerama) {
1409 gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1410 gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
1412 *xin_areas = g_new(Rect, *nxin + 1);
1413 RECT_SET((*xin_areas)[0], 0, 0, w/2, h);
1414 RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h);
1415 } else if (config_emulate_xinerama) {
1417 *xin_areas = g_new(Rect, *nxin + 1);
1418 RECT_SET((*xin_areas)[0], 0, 0,
1419 WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)) / 2,
1420 HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1421 RECT_SET((*xin_areas)[1],
1422 WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)) / 2,
1424 WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)) / 2,
1425 HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1426 RECT_SET((*xin_areas)[*nxin], 0, 0,
1427 WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1428 HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1431 else if (obt_display_extension_xinerama &&
1432 (info = XineramaQueryScreens(obt_display, &n))) {
1434 *xin_areas = g_new(Rect, *nxin + 1);
1435 for (i = 0; i < *nxin; ++i)
1436 RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org,
1437 info[i].width, info[i].height);
1443 *xin_areas = g_new(Rect, *nxin + 1);
1444 RECT_SET((*xin_areas)[0], 0, 0,
1445 WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)),
1446 HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)));
1449 /* returns one extra with the total area in it */
1450 l = (*xin_areas)[0].x;
1451 t = (*xin_areas)[0].y;
1452 r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1;
1453 b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1;
1454 for (i = 1; i < *nxin; ++i) {
1455 l = MIN(l, (*xin_areas)[i].x);
1456 t = MIN(l, (*xin_areas)[i].y);
1457 r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);
1458 b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
1460 RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
1462 for (i = 0; i < *nxin; ++i)
1463 ob_debug("Monitor %d @ %d,%d %dx%d\n", i,
1464 (*xin_areas)[i].x, (*xin_areas)[i].y,
1465 (*xin_areas)[i].width, (*xin_areas)[i].height);
1466 ob_debug("Full desktop @ %d,%d %dx%d\n",
1467 (*xin_areas)[i].x, (*xin_areas)[i].y,
1468 (*xin_areas)[i].width, (*xin_areas)[i].height);
1471 void screen_update_areas(void)
1475 GList *it, *onscreen;
1477 /* collect the clients that are on screen */
1479 for (it = client_list; it; it = g_list_next(it)) {
1480 if (client_monitor(it->data) != screen_num_monitors)
1481 onscreen = g_list_prepend(onscreen, it->data);
1484 g_free(monitor_area);
1485 get_xinerama_screens(&monitor_area, &screen_num_monitors);
1487 if (!desktop_popup) {
1488 desktop_popup = g_new(ObPagerPopup*, screen_num_monitors);
1489 for (i = 0; i < screen_num_monitors; i++) {
1490 desktop_popup[i] = pager_popup_new();
1491 pager_popup_height(desktop_popup[i], POPUP_HEIGHT);
1493 if (screen_desktop_names)
1494 /* update the pager popup's width */
1495 pager_popup_text_width_to_strings(desktop_popup[i],
1496 screen_desktop_names,
1497 screen_num_desktops);
1501 /* set up the user-specified margins */
1502 config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1503 config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1504 config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]);
1505 config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]);
1506 config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]);
1507 config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1508 config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
1509 config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1511 RESET_STRUT_LIST(struts_left);
1512 RESET_STRUT_LIST(struts_top);
1513 RESET_STRUT_LIST(struts_right);
1514 RESET_STRUT_LIST(struts_bottom);
1516 /* collect the struts */
1517 for (it = client_list; it; it = g_list_next(it)) {
1518 ObClient *c = it->data;
1520 ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut);
1522 ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut);
1524 ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut);
1525 if (c->strut.bottom)
1526 ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut);
1528 if (dock_strut.left)
1529 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut);
1531 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut);
1532 if (dock_strut.right)
1533 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut);
1534 if (dock_strut.bottom)
1535 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut);
1537 if (config_margins.left)
1538 ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins);
1539 if (config_margins.top)
1540 ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins);
1541 if (config_margins.right)
1542 ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins);
1543 if (config_margins.bottom)
1544 ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins);
1546 VALIDATE_STRUTS(struts_left, left,
1547 monitor_area[screen_num_monitors].width / 2);
1548 VALIDATE_STRUTS(struts_right, right,
1549 monitor_area[screen_num_monitors].width / 2);
1550 VALIDATE_STRUTS(struts_top, top,
1551 monitor_area[screen_num_monitors].height / 2);
1552 VALIDATE_STRUTS(struts_bottom, bottom,
1553 monitor_area[screen_num_monitors].height / 2);
1555 dims = g_new(gulong, 4 * screen_num_desktops);
1556 for (i = 0; i < screen_num_desktops; ++i) {
1557 Rect *area = screen_area(i, SCREEN_AREA_ALL_MONITORS, NULL);
1558 dims[i*4+0] = area->x;
1559 dims[i*4+1] = area->y;
1560 dims[i*4+2] = area->width;
1561 dims[i*4+3] = area->height;
1562 g_slice_free(Rect, area);
1565 /* set the legacy workarea hint to the union of all the monitors */
1566 OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL,
1567 dims, 4 * screen_num_desktops);
1569 /* the area has changed, adjust all the windows if they need it */
1570 for (it = onscreen; it; it = g_list_next(it))
1571 client_reconfigure(it->data, FALSE);
1577 Rect* screen_area_all_monitors(guint desktop)
1582 a = screen_area_monitor(desktop, 0);
1584 /* combine all the monitors together */
1585 for (i = 1; i < screen_num_monitors; ++i) {
1586 Rect *m = screen_area_monitor(desktop, i);
1589 l = MIN(RECT_LEFT(*a), RECT_LEFT(*m));
1590 t = MIN(RECT_TOP(*a), RECT_TOP(*m));
1591 r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m));
1592 b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m));
1594 RECT_SET(*a, l, t, r - l + 1, b - t + 1);
1603 #define STRUT_LEFT_IN_SEARCH(s, search) \
1604 (RANGES_INTERSECT(search->y, search->height, \
1605 s->left_start, s->left_end - s->left_start + 1))
1606 #define STRUT_RIGHT_IN_SEARCH(s, search) \
1607 (RANGES_INTERSECT(search->y, search->height, \
1608 s->right_start, s->right_end - s->right_start + 1))
1609 #define STRUT_TOP_IN_SEARCH(s, search) \
1610 (RANGES_INTERSECT(search->x, search->width, \
1611 s->top_start, s->top_end - s->top_start + 1))
1612 #define STRUT_BOTTOM_IN_SEARCH(s, search) \
1613 (RANGES_INTERSECT(search->x, search->width, \
1614 s->bottom_start, s->bottom_end - s->bottom_start + 1))
1616 #define STRUT_LEFT_IGNORE(s, us, search) \
1617 (head == SCREEN_AREA_ALL_MONITORS && us && \
1618 RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search))
1619 #define STRUT_RIGHT_IGNORE(s, us, search) \
1620 (head == SCREEN_AREA_ALL_MONITORS && us && \
1621 RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search))
1622 #define STRUT_TOP_IGNORE(s, us, search) \
1623 (head == SCREEN_AREA_ALL_MONITORS && us && \
1624 RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search))
1625 #define STRUT_BOTTOM_IGNORE(s, us, search) \
1626 (head == SCREEN_AREA_ALL_MONITORS && us && \
1627 RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search))
1629 Rect* screen_area(guint desktop, guint head, Rect *search)
1635 gboolean us = search != NULL; /* user provided search */
1637 g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL);
1638 g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR ||
1639 head == SCREEN_AREA_ALL_MONITORS);
1640 g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL));
1642 /* find any struts for this monitor
1643 which will be affecting the search area.
1646 /* search everything if search is null */
1648 if (head < screen_num_monitors) search = &monitor_area[head];
1649 else search = &monitor_area[screen_num_monitors];
1651 if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search);
1653 /* al is "all left" meaning the furthest left you can get, l is our
1654 "working left" meaning our current strut edge which we're calculating
1657 /* only include monitors which the search area lines up with */
1658 if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) {
1659 l = RECT_RIGHT(monitor_area[screen_num_monitors]);
1660 t = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1661 r = RECT_LEFT(monitor_area[screen_num_monitors]);
1662 b = RECT_TOP(monitor_area[screen_num_monitors]);
1663 for (i = 0; i < screen_num_monitors; ++i) {
1664 /* add the monitor if applicable */
1665 if (RANGES_INTERSECT(search->x, search->width,
1666 monitor_area[i].x, monitor_area[i].width))
1668 t = MIN(t, RECT_TOP(monitor_area[i]));
1669 b = MAX(b, RECT_BOTTOM(monitor_area[i]));
1671 if (RANGES_INTERSECT(search->y, search->height,
1672 monitor_area[i].y, monitor_area[i].height))
1674 l = MIN(l, RECT_LEFT(monitor_area[i]));
1675 r = MAX(r, RECT_RIGHT(monitor_area[i]));
1679 l = RECT_LEFT(monitor_area[screen_num_monitors]);
1680 t = RECT_TOP(monitor_area[screen_num_monitors]);
1681 r = RECT_RIGHT(monitor_area[screen_num_monitors]);
1682 b = RECT_BOTTOM(monitor_area[screen_num_monitors]);
1685 for (d = 0; d < screen_num_desktops; ++d) {
1686 if (d != desktop && desktop != DESKTOP_ALL) continue;
1688 for (i = 0; i < screen_num_monitors; ++i) {
1689 if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue;
1691 for (it = struts_left; it; it = g_slist_next(it)) {
1692 ObScreenStrut *s = it->data;
1693 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1694 STRUT_LEFT_IN_SEARCH(s->strut, search) &&
1695 !STRUT_LEFT_IGNORE(s->strut, us, search))
1696 l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors])
1699 for (it = struts_top; it; it = g_slist_next(it)) {
1700 ObScreenStrut *s = it->data;
1701 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1702 STRUT_TOP_IN_SEARCH(s->strut, search) &&
1703 !STRUT_TOP_IGNORE(s->strut, us, search))
1704 t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors])
1707 for (it = struts_right; it; it = g_slist_next(it)) {
1708 ObScreenStrut *s = it->data;
1709 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1710 STRUT_RIGHT_IN_SEARCH(s->strut, search) &&
1711 !STRUT_RIGHT_IGNORE(s->strut, us, search))
1712 r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors])
1715 for (it = struts_bottom; it; it = g_slist_next(it)) {
1716 ObScreenStrut *s = it->data;
1717 if ((s->desktop == d || s->desktop == DESKTOP_ALL) &&
1718 STRUT_BOTTOM_IN_SEARCH(s->strut, search) &&
1719 !STRUT_BOTTOM_IGNORE(s->strut, us, search))
1720 b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors])
1721 - s->strut->bottom);
1724 /* limit to this monitor */
1726 l = MAX(l, RECT_LEFT(monitor_area[i]));
1727 t = MAX(t, RECT_TOP(monitor_area[i]));
1728 r = MIN(r, RECT_RIGHT(monitor_area[i]));
1729 b = MIN(b, RECT_BOTTOM(monitor_area[i]));
1734 a = g_slice_new(Rect);
1737 a->width = r - l + 1;
1738 a->height = b - t + 1;
1747 guint screen_find_monitor(const Rect *search)
1750 guint mostpx_index = screen_num_monitors;
1752 guint closest_distance_index = screen_num_monitors;
1753 guint closest_distance = G_MAXUINT;
1754 GSList *counted = NULL;
1756 /* we want to count the number of pixels search has on each monitor, but not
1757 double count. so if a pixel is counted on monitor A then we should not
1758 count it again on monitor B. in the end we want to return the monitor
1759 that had the most pixels counted under this scheme.
1761 this assumes that monitors earlier in the list are more desirable to be
1762 considered the search area's monitor. we try the configured primary
1763 monitor first, so it gets the highest preference.
1765 if we have counted an area A, then we want to subtract the intersection
1766 of A with the area on future monitors.
1767 but now consider if we count an area B that intersects A. we want to
1768 subtract the area B from that counted on future monitors, but not
1769 subtract the intersection of A and B twice! so we would add the
1770 intersection of A and B back, to account for it being subtracted both
1773 this is the idea behind the algorithm. we always subtract the full area
1774 for monitor M intersected with the search area. we'll call that AREA.
1775 but then we go through the list |counted| and for each rectangle in
1776 the list that is being subtracted from future monitors, we insert a
1777 request to add back the intersection of the subtracted rect with AREA.
1778 vice versa for a rect in |counted| that is getting added back.
1781 if (config_primary_monitor_index < screen_num_monitors) {
1782 const Rect *monitor;
1783 Rect on_current_monitor;
1786 monitor = screen_physical_area_monitor(config_primary_monitor_index);
1788 if (RECT_INTERSECTS_RECT(*monitor, *search)) {
1789 RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1790 area = RECT_AREA(on_current_monitor);
1792 if (area > mostpx) {
1794 mostpx_index = config_primary_monitor_index;
1797 /* add the intersection rect on the current monitor to the
1798 counted list. that's easy for the first one, we just mark it for
1801 RectArithmetic *ra = g_slice_new(RectArithmetic);
1802 ra->r = on_current_monitor;
1803 ra->subtract = TRUE;
1804 counted = g_slist_prepend(counted, ra);
1809 for (i = 0; i < screen_num_monitors; ++i) {
1810 const Rect *monitor;
1811 Rect on_current_monitor;
1815 monitor = screen_physical_area_monitor(i);
1817 if (!RECT_INTERSECTS_RECT(*monitor, *search)) {
1818 /* If we don't intersect then find the distance between the search
1819 rect and the monitor. We'll use the closest monitor from this
1820 metric if none of the monitors intersect. */
1821 guint distance = rect_manhatten_distance(*monitor, *search);
1823 if (distance < closest_distance) {
1824 closest_distance = distance;
1825 closest_distance_index = i;
1830 if (i == config_primary_monitor_index)
1831 continue; /* already did this one */
1833 RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
1834 area = RECT_AREA(on_current_monitor);
1836 /* remove pixels we already counted on any previous monitors. */
1837 for (it = counted; it; it = g_slist_next(it)) {
1838 RectArithmetic *ra = it->data;
1841 RECT_SET_INTERSECTION(intersection, ra->r, *search);
1842 if (ra->subtract) area -= RECT_AREA(intersection);
1843 else area += RECT_AREA(intersection);
1846 if (area > mostpx) {
1851 /* add the intersection rect on the current monitor I to the counted
1853 but now we need to compensate for every rectangle R already in the
1854 counted list, and add a new rect R' that is the intersection of
1855 R and I, but with the reverse subtraction/addition operation.
1857 for (it = counted; it; it = g_slist_next(it)) {
1858 RectArithmetic *saved = it->data;
1860 if (!RECT_INTERSECTS_RECT(saved->r, on_current_monitor))
1862 /* we are going to subtract our rect from future monitors, but
1863 part of it may already be being subtracted/added, so compensate
1864 to not double add/subtract. */
1865 RectArithmetic *reverse = g_slice_new(RectArithmetic);
1866 RECT_SET_INTERSECTION(reverse->r, saved->r, on_current_monitor);
1867 reverse->subtract = !saved->subtract;
1868 /* prepend so we can continue thru the list uninterupted */
1869 counted = g_slist_prepend(counted, reverse);
1872 RectArithmetic *ra = g_slice_new(RectArithmetic);
1873 ra->r = on_current_monitor;
1874 ra->subtract = TRUE;
1875 counted = g_slist_prepend(counted, ra);
1880 g_slice_free(RectArithmetic, counted->data);
1881 counted = g_slist_delete_link(counted, counted);
1884 if (mostpx_index < screen_num_monitors)
1885 return mostpx_index;
1887 g_assert(closest_distance_index < screen_num_monitors);
1888 return closest_distance_index;
1891 const Rect* screen_physical_area_all_monitors(void)
1893 return screen_physical_area_monitor(screen_num_monitors);
1896 const Rect* screen_physical_area_monitor(guint head)
1898 g_assert(head <= screen_num_monitors);
1900 return &monitor_area[head];
1903 gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
1905 g_assert(head <= screen_num_monitors);
1907 return RECT_INTERSECTS_RECT(monitor_area[head], *search);
1910 guint screen_monitor_active(void)
1912 if (moveresize_client)
1913 return client_monitor(moveresize_client);
1914 else if (focus_client)
1915 return client_monitor(focus_client);
1917 return screen_monitor_pointer();
1920 const Rect* screen_physical_area_active(void)
1922 return screen_physical_area_monitor(screen_monitor_active());
1925 guint screen_monitor_primary(gboolean fixed)
1927 if (config_primary_monitor_index > 0) {
1928 if (config_primary_monitor_index-1 < screen_num_monitors)
1929 return config_primary_monitor_index - 1;
1935 else if (config_primary_monitor == OB_PLACE_MONITOR_ACTIVE)
1936 return screen_monitor_active();
1937 else /* config_primary_monitor == OB_PLACE_MONITOR_MOUSE */
1938 return screen_monitor_pointer();
1941 const Rect* screen_physical_area_primary(gboolean fixed)
1943 return screen_physical_area_monitor(screen_monitor_primary(fixed));
1946 void screen_set_root_cursor(void)
1948 if (sn_app_starting())
1949 XDefineCursor(obt_display, obt_root(ob_screen),
1950 ob_cursor(OB_CURSOR_BUSY));
1952 XDefineCursor(obt_display, obt_root(ob_screen),
1953 ob_cursor(OB_CURSOR_POINTER));
1956 guint screen_find_monitor_point(guint x, guint y)
1959 RECT_SET(mon, x, y, 1, 1);
1960 return screen_find_monitor(&mon);
1963 guint screen_monitor_pointer()
1966 if (!screen_pointer_pos(&x, &y))
1968 return screen_find_monitor_point(x, y);
1971 gboolean screen_pointer_pos(gint *x, gint *y)
1976 /* we don't care about any of these return values, but we can't pass NULL */
1981 ret = !!XQueryPointer(obt_display, obt_root(ob_screen),
1982 &w, &w, x, y, &j, &j, &u);
1984 for (i = 0; i < ScreenCount(obt_display); ++i)
1986 if ((ret=XQueryPointer(obt_display, obt_root(i),
1987 &w, &w, x, y, &j, &j, &u)))
1993 gboolean screen_compare_desktops(guint a, guint b)
1995 if (a == DESKTOP_ALL)
1997 if (b == DESKTOP_ALL)
2002 void screen_apply_gravity_point(gint *x, gint *y, gint width, gint height,
2003 const GravityPoint *position, const Rect *area)
2005 if (position->x.center)
2006 *x = area->width / 2 - width / 2;
2008 *x = position->x.pos;
2009 if (position->x.denom)
2010 *x = (*x * area->width) / position->x.denom;
2011 if (position->x.opposite)
2012 *x = area->width - width - *x;
2015 if (position->y.center)
2016 *y = area->height / 2 - height / 2;
2018 *y = position->y.pos;
2019 if (position->y.denom)
2020 *y = (*y * area->height) / position->y.denom;
2021 if (position->y.opposite)
2022 *y = area->height - height - *y;