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