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.
26 #include "startupnotify.h"
27 #include "moveresize.h"
36 #include "extensions.h"
37 #include "render/render.h"
42 # include <sys/types.h>
47 /*! The event mask to grab on the root window */
48 #define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \
49 EnterWindowMask | LeaveWindowMask | \
50 SubstructureRedirectMask | FocusChangeMask | \
51 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask)
53 guint screen_num_desktops
;
54 guint screen_num_monitors
;
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
;
64 static Rect
**area
; /* array of desktop holding array of xinerama areas */
65 static Rect
*monitor_area
;
67 static ObPagerPopup
*desktop_cycle_popup
;
69 static gboolean
replace_wm()
73 Window current_wm_sn_owner
;
76 wm_sn
= g_strdup_printf("WM_S%d", ob_screen
);
77 wm_sn_atom
= XInternAtom(ob_display
, wm_sn
, FALSE
);
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
) {
85 g_message(_("A window manager is already running on screen %d"),
89 xerror_set_ignore(TRUE
);
90 xerror_occured
= FALSE
;
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
);
96 xerror_set_ignore(FALSE
);
98 current_wm_sn_owner
= None
;
102 /* Generate a timestamp */
105 XSelectInput(ob_display
, screen_support_win
, PropertyChangeMask
);
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
);
113 XSelectInput(ob_display
, screen_support_win
, NoEventMask
);
115 timestamp
= event
.xproperty
.time
;
118 XSetSelectionOwner(ob_display
, wm_sn_atom
, screen_support_win
,
121 if (XGetSelectionOwner(ob_display
, wm_sn_atom
) != screen_support_win
) {
122 g_message(_("Could not acquire window manager selection on screen %d"),
127 /* Wait for old window manager to go away */
128 if (current_wm_sn_owner
) {
131 const gulong timeout
= G_USEC_PER_SEC
* 15; /* wait for 15s max */
133 while (wait
< timeout
) {
134 if (XCheckWindowEvent(ob_display
, current_wm_sn_owner
,
135 StructureNotifyMask
, &event
) &&
136 event
.type
== DestroyNotify
)
138 g_usleep(G_USEC_PER_SEC
/ 10);
139 wait
+= G_USEC_PER_SEC
/ 10;
142 if (wait
>= timeout
) {
143 g_message(_("The WM on screen %d is not exiting"), ob_screen
);
148 /* Send client message indicating that we are now the WM */
149 prop_message(RootWindow(ob_display
, ob_screen
), prop_atoms
.manager
,
150 timestamp
, wm_sn_atom
, screen_support_win
, 0,
151 SubstructureNotifyMask
);
156 gboolean
screen_annex(const gchar
*program_name
)
158 XSetWindowAttributes attrib
;
161 Atom
*prop_atoms_start
, *wm_supported_pos
;
164 /* create the netwm support window */
165 attrib
.override_redirect
= TRUE
;
166 screen_support_win
= XCreateWindow(ob_display
,
167 RootWindow(ob_display
, ob_screen
),
169 CopyFromParent
, InputOutput
,
171 CWOverrideRedirect
, &attrib
);
172 XMapWindow(ob_display
, screen_support_win
);
173 XLowerWindow(ob_display
, screen_support_win
);
176 XDestroyWindow(ob_display
, screen_support_win
);
180 xerror_set_ignore(TRUE
);
181 xerror_occured
= FALSE
;
182 XSelectInput(ob_display
, RootWindow(ob_display
, ob_screen
),
184 xerror_set_ignore(FALSE
);
185 if (xerror_occured
) {
186 g_message(_("A window manager is already running on screen %d"),
189 XDestroyWindow(ob_display
, screen_support_win
);
194 screen_set_root_cursor();
196 /* set the OPENBOX_PID hint */
198 PROP_SET32(RootWindow(ob_display
, ob_screen
),
199 openbox_pid
, cardinal
, pid
);
201 /* set supporting window */
202 PROP_SET32(RootWindow(ob_display
, ob_screen
),
203 net_supporting_wm_check
, window
, screen_support_win
);
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
);
210 /* set the _NET_SUPPORTED_ATOMS hint */
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;
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_frame_extents
;
277 supported
[i
++] = prop_atoms
.net_startup_id
;
279 supported
[i
++] = prop_atoms
.net_wm_sync_request
;
280 supported
[i
++] = prop_atoms
.net_wm_sync_request_counter
;
283 supported
[i
++] = prop_atoms
.kde_wm_change_state
;
284 supported
[i
++] = prop_atoms
.kde_net_wm_frame_strut
;
285 supported
[i
++] = prop_atoms
.kde_net_wm_window_type_override
;
287 supported
[i
++] = prop_atoms
.openbox_wm_state_undecorated
;
288 supported
[i
++] = prop_atoms
.openbox_pid
;
289 supported
[i
++] = prop_atoms
.openbox_config
;
290 supported
[i
++] = prop_atoms
.openbox_control
;
291 g_assert(i
== num_support
);
293 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
294 net_supported
, atom
, supported
, num_support
);
300 void screen_startup(gboolean reconfig
)
307 desktop_cycle_popup
= pager_popup_new(FALSE
);
308 pager_popup_height(desktop_cycle_popup
, POPUP_HEIGHT
);
313 /* get the initial size */
316 /* get the desktop names */
317 numnames
= g_slist_length(config_desktops_names
);
318 names
= g_new(gchar
*, numnames
+ 1);
319 names
[numnames
] = NULL
;
320 for (i
= 0, it
= config_desktops_names
; it
; ++i
, it
= g_slist_next(it
))
321 names
[i
] = g_strdup(it
->data
);
323 /* set the root window property */
324 PROP_SETSS(RootWindow(ob_display
, ob_screen
), net_desktop_names
,names
);
328 /* set the number of desktops */
329 screen_num_desktops
= 0;
330 screen_set_num_desktops(config_desktops_num
);
332 /* start on the current desktop when a wm was already running */
333 if (PROP_GET32(RootWindow(ob_display
, ob_screen
),
334 net_current_desktop
, cardinal
, &d
) &&
335 d
< screen_num_desktops
)
337 screen_set_desktop(d
, FALSE
);
338 } else if (session_desktop
>= 0)
339 screen_set_desktop(MIN((guint
)session_desktop
,
340 screen_num_desktops
), FALSE
);
342 screen_set_desktop(MIN(config_screen_firstdesk
,
343 screen_num_desktops
) - 1, FALSE
);
345 /* don't start in showing-desktop mode */
346 screen_showing_desktop
= FALSE
;
347 PROP_SET32(RootWindow(ob_display
, ob_screen
),
348 net_showing_desktop
, cardinal
, screen_showing_desktop
);
350 screen_update_layout();
353 void screen_shutdown(gboolean reconfig
)
357 pager_popup_free(desktop_cycle_popup
);
362 XSelectInput(ob_display
, RootWindow(ob_display
, ob_screen
),
365 /* we're not running here no more! */
366 PROP_ERASE(RootWindow(ob_display
, ob_screen
), openbox_pid
);
368 PROP_ERASE(RootWindow(ob_display
, ob_screen
), net_supported
);
369 /* don't keep this mode */
370 PROP_ERASE(RootWindow(ob_display
, ob_screen
), net_showing_desktop
);
372 XDestroyWindow(ob_display
, screen_support_win
);
374 g_strfreev(screen_desktop_names
);
375 screen_desktop_names
= NULL
;
377 for (r
= area
; *r
; ++r
)
385 static gint oldw
= 0, oldh
= 0;
390 w
= WidthOfScreen(ScreenOfDisplay(ob_display
, ob_screen
));
391 h
= HeightOfScreen(ScreenOfDisplay(ob_display
, ob_screen
));
393 if (w
== oldw
&& h
== oldh
) return;
397 /* Set the _NET_DESKTOP_GEOMETRY hint */
398 screen_physical_size
.width
= geometry
[0] = w
;
399 screen_physical_size
.height
= geometry
[1] = h
;
400 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
401 net_desktop_geometry
, cardinal
, geometry
, 2);
403 if (ob_state() == OB_STATE_STARTING
)
406 screen_update_areas();
409 for (it
= client_list
; it
; it
= g_list_next(it
))
410 client_move_onscreen(it
->data
, FALSE
);
413 void screen_set_num_desktops(guint num
)
421 if (screen_num_desktops
== num
) return;
423 old
= screen_num_desktops
;
424 screen_num_desktops
= num
;
425 PROP_SET32(RootWindow(ob_display
, ob_screen
),
426 net_number_of_desktops
, cardinal
, num
);
428 /* set the viewport hint */
429 viewport
= g_new0(gulong
, num
* 2);
430 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
431 net_desktop_viewport
, cardinal
, viewport
, num
* 2);
434 /* the number of rows/columns will differ */
435 screen_update_layout();
437 /* move windows on desktops that will no longer exist! */
438 for (it
= client_list
; it
; it
= g_list_next(it
)) {
439 ObClient
*c
= it
->data
;
440 if (c
->desktop
>= num
&& c
->desktop
!= DESKTOP_ALL
)
441 client_set_desktop(c
, num
- 1, FALSE
);
444 /* change our struts/area to match (after moving windows) */
445 screen_update_areas();
447 /* may be some unnamed desktops that we need to fill in with names
448 (after updating the areas so the popup can resize) */
449 screen_update_desktop_names();
451 /* change our desktop if we're on one that no longer exists! */
452 if (screen_desktop
>= screen_num_desktops
)
453 screen_set_desktop(num
- 1, TRUE
);
456 void screen_set_desktop(guint num
, gboolean dofocus
)
462 g_assert(num
< screen_num_desktops
);
464 old
= screen_desktop
;
465 screen_desktop
= num
;
466 PROP_SET32(RootWindow(ob_display
, ob_screen
),
467 net_current_desktop
, cardinal
, num
);
469 if (old
== num
) return;
471 screen_last_desktop
= old
;
473 ob_debug("Moving to desktop %d\n", num
+1);
475 if (moveresize_client
)
476 client_set_desktop(moveresize_client
, num
, TRUE
);
478 /* show windows before hiding the rest to lessen the enter/leave events */
480 /* show windows from top to bottom */
481 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
482 if (WINDOW_IS_CLIENT(it
->data
)) {
483 ObClient
*c
= it
->data
;
488 /* have to try focus here because when you leave an empty desktop
489 there is no focus out to watch for
491 do this before hiding the windows so if helper windows are coming
492 with us, they don't get hidden
494 if (dofocus
&& (c
= focus_fallback_target(TRUE
, focus_client
))) {
495 /* only do the flicker reducing stuff ahead of time if we are going
496 to call xsetinputfocus on the window ourselves. otherwise there is
497 no guarantee the window will actually take focus.. */
499 /* do this here so that if you switch desktops to a window with
500 helper windows then the helper windows won't flash */
501 client_bring_helper_windows(c
);
502 /* reduce flicker by hiliting now rather than waiting for the
503 server FocusIn event */
504 frame_adjust_focus(c
->frame
, TRUE
);
509 /* hide windows from bottom to top */
510 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
)) {
511 if (WINDOW_IS_CLIENT(it
->data
)) {
512 ObClient
*c
= it
->data
;
517 event_ignore_queued_enters();
519 if (event_curtime
!= CurrentTime
)
520 screen_desktop_user_time
= event_curtime
;
523 static void get_row_col(guint d
, guint
*r
, guint
*c
)
525 switch (screen_desktop_layout
.orientation
) {
526 case OB_ORIENTATION_HORZ
:
527 switch (screen_desktop_layout
.start_corner
) {
528 case OB_CORNER_TOPLEFT
:
529 *r
= d
/ screen_desktop_layout
.columns
;
530 *c
= d
% screen_desktop_layout
.columns
;
532 case OB_CORNER_BOTTOMLEFT
:
533 *r
= screen_desktop_layout
.rows
- 1 -
534 d
/ screen_desktop_layout
.columns
;
535 *c
= d
% screen_desktop_layout
.columns
;
537 case OB_CORNER_TOPRIGHT
:
538 *r
= d
/ screen_desktop_layout
.columns
;
539 *c
= screen_desktop_layout
.columns
- 1 -
540 d
% screen_desktop_layout
.columns
;
542 case OB_CORNER_BOTTOMRIGHT
:
543 *r
= screen_desktop_layout
.rows
- 1 -
544 d
/ screen_desktop_layout
.columns
;
545 *c
= screen_desktop_layout
.columns
- 1 -
546 d
% screen_desktop_layout
.columns
;
550 case OB_ORIENTATION_VERT
:
551 switch (screen_desktop_layout
.start_corner
) {
552 case OB_CORNER_TOPLEFT
:
553 *r
= d
% screen_desktop_layout
.rows
;
554 *c
= d
/ screen_desktop_layout
.rows
;
556 case OB_CORNER_BOTTOMLEFT
:
557 *r
= screen_desktop_layout
.rows
- 1 -
558 d
% screen_desktop_layout
.rows
;
559 *c
= d
/ screen_desktop_layout
.rows
;
561 case OB_CORNER_TOPRIGHT
:
562 *r
= d
% screen_desktop_layout
.rows
;
563 *c
= screen_desktop_layout
.columns
- 1 -
564 d
/ screen_desktop_layout
.rows
;
566 case OB_CORNER_BOTTOMRIGHT
:
567 *r
= screen_desktop_layout
.rows
- 1 -
568 d
% screen_desktop_layout
.rows
;
569 *c
= screen_desktop_layout
.columns
- 1 -
570 d
/ screen_desktop_layout
.rows
;
577 static guint
translate_row_col(guint r
, guint c
)
579 switch (screen_desktop_layout
.orientation
) {
580 case OB_ORIENTATION_HORZ
:
581 switch (screen_desktop_layout
.start_corner
) {
582 case OB_CORNER_TOPLEFT
:
583 return r
% screen_desktop_layout
.rows
*
584 screen_desktop_layout
.columns
+
585 c
% screen_desktop_layout
.columns
;
586 case OB_CORNER_BOTTOMLEFT
:
587 return (screen_desktop_layout
.rows
- 1 -
588 r
% screen_desktop_layout
.rows
) *
589 screen_desktop_layout
.columns
+
590 c
% screen_desktop_layout
.columns
;
591 case OB_CORNER_TOPRIGHT
:
592 return r
% screen_desktop_layout
.rows
*
593 screen_desktop_layout
.columns
+
594 (screen_desktop_layout
.columns
- 1 -
595 c
% screen_desktop_layout
.columns
);
596 case OB_CORNER_BOTTOMRIGHT
:
597 return (screen_desktop_layout
.rows
- 1 -
598 r
% screen_desktop_layout
.rows
) *
599 screen_desktop_layout
.columns
+
600 (screen_desktop_layout
.columns
- 1 -
601 c
% screen_desktop_layout
.columns
);
603 case OB_ORIENTATION_VERT
:
604 switch (screen_desktop_layout
.start_corner
) {
605 case OB_CORNER_TOPLEFT
:
606 return c
% screen_desktop_layout
.columns
*
607 screen_desktop_layout
.rows
+
608 r
% screen_desktop_layout
.rows
;
609 case OB_CORNER_BOTTOMLEFT
:
610 return c
% screen_desktop_layout
.columns
*
611 screen_desktop_layout
.rows
+
612 (screen_desktop_layout
.rows
- 1 -
613 r
% screen_desktop_layout
.rows
);
614 case OB_CORNER_TOPRIGHT
:
615 return (screen_desktop_layout
.columns
- 1 -
616 c
% screen_desktop_layout
.columns
) *
617 screen_desktop_layout
.rows
+
618 r
% screen_desktop_layout
.rows
;
619 case OB_CORNER_BOTTOMRIGHT
:
620 return (screen_desktop_layout
.columns
- 1 -
621 c
% screen_desktop_layout
.columns
) *
622 screen_desktop_layout
.rows
+
623 (screen_desktop_layout
.rows
- 1 -
624 r
% screen_desktop_layout
.rows
);
627 g_assert_not_reached();
631 void screen_desktop_popup(guint d
, gboolean show
)
636 pager_popup_hide(desktop_cycle_popup
);
638 a
= screen_physical_area_monitor(0);
639 pager_popup_position(desktop_cycle_popup
, CenterGravity
,
640 a
->x
+ a
->width
/ 2, a
->y
+ a
->height
/ 2);
641 pager_popup_max_width(desktop_cycle_popup
,
642 MAX(a
->width
/3, POPUP_WIDTH
));
643 pager_popup_show(desktop_cycle_popup
, screen_desktop_names
[d
], d
);
647 guint
screen_cycle_desktop(ObDirection dir
, gboolean wrap
, gboolean linear
,
648 gboolean dialog
, gboolean done
, gboolean cancel
)
650 static gboolean first
= TRUE
;
651 static guint origd
, d
;
657 } else if (done
&& dialog
) {
662 d
= origd
= screen_desktop
;
665 get_row_col(d
, &r
, &c
);
669 case OB_DIRECTION_EAST
:
670 if (d
< screen_num_desktops
- 1)
675 case OB_DIRECTION_WEST
:
679 d
= screen_num_desktops
- 1;
683 return screen_desktop
;
687 case OB_DIRECTION_EAST
:
689 if (c
>= screen_desktop_layout
.columns
) {
694 goto show_cycle_dialog
;
697 d
= translate_row_col(r
, c
);
698 if (d
>= screen_num_desktops
) {
703 goto show_cycle_dialog
;
707 case OB_DIRECTION_WEST
:
709 if (c
>= screen_desktop_layout
.columns
) {
711 c
= screen_desktop_layout
.columns
- 1;
714 goto show_cycle_dialog
;
717 d
= translate_row_col(r
, c
);
718 if (d
>= screen_num_desktops
) {
723 goto show_cycle_dialog
;
727 case OB_DIRECTION_SOUTH
:
729 if (r
>= screen_desktop_layout
.rows
) {
734 goto show_cycle_dialog
;
737 d
= translate_row_col(r
, c
);
738 if (d
>= screen_num_desktops
) {
743 goto show_cycle_dialog
;
747 case OB_DIRECTION_NORTH
:
749 if (r
>= screen_desktop_layout
.rows
) {
751 r
= screen_desktop_layout
.rows
- 1;
754 goto show_cycle_dialog
;
757 d
= translate_row_col(r
, c
);
758 if (d
>= screen_num_desktops
) {
763 goto show_cycle_dialog
;
769 return d
= screen_desktop
;
772 d
= translate_row_col(r
, c
);
777 screen_desktop_popup(d
, TRUE
);
784 screen_desktop_popup(0, FALSE
);
789 void screen_update_layout()
791 ObOrientation orient
;
797 gboolean valid
= FALSE
;
799 if (PROP_GETA32(RootWindow(ob_display
, ob_screen
),
800 net_desktop_layout
, cardinal
, &data
, &num
)) {
801 if (num
== 3 || num
== 4) {
803 if (data
[0] == prop_atoms
.net_wm_orientation_vert
)
804 orient
= OB_ORIENTATION_VERT
;
805 else if (data
[0] == prop_atoms
.net_wm_orientation_horz
)
806 orient
= OB_ORIENTATION_HORZ
;
808 goto screen_update_layout_bail
;
811 corner
= OB_CORNER_TOPLEFT
;
813 if (data
[3] == prop_atoms
.net_wm_topleft
)
814 corner
= OB_CORNER_TOPLEFT
;
815 else if (data
[3] == prop_atoms
.net_wm_topright
)
816 corner
= OB_CORNER_TOPRIGHT
;
817 else if (data
[3] == prop_atoms
.net_wm_bottomright
)
818 corner
= OB_CORNER_BOTTOMRIGHT
;
819 else if (data
[3] == prop_atoms
.net_wm_bottomleft
)
820 corner
= OB_CORNER_BOTTOMLEFT
;
822 goto screen_update_layout_bail
;
828 /* fill in a zero rows/columns */
829 if ((cols
== 0 && rows
== 0)) { /* both 0's is bad data.. */
830 goto screen_update_layout_bail
;
833 cols
= screen_num_desktops
/ rows
;
834 if (rows
* cols
< screen_num_desktops
)
836 if (rows
* cols
>= screen_num_desktops
+ cols
)
838 } else if (rows
== 0) {
839 rows
= screen_num_desktops
/ cols
;
840 if (cols
* rows
< screen_num_desktops
)
842 if (cols
* rows
>= screen_num_desktops
+ rows
)
847 /* bounds checking */
848 if (orient
== OB_ORIENTATION_HORZ
) {
849 cols
= MIN(screen_num_desktops
, cols
);
850 rows
= MIN(rows
, (screen_num_desktops
+ cols
- 1) / cols
);
851 cols
= screen_num_desktops
/ rows
+
852 !!(screen_num_desktops
% rows
);
854 rows
= MIN(screen_num_desktops
, rows
);
855 cols
= MIN(cols
, (screen_num_desktops
+ rows
- 1) / rows
);
856 rows
= screen_num_desktops
/ cols
+
857 !!(screen_num_desktops
% cols
);
862 screen_update_layout_bail
:
868 orient
= OB_ORIENTATION_HORZ
;
869 corner
= OB_CORNER_TOPLEFT
;
871 cols
= screen_num_desktops
;
874 screen_desktop_layout
.orientation
= orient
;
875 screen_desktop_layout
.start_corner
= corner
;
876 screen_desktop_layout
.rows
= rows
;
877 screen_desktop_layout
.columns
= cols
;
880 void screen_update_desktop_names()
884 /* empty the array */
885 g_strfreev(screen_desktop_names
);
886 screen_desktop_names
= NULL
;
888 if (PROP_GETSS(RootWindow(ob_display
, ob_screen
),
889 net_desktop_names
, utf8
, &screen_desktop_names
))
890 for (i
= 0; screen_desktop_names
[i
] && i
< screen_num_desktops
; ++i
);
893 if (i
< screen_num_desktops
) {
894 screen_desktop_names
= g_renew(gchar
*, screen_desktop_names
,
895 screen_num_desktops
+ 1);
896 screen_desktop_names
[screen_num_desktops
] = NULL
;
897 for (; i
< screen_num_desktops
; ++i
)
898 screen_desktop_names
[i
] = g_strdup_printf("desktop %i", i
+ 1);
901 /* resize the pager for these names */
902 pager_popup_text_width_to_strings(desktop_cycle_popup
,
903 screen_desktop_names
,
904 screen_num_desktops
);
907 void screen_show_desktop(gboolean show
, gboolean restore_focus
)
911 if (show
== screen_showing_desktop
) return; /* no change */
913 screen_showing_desktop
= show
;
917 for (it
= g_list_last(stacking_list
); it
; it
= g_list_previous(it
)) {
918 if (WINDOW_IS_CLIENT(it
->data
)) {
919 ObClient
*client
= it
->data
;
920 client_showhide(client
);
925 for (it
= stacking_list
; it
; it
= g_list_next(it
)) {
926 if (WINDOW_IS_CLIENT(it
->data
)) {
927 ObClient
*client
= it
->data
;
928 client_showhide(client
);
935 for (it
= focus_order
; it
; it
= g_list_next(it
)) {
936 ObClient
*c
= it
->data
;
937 if (c
->type
== OB_CLIENT_TYPE_DESKTOP
&&
938 (c
->desktop
== screen_desktop
|| c
->desktop
== DESKTOP_ALL
) &&
939 client_focus(it
->data
))
942 } else if (restore_focus
) {
945 /* use NULL for the "old" argument because the desktop was focused
946 and we don't want to fallback to the desktop by default */
947 if ((c
= focus_fallback_target(TRUE
, NULL
)))
951 show
= !!show
; /* make it boolean */
952 PROP_SET32(RootWindow(ob_display
, ob_screen
),
953 net_showing_desktop
, cardinal
, show
);
956 void screen_install_colormap(ObClient
*client
, gboolean install
)
958 if (client
== NULL
) {
960 XInstallColormap(RrDisplay(ob_rr_inst
), RrColormap(ob_rr_inst
));
962 XUninstallColormap(RrDisplay(ob_rr_inst
), RrColormap(ob_rr_inst
));
964 xerror_set_ignore(TRUE
);
966 if (client
->colormap
!= None
)
967 XInstallColormap(RrDisplay(ob_rr_inst
), client
->colormap
);
969 XUninstallColormap(RrDisplay(ob_rr_inst
), client
->colormap
);
970 xerror_set_ignore(FALSE
);
975 screen_area_add_strut_left(const StrutPartial
*s
, const Rect
*monitor_area
,
976 gint edge
, Strut
*ret
)
979 ((s
->left_end
<= s
->left_start
) ||
980 (RECT_TOP(*monitor_area
) < s
->left_end
&&
981 RECT_BOTTOM(*monitor_area
) > s
->left_start
)))
982 ret
->left
= MAX(ret
->left
, edge
);
986 screen_area_add_strut_top(const StrutPartial
*s
, const Rect
*monitor_area
,
987 gint edge
, Strut
*ret
)
990 ((s
->top_end
<= s
->top_start
) ||
991 (RECT_LEFT(*monitor_area
) < s
->top_end
&&
992 RECT_RIGHT(*monitor_area
) > s
->top_start
)))
993 ret
->top
= MAX(ret
->top
, edge
);
997 screen_area_add_strut_right(const StrutPartial
*s
, const Rect
*monitor_area
,
998 gint edge
, Strut
*ret
)
1001 ((s
->right_end
<= s
->right_start
) ||
1002 (RECT_TOP(*monitor_area
) < s
->right_end
&&
1003 RECT_BOTTOM(*monitor_area
) > s
->right_start
)))
1004 ret
->right
= MAX(ret
->right
, edge
);
1008 screen_area_add_strut_bottom(const StrutPartial
*s
, const Rect
*monitor_area
,
1009 gint edge
, Strut
*ret
)
1012 ((s
->bottom_end
<= s
->bottom_start
) ||
1013 (RECT_LEFT(*monitor_area
) < s
->bottom_end
&&
1014 RECT_RIGHT(*monitor_area
) > s
->bottom_start
)))
1015 ret
->bottom
= MAX(ret
->bottom
, edge
);
1018 void screen_update_areas()
1025 g_free(monitor_area
);
1026 extensions_xinerama_screens(&monitor_area
, &screen_num_monitors
);
1029 for (i
= 0; area
[i
]; ++i
)
1034 area
= g_new(Rect
*, screen_num_desktops
+ 2);
1035 for (i
= 0; i
< screen_num_desktops
+ 1; ++i
)
1036 area
[i
] = g_new0(Rect
, screen_num_monitors
+ 1);
1039 dims
= g_new(gulong
, 4 * screen_num_desktops
);
1041 for (i
= 0; i
< screen_num_desktops
+ 1; ++i
) {
1045 struts
= g_new0(Strut
, screen_num_monitors
);
1047 /* calc the xinerama areas */
1048 for (x
= 0; x
< screen_num_monitors
; ++x
) {
1049 area
[i
][x
] = monitor_area
[x
];
1051 l
= monitor_area
[x
].x
;
1052 t
= monitor_area
[x
].y
;
1053 r
= monitor_area
[x
].x
+ monitor_area
[x
].width
- 1;
1054 b
= monitor_area
[x
].y
+ monitor_area
[x
].height
- 1;
1056 l
= MIN(l
, monitor_area
[x
].x
);
1057 t
= MIN(t
, monitor_area
[x
].y
);
1058 r
= MAX(r
, monitor_area
[x
].x
+ monitor_area
[x
].width
- 1);
1059 b
= MAX(b
, monitor_area
[x
].y
+ monitor_area
[x
].height
- 1);
1062 RECT_SET(area
[i
][x
], l
, t
, r
- l
+ 1, b
- t
+ 1);
1064 /* apply the struts */
1066 /* find the left-most xin heads, i do this in 2 loops :| */
1068 for (x
= 1; x
< screen_num_monitors
; ++x
)
1069 o
= MIN(o
, area
[i
][x
].x
);
1071 for (x
= 0; x
< screen_num_monitors
; ++x
) {
1072 for (it
= client_list
; it
; it
= g_list_next(it
)) {
1073 ObClient
*c
= it
->data
;
1074 screen_area_add_strut_left(&c
->strut
,
1076 o
+ c
->strut
.left
- area
[i
][x
].x
,
1079 screen_area_add_strut_left(&dock_strut
,
1081 o
+ dock_strut
.left
- area
[i
][x
].x
,
1084 area
[i
][x
].x
+= struts
[x
].left
;
1085 area
[i
][x
].width
-= struts
[x
].left
;
1088 /* find the top-most xin heads, i do this in 2 loops :| */
1090 for (x
= 1; x
< screen_num_monitors
; ++x
)
1091 o
= MIN(o
, area
[i
][x
].y
);
1093 for (x
= 0; x
< screen_num_monitors
; ++x
) {
1094 for (it
= client_list
; it
; it
= g_list_next(it
)) {
1095 ObClient
*c
= it
->data
;
1096 screen_area_add_strut_top(&c
->strut
,
1098 o
+ c
->strut
.top
- area
[i
][x
].y
,
1101 screen_area_add_strut_top(&dock_strut
,
1103 o
+ dock_strut
.top
- area
[i
][x
].y
,
1106 area
[i
][x
].y
+= struts
[x
].top
;
1107 area
[i
][x
].height
-= struts
[x
].top
;
1110 /* find the right-most xin heads, i do this in 2 loops :| */
1111 o
= area
[i
][0].x
+ area
[i
][0].width
- 1;
1112 for (x
= 1; x
< screen_num_monitors
; ++x
)
1113 o
= MAX(o
, area
[i
][x
].x
+ area
[i
][x
].width
- 1);
1115 for (x
= 0; x
< screen_num_monitors
; ++x
) {
1116 for (it
= client_list
; it
; it
= g_list_next(it
)) {
1117 ObClient
*c
= it
->data
;
1118 screen_area_add_strut_right(&c
->strut
,
1121 area
[i
][x
].width
- 1) -
1122 (o
- c
->strut
.right
),
1125 screen_area_add_strut_right(&dock_strut
,
1128 area
[i
][x
].width
- 1) -
1129 (o
- dock_strut
.right
),
1132 area
[i
][x
].width
-= struts
[x
].right
;
1135 /* find the bottom-most xin heads, i do this in 2 loops :| */
1136 o
= area
[i
][0].y
+ area
[i
][0].height
- 1;
1137 for (x
= 1; x
< screen_num_monitors
; ++x
)
1138 o
= MAX(o
, area
[i
][x
].y
+ area
[i
][x
].height
- 1);
1140 for (x
= 0; x
< screen_num_monitors
; ++x
) {
1141 for (it
= client_list
; it
; it
= g_list_next(it
)) {
1142 ObClient
*c
= it
->data
;
1143 screen_area_add_strut_bottom(&c
->strut
,
1146 area
[i
][x
].height
- 1) - \
1147 (o
- c
->strut
.bottom
),
1150 screen_area_add_strut_bottom(&dock_strut
,
1153 area
[i
][x
].height
- 1) - \
1154 (o
- dock_strut
.bottom
),
1157 area
[i
][x
].height
-= struts
[x
].bottom
;
1160 l
= RECT_LEFT(area
[i
][0]);
1161 t
= RECT_TOP(area
[i
][0]);
1162 r
= RECT_RIGHT(area
[i
][0]);
1163 b
= RECT_BOTTOM(area
[i
][0]);
1164 for (x
= 1; x
< screen_num_monitors
; ++x
) {
1165 l
= MIN(l
, RECT_LEFT(area
[i
][x
]));
1166 t
= MIN(l
, RECT_TOP(area
[i
][x
]));
1167 r
= MAX(r
, RECT_RIGHT(area
[i
][x
]));
1168 b
= MAX(b
, RECT_BOTTOM(area
[i
][x
]));
1170 RECT_SET(area
[i
][screen_num_monitors
], l
, t
,
1171 r
- l
+ 1, b
- t
+ 1);
1173 /* XXX optimize when this is run? */
1175 /* the area has changed, adjust all the maximized
1177 for (it
= client_list
; it
; it
= g_list_next(it
)) {
1178 ObClient
*c
= it
->data
;
1179 if (i
< screen_num_desktops
) {
1180 if (c
->desktop
== i
)
1181 client_reconfigure(c
);
1182 } else if (c
->desktop
== DESKTOP_ALL
)
1183 client_reconfigure(c
);
1185 if (i
< screen_num_desktops
) {
1186 /* don't set these for the 'all desktops' area */
1187 dims
[(i
* 4) + 0] = area
[i
][screen_num_monitors
].x
;
1188 dims
[(i
* 4) + 1] = area
[i
][screen_num_monitors
].y
;
1189 dims
[(i
* 4) + 2] = area
[i
][screen_num_monitors
].width
;
1190 dims
[(i
* 4) + 3] = area
[i
][screen_num_monitors
].height
;
1196 PROP_SETA32(RootWindow(ob_display
, ob_screen
), net_workarea
, cardinal
,
1197 dims
, 4 * screen_num_desktops
);
1202 Rect
*screen_area(guint desktop
)
1204 return screen_area_monitor(desktop
, screen_num_monitors
);
1207 Rect
*screen_area_monitor(guint desktop
, guint head
)
1209 if (head
> screen_num_monitors
)
1211 if (desktop
>= screen_num_desktops
) {
1212 if (desktop
== DESKTOP_ALL
)
1213 return &area
[screen_num_desktops
][head
];
1216 return &area
[desktop
][head
];
1219 guint
screen_find_monitor(Rect
*search
)
1225 for (i
= 0; i
< screen_num_monitors
; ++i
) {
1226 Rect
*area
= screen_physical_area_monitor(i
);
1227 if (RECT_INTERSECTS_RECT(*area
, *search
)) {
1231 RECT_SET_INTERSECTION(r
, *area
, *search
);
1232 v
= r
.width
* r
.height
;
1243 Rect
*screen_physical_area()
1245 return screen_physical_area_monitor(screen_num_monitors
);
1248 Rect
*screen_physical_area_monitor(guint head
)
1250 if (head
> screen_num_monitors
)
1252 return &monitor_area
[head
];
1255 void screen_set_root_cursor()
1257 if (sn_app_starting())
1258 XDefineCursor(ob_display
, RootWindow(ob_display
, ob_screen
),
1259 ob_cursor(OB_CURSOR_BUSY
));
1261 XDefineCursor(ob_display
, RootWindow(ob_display
, ob_screen
),
1262 ob_cursor(OB_CURSOR_POINTER
));
1265 gboolean
screen_pointer_pos(gint
*x
, gint
*y
)
1271 return !!XQueryPointer(ob_display
, RootWindow(ob_display
, ob_screen
),
1272 &w
, &w
, x
, y
, &i
, &i
, &u
);