7 #include "moveresize.h"
9 #include "extensions.h"
21 #include "render/render.h"
24 #include <X11/Xutil.h>
26 /*! The event mask to grab on client windows */
27 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
30 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
33 GList
*client_list
= NULL
;
35 static void client_get_all(ObClient
*self
);
36 static void client_toggle_border(ObClient
*self
, gboolean show
);
37 static void client_get_area(ObClient
*self
);
38 static void client_get_desktop(ObClient
*self
);
39 static void client_get_state(ObClient
*self
);
40 static void client_get_shaped(ObClient
*self
);
41 static void client_get_mwm_hints(ObClient
*self
);
42 static void client_get_gravity(ObClient
*self
);
43 static void client_showhide(ObClient
*self
);
44 static void client_change_allowed_actions(ObClient
*self
);
45 static void client_change_state(ObClient
*self
);
46 static void client_apply_startup_state(ObClient
*self
);
47 static void client_restore_session_state(ObClient
*self
);
54 void client_shutdown()
58 void client_set_list()
60 Window
*windows
, *win_it
;
62 guint size
= g_list_length(client_list
);
64 /* create an array of the window ids */
66 windows
= g_new(Window
, size
);
68 for (it
= client_list
; it
!= NULL
; it
= it
->next
, ++win_it
)
69 *win_it
= ((ObClient
*)it
->data
)->window
;
73 PROP_SETA32(RootWindow(ob_display
, ob_screen
),
74 net_client_list
, window
, (guint32
*)windows
, size
);
83 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, void *data)
87 for (it = self->transients; it; it = it->next) {
88 if (!func(it->data, data)) return;
89 client_foreach_transient(it->data, func, data);
93 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, void *data)
95 if (self->transient_for) {
96 if (self->transient_for != OB_TRAN_GROUP) {
97 if (!func(self->transient_for, data)) return;
98 client_foreach_ancestor(self->transient_for, func, data);
102 for (it = self->group->members; it; it = it->next)
103 if (it->data != self &&
104 !((ObClient*)it->data)->transient_for) {
105 if (!func(it->data, data)) return;
106 client_foreach_ancestor(it->data, func, data);
113 void client_manage_all()
115 unsigned int i
, j
, nchild
;
118 XWindowAttributes attrib
;
120 XQueryTree(ob_display
, RootWindow(ob_display
, ob_screen
),
121 &w
, &w
, &children
, &nchild
);
123 /* remove all icon windows from the list */
124 for (i
= 0; i
< nchild
; i
++) {
125 if (children
[i
] == None
) continue;
126 wmhints
= XGetWMHints(ob_display
, children
[i
]);
128 if ((wmhints
->flags
& IconWindowHint
) &&
129 (wmhints
->icon_window
!= children
[i
]))
130 for (j
= 0; j
< nchild
; j
++)
131 if (children
[j
] == wmhints
->icon_window
) {
139 for (i
= 0; i
< nchild
; ++i
) {
140 if (children
[i
] == None
)
142 if (XGetWindowAttributes(ob_display
, children
[i
], &attrib
)) {
143 if (attrib
.override_redirect
) continue;
145 if (attrib
.map_state
!= IsUnmapped
)
146 client_manage(children
[i
]);
151 /* stack them as they were on startup!
152 why with stacking_lower? Why, because then windows who aren't in the
153 stacking list are on the top where you can see them instead of buried
155 for (i
= startup_stack_size
; i
> 0; --i
) {
158 w
= startup_stack_order
[i
-1];
159 obw
= g_hash_table_lookup(window_map
, &w
);
161 g_assert(WINDOW_IS_CLIENT(obw
));
162 stacking_lower(CLIENT_AS_WINDOW(obw
));
165 g_free(startup_stack_order
);
166 startup_stack_order
= NULL
;
167 startup_stack_size
= 0;
169 if (config_focus_new
) {
172 active
= g_hash_table_lookup(window_map
, &startup_active
);
174 g_assert(WINDOW_IS_CLIENT(active
));
175 if (!client_focus(WINDOW_AS_CLIENT(active
)))
176 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
178 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
182 void client_manage(Window window
)
186 XWindowAttributes attrib
;
187 XSetWindowAttributes attrib_set
;
189 gboolean activate
= FALSE
;
193 /* check if it has already been unmapped by the time we started mapping
194 the grab does a sync so we don't have to here */
195 if (XCheckTypedWindowEvent(ob_display
, window
, DestroyNotify
, &e
) ||
196 XCheckTypedWindowEvent(ob_display
, window
, UnmapNotify
, &e
)) {
197 XPutBackEvent(ob_display
, &e
);
200 return; /* don't manage it */
203 /* make sure it isn't an override-redirect window */
204 if (!XGetWindowAttributes(ob_display
, window
, &attrib
) ||
205 attrib
.override_redirect
) {
207 return; /* don't manage it */
210 /* is the window a docking app */
211 if ((wmhint
= XGetWMHints(ob_display
, window
))) {
212 if ((wmhint
->flags
& StateHint
) &&
213 wmhint
->initial_state
== WithdrawnState
) {
214 dock_add(window
, wmhint
);
222 ob_debug("Managing window: %lx\n", window
);
224 /* choose the events we want to receive on the CLIENT window */
225 attrib_set
.event_mask
= CLIENT_EVENTMASK
;
226 attrib_set
.do_not_propagate_mask
= CLIENT_NOPROPAGATEMASK
;
227 XChangeWindowAttributes(ob_display
, window
,
228 CWEventMask
|CWDontPropagate
, &attrib_set
);
231 /* create the ObClient struct, and populate it from the hints on the
233 self
= g_new(ObClient
, 1);
234 self
->obwin
.type
= Window_Client
;
235 self
->window
= window
;
237 client_get_all(self
);
238 client_restore_session_state(self
);
240 client_change_state(self
);
242 /* remove the client's border (and adjust re gravity) */
243 client_toggle_border(self
, FALSE
);
245 /* specify that if we exit, the window should not be destroyed and should
246 be reparented back to root automatically */
247 XChangeSaveSet(ob_display
, window
, SetModeInsert
);
249 /* create the decoration frame for the client window */
250 self
->frame
= frame_new();
252 frame_grab_client(self
->frame
, self
);
254 client_apply_startup_state(self
);
258 /* update the focus lists */
259 focus_order_add_new(self
);
261 stacking_add(CLIENT_AS_WINDOW(self
));
263 /* focus the new window? */
264 if (ob_state() != OB_STATE_STARTING
&& config_focus_new
&&
265 /* note the check against Type_Normal/Dialog, not client_normal(self),
266 which would also include other types. in this case we want more
267 strict rules for focus */
268 (self
->type
== OB_CLIENT_TYPE_NORMAL
||
269 self
->type
== OB_CLIENT_TYPE_DIALOG
))
271 if (self
->desktop
!= screen_desktop
) {
272 /* activate the window */
275 gboolean group_foc
= FALSE
;
280 for (it
= self
->group
->members
; it
; it
= it
->next
)
282 if (client_focused(it
->data
))
290 (!self
->transient_for
&& (!self
->group
||
291 !self
->group
->members
->next
))) ||
292 client_search_focus_tree_full(self
) ||
294 !client_normal(focus_client
))
296 /* activate the window */
302 dispatch_client(Event_Client_New
, self
, 0, 0);
304 /* make sure the window is visible */
305 client_move_onscreen(self
, TRUE
);
307 screen_update_areas();
309 client_showhide(self
);
311 /* use client_focus instead of client_activate cuz client_activate does
312 stuff like switch desktops etc and I'm not interested in all that when
313 a window maps since its not based on an action from the user like
314 clicking a window to activate is. so keep the new window out of the way
316 if (activate
) client_focus(self
);
318 /* client_activate does this but we aret using it so we have to do it
320 if (screen_showing_desktop
)
321 screen_show_desktop(FALSE
);
323 /* add to client list/map */
324 client_list
= g_list_append(client_list
, self
);
325 g_hash_table_insert(window_map
, &self
->window
, self
);
327 /* update the list hints */
330 dispatch_client(Event_Client_Mapped
, self
, 0, 0);
332 ob_debug("Managed window 0x%lx (%s)\n", window
, self
->class);
335 void client_unmanage_all()
337 while (client_list
!= NULL
)
338 client_unmanage(client_list
->data
);
341 /* called by client_unmanage() to close any menus referencing this client */
342 void client_close_menus(gpointer key
, gpointer value
, gpointer self
)
344 if (((ObMenu
*)value
)->client
== (ObClient
*)self
)
345 menu_hide((ObMenu
*)value
);
348 void client_unmanage(ObClient
*self
)
353 ob_debug("Unmanaging window: %lx (%s)\n", self
->window
, self
->class);
355 dispatch_client(Event_Client_Destroy
, self
, 0, 0);
356 g_assert(self
!= NULL
);
358 /* remove the window from our save set */
359 XChangeSaveSet(ob_display
, self
->window
, SetModeDelete
);
361 /* we dont want events no more */
362 XSelectInput(ob_display
, self
->window
, NoEventMask
);
364 frame_hide(self
->frame
);
366 client_list
= g_list_remove(client_list
, self
);
367 stacking_remove(self
);
368 g_hash_table_remove(window_map
, &self
->window
);
370 /* update the focus lists */
371 focus_order_remove(self
);
373 /* once the client is out of the list, update the struts to remove it's
375 screen_update_areas();
377 /* tell our parent(s) that we're gone */
378 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
381 for (it
= self
->group
->members
; it
; it
= it
->next
)
382 if (it
->data
!= self
)
383 ((ObClient
*)it
->data
)->transients
=
384 g_slist_remove(((ObClient
*)it
->data
)->transients
, self
);
385 } else if (self
->transient_for
) { /* transient of window */
386 self
->transient_for
->transients
=
387 g_slist_remove(self
->transient_for
->transients
, self
);
390 /* tell our transients that we're gone */
391 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
392 if (((ObClient
*)it
->data
)->transient_for
!= OB_TRAN_GROUP
) {
393 ((ObClient
*)it
->data
)->transient_for
= NULL
;
394 client_calc_layer(it
->data
);
398 if (moveresize_client
== self
)
399 moveresize_end(TRUE
);
401 /* close any windows that are attached to this window */
402 g_hash_table_foreach(menu_hash
, client_close_menus
, self
);
405 if (focus_client
== self
) {
408 /* focus the last focused window on the desktop, and ignore enter
409 events from the unmap so it doesnt mess with the focus */
410 while (XCheckTypedEvent(ob_display
, EnterNotify
, &e
));
411 client_unfocus(self
);
414 /* remove from its group */
416 group_remove(self
->group
, self
);
420 /* dispatch the unmapped event */
421 dispatch_client(Event_Client_Unmapped
, self
, 0, 0);
422 g_assert(self
!= NULL
);
424 /* give the client its border back */
425 client_toggle_border(self
, TRUE
);
427 /* reparent the window out of the frame, and free the frame */
428 frame_release_client(self
->frame
, self
);
431 if (ob_state() != OB_STATE_EXITING
) {
432 /* these values should not be persisted across a window
434 PROP_ERASE(self
->window
, net_wm_desktop
);
435 PROP_ERASE(self
->window
, net_wm_state
);
436 PROP_ERASE(self
->window
, wm_state
);
438 /* if we're left in an iconic state, the client wont be mapped. this is
439 bad, since we will no longer be managing the window on restart */
441 XMapWindow(ob_display
, self
->window
);
445 ob_debug("Unmanaged window 0x%lx\n", self
->window
);
447 /* free all data allocated in the client struct */
448 g_slist_free(self
->transients
);
449 for (j
= 0; j
< self
->nicons
; ++j
)
450 g_free(self
->icons
[j
].data
);
451 if (self
->nicons
> 0)
454 g_free(self
->icon_title
);
460 /* update the list hints */
464 static void client_restore_session_state(ObClient
*self
)
468 s
= session_state_find(self
);
471 RECT_SET(self
->area
, s
->x
, s
->y
, s
->w
, s
->h
);
472 self
->positioned
= TRUE
;
473 XResizeWindow(ob_display
, self
->window
, s
->w
, s
->h
);
475 self
->desktop
= s
->desktop
== DESKTOP_ALL
? s
->desktop
:
476 MIN(screen_num_desktops
- 1, s
->desktop
);
477 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
479 self
->shaded
= s
->shaded
;
480 self
->iconic
= s
->iconic
;
481 self
->skip_pager
= s
->skip_pager
;
482 self
->skip_taskbar
= s
->skip_taskbar
;
483 self
->fullscreen
= s
->fullscreen
;
484 self
->above
= s
->above
;
485 self
->below
= s
->below
;
486 self
->max_horz
= s
->max_horz
;
487 self
->max_vert
= s
->max_vert
;
489 session_state_free(s
);
492 void client_move_onscreen(ObClient
*self
, gboolean rude
)
494 int x
= self
->area
.x
;
495 int y
= self
->area
.y
;
496 if (client_find_onscreen(self
, &x
, &y
,
497 self
->frame
->area
.width
,
498 self
->frame
->area
.height
, rude
)) {
499 client_configure(self
, OB_CORNER_TOPLEFT
, x
, y
,
500 self
->area
.width
, self
->area
.height
,
505 gboolean
client_find_onscreen(ObClient
*self
, int *x
, int *y
, int w
, int h
,
509 int ox
= *x
, oy
= *y
;
511 frame_client_gravity(self
->frame
, x
, y
); /* get where the frame
514 /* XXX watch for xinerama dead areas */
516 a
= screen_area(self
->desktop
);
517 if (!self
->strut
.right
&& *x
>= a
->x
+ a
->width
- 1)
518 *x
= a
->x
+ a
->width
- self
->frame
->area
.width
;
519 if (!self
->strut
.bottom
&& *y
>= a
->y
+ a
->height
- 1)
520 *y
= a
->y
+ a
->height
- self
->frame
->area
.height
;
521 if (!self
->strut
.left
&& *x
+ self
->frame
->area
.width
- 1 < a
->x
)
523 if (!self
->strut
.top
&& *y
+ self
->frame
->area
.height
- 1 < a
->y
)
527 /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good.
528 Java can suck it too. */
530 /* dont let windows map/move into the strut unless they
531 are bigger than the available area */
533 if (!self
->strut
.left
&& *x
< a
->x
) *x
= a
->x
;
534 if (!self
->strut
.right
&& *x
+ w
> a
->x
+ a
->width
)
535 *x
= a
->x
+ a
->width
- w
;
537 if (h
<= a
->height
) {
538 if (!self
->strut
.top
&& *y
< a
->y
) *y
= a
->y
;
539 if (!self
->strut
.bottom
&& *y
+ h
> a
->y
+ a
->height
)
540 *y
= a
->y
+ a
->height
- h
;
544 frame_frame_gravity(self
->frame
, x
, y
); /* get where the client
547 return ox
!= *x
|| oy
!= *y
;
550 static void client_toggle_border(ObClient
*self
, gboolean show
)
552 /* adjust our idea of where the client is, based on its border. When the
553 border is removed, the client should now be considered to be in a
555 when re-adding the border to the client, the same operation needs to be
557 int oldx
= self
->area
.x
, oldy
= self
->area
.y
;
558 int x
= oldx
, y
= oldy
;
559 switch(self
->gravity
) {
561 case NorthWestGravity
:
563 case SouthWestGravity
:
565 case NorthEastGravity
:
567 case SouthEastGravity
:
568 if (show
) x
-= self
->border_width
* 2;
569 else x
+= self
->border_width
* 2;
576 if (show
) x
-= self
->border_width
;
577 else x
+= self
->border_width
;
580 switch(self
->gravity
) {
582 case NorthWestGravity
:
584 case NorthEastGravity
:
586 case SouthWestGravity
:
588 case SouthEastGravity
:
589 if (show
) y
-= self
->border_width
* 2;
590 else y
+= self
->border_width
* 2;
597 if (show
) y
-= self
->border_width
;
598 else y
+= self
->border_width
;
605 XSetWindowBorderWidth(ob_display
, self
->window
, self
->border_width
);
607 /* move the client so it is back it the right spot _with_ its
609 if (x
!= oldx
|| y
!= oldy
)
610 XMoveWindow(ob_display
, self
->window
, x
, y
);
612 XSetWindowBorderWidth(ob_display
, self
->window
, 0);
616 static void client_get_all(ObClient
*self
)
618 /* update EVERYTHING!! */
620 self
->ignore_unmaps
= 0;
624 self
->title
= self
->icon_title
= NULL
;
625 self
->title_count
= 1;
626 self
->name
= self
->class = self
->role
= NULL
;
627 self
->wmstate
= NormalState
;
628 self
->transient
= FALSE
;
629 self
->transients
= NULL
;
630 self
->transient_for
= NULL
;
632 self
->urgent
= FALSE
;
633 self
->positioned
= FALSE
;
634 self
->decorate
= TRUE
;
638 client_get_area(self
);
639 client_update_transient_for(self
);
640 client_update_wmhints(self
);
641 client_get_desktop(self
);
642 client_get_state(self
);
643 client_get_shaped(self
);
645 client_get_mwm_hints(self
);
646 client_get_type(self
);/* this can change the mwmhints for special cases */
648 client_update_protocols(self
);
650 client_get_gravity(self
); /* get the attribute gravity */
651 client_update_normal_hints(self
); /* this may override the attribute
654 /* got the type, the mwmhints, the protocols, and the normal hints
655 (min/max sizes), so we're ready to set up the decorations/functions */
656 client_setup_decor_and_functions(self
);
658 client_update_title(self
);
659 client_update_class(self
);
660 client_update_strut(self
);
661 client_update_icons(self
);
664 static void client_get_area(ObClient
*self
)
666 XWindowAttributes wattrib
;
669 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
670 g_assert(ret
!= BadWindow
);
672 RECT_SET(self
->area
, wattrib
.x
, wattrib
.y
, wattrib
.width
, wattrib
.height
);
673 self
->border_width
= wattrib
.border_width
;
676 static void client_get_desktop(ObClient
*self
)
678 guint32 d
= screen_num_desktops
; /* an always-invalid value */
680 if (PROP_GET32(self
->window
, net_wm_desktop
, cardinal
, &d
)) {
681 if (d
>= screen_num_desktops
&& d
!= DESKTOP_ALL
)
682 self
->desktop
= screen_num_desktops
- 1;
686 gboolean trdesk
= FALSE
;
688 if (self
->transient_for
) {
689 if (self
->transient_for
!= OB_TRAN_GROUP
) {
690 self
->desktop
= self
->transient_for
->desktop
;
695 for (it
= self
->group
->members
; it
; it
= it
->next
)
696 if (it
->data
!= self
&&
697 !((ObClient
*)it
->data
)->transient_for
) {
698 self
->desktop
= ((ObClient
*)it
->data
)->desktop
;
705 /* defaults to the current desktop */
706 self
->desktop
= screen_desktop
;
708 if (self
->desktop
!= d
) {
709 /* set the desktop hint, to make sure that it always exists */
710 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, self
->desktop
);
714 static void client_get_state(ObClient
*self
)
719 self
->modal
= self
->shaded
= self
->max_horz
= self
->max_vert
=
720 self
->fullscreen
= self
->above
= self
->below
= self
->iconic
=
721 self
->skip_taskbar
= self
->skip_pager
= FALSE
;
723 if (PROP_GETA32(self
->window
, net_wm_state
, atom
, &state
, &num
)) {
725 for (i
= 0; i
< num
; ++i
) {
726 if (state
[i
] == prop_atoms
.net_wm_state_modal
)
728 else if (state
[i
] == prop_atoms
.net_wm_state_shaded
)
730 else if (state
[i
] == prop_atoms
.net_wm_state_hidden
)
732 else if (state
[i
] == prop_atoms
.net_wm_state_skip_taskbar
)
733 self
->skip_taskbar
= TRUE
;
734 else if (state
[i
] == prop_atoms
.net_wm_state_skip_pager
)
735 self
->skip_pager
= TRUE
;
736 else if (state
[i
] == prop_atoms
.net_wm_state_fullscreen
)
737 self
->fullscreen
= TRUE
;
738 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_vert
)
739 self
->max_vert
= TRUE
;
740 else if (state
[i
] == prop_atoms
.net_wm_state_maximized_horz
)
741 self
->max_horz
= TRUE
;
742 else if (state
[i
] == prop_atoms
.net_wm_state_above
)
744 else if (state
[i
] == prop_atoms
.net_wm_state_below
)
752 static void client_get_shaped(ObClient
*self
)
754 self
->shaped
= FALSE
;
756 if (extensions_shape
) {
761 XShapeSelectInput(ob_display
, self
->window
, ShapeNotifyMask
);
763 XShapeQueryExtents(ob_display
, self
->window
, &s
, &foo
,
764 &foo
, &ufoo
, &ufoo
, &foo
, &foo
, &foo
, &ufoo
,
766 self
->shaped
= (s
!= 0);
771 void client_update_transient_for(ObClient
*self
)
774 ObClient
*target
= NULL
;
776 if (XGetTransientForHint(ob_display
, self
->window
, &t
)) {
777 self
->transient
= TRUE
;
778 if (t
!= self
->window
) { /* cant be transient to itself! */
779 target
= g_hash_table_lookup(window_map
, &t
);
780 /* if this happens then we need to check for it*/
781 g_assert(target
!= self
);
782 g_assert(!target
|| WINDOW_IS_CLIENT(target
));
784 if (!target
&& self
->group
) {
785 /* not transient to a client, see if it is transient for a
787 if (t
== self
->group
->leader
||
789 t
== RootWindow(ob_display
, ob_screen
)) {
790 /* window is a transient for its group! */
791 target
= OB_TRAN_GROUP
;
796 self
->transient
= FALSE
;
798 /* if anything has changed... */
799 if (target
!= self
->transient_for
) {
800 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
803 /* remove from old parents */
804 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
805 ObClient
*c
= it
->data
;
806 if (c
!= self
&& !c
->transient_for
)
807 c
->transients
= g_slist_remove(c
->transients
, self
);
809 } else if (self
->transient_for
!= NULL
) { /* transient of window */
810 /* remove from old parent */
811 self
->transient_for
->transients
=
812 g_slist_remove(self
->transient_for
->transients
, self
);
814 self
->transient_for
= target
;
815 if (self
->transient_for
== OB_TRAN_GROUP
) { /* transient of group */
818 /* add to new parents */
819 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
820 ObClient
*c
= it
->data
;
821 if (c
!= self
&& !c
->transient_for
)
822 c
->transients
= g_slist_append(c
->transients
, self
);
825 /* remove all transients which are in the group, that causes
826 circlular pointer hell of doom */
827 for (it
= self
->group
->members
; it
; it
= g_slist_next(it
)) {
829 for (sit
= self
->transients
; sit
; sit
= next
) {
830 next
= g_slist_next(sit
);
831 if (sit
->data
== it
->data
)
833 g_slist_delete_link(self
->transients
, sit
);
836 } else if (self
->transient_for
!= NULL
) { /* transient of window */
837 /* add to new parent */
838 self
->transient_for
->transients
=
839 g_slist_append(self
->transient_for
->transients
, self
);
844 static void client_get_mwm_hints(ObClient
*self
)
849 self
->mwmhints
.flags
= 0; /* default to none */
851 if (PROP_GETA32(self
->window
, motif_wm_hints
, motif_wm_hints
,
853 if (num
>= OB_MWM_ELEMENTS
) {
854 self
->mwmhints
.flags
= hints
[0];
855 self
->mwmhints
.functions
= hints
[1];
856 self
->mwmhints
.decorations
= hints
[2];
862 void client_get_type(ObClient
*self
)
869 if (PROP_GETA32(self
->window
, net_wm_window_type
, atom
, &val
, &num
)) {
870 /* use the first value that we know about in the array */
871 for (i
= 0; i
< num
; ++i
) {
872 if (val
[i
] == prop_atoms
.net_wm_window_type_desktop
)
873 self
->type
= OB_CLIENT_TYPE_DESKTOP
;
874 else if (val
[i
] == prop_atoms
.net_wm_window_type_dock
)
875 self
->type
= OB_CLIENT_TYPE_DOCK
;
876 else if (val
[i
] == prop_atoms
.net_wm_window_type_toolbar
)
877 self
->type
= OB_CLIENT_TYPE_TOOLBAR
;
878 else if (val
[i
] == prop_atoms
.net_wm_window_type_menu
)
879 self
->type
= OB_CLIENT_TYPE_MENU
;
880 else if (val
[i
] == prop_atoms
.net_wm_window_type_utility
)
881 self
->type
= OB_CLIENT_TYPE_UTILITY
;
882 else if (val
[i
] == prop_atoms
.net_wm_window_type_splash
)
883 self
->type
= OB_CLIENT_TYPE_SPLASH
;
884 else if (val
[i
] == prop_atoms
.net_wm_window_type_dialog
)
885 self
->type
= OB_CLIENT_TYPE_DIALOG
;
886 else if (val
[i
] == prop_atoms
.net_wm_window_type_normal
)
887 self
->type
= OB_CLIENT_TYPE_NORMAL
;
888 else if (val
[i
] == prop_atoms
.kde_net_wm_window_type_override
) {
889 /* prevent this window from getting any decor or
891 self
->mwmhints
.flags
&= (OB_MWM_FLAG_FUNCTIONS
|
892 OB_MWM_FLAG_DECORATIONS
);
893 self
->mwmhints
.decorations
= 0;
894 self
->mwmhints
.functions
= 0;
896 if (self
->type
!= (ObClientType
) -1)
897 break; /* grab the first legit type */
902 if (self
->type
== (ObClientType
) -1) {
903 /*the window type hint was not set, which means we either classify
904 ourself as a normal window or a dialog, depending on if we are a
907 self
->type
= OB_CLIENT_TYPE_DIALOG
;
909 self
->type
= OB_CLIENT_TYPE_NORMAL
;
913 void client_update_protocols(ObClient
*self
)
918 self
->focus_notify
= FALSE
;
919 self
->delete_window
= FALSE
;
921 if (PROP_GETA32(self
->window
, wm_protocols
, atom
, &proto
, &num_return
)) {
922 for (i
= 0; i
< num_return
; ++i
) {
923 if (proto
[i
] == prop_atoms
.wm_delete_window
) {
924 /* this means we can request the window to close */
925 self
->delete_window
= TRUE
;
926 } else if (proto
[i
] == prop_atoms
.wm_take_focus
)
927 /* if this protocol is requested, then the window will be
928 notified whenever we want it to receive focus */
929 self
->focus_notify
= TRUE
;
935 static void client_get_gravity(ObClient
*self
)
937 XWindowAttributes wattrib
;
940 ret
= XGetWindowAttributes(ob_display
, self
->window
, &wattrib
);
941 g_assert(ret
!= BadWindow
);
942 self
->gravity
= wattrib
.win_gravity
;
945 void client_update_normal_hints(ObClient
*self
)
949 int oldgravity
= self
->gravity
;
952 self
->min_ratio
= 0.0f
;
953 self
->max_ratio
= 0.0f
;
954 SIZE_SET(self
->size_inc
, 1, 1);
955 SIZE_SET(self
->base_size
, 0, 0);
956 SIZE_SET(self
->min_size
, 0, 0);
957 SIZE_SET(self
->max_size
, G_MAXINT
, G_MAXINT
);
959 /* get the hints from the window */
960 if (XGetWMNormalHints(ob_display
, self
->window
, &size
, &ret
)) {
961 self
->positioned
= !!(size
.flags
& (PPosition
|USPosition
));
963 if (size
.flags
& PWinGravity
) {
964 self
->gravity
= size
.win_gravity
;
966 /* if the client has a frame, i.e. has already been mapped and
967 is changing its gravity */
968 if (self
->frame
&& self
->gravity
!= oldgravity
) {
969 /* move our idea of the client's position based on its new
971 self
->area
.x
= self
->frame
->area
.x
;
972 self
->area
.y
= self
->frame
->area
.y
;
973 frame_frame_gravity(self
->frame
, &self
->area
.x
, &self
->area
.y
);
977 if (size
.flags
& PAspect
) {
978 if (size
.min_aspect
.y
)
979 self
->min_ratio
= (float)size
.min_aspect
.x
/ size
.min_aspect
.y
;
980 if (size
.max_aspect
.y
)
981 self
->max_ratio
= (float)size
.max_aspect
.x
/ size
.max_aspect
.y
;
984 if (size
.flags
& PMinSize
)
985 SIZE_SET(self
->min_size
, size
.min_width
, size
.min_height
);
987 if (size
.flags
& PMaxSize
)
988 SIZE_SET(self
->max_size
, size
.max_width
, size
.max_height
);
990 if (size
.flags
& PBaseSize
)
991 SIZE_SET(self
->base_size
, size
.base_width
, size
.base_height
);
993 if (size
.flags
& PResizeInc
)
994 SIZE_SET(self
->size_inc
, size
.width_inc
, size
.height_inc
);
998 void client_setup_decor_and_functions(ObClient
*self
)
1000 /* start with everything (cept fullscreen) */
1001 self
->decorations
= (OB_FRAME_DECOR_TITLEBAR
|
1002 OB_FRAME_DECOR_HANDLE
|
1003 OB_FRAME_DECOR_GRIPS
|
1004 OB_FRAME_DECOR_BORDER
|
1005 OB_FRAME_DECOR_ICON
|
1006 OB_FRAME_DECOR_ALLDESKTOPS
|
1007 OB_FRAME_DECOR_ICONIFY
|
1008 OB_FRAME_DECOR_MAXIMIZE
|
1009 OB_FRAME_DECOR_SHADE
);
1010 self
->functions
= (OB_CLIENT_FUNC_RESIZE
|
1011 OB_CLIENT_FUNC_MOVE
|
1012 OB_CLIENT_FUNC_ICONIFY
|
1013 OB_CLIENT_FUNC_MAXIMIZE
|
1014 OB_CLIENT_FUNC_SHADE
);
1015 if (self
->delete_window
) {
1016 self
->functions
|= OB_CLIENT_FUNC_CLOSE
;
1017 self
->decorations
|= OB_FRAME_DECOR_CLOSE
;
1020 if (!(self
->min_size
.width
< self
->max_size
.width
||
1021 self
->min_size
.height
< self
->max_size
.height
))
1022 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1024 switch (self
->type
) {
1025 case OB_CLIENT_TYPE_NORMAL
:
1026 /* normal windows retain all of the possible decorations and
1027 functionality, and are the only windows that you can fullscreen */
1028 self
->functions
|= OB_CLIENT_FUNC_FULLSCREEN
;
1031 case OB_CLIENT_TYPE_DIALOG
:
1032 case OB_CLIENT_TYPE_UTILITY
:
1033 /* these windows cannot be maximized */
1034 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1037 case OB_CLIENT_TYPE_MENU
:
1038 case OB_CLIENT_TYPE_TOOLBAR
:
1039 /* these windows get less functionality */
1040 self
->functions
&= ~(OB_CLIENT_FUNC_ICONIFY
| OB_CLIENT_FUNC_RESIZE
);
1043 case OB_CLIENT_TYPE_DESKTOP
:
1044 case OB_CLIENT_TYPE_DOCK
:
1045 case OB_CLIENT_TYPE_SPLASH
:
1046 /* none of these windows are manipulated by the window manager */
1047 self
->decorations
= 0;
1048 self
->functions
= 0;
1052 /* Mwm Hints are applied subtractively to what has already been chosen for
1053 decor and functionality */
1054 if (self
->mwmhints
.flags
& OB_MWM_FLAG_DECORATIONS
) {
1055 if (! (self
->mwmhints
.decorations
& OB_MWM_DECOR_ALL
)) {
1056 if (! ((self
->mwmhints
.decorations
& OB_MWM_DECOR_HANDLE
) ||
1057 (self
->mwmhints
.decorations
& OB_MWM_DECOR_TITLE
)))
1058 /* if the mwm hints request no handle or title, then all
1059 decorations are disabled */
1060 self
->decorations
= 0;
1064 if (self
->mwmhints
.flags
& OB_MWM_FLAG_FUNCTIONS
) {
1065 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_ALL
)) {
1066 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_RESIZE
))
1067 self
->functions
&= ~OB_CLIENT_FUNC_RESIZE
;
1068 if (! (self
->mwmhints
.functions
& OB_MWM_FUNC_MOVE
))
1069 self
->functions
&= ~OB_CLIENT_FUNC_MOVE
;
1070 /* dont let mwm hints kill any buttons
1071 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1072 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1073 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1074 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1076 /* dont let mwm hints kill the close button
1077 if (! (self->mwmhints.functions & MwmFunc_Close))
1078 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1082 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
))
1083 self
->decorations
&= ~OB_FRAME_DECOR_SHADE
;
1084 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
))
1085 self
->decorations
&= ~OB_FRAME_DECOR_ICONIFY
;
1086 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
))
1087 self
->decorations
&= ~OB_FRAME_DECOR_GRIPS
;
1089 /* can't maximize without moving/resizing */
1090 if (!((self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) &&
1091 (self
->functions
& OB_CLIENT_FUNC_MOVE
) &&
1092 (self
->functions
& OB_CLIENT_FUNC_RESIZE
))) {
1093 self
->functions
&= ~OB_CLIENT_FUNC_MAXIMIZE
;
1094 self
->decorations
&= ~OB_FRAME_DECOR_MAXIMIZE
;
1097 /* finally, the user can have requested no decorations, which overrides
1099 if (!self
->decorate
)
1100 self
->decorations
= 0;
1102 /* if we don't have a titlebar, then we cannot shade! */
1103 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1104 self
->functions
&= ~OB_CLIENT_FUNC_SHADE
;
1106 /* now we need to check against rules for the client's current state */
1107 if (self
->fullscreen
) {
1108 self
->functions
&= (OB_CLIENT_FUNC_CLOSE
|
1109 OB_CLIENT_FUNC_FULLSCREEN
|
1110 OB_CLIENT_FUNC_ICONIFY
);
1111 self
->decorations
= 0;
1114 client_change_allowed_actions(self
);
1117 if (self
->decorations
!= self
->frame
->decorations
)
1118 /* adjust the client's decorations, etc. */
1119 client_reconfigure(self
);
1120 /* we actually have to do this twice :P
1121 the first time it removes the decorations, but now it may need to
1122 be reconstrained for being maximized etc, so calling this again
1123 will work with the new setup of decorations on the window */
1124 client_reconfigure(self
);
1126 /* this makes sure that these windows appear on all desktops */
1127 if (self
->type
== OB_CLIENT_TYPE_DESKTOP
&&
1128 self
->desktop
!= DESKTOP_ALL
)
1130 self
->desktop
= DESKTOP_ALL
;
1135 static void client_change_allowed_actions(ObClient
*self
)
1140 /* desktop windows are kept on all desktops */
1141 if (self
->type
!= OB_CLIENT_TYPE_DESKTOP
)
1142 actions
[num
++] = prop_atoms
.net_wm_action_change_desktop
;
1144 if (self
->functions
& OB_CLIENT_FUNC_SHADE
)
1145 actions
[num
++] = prop_atoms
.net_wm_action_shade
;
1146 if (self
->functions
& OB_CLIENT_FUNC_CLOSE
)
1147 actions
[num
++] = prop_atoms
.net_wm_action_close
;
1148 if (self
->functions
& OB_CLIENT_FUNC_MOVE
)
1149 actions
[num
++] = prop_atoms
.net_wm_action_move
;
1150 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
)
1151 actions
[num
++] = prop_atoms
.net_wm_action_minimize
;
1152 if (self
->functions
& OB_CLIENT_FUNC_RESIZE
)
1153 actions
[num
++] = prop_atoms
.net_wm_action_resize
;
1154 if (self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
)
1155 actions
[num
++] = prop_atoms
.net_wm_action_fullscreen
;
1156 if (self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) {
1157 actions
[num
++] = prop_atoms
.net_wm_action_maximize_horz
;
1158 actions
[num
++] = prop_atoms
.net_wm_action_maximize_vert
;
1161 PROP_SETA32(self
->window
, net_wm_allowed_actions
, atom
, actions
, num
);
1163 /* make sure the window isn't breaking any rules now */
1165 if (!(self
->functions
& OB_CLIENT_FUNC_SHADE
) && self
->shaded
) {
1166 if (self
->frame
) client_shade(self
, FALSE
);
1167 else self
->shaded
= FALSE
;
1169 if (!(self
->functions
& OB_CLIENT_FUNC_ICONIFY
) && self
->iconic
) {
1170 if (self
->frame
) client_iconify(self
, FALSE
, TRUE
);
1171 else self
->iconic
= FALSE
;
1173 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) && self
->fullscreen
) {
1174 if (self
->frame
) client_fullscreen(self
, FALSE
, TRUE
);
1175 else self
->fullscreen
= FALSE
;
1177 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
) && (self
->max_horz
||
1179 if (self
->frame
) client_maximize(self
, FALSE
, 0, TRUE
);
1180 else self
->max_vert
= self
->max_horz
= FALSE
;
1184 void client_reconfigure(ObClient
*self
)
1186 /* by making this pass FALSE for user, we avoid the emacs event storm where
1187 every configurenotify causes an update in its normal hints, i think this
1188 is generally what we want anyways... */
1189 client_configure(self
, OB_CORNER_TOPLEFT
, self
->area
.x
, self
->area
.y
,
1190 self
->area
.width
, self
->area
.height
, FALSE
, TRUE
);
1193 void client_update_wmhints(ObClient
*self
)
1196 gboolean ur
= FALSE
;
1199 /* assume a window takes input if it doesnt specify */
1200 self
->can_focus
= TRUE
;
1202 if ((hints
= XGetWMHints(ob_display
, self
->window
)) != NULL
) {
1203 if (hints
->flags
& InputHint
)
1204 self
->can_focus
= hints
->input
;
1206 /* only do this when first managing the window *AND* when we aren't
1208 if (ob_state() != OB_STATE_STARTING
&& self
->frame
== NULL
)
1209 if (hints
->flags
& StateHint
)
1210 self
->iconic
= hints
->initial_state
== IconicState
;
1212 if (hints
->flags
& XUrgencyHint
)
1215 if (!(hints
->flags
& WindowGroupHint
))
1216 hints
->window_group
= None
;
1218 /* did the group state change? */
1219 if (hints
->window_group
!=
1220 (self
->group
? self
->group
->leader
: None
)) {
1221 /* remove from the old group if there was one */
1222 if (self
->group
!= NULL
) {
1223 /* remove transients of the group */
1224 for (it
= self
->group
->members
; it
; it
= it
->next
)
1225 self
->transients
= g_slist_remove(self
->transients
,
1227 group_remove(self
->group
, self
);
1230 if (hints
->window_group
!= None
) {
1231 self
->group
= group_add(hints
->window_group
, self
);
1233 /* i can only have transients from the group if i am not
1235 if (!self
->transient_for
) {
1236 /* add other transients of the group that are already
1238 for (it
= self
->group
->members
; it
; it
= it
->next
) {
1239 ObClient
*c
= it
->data
;
1240 if (c
!= self
&& c
->transient_for
== OB_TRAN_GROUP
)
1242 g_slist_append(self
->transients
, c
);
1247 /* because the self->transient flag wont change from this call,
1248 we don't need to update the window's type and such, only its
1249 transient_for, and the transients lists of other windows in
1250 the group may be affected */
1251 client_update_transient_for(self
);
1254 /* the WM_HINTS can contain an icon */
1255 client_update_icons(self
);
1260 if (ur
!= self
->urgent
) {
1262 ob_debug("Urgent Hint for 0x%lx: %s\n", self
->window
,
1264 /* fire the urgent callback if we're mapped, otherwise, wait until
1265 after we're mapped */
1267 dispatch_client(Event_Client_Urgent
, self
, self
->urgent
, 0);
1271 void client_update_title(ObClient
*self
)
1277 gboolean read_title
;
1279 g_free(self
->title
);
1282 if (!PROP_GETS(self
->window
, net_wm_name
, utf8
, &data
))
1283 /* try old x stuff */
1284 if (!PROP_GETS(self
->window
, wm_name
, locale
, &data
))
1285 data
= g_strdup("Unnamed Window");
1287 /* look for duplicates and append a number */
1289 for (it
= client_list
; it
; it
= it
->next
)
1290 if (it
->data
!= self
) {
1291 ObClient
*c
= it
->data
;
1292 if (0 == strncmp(c
->title
, data
, strlen(data
)))
1293 nums
|= 1 << c
->title_count
;
1295 /* find first free number */
1296 for (i
= 1; i
<= 32; ++i
)
1297 if (!(nums
& (1 << i
))) {
1298 if (self
->title_count
== 1 || i
== 1)
1299 self
->title_count
= i
;
1302 /* dont display the number for the first window */
1303 if (self
->title_count
> 1) {
1304 char *vdata
, *ndata
;
1305 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1306 vdata
= g_strconcat(data
, ndata
, NULL
);
1312 PROP_SETS(self
->window
, net_wm_visible_name
, data
);
1317 frame_adjust_title(self
->frame
);
1319 /* update the icon title */
1321 g_free(self
->icon_title
);
1325 if (!PROP_GETS(self
->window
, net_wm_icon_name
, utf8
, &data
))
1326 /* try old x stuff */
1327 if (!PROP_GETS(self
->window
, wm_icon_name
, locale
, &data
)) {
1328 data
= g_strdup(self
->title
);
1332 /* append the title count, dont display the number for the first window */
1333 if (read_title
&& self
->title_count
> 1) {
1334 char *vdata
, *ndata
;
1335 ndata
= g_strdup_printf(" - [%u]", self
->title_count
);
1336 vdata
= g_strconcat(data
, ndata
, NULL
);
1342 PROP_SETS(self
->window
, net_wm_visible_icon_name
, data
);
1344 self
->icon_title
= data
;
1347 void client_update_class(ObClient
*self
)
1352 if (self
->name
) g_free(self
->name
);
1353 if (self
->class) g_free(self
->class);
1354 if (self
->role
) g_free(self
->role
);
1356 self
->name
= self
->class = self
->role
= NULL
;
1358 if (PROP_GETSS(self
->window
, wm_class
, locale
, &data
)) {
1360 self
->name
= g_strdup(data
[0]);
1362 self
->class = g_strdup(data
[1]);
1367 if (PROP_GETS(self
->window
, wm_window_role
, locale
, &s
))
1368 self
->role
= g_strdup(s
);
1370 if (self
->name
== NULL
) self
->name
= g_strdup("");
1371 if (self
->class == NULL
) self
->class = g_strdup("");
1372 if (self
->role
== NULL
) self
->role
= g_strdup("");
1375 void client_update_strut(ObClient
*self
)
1379 gboolean got
= FALSE
;
1381 if (PROP_GETA32(self
->window
, net_wm_strut_partial
, cardinal
,
1385 STRUT_PARTIAL_SET(self
->strut
,
1386 data
[0], data
[2], data
[1], data
[3],
1387 data
[4], data
[5], data
[8], data
[9],
1388 data
[6], data
[7], data
[10], data
[11]);
1394 PROP_GETA32(self
->window
, net_wm_strut
, cardinal
, &data
, &num
)) {
1397 STRUT_PARTIAL_SET(self
->strut
,
1398 data
[0], data
[2], data
[1], data
[3],
1399 0, 0, 0, 0, 0, 0, 0, 0);
1405 STRUT_PARTIAL_SET(self
->strut
, 0, 0, 0, 0,
1406 0, 0, 0, 0, 0, 0, 0, 0);
1408 /* updating here is pointless while we're being mapped cuz we're not in
1409 the client list yet */
1411 screen_update_areas();
1414 void client_update_icons(ObClient
*self
)
1420 for (i
= 0; i
< self
->nicons
; ++i
)
1421 g_free(self
->icons
[i
].data
);
1422 if (self
->nicons
> 0)
1423 g_free(self
->icons
);
1426 if (PROP_GETA32(self
->window
, net_wm_icon
, cardinal
, &data
, &num
)) {
1427 /* figure out how many valid icons are in here */
1429 while (num
- i
> 2) {
1433 if (i
> num
|| w
*h
== 0) break;
1437 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1439 /* store the icons */
1441 for (j
= 0; j
< self
->nicons
; ++j
) {
1444 w
= self
->icons
[j
].width
= data
[i
++];
1445 h
= self
->icons
[j
].height
= data
[i
++];
1447 if (w
*h
== 0) continue;
1449 self
->icons
[j
].data
= g_new(RrPixel32
, w
* h
);
1450 for (x
= 0, y
= 0, t
= 0; t
< w
* h
; ++t
, ++x
, ++i
) {
1455 self
->icons
[j
].data
[t
] =
1456 (((data
[i
] >> 24) & 0xff) << RrDefaultAlphaOffset
) +
1457 (((data
[i
] >> 16) & 0xff) << RrDefaultRedOffset
) +
1458 (((data
[i
] >> 8) & 0xff) << RrDefaultGreenOffset
) +
1459 (((data
[i
] >> 0) & 0xff) << RrDefaultBlueOffset
);
1465 } else if (PROP_GETA32(self
->window
, kwm_win_icon
,
1466 kwm_win_icon
, &data
, &num
)) {
1469 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1470 xerror_set_ignore(TRUE
);
1471 if (!RrPixmapToRGBA(ob_rr_inst
,
1473 &self
->icons
[self
->nicons
-1].width
,
1474 &self
->icons
[self
->nicons
-1].height
,
1475 &self
->icons
[self
->nicons
-1].data
)) {
1476 g_free(&self
->icons
[self
->nicons
-1]);
1479 xerror_set_ignore(FALSE
);
1485 if ((hints
= XGetWMHints(ob_display
, self
->window
))) {
1486 if (hints
->flags
& IconPixmapHint
) {
1488 self
->icons
= g_new(ObClientIcon
, self
->nicons
);
1489 xerror_set_ignore(TRUE
);
1490 if (!RrPixmapToRGBA(ob_rr_inst
,
1492 (hints
->flags
& IconMaskHint
?
1493 hints
->icon_mask
: None
),
1494 &self
->icons
[self
->nicons
-1].width
,
1495 &self
->icons
[self
->nicons
-1].height
,
1496 &self
->icons
[self
->nicons
-1].data
)){
1497 g_free(&self
->icons
[self
->nicons
-1]);
1500 xerror_set_ignore(FALSE
);
1507 frame_adjust_icon(self
->frame
);
1510 static void client_change_state(ObClient
*self
)
1513 guint32 netstate
[10];
1516 state
[0] = self
->wmstate
;
1518 PROP_SETA32(self
->window
, wm_state
, wm_state
, state
, 2);
1522 netstate
[num
++] = prop_atoms
.net_wm_state_modal
;
1524 netstate
[num
++] = prop_atoms
.net_wm_state_shaded
;
1526 netstate
[num
++] = prop_atoms
.net_wm_state_hidden
;
1527 if (self
->skip_taskbar
)
1528 netstate
[num
++] = prop_atoms
.net_wm_state_skip_taskbar
;
1529 if (self
->skip_pager
)
1530 netstate
[num
++] = prop_atoms
.net_wm_state_skip_pager
;
1531 if (self
->fullscreen
)
1532 netstate
[num
++] = prop_atoms
.net_wm_state_fullscreen
;
1534 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_vert
;
1536 netstate
[num
++] = prop_atoms
.net_wm_state_maximized_horz
;
1538 netstate
[num
++] = prop_atoms
.net_wm_state_above
;
1540 netstate
[num
++] = prop_atoms
.net_wm_state_below
;
1541 PROP_SETA32(self
->window
, net_wm_state
, atom
, netstate
, num
);
1543 client_calc_layer(self
);
1546 frame_adjust_state(self
->frame
);
1549 ObClient
*client_search_focus_tree(ObClient
*self
)
1554 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
1555 if (client_focused(it
->data
)) return it
->data
;
1556 if ((ret
= client_search_focus_tree(it
->data
))) return ret
;
1561 ObClient
*client_search_focus_tree_full(ObClient
*self
)
1563 if (self
->transient_for
) {
1564 if (self
->transient_for
!= OB_TRAN_GROUP
) {
1565 return client_search_focus_tree_full(self
->transient_for
);
1568 gboolean recursed
= FALSE
;
1570 for (it
= self
->group
->members
; it
; it
= it
->next
)
1571 if (!((ObClient
*)it
->data
)->transient_for
) {
1573 if ((c
= client_search_focus_tree_full(it
->data
)))
1582 /* this function checks the whole tree, the client_search_focus_tree~
1583 does not, so we need to check this window */
1584 if (client_focused(self
))
1586 return client_search_focus_tree(self
);
1589 static ObStackingLayer
calc_layer(ObClient
*self
)
1593 if (self
->fullscreen
) l
= OB_STACKING_LAYER_FULLSCREEN
;
1594 else if (self
->type
== OB_CLIENT_TYPE_DESKTOP
)
1595 l
= OB_STACKING_LAYER_DESKTOP
;
1596 else if (self
->type
== OB_CLIENT_TYPE_DOCK
) {
1597 if (!self
->below
) l
= OB_STACKING_LAYER_TOP
;
1598 else l
= OB_STACKING_LAYER_NORMAL
;
1600 else if (self
->above
) l
= OB_STACKING_LAYER_ABOVE
;
1601 else if (self
->below
) l
= OB_STACKING_LAYER_BELOW
;
1602 else l
= OB_STACKING_LAYER_NORMAL
;
1607 static void client_calc_layer_recursive(ObClient
*self
, ObClient
*orig
,
1608 ObStackingLayer l
, gboolean raised
)
1610 ObStackingLayer old
, own
;
1614 own
= calc_layer(self
);
1615 self
->layer
= l
> own
? l
: own
;
1617 for (it
= self
->transients
; it
; it
= it
->next
)
1618 client_calc_layer_recursive(it
->data
, orig
,
1619 l
, raised
? raised
: l
!= old
);
1621 if (!raised
&& l
!= old
)
1622 if (orig
->frame
) { /* only restack if the original window is managed */
1623 /* XXX add_non_intrusive ever? */
1624 stacking_remove(CLIENT_AS_WINDOW(self
));
1625 stacking_add(CLIENT_AS_WINDOW(self
));
1629 void client_calc_layer(ObClient
*self
)
1636 /* transients take on the layer of their parents */
1637 self
= client_search_top_transient(self
);
1639 l
= calc_layer(self
);
1641 client_calc_layer_recursive(self
, orig
, l
, FALSE
);
1644 gboolean
client_should_show(ObClient
*self
)
1646 if (self
->iconic
) return FALSE
;
1647 else if (!(self
->desktop
== screen_desktop
||
1648 self
->desktop
== DESKTOP_ALL
)) return FALSE
;
1649 else if (client_normal(self
) && screen_showing_desktop
) return FALSE
;
1654 static void client_showhide(ObClient
*self
)
1657 if (client_should_show(self
))
1658 frame_show(self
->frame
);
1660 frame_hide(self
->frame
);
1663 gboolean
client_normal(ObClient
*self
) {
1664 return ! (self
->type
== OB_CLIENT_TYPE_DESKTOP
||
1665 self
->type
== OB_CLIENT_TYPE_DOCK
||
1666 self
->type
== OB_CLIENT_TYPE_SPLASH
);
1669 static void client_apply_startup_state(ObClient
*self
)
1671 /* these are in a carefully crafted order.. */
1674 self
->iconic
= FALSE
;
1675 client_iconify(self
, TRUE
, FALSE
);
1677 if (self
->fullscreen
) {
1678 self
->fullscreen
= FALSE
;
1679 client_fullscreen(self
, TRUE
, FALSE
);
1682 self
->shaded
= FALSE
;
1683 client_shade(self
, TRUE
);
1686 dispatch_client(Event_Client_Urgent
, self
, self
->urgent
, 0);
1688 if (self
->max_vert
&& self
->max_horz
) {
1689 self
->max_vert
= self
->max_horz
= FALSE
;
1690 client_maximize(self
, TRUE
, 0, FALSE
);
1691 } else if (self
->max_vert
) {
1692 self
->max_vert
= FALSE
;
1693 client_maximize(self
, TRUE
, 2, FALSE
);
1694 } else if (self
->max_horz
) {
1695 self
->max_horz
= FALSE
;
1696 client_maximize(self
, TRUE
, 1, FALSE
);
1699 /* nothing to do for the other states:
1708 void client_configure_full(ObClient
*self
, ObCorner anchor
,
1709 int x
, int y
, int w
, int h
,
1710 gboolean user
, gboolean final
,
1711 gboolean force_reply
)
1713 gboolean moved
= FALSE
, resized
= FALSE
;
1715 /* gets the frame's position */
1716 frame_client_gravity(self
->frame
, &x
, &y
);
1718 /* these positions are frame positions, not client positions */
1720 /* set the size and position if fullscreen */
1721 if (self
->fullscreen
) {
1724 XF86VidModeModeLine mode
;
1729 i
= client_monitor(self
);
1730 a
= screen_physical_area_monitor(i
);
1733 if (i
== 0 && /* primary head */
1734 extensions_vidmode
&&
1735 XF86VidModeGetViewPort(ob_display
, ob_screen
, &x
, &y
) &&
1736 /* get the mode last so the mode.privsize isnt freed incorrectly */
1737 XF86VidModeGetModeLine(ob_display
, ob_screen
, &dot
, &mode
)) {
1742 if (mode
.privsize
) XFree(mode
.private);
1752 user
= FALSE
; /* ignore that increment etc shit when in fullscreen */
1756 a
= screen_area_monitor(self
->desktop
, client_monitor(self
));
1758 /* set the size and position if maximized */
1759 if (self
->max_horz
) {
1760 x
= a
->x
- self
->frame
->size
.left
;
1763 if (self
->max_vert
) {
1765 h
= a
->height
- self
->frame
->size
.top
- self
->frame
->size
.bottom
;
1769 /* gets the client's position */
1770 frame_frame_gravity(self
->frame
, &x
, &y
);
1772 /* these override the above states! if you cant move you can't move! */
1774 if (!(self
->functions
& OB_CLIENT_FUNC_MOVE
)) {
1778 if (!(self
->functions
& OB_CLIENT_FUNC_RESIZE
)) {
1779 w
= self
->area
.width
;
1780 h
= self
->area
.height
;
1784 if (!(w
== self
->area
.width
&& h
== self
->area
.height
)) {
1785 int basew
, baseh
, minw
, minh
;
1787 /* base size is substituted with min size if not specified */
1788 if (self
->base_size
.width
|| self
->base_size
.height
) {
1789 basew
= self
->base_size
.width
;
1790 baseh
= self
->base_size
.height
;
1792 basew
= self
->min_size
.width
;
1793 baseh
= self
->min_size
.height
;
1795 /* min size is substituted with base size if not specified */
1796 if (self
->min_size
.width
|| self
->min_size
.height
) {
1797 minw
= self
->min_size
.width
;
1798 minh
= self
->min_size
.height
;
1800 minw
= self
->base_size
.width
;
1801 minh
= self
->base_size
.height
;
1805 /* for interactive resizing. have to move half an increment in each
1808 /* how far we are towards the next size inc */
1809 int mw
= (w
- basew
) % self
->size_inc
.width
;
1810 int mh
= (h
- baseh
) % self
->size_inc
.height
;
1812 int aw
= self
->size_inc
.width
/ 2;
1813 int ah
= self
->size_inc
.height
/ 2;
1814 /* don't let us move into a new size increment */
1815 if (mw
+ aw
>= self
->size_inc
.width
)
1816 aw
= self
->size_inc
.width
- mw
- 1;
1817 if (mh
+ ah
>= self
->size_inc
.height
)
1818 ah
= self
->size_inc
.height
- mh
- 1;
1822 /* if this is a user-requested resize, then check against min/max
1825 /* smaller than min size or bigger than max size? */
1826 if (w
> self
->max_size
.width
) w
= self
->max_size
.width
;
1827 if (w
< minw
) w
= minw
;
1828 if (h
> self
->max_size
.height
) h
= self
->max_size
.height
;
1829 if (h
< minh
) h
= minh
;
1835 /* keep to the increments */
1836 w
/= self
->size_inc
.width
;
1837 h
/= self
->size_inc
.height
;
1839 /* you cannot resize to nothing */
1840 if (basew
+ w
< 1) w
= 1 - basew
;
1841 if (baseh
+ h
< 1) h
= 1 - baseh
;
1843 /* store the logical size */
1844 SIZE_SET(self
->logical_size
,
1845 self
->size_inc
.width
> 1 ? w
: w
+ basew
,
1846 self
->size_inc
.height
> 1 ? h
: h
+ baseh
);
1848 w
*= self
->size_inc
.width
;
1849 h
*= self
->size_inc
.height
;
1855 /* adjust the height to match the width for the aspect ratios.
1856 for this, min size is not substituted for base size ever. */
1857 w
-= self
->base_size
.width
;
1858 h
-= self
->base_size
.height
;
1860 if (self
->min_ratio
)
1861 if (h
* self
->min_ratio
> w
) h
= (int)(w
/ self
->min_ratio
);
1862 if (self
->max_ratio
)
1863 if (h
* self
->max_ratio
< w
) h
= (int)(w
/ self
->max_ratio
);
1865 w
+= self
->base_size
.width
;
1866 h
+= self
->base_size
.height
;
1871 case OB_CORNER_TOPLEFT
:
1873 case OB_CORNER_TOPRIGHT
:
1874 x
-= w
- self
->area
.width
;
1876 case OB_CORNER_BOTTOMLEFT
:
1877 y
-= h
- self
->area
.height
;
1879 case OB_CORNER_BOTTOMRIGHT
:
1880 x
-= w
- self
->area
.width
;
1881 y
-= h
- self
->area
.height
;
1885 moved
= x
!= self
->area
.x
|| y
!= self
->area
.y
;
1886 resized
= w
!= self
->area
.width
|| h
!= self
->area
.height
;
1888 RECT_SET(self
->area
, x
, y
, w
, h
);
1890 /* for app-requested resizes, always resize if 'resized' is true.
1891 for user-requested ones, only resize if final is true, or when
1892 resizing in redraw mode */
1893 if ((!user
&& resized
) ||
1894 (user
&& (final
|| (resized
&& config_redraw_resize
))))
1895 XResizeWindow(ob_display
, self
->window
, w
, h
);
1897 /* move/resize the frame to match the request */
1899 if (self
->decorations
!= self
->frame
->decorations
)
1900 moved
= resized
= TRUE
;
1902 if (moved
|| resized
)
1903 frame_adjust_area(self
->frame
, moved
, resized
);
1905 if (!resized
&& (force_reply
|| ((!user
&& moved
) || (user
&& final
))))
1908 event
.type
= ConfigureNotify
;
1909 event
.xconfigure
.display
= ob_display
;
1910 event
.xconfigure
.event
= self
->window
;
1911 event
.xconfigure
.window
= self
->window
;
1913 /* root window real coords */
1914 event
.xconfigure
.x
= self
->frame
->area
.x
+ self
->frame
->size
.left
-
1916 event
.xconfigure
.y
= self
->frame
->area
.y
+ self
->frame
->size
.top
-
1918 event
.xconfigure
.width
= w
;
1919 event
.xconfigure
.height
= h
;
1920 event
.xconfigure
.border_width
= 0;
1921 event
.xconfigure
.above
= self
->frame
->plate
;
1922 event
.xconfigure
.override_redirect
= FALSE
;
1923 XSendEvent(event
.xconfigure
.display
, event
.xconfigure
.window
,
1924 FALSE
, StructureNotifyMask
, &event
);
1929 void client_fullscreen(ObClient
*self
, gboolean fs
, gboolean savearea
)
1933 if (!(self
->functions
& OB_CLIENT_FUNC_FULLSCREEN
) || /* can't */
1934 self
->fullscreen
== fs
) return; /* already done */
1936 self
->fullscreen
= fs
;
1937 client_change_state(self
); /* change the state hints on the client,
1938 and adjust out layer/stacking */
1942 guint32 dimensions
[4];
1943 dimensions
[0] = self
->area
.x
;
1944 dimensions
[1] = self
->area
.y
;
1945 dimensions
[2] = self
->area
.width
;
1946 dimensions
[3] = self
->area
.height
;
1948 PROP_SETA32(self
->window
, openbox_premax
, cardinal
,
1952 /* these are not actually used cuz client_configure will set them
1953 as appropriate when the window is fullscreened */
1960 /* pick some fallbacks... */
1961 a
= screen_area_monitor(self
->desktop
, 0);
1962 x
= a
->x
+ a
->width
/ 4;
1963 y
= a
->y
+ a
->height
/ 4;
1967 if (PROP_GETA32(self
->window
, openbox_premax
, cardinal
,
1968 (guint32
**)&dimensions
, &num
)) {
1979 client_setup_decor_and_functions(self
);
1981 client_configure(self
, OB_CORNER_TOPLEFT
, x
, y
, w
, h
, TRUE
, TRUE
);
1983 /* try focus us when we go into fullscreen mode */
1987 static void client_iconify_recursive(ObClient
*self
,
1988 gboolean iconic
, gboolean curdesk
)
1991 gboolean changed
= FALSE
;
1994 if (self
->iconic
!= iconic
) {
1995 ob_debug("%sconifying window: 0x%lx\n", (iconic
? "I" : "Uni"),
1998 self
->iconic
= iconic
;
2001 if (self
->functions
& OB_CLIENT_FUNC_ICONIFY
) {
2002 self
->wmstate
= IconicState
;
2003 self
->ignore_unmaps
++;
2004 /* we unmap the client itself so that we can get MapRequest
2005 events, and because the ICCCM tells us to! */
2006 XUnmapWindow(ob_display
, self
->window
);
2008 /* update the focus lists.. iconic windows go to the bottom of
2009 the list, put the new iconic window at the 'top of the
2011 focus_order_to_top(self
);
2017 client_set_desktop(self
, screen_desktop
, FALSE
);
2018 self
->wmstate
= self
->shaded
? IconicState
: NormalState
;
2019 XMapWindow(ob_display
, self
->window
);
2021 /* this puts it after the current focused window */
2022 focus_order_remove(self
);
2023 focus_order_add_new(self
);
2025 /* this is here cuz with the VIDMODE extension, the viewport can
2026 change while a fullscreen window is iconic, and when it
2027 uniconifies, it would be nice if it did so to the new position
2029 client_reconfigure(self
);
2036 client_change_state(self
);
2037 client_showhide(self
);
2038 screen_update_areas();
2040 dispatch_client(iconic
? Event_Client_Unmapped
: Event_Client_Mapped
,
2044 /* iconify all transients */
2045 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2046 if (it
->data
!= self
) client_iconify_recursive(it
->data
,
2050 void client_iconify(ObClient
*self
, gboolean iconic
, gboolean curdesk
)
2052 /* move up the transient chain as far as possible first */
2053 self
= client_search_top_transient(self
);
2055 client_iconify_recursive(client_search_top_transient(self
),
2059 void client_maximize(ObClient
*self
, gboolean max
, int dir
, gboolean savearea
)
2063 g_assert(dir
== 0 || dir
== 1 || dir
== 2);
2064 if (!(self
->functions
& OB_CLIENT_FUNC_MAXIMIZE
)) return; /* can't */
2066 /* check if already done */
2068 if (dir
== 0 && self
->max_horz
&& self
->max_vert
) return;
2069 if (dir
== 1 && self
->max_horz
) return;
2070 if (dir
== 2 && self
->max_vert
) return;
2072 if (dir
== 0 && !self
->max_horz
&& !self
->max_vert
) return;
2073 if (dir
== 1 && !self
->max_horz
) return;
2074 if (dir
== 2 && !self
->max_vert
) return;
2077 /* work with the frame's coords */
2078 x
= self
->frame
->area
.x
;
2079 y
= self
->frame
->area
.y
;
2080 w
= self
->area
.width
;
2081 h
= self
->area
.height
;
2085 gint32 dimensions
[4];
2094 /* get the property off the window and use it for the dimensions
2095 we are already maxed on */
2096 if (PROP_GETA32(self
->window
, openbox_premax
, cardinal
,
2097 (guint32
**)&readdim
, &num
)) {
2099 if (self
->max_horz
) {
2100 dimensions
[0] = readdim
[0];
2101 dimensions
[2] = readdim
[2];
2103 if (self
->max_vert
) {
2104 dimensions
[1] = readdim
[1];
2105 dimensions
[3] = readdim
[3];
2111 PROP_SETA32(self
->window
, openbox_premax
, cardinal
,
2112 (guint32
*)dimensions
, 4);
2119 /* pick some fallbacks... */
2120 a
= screen_area_monitor(self
->desktop
, 0);
2121 if (dir
== 0 || dir
== 1) { /* horz */
2122 x
= a
->x
+ a
->width
/ 4;
2125 if (dir
== 0 || dir
== 2) { /* vert */
2126 y
= a
->y
+ a
->height
/ 4;
2130 if (PROP_GETA32(self
->window
, openbox_premax
, cardinal
,
2131 (guint32
**)&dimensions
, &num
)) {
2133 if (dir
== 0 || dir
== 1) { /* horz */
2137 if (dir
== 0 || dir
== 2) { /* vert */
2146 if (dir
== 0 || dir
== 1) /* horz */
2147 self
->max_horz
= max
;
2148 if (dir
== 0 || dir
== 2) /* vert */
2149 self
->max_vert
= max
;
2151 if (!self
->max_horz
&& !self
->max_vert
)
2152 PROP_ERASE(self
->window
, openbox_premax
);
2154 client_change_state(self
); /* change the state hints on the client */
2156 /* figure out where the client should be going */
2157 frame_frame_gravity(self
->frame
, &x
, &y
);
2158 client_configure(self
, OB_CORNER_TOPLEFT
, x
, y
, w
, h
, TRUE
, TRUE
);
2161 void client_shade(ObClient
*self
, gboolean shade
)
2163 if ((!(self
->functions
& OB_CLIENT_FUNC_SHADE
) &&
2164 shade
) || /* can't shade */
2165 self
->shaded
== shade
) return; /* already done */
2167 /* when we're iconic, don't change the wmstate */
2169 self
->wmstate
= shade
? IconicState
: NormalState
;
2170 self
->shaded
= shade
;
2171 client_change_state(self
);
2172 /* resize the frame to just the titlebar */
2173 frame_adjust_area(self
->frame
, FALSE
, FALSE
);
2176 void client_close(ObClient
*self
)
2180 if (!(self
->functions
& OB_CLIENT_FUNC_CLOSE
)) return;
2183 XXX: itd be cool to do timeouts and shit here for killing the client's
2185 like... if the window is around after 5 seconds, then the close button
2186 turns a nice red, and if this function is called again, the client is
2190 ce
.xclient
.type
= ClientMessage
;
2191 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2192 ce
.xclient
.display
= ob_display
;
2193 ce
.xclient
.window
= self
->window
;
2194 ce
.xclient
.format
= 32;
2195 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_delete_window
;
2196 ce
.xclient
.data
.l
[1] = event_lasttime
;
2197 ce
.xclient
.data
.l
[2] = 0l;
2198 ce
.xclient
.data
.l
[3] = 0l;
2199 ce
.xclient
.data
.l
[4] = 0l;
2200 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2203 void client_kill(ObClient
*self
)
2205 XKillClient(ob_display
, self
->window
);
2208 void client_set_desktop_recursive(ObClient
*self
,
2209 guint target
, gboolean donthide
)
2214 if (target
!= self
->desktop
) {
2216 ob_debug("Setting desktop %u\n", target
+1);
2218 g_assert(target
< screen_num_desktops
|| target
== DESKTOP_ALL
);
2220 /* remove from the old desktop(s) */
2221 focus_order_remove(self
);
2223 old
= self
->desktop
;
2224 self
->desktop
= target
;
2225 PROP_SET32(self
->window
, net_wm_desktop
, cardinal
, target
);
2226 /* the frame can display the current desktop state */
2227 frame_adjust_state(self
->frame
);
2228 /* 'move' the window to the new desktop */
2230 client_showhide(self
);
2231 /* raise if it was not already on the desktop */
2232 if (old
!= DESKTOP_ALL
)
2233 stacking_raise(CLIENT_AS_WINDOW(self
));
2234 screen_update_areas();
2236 /* add to the new desktop(s) */
2237 if (config_focus_new
)
2238 focus_order_to_top(self
);
2240 focus_order_to_bottom(self
);
2242 dispatch_client(Event_Client_Desktop
, self
, target
, old
);
2245 /* move all transients */
2246 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
)
2247 if (it
->data
!= self
) client_set_desktop_recursive(it
->data
,
2251 void client_set_desktop(ObClient
*self
, guint target
, gboolean donthide
)
2253 client_set_desktop_recursive(client_search_top_transient(self
),
2257 ObClient
*client_search_modal_child(ObClient
*self
)
2262 for (it
= self
->transients
; it
!= NULL
; it
= it
->next
) {
2263 ObClient
*c
= it
->data
;
2264 if ((ret
= client_search_modal_child(c
))) return ret
;
2265 if (c
->modal
) return c
;
2270 gboolean
client_validate(ObClient
*self
)
2274 XSync(ob_display
, FALSE
); /* get all events on the server */
2276 if (XCheckTypedWindowEvent(ob_display
, self
->window
, DestroyNotify
, &e
) ||
2277 XCheckTypedWindowEvent(ob_display
, self
->window
, UnmapNotify
, &e
)) {
2278 XPutBackEvent(ob_display
, &e
);
2285 void client_set_wm_state(ObClient
*self
, long state
)
2287 if (state
== self
->wmstate
) return; /* no change */
2291 client_iconify(self
, TRUE
, TRUE
);
2294 client_iconify(self
, FALSE
, TRUE
);
2299 void client_set_state(ObClient
*self
, Atom action
, long data1
, long data2
)
2301 gboolean shaded
= self
->shaded
;
2302 gboolean fullscreen
= self
->fullscreen
;
2303 gboolean max_horz
= self
->max_horz
;
2304 gboolean max_vert
= self
->max_vert
;
2307 if (!(action
== prop_atoms
.net_wm_state_add
||
2308 action
== prop_atoms
.net_wm_state_remove
||
2309 action
== prop_atoms
.net_wm_state_toggle
))
2310 /* an invalid action was passed to the client message, ignore it */
2313 for (i
= 0; i
< 2; ++i
) {
2314 Atom state
= i
== 0 ? data1
: data2
;
2316 if (!state
) continue;
2318 /* if toggling, then pick whether we're adding or removing */
2319 if (action
== prop_atoms
.net_wm_state_toggle
) {
2320 if (state
== prop_atoms
.net_wm_state_modal
)
2321 action
= self
->modal
? prop_atoms
.net_wm_state_remove
:
2322 prop_atoms
.net_wm_state_add
;
2323 else if (state
== prop_atoms
.net_wm_state_maximized_vert
)
2324 action
= self
->max_vert
? prop_atoms
.net_wm_state_remove
:
2325 prop_atoms
.net_wm_state_add
;
2326 else if (state
== prop_atoms
.net_wm_state_maximized_horz
)
2327 action
= self
->max_horz
? prop_atoms
.net_wm_state_remove
:
2328 prop_atoms
.net_wm_state_add
;
2329 else if (state
== prop_atoms
.net_wm_state_shaded
)
2330 action
= self
->shaded
? prop_atoms
.net_wm_state_remove
:
2331 prop_atoms
.net_wm_state_add
;
2332 else if (state
== prop_atoms
.net_wm_state_skip_taskbar
)
2333 action
= self
->skip_taskbar
?
2334 prop_atoms
.net_wm_state_remove
:
2335 prop_atoms
.net_wm_state_add
;
2336 else if (state
== prop_atoms
.net_wm_state_skip_pager
)
2337 action
= self
->skip_pager
?
2338 prop_atoms
.net_wm_state_remove
:
2339 prop_atoms
.net_wm_state_add
;
2340 else if (state
== prop_atoms
.net_wm_state_fullscreen
)
2341 action
= self
->fullscreen
?
2342 prop_atoms
.net_wm_state_remove
:
2343 prop_atoms
.net_wm_state_add
;
2344 else if (state
== prop_atoms
.net_wm_state_above
)
2345 action
= self
->above
? prop_atoms
.net_wm_state_remove
:
2346 prop_atoms
.net_wm_state_add
;
2347 else if (state
== prop_atoms
.net_wm_state_below
)
2348 action
= self
->below
? prop_atoms
.net_wm_state_remove
:
2349 prop_atoms
.net_wm_state_add
;
2352 if (action
== prop_atoms
.net_wm_state_add
) {
2353 if (state
== prop_atoms
.net_wm_state_modal
) {
2354 /* XXX raise here or something? */
2356 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2358 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2360 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2362 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2363 self
->skip_taskbar
= TRUE
;
2364 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2365 self
->skip_pager
= TRUE
;
2366 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2368 } else if (state
== prop_atoms
.net_wm_state_above
) {
2370 } else if (state
== prop_atoms
.net_wm_state_below
) {
2374 } else { /* action == prop_atoms.net_wm_state_remove */
2375 if (state
== prop_atoms
.net_wm_state_modal
) {
2376 self
->modal
= FALSE
;
2377 } else if (state
== prop_atoms
.net_wm_state_maximized_vert
) {
2379 } else if (state
== prop_atoms
.net_wm_state_maximized_horz
) {
2381 } else if (state
== prop_atoms
.net_wm_state_shaded
) {
2383 } else if (state
== prop_atoms
.net_wm_state_skip_taskbar
) {
2384 self
->skip_taskbar
= FALSE
;
2385 } else if (state
== prop_atoms
.net_wm_state_skip_pager
) {
2386 self
->skip_pager
= FALSE
;
2387 } else if (state
== prop_atoms
.net_wm_state_fullscreen
) {
2389 } else if (state
== prop_atoms
.net_wm_state_above
) {
2390 self
->above
= FALSE
;
2391 } else if (state
== prop_atoms
.net_wm_state_below
) {
2392 self
->below
= FALSE
;
2396 if (max_horz
!= self
->max_horz
|| max_vert
!= self
->max_vert
) {
2397 if (max_horz
!= self
->max_horz
&& max_vert
!= self
->max_vert
) {
2399 if (max_horz
== max_vert
) { /* both going the same way */
2400 client_maximize(self
, max_horz
, 0, TRUE
);
2402 client_maximize(self
, max_horz
, 1, TRUE
);
2403 client_maximize(self
, max_vert
, 2, TRUE
);
2407 if (max_horz
!= self
->max_horz
)
2408 client_maximize(self
, max_horz
, 1, TRUE
);
2410 client_maximize(self
, max_vert
, 2, TRUE
);
2413 /* change fullscreen state before shading, as it will affect if the window
2415 if (fullscreen
!= self
->fullscreen
)
2416 client_fullscreen(self
, fullscreen
, TRUE
);
2417 if (shaded
!= self
->shaded
)
2418 client_shade(self
, shaded
);
2419 client_calc_layer(self
);
2420 client_change_state(self
); /* change the hint to reflect these changes */
2423 ObClient
*client_focus_target(ObClient
*self
)
2427 /* if we have a modal child, then focus it, not us */
2428 child
= client_search_modal_child(self
);
2429 if (child
) return child
;
2433 gboolean
client_can_focus(ObClient
*self
)
2437 /* choose the correct target */
2438 self
= client_focus_target(self
);
2440 if (!self
->frame
->visible
)
2443 if (!((self
->can_focus
|| self
->focus_notify
) &&
2444 (self
->desktop
== screen_desktop
||
2445 self
->desktop
== DESKTOP_ALL
) &&
2449 /* do a check to see if the window has already been unmapped or destroyed
2450 do this intelligently while watching out for unmaps we've generated
2451 (ignore_unmaps > 0) */
2452 if (XCheckTypedWindowEvent(ob_display
, self
->window
,
2453 DestroyNotify
, &ev
)) {
2454 XPutBackEvent(ob_display
, &ev
);
2457 while (XCheckTypedWindowEvent(ob_display
, self
->window
,
2458 UnmapNotify
, &ev
)) {
2459 if (self
->ignore_unmaps
) {
2460 self
->ignore_unmaps
--;
2462 XPutBackEvent(ob_display
, &ev
);
2470 gboolean
client_focus(ObClient
*self
)
2472 /* choose the correct target */
2473 self
= client_focus_target(self
);
2475 if (!client_can_focus(self
)) {
2476 if (!self
->frame
->visible
) {
2477 /* update the focus lists */
2478 focus_order_to_top(self
);
2483 if (self
->can_focus
)
2484 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2485 I choose to use it always, hopefully to find errors quicker, if any
2486 are left. (I hate X. I hate focus events.) */
2487 XSetInputFocus(ob_display
, self
->window
, RevertToPointerRoot
,
2490 if (self
->focus_notify
) {
2492 ce
.xclient
.type
= ClientMessage
;
2493 ce
.xclient
.message_type
= prop_atoms
.wm_protocols
;
2494 ce
.xclient
.display
= ob_display
;
2495 ce
.xclient
.window
= self
->window
;
2496 ce
.xclient
.format
= 32;
2497 ce
.xclient
.data
.l
[0] = prop_atoms
.wm_take_focus
;
2498 ce
.xclient
.data
.l
[1] = event_lasttime
;
2499 ce
.xclient
.data
.l
[2] = 0l;
2500 ce
.xclient
.data
.l
[3] = 0l;
2501 ce
.xclient
.data
.l
[4] = 0l;
2502 XSendEvent(ob_display
, self
->window
, FALSE
, NoEventMask
, &ce
);
2506 ob_debug("%sively focusing %lx at %d\n",
2507 (self
->can_focus
? "act" : "pass"),
2508 self
->window
, (int) event_lasttime
);
2511 /* Cause the FocusIn to come back to us. Important for desktop switches,
2512 since otherwise we'll have no FocusIn on the queue and send it off to
2513 the focus_backup. */
2514 XSync(ob_display
, FALSE
);
2518 void client_unfocus(ObClient
*self
)
2520 g_assert(focus_client
== self
);
2522 ob_debug("client_unfocus for %lx\n", self
->window
);
2524 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING
);
2527 void client_activate(ObClient
*self
)
2529 if (client_normal(self
) && screen_showing_desktop
)
2530 screen_show_desktop(FALSE
);
2532 client_iconify(self
, FALSE
, FALSE
);
2533 if (self
->desktop
!= DESKTOP_ALL
&&
2534 self
->desktop
!= screen_desktop
)
2535 screen_set_desktop(self
->desktop
);
2536 else if (!self
->frame
->visible
)
2537 /* if its not visible for other reasons, then don't mess
2541 client_shade(self
, FALSE
);
2543 stacking_raise(CLIENT_AS_WINDOW(self
));
2546 gboolean
client_focused(ObClient
*self
)
2548 return self
== focus_client
;
2551 ObClientIcon
*client_icon(ObClient
*self
, int w
, int h
)
2554 /* si is the smallest image >= req */
2555 /* li is the largest image < req */
2556 unsigned long size
, smallest
= 0xffffffff, largest
= 0, si
= 0, li
= 0;
2558 if (!self
->nicons
) return NULL
;
2560 for (i
= 0; i
< self
->nicons
; ++i
) {
2561 size
= self
->icons
[i
].width
* self
->icons
[i
].height
;
2562 if (size
< smallest
&& size
>= (unsigned)(w
* h
)) {
2566 if (size
> largest
&& size
<= (unsigned)(w
* h
)) {
2571 if (largest
== 0) /* didnt find one smaller than the requested size */
2572 return &self
->icons
[si
];
2573 return &self
->icons
[li
];
2576 /* this be mostly ripped from fvwm */
2577 ObClient
*client_find_directional(ObClient
*c
, ObDirection dir
)
2579 int my_cx
, my_cy
, his_cx
, his_cy
;
2582 int score
, best_score
;
2583 ObClient
*best_client
, *cur
;
2589 /* first, find the centre coords of the currently focused window */
2590 my_cx
= c
->frame
->area
.x
+ c
->frame
->area
.width
/ 2;
2591 my_cy
= c
->frame
->area
.y
+ c
->frame
->area
.height
/ 2;
2596 for(it
= g_list_first(client_list
); it
; it
= it
->next
) {
2599 /* the currently selected window isn't interesting */
2602 if (!client_normal(cur
))
2604 if(c
->desktop
!= cur
->desktop
&& cur
->desktop
!= DESKTOP_ALL
)
2608 if(client_focus_target(cur
) == cur
&&
2609 !(cur
->can_focus
|| cur
->focus_notify
))
2612 /* find the centre coords of this window, from the
2613 * currently focused window's point of view */
2614 his_cx
= (cur
->frame
->area
.x
- my_cx
)
2615 + cur
->frame
->area
.width
/ 2;
2616 his_cy
= (cur
->frame
->area
.y
- my_cy
)
2617 + cur
->frame
->area
.height
/ 2;
2621 /* Rotate the diagonals 45 degrees counterclockwise.
2622 * To do this, multiply the matrix /+h +h\ with the
2623 * vector (x y). \-h +h/
2624 * h = sqrt(0.5). We can set h := 1 since absolute
2625 * distance doesn't matter here. */
2626 tx
= his_cx
+ his_cy
;
2627 his_cy
= -his_cx
+ his_cy
;
2632 case OB_DIRECTION_NORTH
:
2633 case OB_DIRECTION_SOUTH
:
2634 case OB_DIRECTION_NORTHEAST
:
2635 case OB_DIRECTION_SOUTHWEST
:
2636 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
2637 distance
= ((dir
== OB_DIRECTION_NORTH
||
2638 dir
== OB_DIRECTION_NORTHEAST
) ?
2641 case OB_DIRECTION_EAST
:
2642 case OB_DIRECTION_WEST
:
2643 case OB_DIRECTION_SOUTHEAST
:
2644 case OB_DIRECTION_NORTHWEST
:
2645 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
2646 distance
= ((dir
== OB_DIRECTION_WEST
||
2647 dir
== OB_DIRECTION_NORTHWEST
) ?
2652 /* the target must be in the requested direction */
2656 /* Calculate score for this window. The smaller the better. */
2657 score
= distance
+ offset
;
2659 /* windows more than 45 degrees off the direction are
2660 * heavily penalized and will only be chosen if nothing
2661 * else within a million pixels */
2662 if(offset
> distance
)
2665 if(best_score
== -1 || score
< best_score
)
2673 void client_set_layer(ObClient
*self
, int layer
)
2677 self
->above
= FALSE
;
2678 } else if (layer
== 0) {
2679 self
->below
= self
->above
= FALSE
;
2681 self
->below
= FALSE
;
2684 client_calc_layer(self
);
2685 client_change_state(self
); /* reflect this in the state hints */
2688 guint
client_monitor(ObClient
*self
)
2692 for (i
= 0; i
< screen_num_monitors
; ++i
) {
2693 Rect
*area
= screen_physical_area_monitor(i
);
2694 if (RECT_INTERSECTS_RECT(*area
, self
->frame
->area
))
2697 if (i
== screen_num_monitors
) i
= 0;
2698 g_assert(i
< screen_num_monitors
);
2702 ObClient
*client_search_top_transient(ObClient
*self
)
2704 /* move up the transient chain as far as possible */
2705 if (self
->transient_for
) {
2706 if (self
->transient_for
!= OB_TRAN_GROUP
) {
2707 return client_search_top_transient(self
->transient_for
);
2711 for (it
= self
->group
->members
; it
; it
= it
->next
) {
2712 ObClient
*c
= it
->data
;
2714 /* checking transient_for prevents infinate loops! */
2715 if (c
!= self
&& !c
->transient_for
)
2726 ObClient
*client_search_transient(ObClient
*self
, ObClient
*search
)
2730 for (sit
= self
->transients
; sit
; sit
= g_slist_next(sit
)) {
2731 if (sit
->data
== search
)
2733 if (client_search_transient(sit
->data
, search
))
2739 gchar
* client_get_sm_client_id(ObClient
*self
)
2743 if (!PROP_GETS(self
->window
, sm_client_id
, locale
, &id
) && self
->group
)
2744 PROP_GETS(self
->group
->leader
, sm_client_id
, locale
, &id
);