]> Dogcows Code - chaz/openbox/blob - openbox/screen.c
add support for _NET_REQUEST_FRAME_EXTENTS
[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_startup_id;
280 #ifdef SYNC
281 supported[i++] = prop_atoms.net_wm_sync_request;
282 supported[i++] = prop_atoms.net_wm_sync_request_counter;
283 #endif
284
285 supported[i++] = prop_atoms.kde_wm_change_state;
286 supported[i++] = prop_atoms.kde_net_wm_frame_strut;
287 supported[i++] = prop_atoms.kde_net_wm_window_type_override;
288
289 supported[i++] = prop_atoms.openbox_wm_state_undecorated;
290 supported[i++] = prop_atoms.openbox_pid;
291 supported[i++] = prop_atoms.openbox_config;
292 supported[i++] = prop_atoms.openbox_control;
293 g_assert(i == num_support);
294
295 PROP_SETA32(RootWindow(ob_display, ob_screen),
296 net_supported, atom, supported, num_support);
297 g_free(supported);
298
299 return TRUE;
300 }
301
302 void screen_startup(gboolean reconfig)
303 {
304 guint i, numnames;
305 gchar **names;
306 GSList *it;
307 guint32 d;
308
309 desktop_cycle_popup = pager_popup_new(FALSE);
310 pager_popup_height(desktop_cycle_popup, POPUP_HEIGHT);
311
312 if (reconfig)
313 return;
314
315 /* get the initial size */
316 screen_resize();
317
318 /* get the desktop names */
319 numnames = g_slist_length(config_desktops_names);
320 names = g_new(gchar*, numnames + 1);
321 names[numnames] = NULL;
322 for (i = 0, it = config_desktops_names; it; ++i, it = g_slist_next(it))
323 names[i] = g_strdup(it->data);
324
325 /* set the root window property */
326 PROP_SETSS(RootWindow(ob_display, ob_screen), net_desktop_names,names);
327
328 g_strfreev(names);
329
330 /* set the number of desktops */
331 screen_num_desktops = 0;
332 screen_set_num_desktops(config_desktops_num);
333
334 /* start on the current desktop when a wm was already running */
335 if (PROP_GET32(RootWindow(ob_display, ob_screen),
336 net_current_desktop, cardinal, &d) &&
337 d < screen_num_desktops)
338 {
339 screen_set_desktop(d, FALSE);
340 } else if (session_desktop >= 0)
341 screen_set_desktop(MIN((guint)session_desktop,
342 screen_num_desktops), FALSE);
343 else
344 screen_set_desktop(MIN(config_screen_firstdesk,
345 screen_num_desktops) - 1, FALSE);
346
347 /* don't start in showing-desktop mode */
348 screen_showing_desktop = FALSE;
349 PROP_SET32(RootWindow(ob_display, ob_screen),
350 net_showing_desktop, cardinal, screen_showing_desktop);
351
352 screen_update_layout();
353 }
354
355 void screen_shutdown(gboolean reconfig)
356 {
357 Rect **r;
358
359 pager_popup_free(desktop_cycle_popup);
360
361 if (reconfig)
362 return;
363
364 XSelectInput(ob_display, RootWindow(ob_display, ob_screen),
365 NoEventMask);
366
367 /* we're not running here no more! */
368 PROP_ERASE(RootWindow(ob_display, ob_screen), openbox_pid);
369 /* not without us */
370 PROP_ERASE(RootWindow(ob_display, ob_screen), net_supported);
371 /* don't keep this mode */
372 PROP_ERASE(RootWindow(ob_display, ob_screen), net_showing_desktop);
373
374 XDestroyWindow(ob_display, screen_support_win);
375
376 g_strfreev(screen_desktop_names);
377 screen_desktop_names = NULL;
378
379 for (r = area; *r; ++r)
380 g_free(*r);
381 g_free(area);
382 area = NULL;
383 }
384
385 void screen_resize()
386 {
387 static gint oldw = 0, oldh = 0;
388 gint w, h;
389 GList *it;
390 gulong geometry[2];
391
392 w = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen));
393 h = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen));
394
395 if (w == oldw && h == oldh) return;
396
397 oldw = w; oldh = h;
398
399 /* Set the _NET_DESKTOP_GEOMETRY hint */
400 screen_physical_size.width = geometry[0] = w;
401 screen_physical_size.height = geometry[1] = h;
402 PROP_SETA32(RootWindow(ob_display, ob_screen),
403 net_desktop_geometry, cardinal, geometry, 2);
404
405 if (ob_state() == OB_STATE_STARTING)
406 return;
407
408 screen_update_areas();
409 dock_configure();
410
411 for (it = client_list; it; it = g_list_next(it))
412 client_move_onscreen(it->data, FALSE);
413 }
414
415 void screen_set_num_desktops(guint num)
416 {
417 guint old;
418 gulong *viewport;
419 GList *it;
420
421 g_assert(num > 0);
422
423 if (screen_num_desktops == num) return;
424
425 old = screen_num_desktops;
426 screen_num_desktops = num;
427 PROP_SET32(RootWindow(ob_display, ob_screen),
428 net_number_of_desktops, cardinal, num);
429
430 /* set the viewport hint */
431 viewport = g_new0(gulong, num * 2);
432 PROP_SETA32(RootWindow(ob_display, ob_screen),
433 net_desktop_viewport, cardinal, viewport, num * 2);
434 g_free(viewport);
435
436 /* the number of rows/columns will differ */
437 screen_update_layout();
438
439 /* move windows on desktops that will no longer exist! */
440 for (it = client_list; it; it = g_list_next(it)) {
441 ObClient *c = it->data;
442 if (c->desktop >= num && c->desktop != DESKTOP_ALL)
443 client_set_desktop(c, num - 1, FALSE);
444 }
445
446 /* change our struts/area to match (after moving windows) */
447 screen_update_areas();
448
449 /* may be some unnamed desktops that we need to fill in with names
450 (after updating the areas so the popup can resize) */
451 screen_update_desktop_names();
452
453 /* change our desktop if we're on one that no longer exists! */
454 if (screen_desktop >= screen_num_desktops)
455 screen_set_desktop(num - 1, TRUE);
456 }
457
458 void screen_set_desktop(guint num, gboolean dofocus)
459 {
460 ObClient *c;
461 GList *it;
462 guint old;
463
464 g_assert(num < screen_num_desktops);
465
466 old = screen_desktop;
467 screen_desktop = num;
468 PROP_SET32(RootWindow(ob_display, ob_screen),
469 net_current_desktop, cardinal, num);
470
471 if (old == num) return;
472
473 screen_last_desktop = old;
474
475 ob_debug("Moving to desktop %d\n", num+1);
476
477 if (moveresize_client)
478 client_set_desktop(moveresize_client, num, TRUE);
479
480 /* show windows before hiding the rest to lessen the enter/leave events */
481
482 /* show windows from top to bottom */
483 for (it = stacking_list; it; it = g_list_next(it)) {
484 if (WINDOW_IS_CLIENT(it->data)) {
485 ObClient *c = it->data;
486 client_show(c);
487 }
488 }
489
490 /* have to try focus here because when you leave an empty desktop
491 there is no focus out to watch for
492
493 do this before hiding the windows so if helper windows are coming
494 with us, they don't get hidden
495 */
496 if (dofocus && (c = focus_fallback_target(TRUE, focus_client))) {
497 /* only do the flicker reducing stuff ahead of time if we are going
498 to call xsetinputfocus on the window ourselves. otherwise there is
499 no guarantee the window will actually take focus.. */
500 if (c->can_focus) {
501 /* do this here so that if you switch desktops to a window with
502 helper windows then the helper windows won't flash */
503 client_bring_helper_windows(c);
504 /* reduce flicker by hiliting now rather than waiting for the
505 server FocusIn event */
506 frame_adjust_focus(c->frame, TRUE);
507 }
508 client_focus(c);
509 }
510
511 /* hide windows from bottom to top */
512 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
513 if (WINDOW_IS_CLIENT(it->data)) {
514 ObClient *c = it->data;
515 client_hide(c);
516 }
517 }
518
519 event_ignore_queued_enters();
520
521 if (event_curtime != CurrentTime)
522 screen_desktop_user_time = event_curtime;
523 }
524
525 static void get_row_col(guint d, guint *r, guint *c)
526 {
527 switch (screen_desktop_layout.orientation) {
528 case OB_ORIENTATION_HORZ:
529 switch (screen_desktop_layout.start_corner) {
530 case OB_CORNER_TOPLEFT:
531 *r = d / screen_desktop_layout.columns;
532 *c = d % screen_desktop_layout.columns;
533 break;
534 case OB_CORNER_BOTTOMLEFT:
535 *r = screen_desktop_layout.rows - 1 -
536 d / screen_desktop_layout.columns;
537 *c = d % screen_desktop_layout.columns;
538 break;
539 case OB_CORNER_TOPRIGHT:
540 *r = d / screen_desktop_layout.columns;
541 *c = screen_desktop_layout.columns - 1 -
542 d % screen_desktop_layout.columns;
543 break;
544 case OB_CORNER_BOTTOMRIGHT:
545 *r = screen_desktop_layout.rows - 1 -
546 d / screen_desktop_layout.columns;
547 *c = screen_desktop_layout.columns - 1 -
548 d % screen_desktop_layout.columns;
549 break;
550 }
551 break;
552 case OB_ORIENTATION_VERT:
553 switch (screen_desktop_layout.start_corner) {
554 case OB_CORNER_TOPLEFT:
555 *r = d % screen_desktop_layout.rows;
556 *c = d / screen_desktop_layout.rows;
557 break;
558 case OB_CORNER_BOTTOMLEFT:
559 *r = screen_desktop_layout.rows - 1 -
560 d % screen_desktop_layout.rows;
561 *c = d / screen_desktop_layout.rows;
562 break;
563 case OB_CORNER_TOPRIGHT:
564 *r = d % screen_desktop_layout.rows;
565 *c = screen_desktop_layout.columns - 1 -
566 d / screen_desktop_layout.rows;
567 break;
568 case OB_CORNER_BOTTOMRIGHT:
569 *r = screen_desktop_layout.rows - 1 -
570 d % screen_desktop_layout.rows;
571 *c = screen_desktop_layout.columns - 1 -
572 d / screen_desktop_layout.rows;
573 break;
574 }
575 break;
576 }
577 }
578
579 static guint translate_row_col(guint r, guint c)
580 {
581 switch (screen_desktop_layout.orientation) {
582 case OB_ORIENTATION_HORZ:
583 switch (screen_desktop_layout.start_corner) {
584 case OB_CORNER_TOPLEFT:
585 return r % screen_desktop_layout.rows *
586 screen_desktop_layout.columns +
587 c % screen_desktop_layout.columns;
588 case OB_CORNER_BOTTOMLEFT:
589 return (screen_desktop_layout.rows - 1 -
590 r % screen_desktop_layout.rows) *
591 screen_desktop_layout.columns +
592 c % screen_desktop_layout.columns;
593 case OB_CORNER_TOPRIGHT:
594 return r % screen_desktop_layout.rows *
595 screen_desktop_layout.columns +
596 (screen_desktop_layout.columns - 1 -
597 c % screen_desktop_layout.columns);
598 case OB_CORNER_BOTTOMRIGHT:
599 return (screen_desktop_layout.rows - 1 -
600 r % screen_desktop_layout.rows) *
601 screen_desktop_layout.columns +
602 (screen_desktop_layout.columns - 1 -
603 c % screen_desktop_layout.columns);
604 }
605 case OB_ORIENTATION_VERT:
606 switch (screen_desktop_layout.start_corner) {
607 case OB_CORNER_TOPLEFT:
608 return c % screen_desktop_layout.columns *
609 screen_desktop_layout.rows +
610 r % screen_desktop_layout.rows;
611 case OB_CORNER_BOTTOMLEFT:
612 return c % screen_desktop_layout.columns *
613 screen_desktop_layout.rows +
614 (screen_desktop_layout.rows - 1 -
615 r % screen_desktop_layout.rows);
616 case OB_CORNER_TOPRIGHT:
617 return (screen_desktop_layout.columns - 1 -
618 c % screen_desktop_layout.columns) *
619 screen_desktop_layout.rows +
620 r % screen_desktop_layout.rows;
621 case OB_CORNER_BOTTOMRIGHT:
622 return (screen_desktop_layout.columns - 1 -
623 c % screen_desktop_layout.columns) *
624 screen_desktop_layout.rows +
625 (screen_desktop_layout.rows - 1 -
626 r % screen_desktop_layout.rows);
627 }
628 }
629 g_assert_not_reached();
630 return 0;
631 }
632
633 void screen_desktop_popup(guint d, gboolean show)
634 {
635 Rect *a;
636
637 if (!show) {
638 pager_popup_hide(desktop_cycle_popup);
639 } else {
640 a = screen_physical_area_monitor(0);
641 pager_popup_position(desktop_cycle_popup, CenterGravity,
642 a->x + a->width / 2, a->y + a->height / 2);
643 pager_popup_max_width(desktop_cycle_popup,
644 MAX(a->width/3, POPUP_WIDTH));
645 pager_popup_show(desktop_cycle_popup, screen_desktop_names[d], d);
646 }
647 }
648
649 guint screen_cycle_desktop(ObDirection dir, gboolean wrap, gboolean linear,
650 gboolean dialog, gboolean done, gboolean cancel)
651 {
652 guint d, r, c;
653
654 d = screen_desktop;
655
656 if ((cancel || done) && dialog)
657 goto show_cycle_dialog;
658
659 get_row_col(d, &r, &c);
660
661 if (linear) {
662 switch (dir) {
663 case OB_DIRECTION_EAST:
664 if (d < screen_num_desktops - 1)
665 ++d;
666 else if (wrap)
667 d = 0;
668 break;
669 case OB_DIRECTION_WEST:
670 if (d > 0)
671 --d;
672 else if (wrap)
673 d = screen_num_desktops - 1;
674 break;
675 default:
676 assert(0);
677 return screen_desktop;
678 }
679 } else {
680 switch (dir) {
681 case OB_DIRECTION_EAST:
682 ++c;
683 if (c >= screen_desktop_layout.columns) {
684 if (wrap) {
685 c = 0;
686 } else {
687 d = screen_desktop;
688 goto show_cycle_dialog;
689 }
690 }
691 d = translate_row_col(r, c);
692 if (d >= screen_num_desktops) {
693 if (wrap) {
694 ++c;
695 } else {
696 d = screen_desktop;
697 goto show_cycle_dialog;
698 }
699 }
700 break;
701 case OB_DIRECTION_WEST:
702 --c;
703 if (c >= screen_desktop_layout.columns) {
704 if (wrap) {
705 c = screen_desktop_layout.columns - 1;
706 } else {
707 d = screen_desktop;
708 goto show_cycle_dialog;
709 }
710 }
711 d = translate_row_col(r, c);
712 if (d >= screen_num_desktops) {
713 if (wrap) {
714 --c;
715 } else {
716 d = screen_desktop;
717 goto show_cycle_dialog;
718 }
719 }
720 break;
721 case OB_DIRECTION_SOUTH:
722 ++r;
723 if (r >= screen_desktop_layout.rows) {
724 if (wrap) {
725 r = 0;
726 } else {
727 d = screen_desktop;
728 goto show_cycle_dialog;
729 }
730 }
731 d = translate_row_col(r, c);
732 if (d >= screen_num_desktops) {
733 if (wrap) {
734 ++r;
735 } else {
736 d = screen_desktop;
737 goto show_cycle_dialog;
738 }
739 }
740 break;
741 case OB_DIRECTION_NORTH:
742 --r;
743 if (r >= screen_desktop_layout.rows) {
744 if (wrap) {
745 r = screen_desktop_layout.rows - 1;
746 } else {
747 d = screen_desktop;
748 goto show_cycle_dialog;
749 }
750 }
751 d = translate_row_col(r, c);
752 if (d >= screen_num_desktops) {
753 if (wrap) {
754 --r;
755 } else {
756 d = screen_desktop;
757 goto show_cycle_dialog;
758 }
759 }
760 break;
761 default:
762 assert(0);
763 return d = screen_desktop;
764 }
765
766 d = translate_row_col(r, c);
767 }
768
769 show_cycle_dialog:
770 if (dialog && !cancel && !done) {
771 screen_desktop_popup(d, TRUE);
772 } else
773 screen_desktop_popup(0, FALSE);
774 return d;
775 }
776
777 void screen_update_layout()
778 {
779 ObOrientation orient;
780 ObCorner corner;
781 guint rows;
782 guint cols;
783 guint32 *data;
784 guint num;
785 gboolean valid = FALSE;
786
787 if (PROP_GETA32(RootWindow(ob_display, ob_screen),
788 net_desktop_layout, cardinal, &data, &num)) {
789 if (num == 3 || num == 4) {
790
791 if (data[0] == prop_atoms.net_wm_orientation_vert)
792 orient = OB_ORIENTATION_VERT;
793 else if (data[0] == prop_atoms.net_wm_orientation_horz)
794 orient = OB_ORIENTATION_HORZ;
795 else
796 goto screen_update_layout_bail;
797
798 if (num < 4)
799 corner = OB_CORNER_TOPLEFT;
800 else {
801 if (data[3] == prop_atoms.net_wm_topleft)
802 corner = OB_CORNER_TOPLEFT;
803 else if (data[3] == prop_atoms.net_wm_topright)
804 corner = OB_CORNER_TOPRIGHT;
805 else if (data[3] == prop_atoms.net_wm_bottomright)
806 corner = OB_CORNER_BOTTOMRIGHT;
807 else if (data[3] == prop_atoms.net_wm_bottomleft)
808 corner = OB_CORNER_BOTTOMLEFT;
809 else
810 goto screen_update_layout_bail;
811 }
812
813 cols = data[1];
814 rows = data[2];
815
816 /* fill in a zero rows/columns */
817 if ((cols == 0 && rows == 0)) { /* both 0's is bad data.. */
818 goto screen_update_layout_bail;
819 } else {
820 if (cols == 0) {
821 cols = screen_num_desktops / rows;
822 if (rows * cols < screen_num_desktops)
823 cols++;
824 if (rows * cols >= screen_num_desktops + cols)
825 rows--;
826 } else if (rows == 0) {
827 rows = screen_num_desktops / cols;
828 if (cols * rows < screen_num_desktops)
829 rows++;
830 if (cols * rows >= screen_num_desktops + rows)
831 cols--;
832 }
833 }
834
835 /* bounds checking */
836 if (orient == OB_ORIENTATION_HORZ) {
837 cols = MIN(screen_num_desktops, cols);
838 rows = MIN(rows, (screen_num_desktops + cols - 1) / cols);
839 cols = screen_num_desktops / rows +
840 !!(screen_num_desktops % rows);
841 } else {
842 rows = MIN(screen_num_desktops, rows);
843 cols = MIN(cols, (screen_num_desktops + rows - 1) / rows);
844 rows = screen_num_desktops / cols +
845 !!(screen_num_desktops % cols);
846 }
847
848 valid = TRUE;
849 }
850 screen_update_layout_bail:
851 g_free(data);
852 }
853
854 if (!valid) {
855 /* defaults */
856 orient = OB_ORIENTATION_HORZ;
857 corner = OB_CORNER_TOPLEFT;
858 rows = 1;
859 cols = screen_num_desktops;
860 }
861
862 screen_desktop_layout.orientation = orient;
863 screen_desktop_layout.start_corner = corner;
864 screen_desktop_layout.rows = rows;
865 screen_desktop_layout.columns = cols;
866 }
867
868 void screen_update_desktop_names()
869 {
870 guint i;
871
872 /* empty the array */
873 g_strfreev(screen_desktop_names);
874 screen_desktop_names = NULL;
875
876 if (PROP_GETSS(RootWindow(ob_display, ob_screen),
877 net_desktop_names, utf8, &screen_desktop_names))
878 for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
879 else
880 i = 0;
881 if (i < screen_num_desktops) {
882 screen_desktop_names = g_renew(gchar*, screen_desktop_names,
883 screen_num_desktops + 1);
884 screen_desktop_names[screen_num_desktops] = NULL;
885 for (; i < screen_num_desktops; ++i)
886 screen_desktop_names[i] = g_strdup_printf("desktop %i", i + 1);
887 }
888
889 /* resize the pager for these names */
890 pager_popup_text_width_to_strings(desktop_cycle_popup,
891 screen_desktop_names,
892 screen_num_desktops);
893 }
894
895 void screen_show_desktop(gboolean show, gboolean restore_focus)
896 {
897 GList *it;
898
899 if (show == screen_showing_desktop) return; /* no change */
900
901 screen_showing_desktop = show;
902
903 if (show) {
904 /* bottom to top */
905 for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
906 if (WINDOW_IS_CLIENT(it->data)) {
907 ObClient *client = it->data;
908 client_showhide(client);
909 }
910 }
911 } else {
912 /* top to bottom */
913 for (it = stacking_list; it; it = g_list_next(it)) {
914 if (WINDOW_IS_CLIENT(it->data)) {
915 ObClient *client = it->data;
916 client_showhide(client);
917 }
918 }
919 }
920
921 if (show) {
922 /* focus desktop */
923 for (it = focus_order; it; it = g_list_next(it)) {
924 ObClient *c = it->data;
925 if (c->type == OB_CLIENT_TYPE_DESKTOP &&
926 (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) &&
927 client_focus(it->data))
928 break;
929 }
930 } else if (restore_focus) {
931 ObClient *c;
932
933 /* use NULL for the "old" argument because the desktop was focused
934 and we don't want to fallback to the desktop by default */
935 if ((c = focus_fallback_target(TRUE, NULL)))
936 client_focus(c);
937 }
938
939 show = !!show; /* make it boolean */
940 PROP_SET32(RootWindow(ob_display, ob_screen),
941 net_showing_desktop, cardinal, show);
942 }
943
944 void screen_install_colormap(ObClient *client, gboolean install)
945 {
946 if (client == NULL) {
947 if (install)
948 XInstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
949 else
950 XUninstallColormap(RrDisplay(ob_rr_inst), RrColormap(ob_rr_inst));
951 } else {
952 xerror_set_ignore(TRUE);
953 if (install) {
954 if (client->colormap != None)
955 XInstallColormap(RrDisplay(ob_rr_inst), client->colormap);
956 } else
957 XUninstallColormap(RrDisplay(ob_rr_inst), client->colormap);
958 xerror_set_ignore(FALSE);
959 }
960 }
961
962 static inline void
963 screen_area_add_strut_left(const StrutPartial *s, const Rect *monitor_area,
964 gint edge, Strut *ret)
965 {
966 if (s->left &&
967 ((s->left_end <= s->left_start) ||
968 (RECT_TOP(*monitor_area) < s->left_end &&
969 RECT_BOTTOM(*monitor_area) > s->left_start)))
970 ret->left = MAX(ret->left, edge);
971 }
972
973 static inline void
974 screen_area_add_strut_top(const StrutPartial *s, const Rect *monitor_area,
975 gint edge, Strut *ret)
976 {
977 if (s->top &&
978 ((s->top_end <= s->top_start) ||
979 (RECT_LEFT(*monitor_area) < s->top_end &&
980 RECT_RIGHT(*monitor_area) > s->top_start)))
981 ret->top = MAX(ret->top, edge);
982 }
983
984 static inline void
985 screen_area_add_strut_right(const StrutPartial *s, const Rect *monitor_area,
986 gint edge, Strut *ret)
987 {
988 if (s->right &&
989 ((s->right_end <= s->right_start) ||
990 (RECT_TOP(*monitor_area) < s->right_end &&
991 RECT_BOTTOM(*monitor_area) > s->right_start)))
992 ret->right = MAX(ret->right, edge);
993 }
994
995 static inline void
996 screen_area_add_strut_bottom(const StrutPartial *s, const Rect *monitor_area,
997 gint edge, Strut *ret)
998 {
999 if (s->bottom &&
1000 ((s->bottom_end <= s->bottom_start) ||
1001 (RECT_LEFT(*monitor_area) < s->bottom_end &&
1002 RECT_RIGHT(*monitor_area) > s->bottom_start)))
1003 ret->bottom = MAX(ret->bottom, edge);
1004 }
1005
1006 void screen_update_areas()
1007 {
1008 guint i, x;
1009 gulong *dims;
1010 GList *it;
1011 gint o;
1012
1013 g_free(monitor_area);
1014 extensions_xinerama_screens(&monitor_area, &screen_num_monitors);
1015
1016 if (area) {
1017 for (i = 0; area[i]; ++i)
1018 g_free(area[i]);
1019 g_free(area);
1020 }
1021
1022 area = g_new(Rect*, screen_num_desktops + 2);
1023 for (i = 0; i < screen_num_desktops + 1; ++i)
1024 area[i] = g_new0(Rect, screen_num_monitors + 1);
1025 area[i] = NULL;
1026
1027 dims = g_new(gulong, 4 * screen_num_desktops);
1028
1029 for (i = 0; i < screen_num_desktops + 1; ++i) {
1030 Strut *struts;
1031 gint l, r, t, b;
1032
1033 struts = g_new0(Strut, screen_num_monitors);
1034
1035 /* calc the xinerama areas */
1036 for (x = 0; x < screen_num_monitors; ++x) {
1037 area[i][x] = monitor_area[x];
1038 if (x == 0) {
1039 l = monitor_area[x].x;
1040 t = monitor_area[x].y;
1041 r = monitor_area[x].x + monitor_area[x].width - 1;
1042 b = monitor_area[x].y + monitor_area[x].height - 1;
1043 } else {
1044 l = MIN(l, monitor_area[x].x);
1045 t = MIN(t, monitor_area[x].y);
1046 r = MAX(r, monitor_area[x].x + monitor_area[x].width - 1);
1047 b = MAX(b, monitor_area[x].y + monitor_area[x].height - 1);
1048 }
1049 }
1050 RECT_SET(area[i][x], l, t, r - l + 1, b - t + 1);
1051
1052 /* apply the struts */
1053
1054 /* find the left-most xin heads, i do this in 2 loops :| */
1055 o = area[i][0].x;
1056 for (x = 1; x < screen_num_monitors; ++x)
1057 o = MIN(o, area[i][x].x);
1058
1059 for (x = 0; x < screen_num_monitors; ++x) {
1060 for (it = client_list; it; it = g_list_next(it)) {
1061 ObClient *c = it->data;
1062 screen_area_add_strut_left(&c->strut,
1063 &monitor_area[x],
1064 o + c->strut.left - area[i][x].x,
1065 &struts[x]);
1066 }
1067 screen_area_add_strut_left(&dock_strut,
1068 &monitor_area[x],
1069 o + dock_strut.left - area[i][x].x,
1070 &struts[x]);
1071
1072 area[i][x].x += struts[x].left;
1073 area[i][x].width -= struts[x].left;
1074 }
1075
1076 /* find the top-most xin heads, i do this in 2 loops :| */
1077 o = area[i][0].y;
1078 for (x = 1; x < screen_num_monitors; ++x)
1079 o = MIN(o, area[i][x].y);
1080
1081 for (x = 0; x < screen_num_monitors; ++x) {
1082 for (it = client_list; it; it = g_list_next(it)) {
1083 ObClient *c = it->data;
1084 screen_area_add_strut_top(&c->strut,
1085 &monitor_area[x],
1086 o + c->strut.top - area[i][x].y,
1087 &struts[x]);
1088 }
1089 screen_area_add_strut_top(&dock_strut,
1090 &monitor_area[x],
1091 o + dock_strut.top - area[i][x].y,
1092 &struts[x]);
1093
1094 area[i][x].y += struts[x].top;
1095 area[i][x].height -= struts[x].top;
1096 }
1097
1098 /* find the right-most xin heads, i do this in 2 loops :| */
1099 o = area[i][0].x + area[i][0].width - 1;
1100 for (x = 1; x < screen_num_monitors; ++x)
1101 o = MAX(o, area[i][x].x + area[i][x].width - 1);
1102
1103 for (x = 0; x < screen_num_monitors; ++x) {
1104 for (it = client_list; it; it = g_list_next(it)) {
1105 ObClient *c = it->data;
1106 screen_area_add_strut_right(&c->strut,
1107 &monitor_area[x],
1108 (area[i][x].x +
1109 area[i][x].width - 1) -
1110 (o - c->strut.right),
1111 &struts[x]);
1112 }
1113 screen_area_add_strut_right(&dock_strut,
1114 &monitor_area[x],
1115 (area[i][x].x +
1116 area[i][x].width - 1) -
1117 (o - dock_strut.right),
1118 &struts[x]);
1119
1120 area[i][x].width -= struts[x].right;
1121 }
1122
1123 /* find the bottom-most xin heads, i do this in 2 loops :| */
1124 o = area[i][0].y + area[i][0].height - 1;
1125 for (x = 1; x < screen_num_monitors; ++x)
1126 o = MAX(o, area[i][x].y + area[i][x].height - 1);
1127
1128 for (x = 0; x < screen_num_monitors; ++x) {
1129 for (it = client_list; it; it = g_list_next(it)) {
1130 ObClient *c = it->data;
1131 screen_area_add_strut_bottom(&c->strut,
1132 &monitor_area[x],
1133 (area[i][x].y +
1134 area[i][x].height - 1) - \
1135 (o - c->strut.bottom),
1136 &struts[x]);
1137 }
1138 screen_area_add_strut_bottom(&dock_strut,
1139 &monitor_area[x],
1140 (area[i][x].y +
1141 area[i][x].height - 1) - \
1142 (o - dock_strut.bottom),
1143 &struts[x]);
1144
1145 area[i][x].height -= struts[x].bottom;
1146 }
1147
1148 l = RECT_LEFT(area[i][0]);
1149 t = RECT_TOP(area[i][0]);
1150 r = RECT_RIGHT(area[i][0]);
1151 b = RECT_BOTTOM(area[i][0]);
1152 for (x = 1; x < screen_num_monitors; ++x) {
1153 l = MIN(l, RECT_LEFT(area[i][x]));
1154 t = MIN(l, RECT_TOP(area[i][x]));
1155 r = MAX(r, RECT_RIGHT(area[i][x]));
1156 b = MAX(b, RECT_BOTTOM(area[i][x]));
1157 }
1158 RECT_SET(area[i][screen_num_monitors], l, t,
1159 r - l + 1, b - t + 1);
1160
1161 /* XXX optimize when this is run? */
1162
1163 /* the area has changed, adjust all the maximized
1164 windows */
1165 for (it = client_list; it; it = g_list_next(it)) {
1166 ObClient *c = it->data;
1167 if (i < screen_num_desktops) {
1168 if (c->desktop == i)
1169 client_reconfigure(c);
1170 } else if (c->desktop == DESKTOP_ALL)
1171 client_reconfigure(c);
1172 }
1173 if (i < screen_num_desktops) {
1174 /* don't set these for the 'all desktops' area */
1175 dims[(i * 4) + 0] = area[i][screen_num_monitors].x;
1176 dims[(i * 4) + 1] = area[i][screen_num_monitors].y;
1177 dims[(i * 4) + 2] = area[i][screen_num_monitors].width;
1178 dims[(i * 4) + 3] = area[i][screen_num_monitors].height;
1179 }
1180
1181 g_free(struts);
1182 }
1183
1184 PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal,
1185 dims, 4 * screen_num_desktops);
1186
1187 g_free(dims);
1188 }
1189
1190 Rect *screen_area(guint desktop)
1191 {
1192 return screen_area_monitor(desktop, screen_num_monitors);
1193 }
1194
1195 Rect *screen_area_monitor(guint desktop, guint head)
1196 {
1197 if (head > screen_num_monitors)
1198 return NULL;
1199 if (desktop >= screen_num_desktops) {
1200 if (desktop == DESKTOP_ALL)
1201 return &area[screen_num_desktops][head];
1202 return NULL;
1203 }
1204 return &area[desktop][head];
1205 }
1206
1207 guint screen_find_monitor(Rect *search)
1208 {
1209 guint i;
1210 guint most = 0;
1211 guint mostv = 0;
1212
1213 for (i = 0; i < screen_num_monitors; ++i) {
1214 Rect *area = screen_physical_area_monitor(i);
1215 if (RECT_INTERSECTS_RECT(*area, *search)) {
1216 Rect r;
1217 guint v;
1218
1219 RECT_SET_INTERSECTION(r, *area, *search);
1220 v = r.width * r.height;
1221
1222 if (v > mostv) {
1223 mostv = v;
1224 most = i;
1225 }
1226 }
1227 }
1228 return most;
1229 }
1230
1231 Rect *screen_physical_area()
1232 {
1233 return screen_physical_area_monitor(screen_num_monitors);
1234 }
1235
1236 Rect *screen_physical_area_monitor(guint head)
1237 {
1238 if (head > screen_num_monitors)
1239 return NULL;
1240 return &monitor_area[head];
1241 }
1242
1243 void screen_set_root_cursor()
1244 {
1245 if (sn_app_starting())
1246 XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1247 ob_cursor(OB_CURSOR_BUSY));
1248 else
1249 XDefineCursor(ob_display, RootWindow(ob_display, ob_screen),
1250 ob_cursor(OB_CURSOR_POINTER));
1251 }
1252
1253 gboolean screen_pointer_pos(gint *x, gint *y)
1254 {
1255 Window w;
1256 gint i;
1257 guint u;
1258 gboolean ret;
1259
1260 ret = !!XQueryPointer(ob_display, RootWindow(ob_display, ob_screen),
1261 &w, &w, x, y, &i, &i, &u);
1262 if (!ret) {
1263 for (i = 0; i < ScreenCount(ob_display); ++i)
1264 if (i != ob_screen)
1265 if (XQueryPointer(ob_display, RootWindow(ob_display, i),
1266 &w, &w, x, y, &i, &i, &u))
1267 break;
1268 }
1269 return ret;
1270 }
This page took 0.090266 seconds and 4 git commands to generate.