]> Dogcows Code - chaz/openbox/blob - openbox/client.c
a couple frame layout bugfixes hidden inside all this.. wee
[chaz/openbox] / openbox / client.c
1 #include "debug.h"
2 #include "client.h"
3 #include "dock.h"
4 #include "xerror.h"
5 #include "startup.h"
6 #include "screen.h"
7 #include "moveresize.h"
8 #include "prop.h"
9 #include "extensions.h"
10 #include "frame.h"
11 #include "session.h"
12 #include "event.h"
13 #include "grab.h"
14 #include "focus.h"
15 #include "stacking.h"
16 #include "dispatch.h"
17 #include "openbox.h"
18 #include "group.h"
19 #include "config.h"
20 #include "menu.h"
21 #include "render/render.h"
22
23 #include <glib.h>
24 #include <X11/Xutil.h>
25
26 /*! The event mask to grab on client windows */
27 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
28 StructureNotifyMask)
29
30 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
31 ButtonMotionMask)
32
33 GList *client_list = NULL;
34
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);
48
49 void client_startup()
50 {
51 client_set_list();
52 }
53
54 void client_shutdown()
55 {
56 }
57
58 void client_set_list()
59 {
60 Window *windows, *win_it;
61 GList *it;
62 guint size = g_list_length(client_list);
63
64 /* create an array of the window ids */
65 if (size > 0) {
66 windows = g_new(Window, size);
67 win_it = windows;
68 for (it = client_list; it != NULL; it = it->next, ++win_it)
69 *win_it = ((ObClient*)it->data)->window;
70 } else
71 windows = NULL;
72
73 PROP_SETA32(RootWindow(ob_display, ob_screen),
74 net_client_list, window, (guint32*)windows, size);
75
76 if (windows)
77 g_free(windows);
78
79 stacking_set_list();
80 }
81
82 /*
83 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, void *data)
84 {
85 GSList *it;
86
87 for (it = self->transients; it; it = it->next) {
88 if (!func(it->data, data)) return;
89 client_foreach_transient(it->data, func, data);
90 }
91 }
92
93 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, void *data)
94 {
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);
99 } else {
100 GSList *it;
101
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);
107 }
108 }
109 }
110 }
111 */
112
113 void client_manage_all()
114 {
115 unsigned int i, j, nchild;
116 Window w, *children;
117 XWMHints *wmhints;
118 XWindowAttributes attrib;
119
120 XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
121 &w, &w, &children, &nchild);
122
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]);
127 if (wmhints) {
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) {
132 children[j] = None;
133 break;
134 }
135 XFree(wmhints);
136 }
137 }
138
139 for (i = 0; i < nchild; ++i) {
140 if (children[i] == None)
141 continue;
142 if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
143 if (attrib.override_redirect) continue;
144
145 if (attrib.map_state != IsUnmapped)
146 client_manage(children[i]);
147 }
148 }
149 XFree(children);
150
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
154 at the bottom! */
155 for (i = startup_stack_size; i > 0; --i) {
156 ObWindow *obw;
157
158 w = startup_stack_order[i-1];
159 obw = g_hash_table_lookup(window_map, &w);
160 if (obw) {
161 g_assert(WINDOW_IS_CLIENT(obw));
162 stacking_lower(CLIENT_AS_WINDOW(obw));
163 }
164 }
165 g_free(startup_stack_order);
166 startup_stack_order = NULL;
167 startup_stack_size = 0;
168
169 if (config_focus_new) {
170 ObWindow *active;
171
172 active = g_hash_table_lookup(window_map, &startup_active);
173 if (active) {
174 g_assert(WINDOW_IS_CLIENT(active));
175 if (!client_focus(WINDOW_AS_CLIENT(active)))
176 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
177 } else
178 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
179 }
180 }
181
182 void client_manage(Window window)
183 {
184 ObClient *self;
185 XEvent e;
186 XWindowAttributes attrib;
187 XSetWindowAttributes attrib_set;
188 XWMHints *wmhint;
189 gboolean activate = FALSE;
190
191 grab_server(TRUE);
192
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);
198
199 grab_server(FALSE);
200 return; /* don't manage it */
201 }
202
203 /* make sure it isn't an override-redirect window */
204 if (!XGetWindowAttributes(ob_display, window, &attrib) ||
205 attrib.override_redirect) {
206 grab_server(FALSE);
207 return; /* don't manage it */
208 }
209
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);
215 grab_server(FALSE);
216 XFree(wmhint);
217 return;
218 }
219 XFree(wmhint);
220 }
221
222 ob_debug("Managing window: %lx\n", window);
223
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);
229
230
231 /* create the ObClient struct, and populate it from the hints on the
232 window */
233 self = g_new(ObClient, 1);
234 self->obwin.type = Window_Client;
235 self->window = window;
236
237 client_get_all(self);
238 client_restore_session_state(self);
239
240 client_change_state(self);
241
242 /* remove the client's border (and adjust re gravity) */
243 client_toggle_border(self, FALSE);
244
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);
248
249 /* create the decoration frame for the client window */
250 self->frame = frame_new();
251
252 frame_grab_client(self->frame, self);
253
254 client_apply_startup_state(self);
255
256 grab_server(FALSE);
257
258 /* update the focus lists */
259 focus_order_add_new(self);
260
261 stacking_add(CLIENT_AS_WINDOW(self));
262
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))
270 {
271 if (self->desktop != screen_desktop) {
272 /* activate the window */
273 activate = TRUE;
274 } else {
275 gboolean group_foc = FALSE;
276
277 if (self->group) {
278 GSList *it;
279
280 for (it = self->group->members; it; it = it->next)
281 {
282 if (client_focused(it->data))
283 {
284 group_foc = TRUE;
285 break;
286 }
287 }
288 }
289 if ((group_foc ||
290 (!self->transient_for && (!self->group ||
291 !self->group->members->next))) ||
292 client_search_focus_tree_full(self) ||
293 !focus_client ||
294 !client_normal(focus_client))
295 {
296 /* activate the window */
297 activate = TRUE;
298 }
299 }
300 }
301
302 dispatch_client(Event_Client_New, self, 0, 0);
303
304 /* make sure the window is visible */
305 client_move_onscreen(self, TRUE);
306
307 screen_update_areas();
308
309 client_showhide(self);
310
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
315 but do focus it. */
316 if (activate) client_focus(self);
317
318 /* client_activate does this but we aret using it so we have to do it
319 here as well */
320 if (screen_showing_desktop)
321 screen_show_desktop(FALSE);
322
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);
326
327 /* update the list hints */
328 client_set_list();
329
330 dispatch_client(Event_Client_Mapped, self, 0, 0);
331
332 ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
333 }
334
335 void client_unmanage_all()
336 {
337 while (client_list != NULL)
338 client_unmanage(client_list->data);
339 }
340
341 /* called by client_unmanage() to close any menus referencing this client */
342 void client_close_menus(gpointer key, gpointer value, gpointer self)
343 {
344 if (((ObMenu *)value)->client == (ObClient *)self)
345 menu_hide((ObMenu *)value);
346 }
347
348 void client_unmanage(ObClient *self)
349 {
350 guint j;
351 GSList *it;
352
353 ob_debug("Unmanaging window: %lx (%s)\n", self->window, self->class);
354
355 dispatch_client(Event_Client_Destroy, self, 0, 0);
356 g_assert(self != NULL);
357
358 /* remove the window from our save set */
359 XChangeSaveSet(ob_display, self->window, SetModeDelete);
360
361 /* we dont want events no more */
362 XSelectInput(ob_display, self->window, NoEventMask);
363
364 frame_hide(self->frame);
365
366 client_list = g_list_remove(client_list, self);
367 stacking_remove(self);
368 g_hash_table_remove(window_map, &self->window);
369
370 /* update the focus lists */
371 focus_order_remove(self);
372
373 /* once the client is out of the list, update the struts to remove it's
374 influence */
375 screen_update_areas();
376
377 /* tell our parent(s) that we're gone */
378 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
379 GSList *it;
380
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);
388 }
389
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);
395 }
396 }
397
398 if (moveresize_client == self)
399 moveresize_end(TRUE);
400
401 /* close any windows that are attached to this window */
402 g_hash_table_foreach(menu_hash, client_close_menus, self);
403
404
405 if (focus_client == self) {
406 XEvent e;
407
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);
412 }
413
414 /* remove from its group */
415 if (self->group) {
416 group_remove(self->group, self);
417 self->group = NULL;
418 }
419
420 /* dispatch the unmapped event */
421 dispatch_client(Event_Client_Unmapped, self, 0, 0);
422 g_assert(self != NULL);
423
424 /* give the client its border back */
425 client_toggle_border(self, TRUE);
426
427 /* reparent the window out of the frame, and free the frame */
428 frame_release_client(self->frame, self);
429 self->frame = NULL;
430
431 if (ob_state() != OB_STATE_EXITING) {
432 /* these values should not be persisted across a window
433 unmapping/mapping */
434 PROP_ERASE(self->window, net_wm_desktop);
435 PROP_ERASE(self->window, net_wm_state);
436 PROP_ERASE(self->window, wm_state);
437 } else {
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 */
440 if (self->iconic)
441 XMapWindow(ob_display, self->window);
442 }
443
444
445 ob_debug("Unmanaged window 0x%lx\n", self->window);
446
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)
452 g_free(self->icons);
453 g_free(self->title);
454 g_free(self->icon_title);
455 g_free(self->name);
456 g_free(self->class);
457 g_free(self->role);
458 g_free(self);
459
460 /* update the list hints */
461 client_set_list();
462 }
463
464 static void client_restore_session_state(ObClient *self)
465 {
466 ObSessionState *s;
467
468 s = session_state_find(self);
469 if (!(s)) return;
470
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);
474
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);
478
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;
488
489 session_state_free(s);
490 }
491
492 void client_move_onscreen(ObClient *self, gboolean rude)
493 {
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,
501 TRUE, TRUE);
502 }
503 }
504
505 gboolean client_find_onscreen(ObClient *self, int *x, int *y, int w, int h,
506 gboolean rude)
507 {
508 Rect *a;
509 int ox = *x, oy = *y;
510
511 frame_client_gravity(self->frame, x, y); /* get where the frame
512 would be */
513
514 /* XXX watch for xinerama dead areas */
515
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)
522 *x = a->x;
523 if (!self->strut.top && *y + self->frame->area.height - 1 < a->y)
524 *y = a->y;
525
526 if (rude) {
527 /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good.
528 Java can suck it too. */
529
530 /* dont let windows map/move into the strut unless they
531 are bigger than the available area */
532 if (w <= a->width) {
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;
536 }
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;
541 }
542 }
543
544 frame_frame_gravity(self->frame, x, y); /* get where the client
545 should be */
546
547 return ox != *x || oy != *y;
548 }
549
550 static void client_toggle_border(ObClient *self, gboolean show)
551 {
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
554 different position.
555 when re-adding the border to the client, the same operation needs to be
556 reversed. */
557 int oldx = self->area.x, oldy = self->area.y;
558 int x = oldx, y = oldy;
559 switch(self->gravity) {
560 default:
561 case NorthWestGravity:
562 case WestGravity:
563 case SouthWestGravity:
564 break;
565 case NorthEastGravity:
566 case EastGravity:
567 case SouthEastGravity:
568 if (show) x -= self->border_width * 2;
569 else x += self->border_width * 2;
570 break;
571 case NorthGravity:
572 case SouthGravity:
573 case CenterGravity:
574 case ForgetGravity:
575 case StaticGravity:
576 if (show) x -= self->border_width;
577 else x += self->border_width;
578 break;
579 }
580 switch(self->gravity) {
581 default:
582 case NorthWestGravity:
583 case NorthGravity:
584 case NorthEastGravity:
585 break;
586 case SouthWestGravity:
587 case SouthGravity:
588 case SouthEastGravity:
589 if (show) y -= self->border_width * 2;
590 else y += self->border_width * 2;
591 break;
592 case WestGravity:
593 case EastGravity:
594 case CenterGravity:
595 case ForgetGravity:
596 case StaticGravity:
597 if (show) y -= self->border_width;
598 else y += self->border_width;
599 break;
600 }
601 self->area.x = x;
602 self->area.y = y;
603
604 if (show) {
605 XSetWindowBorderWidth(ob_display, self->window, self->border_width);
606
607 /* move the client so it is back it the right spot _with_ its
608 border! */
609 if (x != oldx || y != oldy)
610 XMoveWindow(ob_display, self->window, x, y);
611 } else
612 XSetWindowBorderWidth(ob_display, self->window, 0);
613 }
614
615
616 static void client_get_all(ObClient *self)
617 {
618 /* update EVERYTHING!! */
619
620 self->ignore_unmaps = 0;
621
622 /* defaults */
623 self->frame = NULL;
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;
631 self->layer = -1;
632 self->urgent = FALSE;
633 self->positioned = FALSE;
634 self->decorate = TRUE;
635 self->group = NULL;
636 self->nicons = 0;
637
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);
644
645 client_get_mwm_hints(self);
646 client_get_type(self);/* this can change the mwmhints for special cases */
647
648 client_update_protocols(self);
649
650 client_get_gravity(self); /* get the attribute gravity */
651 client_update_normal_hints(self); /* this may override the attribute
652 gravity */
653
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);
657
658 client_update_title(self);
659 client_update_class(self);
660 client_update_strut(self);
661 client_update_icons(self);
662 }
663
664 static void client_get_area(ObClient *self)
665 {
666 XWindowAttributes wattrib;
667 Status ret;
668
669 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
670 g_assert(ret != BadWindow);
671
672 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
673 self->border_width = wattrib.border_width;
674 }
675
676 static void client_get_desktop(ObClient *self)
677 {
678 guint32 d = screen_num_desktops; /* an always-invalid value */
679
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;
683 else
684 self->desktop = d;
685 } else {
686 gboolean trdesk = FALSE;
687
688 if (self->transient_for) {
689 if (self->transient_for != OB_TRAN_GROUP) {
690 self->desktop = self->transient_for->desktop;
691 trdesk = TRUE;
692 } else {
693 GSList *it;
694
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;
699 trdesk = TRUE;
700 break;
701 }
702 }
703 }
704 if (!trdesk)
705 /* defaults to the current desktop */
706 self->desktop = screen_desktop;
707 }
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);
711 }
712 }
713
714 static void client_get_state(ObClient *self)
715 {
716 guint32 *state;
717 guint num;
718
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;
722
723 if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
724 gulong i;
725 for (i = 0; i < num; ++i) {
726 if (state[i] == prop_atoms.net_wm_state_modal)
727 self->modal = TRUE;
728 else if (state[i] == prop_atoms.net_wm_state_shaded)
729 self->shaded = TRUE;
730 else if (state[i] == prop_atoms.net_wm_state_hidden)
731 self->iconic = TRUE;
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)
743 self->above = TRUE;
744 else if (state[i] == prop_atoms.net_wm_state_below)
745 self->below = TRUE;
746 }
747
748 g_free(state);
749 }
750 }
751
752 static void client_get_shaped(ObClient *self)
753 {
754 self->shaped = FALSE;
755 #ifdef SHAPE
756 if (extensions_shape) {
757 int foo;
758 guint ufoo;
759 int s;
760
761 XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
762
763 XShapeQueryExtents(ob_display, self->window, &s, &foo,
764 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
765 &ufoo);
766 self->shaped = (s != 0);
767 }
768 #endif
769 }
770
771 void client_update_transient_for(ObClient *self)
772 {
773 Window t = None;
774 ObClient *target = NULL;
775
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));
783
784 if (!target && self->group) {
785 /* not transient to a client, see if it is transient for a
786 group */
787 if (t == self->group->leader ||
788 t == None ||
789 t == RootWindow(ob_display, ob_screen)) {
790 /* window is a transient for its group! */
791 target = OB_TRAN_GROUP;
792 }
793 }
794 }
795 } else
796 self->transient = FALSE;
797
798 /* if anything has changed... */
799 if (target != self->transient_for) {
800 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
801 GSList *it;
802
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);
808 }
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);
813 }
814 self->transient_for = target;
815 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
816 GSList *it;
817
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);
823 }
824
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)) {
828 GSList *sit, *next;
829 for (sit = self->transients; sit; sit = next) {
830 next = g_slist_next(sit);
831 if (sit->data == it->data)
832 self->transients =
833 g_slist_delete_link(self->transients, sit);
834 }
835 }
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);
840 }
841 }
842 }
843
844 static void client_get_mwm_hints(ObClient *self)
845 {
846 guint num;
847 guint32 *hints;
848
849 self->mwmhints.flags = 0; /* default to none */
850
851 if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
852 &hints, &num)) {
853 if (num >= OB_MWM_ELEMENTS) {
854 self->mwmhints.flags = hints[0];
855 self->mwmhints.functions = hints[1];
856 self->mwmhints.decorations = hints[2];
857 }
858 g_free(hints);
859 }
860 }
861
862 void client_get_type(ObClient *self)
863 {
864 guint num, i;
865 guint32 *val;
866
867 self->type = -1;
868
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
890 functionality */
891 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
892 OB_MWM_FLAG_DECORATIONS);
893 self->mwmhints.decorations = 0;
894 self->mwmhints.functions = 0;
895 }
896 if (self->type != (ObClientType) -1)
897 break; /* grab the first legit type */
898 }
899 g_free(val);
900 }
901
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
905 transient. */
906 if (self->transient)
907 self->type = OB_CLIENT_TYPE_DIALOG;
908 else
909 self->type = OB_CLIENT_TYPE_NORMAL;
910 }
911 }
912
913 void client_update_protocols(ObClient *self)
914 {
915 guint32 *proto;
916 guint num_return, i;
917
918 self->focus_notify = FALSE;
919 self->delete_window = FALSE;
920
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;
930 }
931 g_free(proto);
932 }
933 }
934
935 static void client_get_gravity(ObClient *self)
936 {
937 XWindowAttributes wattrib;
938 Status ret;
939
940 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
941 g_assert(ret != BadWindow);
942 self->gravity = wattrib.win_gravity;
943 }
944
945 void client_update_normal_hints(ObClient *self)
946 {
947 XSizeHints size;
948 long ret;
949 int oldgravity = self->gravity;
950
951 /* defaults */
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);
958
959 /* get the hints from the window */
960 if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
961 self->positioned = !!(size.flags & (PPosition|USPosition));
962
963 if (size.flags & PWinGravity) {
964 self->gravity = size.win_gravity;
965
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
970 gravity */
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);
974 }
975 }
976
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;
982 }
983
984 if (size.flags & PMinSize)
985 SIZE_SET(self->min_size, size.min_width, size.min_height);
986
987 if (size.flags & PMaxSize)
988 SIZE_SET(self->max_size, size.max_width, size.max_height);
989
990 if (size.flags & PBaseSize)
991 SIZE_SET(self->base_size, size.base_width, size.base_height);
992
993 if (size.flags & PResizeInc)
994 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
995 }
996 }
997
998 void client_setup_decor_and_functions(ObClient *self)
999 {
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;
1018 }
1019
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;
1023
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;
1029 break;
1030
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;
1035 break;
1036
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);
1041 break;
1042
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;
1049 break;
1050 }
1051
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;
1061 }
1062 }
1063
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;
1075 */
1076 /* dont let mwm hints kill the close button
1077 if (! (self->mwmhints.functions & MwmFunc_Close))
1078 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1079 }
1080 }
1081
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;
1088
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;
1095 }
1096
1097 /* finally, the user can have requested no decorations, which overrides
1098 everything */
1099 if (!self->decorate)
1100 self->decorations = 0;
1101
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;
1105
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;
1112 }
1113
1114 client_change_allowed_actions(self);
1115
1116 if (self->frame) {
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);
1125 } else {
1126 /* this makes sure that these windows appear on all desktops */
1127 if (self->type == OB_CLIENT_TYPE_DESKTOP &&
1128 self->desktop != DESKTOP_ALL)
1129 {
1130 self->desktop = DESKTOP_ALL;
1131 }
1132 }
1133 }
1134
1135 static void client_change_allowed_actions(ObClient *self)
1136 {
1137 guint32 actions[9];
1138 int num = 0;
1139
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;
1143
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;
1159 }
1160
1161 PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1162
1163 /* make sure the window isn't breaking any rules now */
1164
1165 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1166 if (self->frame) client_shade(self, FALSE);
1167 else self->shaded = FALSE;
1168 }
1169 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1170 if (self->frame) client_iconify(self, FALSE, TRUE);
1171 else self->iconic = FALSE;
1172 }
1173 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1174 if (self->frame) client_fullscreen(self, FALSE, TRUE);
1175 else self->fullscreen = FALSE;
1176 }
1177 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1178 self->max_vert)) {
1179 if (self->frame) client_maximize(self, FALSE, 0, TRUE);
1180 else self->max_vert = self->max_horz = FALSE;
1181 }
1182 }
1183
1184 void client_reconfigure(ObClient *self)
1185 {
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);
1191 }
1192
1193 void client_update_wmhints(ObClient *self)
1194 {
1195 XWMHints *hints;
1196 gboolean ur = FALSE;
1197 GSList *it;
1198
1199 /* assume a window takes input if it doesnt specify */
1200 self->can_focus = TRUE;
1201
1202 if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1203 if (hints->flags & InputHint)
1204 self->can_focus = hints->input;
1205
1206 /* only do this when first managing the window *AND* when we aren't
1207 starting up! */
1208 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1209 if (hints->flags & StateHint)
1210 self->iconic = hints->initial_state == IconicState;
1211
1212 if (hints->flags & XUrgencyHint)
1213 ur = TRUE;
1214
1215 if (!(hints->flags & WindowGroupHint))
1216 hints->window_group = None;
1217
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,
1226 it->data);
1227 group_remove(self->group, self);
1228 self->group = NULL;
1229 }
1230 if (hints->window_group != None) {
1231 self->group = group_add(hints->window_group, self);
1232
1233 /* i can only have transients from the group if i am not
1234 transient myself */
1235 if (!self->transient_for) {
1236 /* add other transients of the group that are already
1237 set up */
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)
1241 self->transients =
1242 g_slist_append(self->transients, c);
1243 }
1244 }
1245 }
1246
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);
1252 }
1253
1254 /* the WM_HINTS can contain an icon */
1255 client_update_icons(self);
1256
1257 XFree(hints);
1258 }
1259
1260 if (ur != self->urgent) {
1261 self->urgent = ur;
1262 ob_debug("Urgent Hint for 0x%lx: %s\n", self->window,
1263 ur ? "ON" : "OFF");
1264 /* fire the urgent callback if we're mapped, otherwise, wait until
1265 after we're mapped */
1266 if (self->frame)
1267 dispatch_client(Event_Client_Urgent, self, self->urgent, 0);
1268 }
1269 }
1270
1271 void client_update_title(ObClient *self)
1272 {
1273 GList *it;
1274 guint32 nums;
1275 guint i;
1276 char *data = NULL;
1277 gboolean read_title;
1278
1279 g_free(self->title);
1280
1281 /* try netwm */
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");
1286
1287 /* look for duplicates and append a number */
1288 nums = 0;
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;
1294 }
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;
1300 break;
1301 }
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);
1307 g_free(ndata);
1308 g_free(data);
1309 data = vdata;
1310 }
1311
1312 PROP_SETS(self->window, net_wm_visible_name, data);
1313
1314 self->title = data;
1315
1316 if (self->frame)
1317 frame_adjust_title(self->frame);
1318
1319 /* update the icon title */
1320 data = NULL;
1321 g_free(self->icon_title);
1322
1323 read_title = TRUE;
1324 /* try netwm */
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);
1329 read_title = FALSE;
1330 }
1331
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);
1337 g_free(ndata);
1338 g_free(data);
1339 data = vdata;
1340 }
1341
1342 PROP_SETS(self->window, net_wm_visible_icon_name, data);
1343
1344 self->icon_title = data;
1345 }
1346
1347 void client_update_class(ObClient *self)
1348 {
1349 char **data;
1350 char *s;
1351
1352 if (self->name) g_free(self->name);
1353 if (self->class) g_free(self->class);
1354 if (self->role) g_free(self->role);
1355
1356 self->name = self->class = self->role = NULL;
1357
1358 if (PROP_GETSS(self->window, wm_class, locale, &data)) {
1359 if (data[0]) {
1360 self->name = g_strdup(data[0]);
1361 if (data[1])
1362 self->class = g_strdup(data[1]);
1363 }
1364 g_strfreev(data);
1365 }
1366
1367 if (PROP_GETS(self->window, wm_window_role, locale, &s))
1368 self->role = g_strdup(s);
1369
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("");
1373 }
1374
1375 void client_update_strut(ObClient *self)
1376 {
1377 guint num;
1378 guint32 *data;
1379 gboolean got = FALSE;
1380
1381 if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1382 &data, &num)) {
1383 if (num == 12) {
1384 got = TRUE;
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]);
1389 }
1390 g_free(data);
1391 }
1392
1393 if (!got &&
1394 PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1395 if (num == 4) {
1396 got = TRUE;
1397 STRUT_PARTIAL_SET(self->strut,
1398 data[0], data[2], data[1], data[3],
1399 0, 0, 0, 0, 0, 0, 0, 0);
1400 }
1401 g_free(data);
1402 }
1403
1404 if (!got)
1405 STRUT_PARTIAL_SET(self->strut, 0, 0, 0, 0,
1406 0, 0, 0, 0, 0, 0, 0, 0);
1407
1408 /* updating here is pointless while we're being mapped cuz we're not in
1409 the client list yet */
1410 if (self->frame)
1411 screen_update_areas();
1412 }
1413
1414 void client_update_icons(ObClient *self)
1415 {
1416 guint num;
1417 guint32 *data;
1418 guint w, h, i, j;
1419
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);
1424 self->nicons = 0;
1425
1426 if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
1427 /* figure out how many valid icons are in here */
1428 i = 0;
1429 while (num - i > 2) {
1430 w = data[i++];
1431 h = data[i++];
1432 i += w * h;
1433 if (i > num || w*h == 0) break;
1434 ++self->nicons;
1435 }
1436
1437 self->icons = g_new(ObClientIcon, self->nicons);
1438
1439 /* store the icons */
1440 i = 0;
1441 for (j = 0; j < self->nicons; ++j) {
1442 guint x, y, t;
1443
1444 w = self->icons[j].width = data[i++];
1445 h = self->icons[j].height = data[i++];
1446
1447 if (w*h == 0) continue;
1448
1449 self->icons[j].data = g_new(RrPixel32, w * h);
1450 for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
1451 if (x >= w) {
1452 x = 0;
1453 ++y;
1454 }
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);
1460 }
1461 g_assert(i <= num);
1462 }
1463
1464 g_free(data);
1465 } else if (PROP_GETA32(self->window, kwm_win_icon,
1466 kwm_win_icon, &data, &num)) {
1467 if (num == 2) {
1468 self->nicons++;
1469 self->icons = g_new(ObClientIcon, self->nicons);
1470 xerror_set_ignore(TRUE);
1471 if (!RrPixmapToRGBA(ob_rr_inst,
1472 data[0], data[1],
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]);
1477 self->nicons--;
1478 }
1479 xerror_set_ignore(FALSE);
1480 }
1481 g_free(data);
1482 } else {
1483 XWMHints *hints;
1484
1485 if ((hints = XGetWMHints(ob_display, self->window))) {
1486 if (hints->flags & IconPixmapHint) {
1487 self->nicons++;
1488 self->icons = g_new(ObClientIcon, self->nicons);
1489 xerror_set_ignore(TRUE);
1490 if (!RrPixmapToRGBA(ob_rr_inst,
1491 hints->icon_pixmap,
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]);
1498 self->nicons--;
1499 }
1500 xerror_set_ignore(FALSE);
1501 }
1502 XFree(hints);
1503 }
1504 }
1505
1506 if (self->frame)
1507 frame_adjust_icon(self->frame);
1508 }
1509
1510 static void client_change_state(ObClient *self)
1511 {
1512 guint32 state[2];
1513 guint32 netstate[10];
1514 guint num;
1515
1516 state[0] = self->wmstate;
1517 state[1] = None;
1518 PROP_SETA32(self->window, wm_state, wm_state, state, 2);
1519
1520 num = 0;
1521 if (self->modal)
1522 netstate[num++] = prop_atoms.net_wm_state_modal;
1523 if (self->shaded)
1524 netstate[num++] = prop_atoms.net_wm_state_shaded;
1525 if (self->iconic)
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;
1533 if (self->max_vert)
1534 netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1535 if (self->max_horz)
1536 netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1537 if (self->above)
1538 netstate[num++] = prop_atoms.net_wm_state_above;
1539 if (self->below)
1540 netstate[num++] = prop_atoms.net_wm_state_below;
1541 PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
1542
1543 client_calc_layer(self);
1544
1545 if (self->frame)
1546 frame_adjust_state(self->frame);
1547 }
1548
1549 ObClient *client_search_focus_tree(ObClient *self)
1550 {
1551 GSList *it;
1552 ObClient *ret;
1553
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;
1557 }
1558 return NULL;
1559 }
1560
1561 ObClient *client_search_focus_tree_full(ObClient *self)
1562 {
1563 if (self->transient_for) {
1564 if (self->transient_for != OB_TRAN_GROUP) {
1565 return client_search_focus_tree_full(self->transient_for);
1566 } else {
1567 GSList *it;
1568 gboolean recursed = FALSE;
1569
1570 for (it = self->group->members; it; it = it->next)
1571 if (!((ObClient*)it->data)->transient_for) {
1572 ObClient *c;
1573 if ((c = client_search_focus_tree_full(it->data)))
1574 return c;
1575 recursed = TRUE;
1576 }
1577 if (recursed)
1578 return NULL;
1579 }
1580 }
1581
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))
1585 return self;
1586 return client_search_focus_tree(self);
1587 }
1588
1589 static ObStackingLayer calc_layer(ObClient *self)
1590 {
1591 ObStackingLayer l;
1592
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;
1599 }
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;
1603
1604 return l;
1605 }
1606
1607 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
1608 ObStackingLayer l, gboolean raised)
1609 {
1610 ObStackingLayer old, own;
1611 GSList *it;
1612
1613 old = self->layer;
1614 own = calc_layer(self);
1615 self->layer = l > own ? l : own;
1616
1617 for (it = self->transients; it; it = it->next)
1618 client_calc_layer_recursive(it->data, orig,
1619 l, raised ? raised : l != old);
1620
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));
1626 }
1627 }
1628
1629 void client_calc_layer(ObClient *self)
1630 {
1631 ObStackingLayer l;
1632 ObClient *orig;
1633
1634 orig = self;
1635
1636 /* transients take on the layer of their parents */
1637 self = client_search_top_transient(self);
1638
1639 l = calc_layer(self);
1640
1641 client_calc_layer_recursive(self, orig, l, FALSE);
1642 }
1643
1644 gboolean client_should_show(ObClient *self)
1645 {
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;
1650
1651 return TRUE;
1652 }
1653
1654 static void client_showhide(ObClient *self)
1655 {
1656
1657 if (client_should_show(self))
1658 frame_show(self->frame);
1659 else
1660 frame_hide(self->frame);
1661 }
1662
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);
1667 }
1668
1669 static void client_apply_startup_state(ObClient *self)
1670 {
1671 /* these are in a carefully crafted order.. */
1672
1673 if (self->iconic) {
1674 self->iconic = FALSE;
1675 client_iconify(self, TRUE, FALSE);
1676 }
1677 if (self->fullscreen) {
1678 self->fullscreen = FALSE;
1679 client_fullscreen(self, TRUE, FALSE);
1680 }
1681 if (self->shaded) {
1682 self->shaded = FALSE;
1683 client_shade(self, TRUE);
1684 }
1685 if (self->urgent)
1686 dispatch_client(Event_Client_Urgent, self, self->urgent, 0);
1687
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);
1697 }
1698
1699 /* nothing to do for the other states:
1700 skip_taskbar
1701 skip_pager
1702 modal
1703 above
1704 below
1705 */
1706 }
1707
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)
1712 {
1713 gboolean moved = FALSE, resized = FALSE;
1714
1715 /* make the frame recalculate its dimentions n shit without changing
1716 anything visible for real, this way the constraints below can work with
1717 the updated frame dimensions. */
1718 frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
1719
1720 /* gets the frame's position */
1721 frame_client_gravity(self->frame, &x, &y);
1722
1723 /* these positions are frame positions, not client positions */
1724
1725 /* set the size and position if fullscreen */
1726 if (self->fullscreen) {
1727 #ifdef VIDMODE
1728 int dot;
1729 XF86VidModeModeLine mode;
1730 #endif
1731 Rect *a;
1732 guint i;
1733
1734 i = client_monitor(self);
1735 a = screen_physical_area_monitor(i);
1736
1737 #ifdef VIDMODE
1738 if (i == 0 && /* primary head */
1739 extensions_vidmode &&
1740 XF86VidModeGetViewPort(ob_display, ob_screen, &x, &y) &&
1741 /* get the mode last so the mode.privsize isnt freed incorrectly */
1742 XF86VidModeGetModeLine(ob_display, ob_screen, &dot, &mode)) {
1743 x += a->x;
1744 y += a->y;
1745 w = mode.hdisplay;
1746 h = mode.vdisplay;
1747 if (mode.privsize) XFree(mode.private);
1748 } else
1749 #endif
1750 {
1751 x = a->x;
1752 y = a->y;
1753 w = a->width;
1754 h = a->height;
1755 }
1756
1757 user = FALSE; /* ignore that increment etc shit when in fullscreen */
1758 } else {
1759 Rect *a;
1760
1761 a = screen_area_monitor(self->desktop, client_monitor(self));
1762
1763 /* set the size and position if maximized */
1764 if (self->max_horz) {
1765 x = a->x;
1766 w = a->width - self->frame->size.left - self->frame->size.right;
1767 }
1768 if (self->max_vert) {
1769 y = a->y;
1770 h = a->height - self->frame->size.top - self->frame->size.bottom;
1771 }
1772 }
1773
1774 /* gets the client's position */
1775 frame_frame_gravity(self->frame, &x, &y);
1776
1777 /* these override the above states! if you cant move you can't move! */
1778 if (user) {
1779 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
1780 x = self->area.x;
1781 y = self->area.y;
1782 }
1783 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
1784 w = self->area.width;
1785 h = self->area.height;
1786 }
1787 }
1788
1789 if (!(w == self->area.width && h == self->area.height)) {
1790 int basew, baseh, minw, minh;
1791
1792 /* base size is substituted with min size if not specified */
1793 if (self->base_size.width || self->base_size.height) {
1794 basew = self->base_size.width;
1795 baseh = self->base_size.height;
1796 } else {
1797 basew = self->min_size.width;
1798 baseh = self->min_size.height;
1799 }
1800 /* min size is substituted with base size if not specified */
1801 if (self->min_size.width || self->min_size.height) {
1802 minw = self->min_size.width;
1803 minh = self->min_size.height;
1804 } else {
1805 minw = self->base_size.width;
1806 minh = self->base_size.height;
1807 }
1808
1809 if (user) {
1810 /* for interactive resizing. have to move half an increment in each
1811 direction. */
1812
1813 /* how far we are towards the next size inc */
1814 int mw = (w - basew) % self->size_inc.width;
1815 int mh = (h - baseh) % self->size_inc.height;
1816 /* amount to add */
1817 int aw = self->size_inc.width / 2;
1818 int ah = self->size_inc.height / 2;
1819 /* don't let us move into a new size increment */
1820 if (mw + aw >= self->size_inc.width)
1821 aw = self->size_inc.width - mw - 1;
1822 if (mh + ah >= self->size_inc.height)
1823 ah = self->size_inc.height - mh - 1;
1824 w += aw;
1825 h += ah;
1826
1827 /* if this is a user-requested resize, then check against min/max
1828 sizes */
1829
1830 /* smaller than min size or bigger than max size? */
1831 if (w > self->max_size.width) w = self->max_size.width;
1832 if (w < minw) w = minw;
1833 if (h > self->max_size.height) h = self->max_size.height;
1834 if (h < minh) h = minh;
1835 }
1836
1837 w -= basew;
1838 h -= baseh;
1839
1840 /* keep to the increments */
1841 w /= self->size_inc.width;
1842 h /= self->size_inc.height;
1843
1844 /* you cannot resize to nothing */
1845 if (basew + w < 1) w = 1 - basew;
1846 if (baseh + h < 1) h = 1 - baseh;
1847
1848 /* store the logical size */
1849 SIZE_SET(self->logical_size,
1850 self->size_inc.width > 1 ? w : w + basew,
1851 self->size_inc.height > 1 ? h : h + baseh);
1852
1853 w *= self->size_inc.width;
1854 h *= self->size_inc.height;
1855
1856 w += basew;
1857 h += baseh;
1858
1859 if (user) {
1860 /* adjust the height to match the width for the aspect ratios.
1861 for this, min size is not substituted for base size ever. */
1862 w -= self->base_size.width;
1863 h -= self->base_size.height;
1864
1865 if (self->min_ratio)
1866 if (h * self->min_ratio > w) h = (int)(w / self->min_ratio);
1867 if (self->max_ratio)
1868 if (h * self->max_ratio < w) h = (int)(w / self->max_ratio);
1869
1870 w += self->base_size.width;
1871 h += self->base_size.height;
1872 }
1873 }
1874
1875 switch (anchor) {
1876 case OB_CORNER_TOPLEFT:
1877 break;
1878 case OB_CORNER_TOPRIGHT:
1879 x -= w - self->area.width;
1880 break;
1881 case OB_CORNER_BOTTOMLEFT:
1882 y -= h - self->area.height;
1883 break;
1884 case OB_CORNER_BOTTOMRIGHT:
1885 x -= w - self->area.width;
1886 y -= h - self->area.height;
1887 break;
1888 }
1889
1890 moved = x != self->area.x || y != self->area.y;
1891 resized = w != self->area.width || h != self->area.height;
1892
1893 RECT_SET(self->area, x, y, w, h);
1894
1895 /* for app-requested resizes, always resize if 'resized' is true.
1896 for user-requested ones, only resize if final is true, or when
1897 resizing in redraw mode */
1898 if ((!user && resized) ||
1899 (user && (final || (resized && config_redraw_resize))))
1900 XResizeWindow(ob_display, self->window, w, h);
1901
1902 /* move/resize the frame to match the request */
1903 if (self->frame) {
1904 if (self->decorations != self->frame->decorations)
1905 moved = resized = TRUE;
1906
1907 if (moved || resized)
1908 frame_adjust_area(self->frame, moved, resized, FALSE);
1909
1910 if (!resized && (force_reply || ((!user && moved) || (user && final))))
1911 {
1912 XEvent event;
1913 event.type = ConfigureNotify;
1914 event.xconfigure.display = ob_display;
1915 event.xconfigure.event = self->window;
1916 event.xconfigure.window = self->window;
1917
1918 /* root window real coords */
1919 event.xconfigure.x = self->frame->area.x + self->frame->size.left -
1920 self->border_width;
1921 event.xconfigure.y = self->frame->area.y + self->frame->size.top -
1922 self->border_width;
1923 event.xconfigure.width = w;
1924 event.xconfigure.height = h;
1925 event.xconfigure.border_width = 0;
1926 event.xconfigure.above = self->frame->plate;
1927 event.xconfigure.override_redirect = FALSE;
1928 XSendEvent(event.xconfigure.display, event.xconfigure.window,
1929 FALSE, StructureNotifyMask, &event);
1930 }
1931 }
1932 }
1933
1934 void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
1935 {
1936 int x, y, w, h;
1937
1938 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
1939 self->fullscreen == fs) return; /* already done */
1940
1941 self->fullscreen = fs;
1942 client_change_state(self); /* change the state hints on the client,
1943 and adjust out layer/stacking */
1944
1945 if (fs) {
1946 if (savearea) {
1947 guint32 dimensions[4];
1948 dimensions[0] = self->area.x;
1949 dimensions[1] = self->area.y;
1950 dimensions[2] = self->area.width;
1951 dimensions[3] = self->area.height;
1952
1953 PROP_SETA32(self->window, openbox_premax, cardinal,
1954 dimensions, 4);
1955 }
1956
1957 /* these are not actually used cuz client_configure will set them
1958 as appropriate when the window is fullscreened */
1959 x = y = w = h = 0;
1960 } else {
1961 guint num;
1962 gint32 *dimensions;
1963 Rect *a;
1964
1965 /* pick some fallbacks... */
1966 a = screen_area_monitor(self->desktop, 0);
1967 x = a->x + a->width / 4;
1968 y = a->y + a->height / 4;
1969 w = a->width / 2;
1970 h = a->height / 2;
1971
1972 if (PROP_GETA32(self->window, openbox_premax, cardinal,
1973 (guint32**)&dimensions, &num)) {
1974 if (num == 4) {
1975 x = dimensions[0];
1976 y = dimensions[1];
1977 w = dimensions[2];
1978 h = dimensions[3];
1979 }
1980 g_free(dimensions);
1981 }
1982 }
1983
1984 client_setup_decor_and_functions(self);
1985
1986 client_configure(self, OB_CORNER_TOPLEFT, x, y, w, h, TRUE, TRUE);
1987
1988 /* try focus us when we go into fullscreen mode */
1989 client_focus(self);
1990 }
1991
1992 static void client_iconify_recursive(ObClient *self,
1993 gboolean iconic, gboolean curdesk)
1994 {
1995 GSList *it;
1996 gboolean changed = FALSE;
1997
1998
1999 if (self->iconic != iconic) {
2000 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2001 self->window);
2002
2003 self->iconic = iconic;
2004
2005 if (iconic) {
2006 if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2007 self->wmstate = IconicState;
2008 self->ignore_unmaps++;
2009 /* we unmap the client itself so that we can get MapRequest
2010 events, and because the ICCCM tells us to! */
2011 XUnmapWindow(ob_display, self->window);
2012
2013 /* update the focus lists.. iconic windows go to the bottom of
2014 the list, put the new iconic window at the 'top of the
2015 bottom'. */
2016 focus_order_to_top(self);
2017
2018 changed = TRUE;
2019 }
2020 } else {
2021 if (curdesk)
2022 client_set_desktop(self, screen_desktop, FALSE);
2023 self->wmstate = self->shaded ? IconicState : NormalState;
2024 XMapWindow(ob_display, self->window);
2025
2026 /* this puts it after the current focused window */
2027 focus_order_remove(self);
2028 focus_order_add_new(self);
2029
2030 /* this is here cuz with the VIDMODE extension, the viewport can
2031 change while a fullscreen window is iconic, and when it
2032 uniconifies, it would be nice if it did so to the new position
2033 of the viewport */
2034 client_reconfigure(self);
2035
2036 changed = TRUE;
2037 }
2038 }
2039
2040 if (changed) {
2041 client_change_state(self);
2042 client_showhide(self);
2043 screen_update_areas();
2044
2045 dispatch_client(iconic ? Event_Client_Unmapped : Event_Client_Mapped,
2046 self, 0, 0);
2047 }
2048
2049 /* iconify all transients */
2050 for (it = self->transients; it != NULL; it = it->next)
2051 if (it->data != self) client_iconify_recursive(it->data,
2052 iconic, curdesk);
2053 }
2054
2055 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2056 {
2057 /* move up the transient chain as far as possible first */
2058 self = client_search_top_transient(self);
2059
2060 client_iconify_recursive(client_search_top_transient(self),
2061 iconic, curdesk);
2062 }
2063
2064 void client_maximize(ObClient *self, gboolean max, int dir, gboolean savearea)
2065 {
2066 int x, y, w, h;
2067
2068 g_assert(dir == 0 || dir == 1 || dir == 2);
2069 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2070
2071 /* check if already done */
2072 if (max) {
2073 if (dir == 0 && self->max_horz && self->max_vert) return;
2074 if (dir == 1 && self->max_horz) return;
2075 if (dir == 2 && self->max_vert) return;
2076 } else {
2077 if (dir == 0 && !self->max_horz && !self->max_vert) return;
2078 if (dir == 1 && !self->max_horz) return;
2079 if (dir == 2 && !self->max_vert) return;
2080 }
2081
2082 /* work with the frame's coords */
2083 x = self->frame->area.x;
2084 y = self->frame->area.y;
2085 w = self->area.width;
2086 h = self->area.height;
2087
2088 if (max) {
2089 if (savearea) {
2090 gint32 dimensions[4];
2091 gint32 *readdim;
2092 guint num;
2093
2094 dimensions[0] = x;
2095 dimensions[1] = y;
2096 dimensions[2] = w;
2097 dimensions[3] = h;
2098
2099 /* get the property off the window and use it for the dimensions
2100 we are already maxed on */
2101 if (PROP_GETA32(self->window, openbox_premax, cardinal,
2102 (guint32**)&readdim, &num)) {
2103 if (num == 4) {
2104 if (self->max_horz) {
2105 dimensions[0] = readdim[0];
2106 dimensions[2] = readdim[2];
2107 }
2108 if (self->max_vert) {
2109 dimensions[1] = readdim[1];
2110 dimensions[3] = readdim[3];
2111 }
2112 }
2113 g_free(readdim);
2114 }
2115
2116 PROP_SETA32(self->window, openbox_premax, cardinal,
2117 (guint32*)dimensions, 4);
2118 }
2119 } else {
2120 guint num;
2121 gint32 *dimensions;
2122 Rect *a;
2123
2124 /* pick some fallbacks... */
2125 a = screen_area_monitor(self->desktop, 0);
2126 if (dir == 0 || dir == 1) { /* horz */
2127 x = a->x + a->width / 4;
2128 w = a->width / 2;
2129 }
2130 if (dir == 0 || dir == 2) { /* vert */
2131 y = a->y + a->height / 4;
2132 h = a->height / 2;
2133 }
2134
2135 if (PROP_GETA32(self->window, openbox_premax, cardinal,
2136 (guint32**)&dimensions, &num)) {
2137 if (num == 4) {
2138 if (dir == 0 || dir == 1) { /* horz */
2139 x = dimensions[0];
2140 w = dimensions[2];
2141 }
2142 if (dir == 0 || dir == 2) { /* vert */
2143 y = dimensions[1];
2144 h = dimensions[3];
2145 }
2146 }
2147 g_free(dimensions);
2148 }
2149 }
2150
2151 if (dir == 0 || dir == 1) /* horz */
2152 self->max_horz = max;
2153 if (dir == 0 || dir == 2) /* vert */
2154 self->max_vert = max;
2155
2156 if (!self->max_horz && !self->max_vert)
2157 PROP_ERASE(self->window, openbox_premax);
2158
2159 client_change_state(self); /* change the state hints on the client */
2160
2161 /* figure out where the client should be going */
2162 frame_frame_gravity(self->frame, &x, &y);
2163 client_configure(self, OB_CORNER_TOPLEFT, x, y, w, h, TRUE, TRUE);
2164 }
2165
2166 void client_shade(ObClient *self, gboolean shade)
2167 {
2168 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2169 shade) || /* can't shade */
2170 self->shaded == shade) return; /* already done */
2171
2172 /* when we're iconic, don't change the wmstate */
2173 if (!self->iconic)
2174 self->wmstate = shade ? IconicState : NormalState;
2175 self->shaded = shade;
2176 client_change_state(self);
2177 /* resize the frame to just the titlebar */
2178 frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2179 }
2180
2181 void client_close(ObClient *self)
2182 {
2183 XEvent ce;
2184
2185 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2186
2187 /*
2188 XXX: itd be cool to do timeouts and shit here for killing the client's
2189 process off
2190 like... if the window is around after 5 seconds, then the close button
2191 turns a nice red, and if this function is called again, the client is
2192 explicitly killed.
2193 */
2194
2195 ce.xclient.type = ClientMessage;
2196 ce.xclient.message_type = prop_atoms.wm_protocols;
2197 ce.xclient.display = ob_display;
2198 ce.xclient.window = self->window;
2199 ce.xclient.format = 32;
2200 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2201 ce.xclient.data.l[1] = event_lasttime;
2202 ce.xclient.data.l[2] = 0l;
2203 ce.xclient.data.l[3] = 0l;
2204 ce.xclient.data.l[4] = 0l;
2205 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2206 }
2207
2208 void client_kill(ObClient *self)
2209 {
2210 XKillClient(ob_display, self->window);
2211 }
2212
2213 void client_set_desktop_recursive(ObClient *self,
2214 guint target, gboolean donthide)
2215 {
2216 guint old;
2217 GSList *it;
2218
2219 if (target != self->desktop) {
2220
2221 ob_debug("Setting desktop %u\n", target+1);
2222
2223 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2224
2225 /* remove from the old desktop(s) */
2226 focus_order_remove(self);
2227
2228 old = self->desktop;
2229 self->desktop = target;
2230 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2231 /* the frame can display the current desktop state */
2232 frame_adjust_state(self->frame);
2233 /* 'move' the window to the new desktop */
2234 if (!donthide)
2235 client_showhide(self);
2236 /* raise if it was not already on the desktop */
2237 if (old != DESKTOP_ALL)
2238 stacking_raise(CLIENT_AS_WINDOW(self));
2239 screen_update_areas();
2240
2241 /* add to the new desktop(s) */
2242 if (config_focus_new)
2243 focus_order_to_top(self);
2244 else
2245 focus_order_to_bottom(self);
2246
2247 dispatch_client(Event_Client_Desktop, self, target, old);
2248 }
2249
2250 /* move all transients */
2251 for (it = self->transients; it != NULL; it = it->next)
2252 if (it->data != self) client_set_desktop_recursive(it->data,
2253 target, donthide);
2254 }
2255
2256 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2257 {
2258 client_set_desktop_recursive(client_search_top_transient(self),
2259 target, donthide);
2260 }
2261
2262 ObClient *client_search_modal_child(ObClient *self)
2263 {
2264 GSList *it;
2265 ObClient *ret;
2266
2267 for (it = self->transients; it != NULL; it = it->next) {
2268 ObClient *c = it->data;
2269 if ((ret = client_search_modal_child(c))) return ret;
2270 if (c->modal) return c;
2271 }
2272 return NULL;
2273 }
2274
2275 gboolean client_validate(ObClient *self)
2276 {
2277 XEvent e;
2278
2279 XSync(ob_display, FALSE); /* get all events on the server */
2280
2281 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2282 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2283 XPutBackEvent(ob_display, &e);
2284 return FALSE;
2285 }
2286
2287 return TRUE;
2288 }
2289
2290 void client_set_wm_state(ObClient *self, long state)
2291 {
2292 if (state == self->wmstate) return; /* no change */
2293
2294 switch (state) {
2295 case IconicState:
2296 client_iconify(self, TRUE, TRUE);
2297 break;
2298 case NormalState:
2299 client_iconify(self, FALSE, TRUE);
2300 break;
2301 }
2302 }
2303
2304 void client_set_state(ObClient *self, Atom action, long data1, long data2)
2305 {
2306 gboolean shaded = self->shaded;
2307 gboolean fullscreen = self->fullscreen;
2308 gboolean max_horz = self->max_horz;
2309 gboolean max_vert = self->max_vert;
2310 int i;
2311
2312 if (!(action == prop_atoms.net_wm_state_add ||
2313 action == prop_atoms.net_wm_state_remove ||
2314 action == prop_atoms.net_wm_state_toggle))
2315 /* an invalid action was passed to the client message, ignore it */
2316 return;
2317
2318 for (i = 0; i < 2; ++i) {
2319 Atom state = i == 0 ? data1 : data2;
2320
2321 if (!state) continue;
2322
2323 /* if toggling, then pick whether we're adding or removing */
2324 if (action == prop_atoms.net_wm_state_toggle) {
2325 if (state == prop_atoms.net_wm_state_modal)
2326 action = self->modal ? prop_atoms.net_wm_state_remove :
2327 prop_atoms.net_wm_state_add;
2328 else if (state == prop_atoms.net_wm_state_maximized_vert)
2329 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2330 prop_atoms.net_wm_state_add;
2331 else if (state == prop_atoms.net_wm_state_maximized_horz)
2332 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2333 prop_atoms.net_wm_state_add;
2334 else if (state == prop_atoms.net_wm_state_shaded)
2335 action = self->shaded ? prop_atoms.net_wm_state_remove :
2336 prop_atoms.net_wm_state_add;
2337 else if (state == prop_atoms.net_wm_state_skip_taskbar)
2338 action = self->skip_taskbar ?
2339 prop_atoms.net_wm_state_remove :
2340 prop_atoms.net_wm_state_add;
2341 else if (state == prop_atoms.net_wm_state_skip_pager)
2342 action = self->skip_pager ?
2343 prop_atoms.net_wm_state_remove :
2344 prop_atoms.net_wm_state_add;
2345 else if (state == prop_atoms.net_wm_state_fullscreen)
2346 action = self->fullscreen ?
2347 prop_atoms.net_wm_state_remove :
2348 prop_atoms.net_wm_state_add;
2349 else if (state == prop_atoms.net_wm_state_above)
2350 action = self->above ? prop_atoms.net_wm_state_remove :
2351 prop_atoms.net_wm_state_add;
2352 else if (state == prop_atoms.net_wm_state_below)
2353 action = self->below ? prop_atoms.net_wm_state_remove :
2354 prop_atoms.net_wm_state_add;
2355 }
2356
2357 if (action == prop_atoms.net_wm_state_add) {
2358 if (state == prop_atoms.net_wm_state_modal) {
2359 /* XXX raise here or something? */
2360 self->modal = TRUE;
2361 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2362 max_vert = TRUE;
2363 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2364 max_horz = TRUE;
2365 } else if (state == prop_atoms.net_wm_state_shaded) {
2366 shaded = TRUE;
2367 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2368 self->skip_taskbar = TRUE;
2369 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2370 self->skip_pager = TRUE;
2371 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2372 fullscreen = TRUE;
2373 } else if (state == prop_atoms.net_wm_state_above) {
2374 self->above = TRUE;
2375 } else if (state == prop_atoms.net_wm_state_below) {
2376 self->below = TRUE;
2377 }
2378
2379 } else { /* action == prop_atoms.net_wm_state_remove */
2380 if (state == prop_atoms.net_wm_state_modal) {
2381 self->modal = FALSE;
2382 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2383 max_vert = FALSE;
2384 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2385 max_horz = FALSE;
2386 } else if (state == prop_atoms.net_wm_state_shaded) {
2387 shaded = FALSE;
2388 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2389 self->skip_taskbar = FALSE;
2390 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2391 self->skip_pager = FALSE;
2392 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2393 fullscreen = FALSE;
2394 } else if (state == prop_atoms.net_wm_state_above) {
2395 self->above = FALSE;
2396 } else if (state == prop_atoms.net_wm_state_below) {
2397 self->below = FALSE;
2398 }
2399 }
2400 }
2401 if (max_horz != self->max_horz || max_vert != self->max_vert) {
2402 if (max_horz != self->max_horz && max_vert != self->max_vert) {
2403 /* toggling both */
2404 if (max_horz == max_vert) { /* both going the same way */
2405 client_maximize(self, max_horz, 0, TRUE);
2406 } else {
2407 client_maximize(self, max_horz, 1, TRUE);
2408 client_maximize(self, max_vert, 2, TRUE);
2409 }
2410 } else {
2411 /* toggling one */
2412 if (max_horz != self->max_horz)
2413 client_maximize(self, max_horz, 1, TRUE);
2414 else
2415 client_maximize(self, max_vert, 2, TRUE);
2416 }
2417 }
2418 /* change fullscreen state before shading, as it will affect if the window
2419 can shade or not */
2420 if (fullscreen != self->fullscreen)
2421 client_fullscreen(self, fullscreen, TRUE);
2422 if (shaded != self->shaded)
2423 client_shade(self, shaded);
2424 client_calc_layer(self);
2425 client_change_state(self); /* change the hint to reflect these changes */
2426 }
2427
2428 ObClient *client_focus_target(ObClient *self)
2429 {
2430 ObClient *child;
2431
2432 /* if we have a modal child, then focus it, not us */
2433 child = client_search_modal_child(self);
2434 if (child) return child;
2435 return self;
2436 }
2437
2438 gboolean client_can_focus(ObClient *self)
2439 {
2440 XEvent ev;
2441
2442 /* choose the correct target */
2443 self = client_focus_target(self);
2444
2445 if (!self->frame->visible)
2446 return FALSE;
2447
2448 if (!((self->can_focus || self->focus_notify) &&
2449 (self->desktop == screen_desktop ||
2450 self->desktop == DESKTOP_ALL) &&
2451 !self->iconic))
2452 return FALSE;
2453
2454 /* do a check to see if the window has already been unmapped or destroyed
2455 do this intelligently while watching out for unmaps we've generated
2456 (ignore_unmaps > 0) */
2457 if (XCheckTypedWindowEvent(ob_display, self->window,
2458 DestroyNotify, &ev)) {
2459 XPutBackEvent(ob_display, &ev);
2460 return FALSE;
2461 }
2462 while (XCheckTypedWindowEvent(ob_display, self->window,
2463 UnmapNotify, &ev)) {
2464 if (self->ignore_unmaps) {
2465 self->ignore_unmaps--;
2466 } else {
2467 XPutBackEvent(ob_display, &ev);
2468 return FALSE;
2469 }
2470 }
2471
2472 return TRUE;
2473 }
2474
2475 gboolean client_focus(ObClient *self)
2476 {
2477 /* choose the correct target */
2478 self = client_focus_target(self);
2479
2480 if (!client_can_focus(self)) {
2481 if (!self->frame->visible) {
2482 /* update the focus lists */
2483 focus_order_to_top(self);
2484 }
2485 return FALSE;
2486 }
2487
2488 if (self->can_focus)
2489 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2490 I choose to use it always, hopefully to find errors quicker, if any
2491 are left. (I hate X. I hate focus events.) */
2492 XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
2493 event_lasttime);
2494
2495 if (self->focus_notify) {
2496 XEvent ce;
2497 ce.xclient.type = ClientMessage;
2498 ce.xclient.message_type = prop_atoms.wm_protocols;
2499 ce.xclient.display = ob_display;
2500 ce.xclient.window = self->window;
2501 ce.xclient.format = 32;
2502 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
2503 ce.xclient.data.l[1] = event_lasttime;
2504 ce.xclient.data.l[2] = 0l;
2505 ce.xclient.data.l[3] = 0l;
2506 ce.xclient.data.l[4] = 0l;
2507 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2508 }
2509
2510 #ifdef DEBUG_FOCUS
2511 ob_debug("%sively focusing %lx at %d\n",
2512 (self->can_focus ? "act" : "pass"),
2513 self->window, (int) event_lasttime);
2514 #endif
2515
2516 /* Cause the FocusIn to come back to us. Important for desktop switches,
2517 since otherwise we'll have no FocusIn on the queue and send it off to
2518 the focus_backup. */
2519 XSync(ob_display, FALSE);
2520 return TRUE;
2521 }
2522
2523 void client_unfocus(ObClient *self)
2524 {
2525 g_assert(focus_client == self);
2526 #ifdef DEBUG_FOCUS
2527 ob_debug("client_unfocus for %lx\n", self->window);
2528 #endif
2529 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
2530 }
2531
2532 void client_activate(ObClient *self)
2533 {
2534 if (client_normal(self) && screen_showing_desktop)
2535 screen_show_desktop(FALSE);
2536 if (self->iconic)
2537 client_iconify(self, FALSE, FALSE);
2538 if (self->desktop != DESKTOP_ALL &&
2539 self->desktop != screen_desktop)
2540 screen_set_desktop(self->desktop);
2541 else if (!self->frame->visible)
2542 /* if its not visible for other reasons, then don't mess
2543 with it */
2544 return;
2545 if (self->shaded)
2546 client_shade(self, FALSE);
2547 client_focus(self);
2548 stacking_raise(CLIENT_AS_WINDOW(self));
2549 }
2550
2551 gboolean client_focused(ObClient *self)
2552 {
2553 return self == focus_client;
2554 }
2555
2556 ObClientIcon *client_icon(ObClient *self, int w, int h)
2557 {
2558 guint i;
2559 /* si is the smallest image >= req */
2560 /* li is the largest image < req */
2561 unsigned long size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
2562
2563 if (!self->nicons) return NULL;
2564
2565 for (i = 0; i < self->nicons; ++i) {
2566 size = self->icons[i].width * self->icons[i].height;
2567 if (size < smallest && size >= (unsigned)(w * h)) {
2568 smallest = size;
2569 si = i;
2570 }
2571 if (size > largest && size <= (unsigned)(w * h)) {
2572 largest = size;
2573 li = i;
2574 }
2575 }
2576 if (largest == 0) /* didnt find one smaller than the requested size */
2577 return &self->icons[si];
2578 return &self->icons[li];
2579 }
2580
2581 /* this be mostly ripped from fvwm */
2582 ObClient *client_find_directional(ObClient *c, ObDirection dir)
2583 {
2584 int my_cx, my_cy, his_cx, his_cy;
2585 int offset = 0;
2586 int distance = 0;
2587 int score, best_score;
2588 ObClient *best_client, *cur;
2589 GList *it;
2590
2591 if(!client_list)
2592 return NULL;
2593
2594 /* first, find the centre coords of the currently focused window */
2595 my_cx = c->frame->area.x + c->frame->area.width / 2;
2596 my_cy = c->frame->area.y + c->frame->area.height / 2;
2597
2598 best_score = -1;
2599 best_client = NULL;
2600
2601 for(it = g_list_first(client_list); it; it = it->next) {
2602 cur = it->data;
2603
2604 /* the currently selected window isn't interesting */
2605 if(cur == c)
2606 continue;
2607 if (!client_normal(cur))
2608 continue;
2609 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2610 continue;
2611 if(cur->iconic)
2612 continue;
2613 if(client_focus_target(cur) == cur &&
2614 !(cur->can_focus || cur->focus_notify))
2615 continue;
2616
2617 /* find the centre coords of this window, from the
2618 * currently focused window's point of view */
2619 his_cx = (cur->frame->area.x - my_cx)
2620 + cur->frame->area.width / 2;
2621 his_cy = (cur->frame->area.y - my_cy)
2622 + cur->frame->area.height / 2;
2623
2624 if(dir > 3) {
2625 int tx;
2626 /* Rotate the diagonals 45 degrees counterclockwise.
2627 * To do this, multiply the matrix /+h +h\ with the
2628 * vector (x y). \-h +h/
2629 * h = sqrt(0.5). We can set h := 1 since absolute
2630 * distance doesn't matter here. */
2631 tx = his_cx + his_cy;
2632 his_cy = -his_cx + his_cy;
2633 his_cx = tx;
2634 }
2635
2636 switch(dir) {
2637 case OB_DIRECTION_NORTH:
2638 case OB_DIRECTION_SOUTH:
2639 case OB_DIRECTION_NORTHEAST:
2640 case OB_DIRECTION_SOUTHWEST:
2641 offset = (his_cx < 0) ? -his_cx : his_cx;
2642 distance = ((dir == OB_DIRECTION_NORTH ||
2643 dir == OB_DIRECTION_NORTHEAST) ?
2644 -his_cy : his_cy);
2645 break;
2646 case OB_DIRECTION_EAST:
2647 case OB_DIRECTION_WEST:
2648 case OB_DIRECTION_SOUTHEAST:
2649 case OB_DIRECTION_NORTHWEST:
2650 offset = (his_cy < 0) ? -his_cy : his_cy;
2651 distance = ((dir == OB_DIRECTION_WEST ||
2652 dir == OB_DIRECTION_NORTHWEST) ?
2653 -his_cx : his_cx);
2654 break;
2655 }
2656
2657 /* the target must be in the requested direction */
2658 if(distance <= 0)
2659 continue;
2660
2661 /* Calculate score for this window. The smaller the better. */
2662 score = distance + offset;
2663
2664 /* windows more than 45 degrees off the direction are
2665 * heavily penalized and will only be chosen if nothing
2666 * else within a million pixels */
2667 if(offset > distance)
2668 score += 1000000;
2669
2670 if(best_score == -1 || score < best_score)
2671 best_client = cur,
2672 best_score = score;
2673 }
2674
2675 return best_client;
2676 }
2677
2678 void client_set_layer(ObClient *self, int layer)
2679 {
2680 if (layer < 0) {
2681 self->below = TRUE;
2682 self->above = FALSE;
2683 } else if (layer == 0) {
2684 self->below = self->above = FALSE;
2685 } else {
2686 self->below = FALSE;
2687 self->above = TRUE;
2688 }
2689 client_calc_layer(self);
2690 client_change_state(self); /* reflect this in the state hints */
2691 }
2692
2693 guint client_monitor(ObClient *self)
2694 {
2695 guint i;
2696
2697 for (i = 0; i < screen_num_monitors; ++i) {
2698 Rect *area = screen_physical_area_monitor(i);
2699 if (RECT_INTERSECTS_RECT(*area, self->frame->area))
2700 break;
2701 }
2702 if (i == screen_num_monitors) i = 0;
2703 g_assert(i < screen_num_monitors);
2704 return i;
2705 }
2706
2707 ObClient *client_search_top_transient(ObClient *self)
2708 {
2709 /* move up the transient chain as far as possible */
2710 if (self->transient_for) {
2711 if (self->transient_for != OB_TRAN_GROUP) {
2712 return client_search_top_transient(self->transient_for);
2713 } else {
2714 GSList *it;
2715
2716 for (it = self->group->members; it; it = it->next) {
2717 ObClient *c = it->data;
2718
2719 /* checking transient_for prevents infinate loops! */
2720 if (c != self && !c->transient_for)
2721 break;
2722 }
2723 if (it)
2724 return it->data;
2725 }
2726 }
2727
2728 return self;
2729 }
2730
2731 ObClient *client_search_transient(ObClient *self, ObClient *search)
2732 {
2733 GSList *sit;
2734
2735 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
2736 if (sit->data == search)
2737 return search;
2738 if (client_search_transient(sit->data, search))
2739 return search;
2740 }
2741 return NULL;
2742 }
2743
2744 gchar* client_get_sm_client_id(ObClient *self)
2745 {
2746 gchar *id = NULL;
2747
2748 if (!PROP_GETS(self->window, sm_client_id, locale, &id) && self->group)
2749 PROP_GETS(self->group->leader, sm_client_id, locale, &id);
2750 return id;
2751 }
This page took 0.164501 seconds and 4 git commands to generate.