]> Dogcows Code - chaz/openbox/blob - openbox/client.c
ebff0bae8d6a0156b9da20f2706e6028c6a2f7b9
[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 /* gets the frame's position */
1716 frame_client_gravity(self->frame, &x, &y);
1717
1718 /* these positions are frame positions, not client positions */
1719
1720 /* set the size and position if fullscreen */
1721 if (self->fullscreen) {
1722 #ifdef VIDMODE
1723 int dot;
1724 XF86VidModeModeLine mode;
1725 #endif
1726 Rect *a;
1727 guint i;
1728
1729 i = client_monitor(self);
1730 a = screen_physical_area_monitor(i);
1731
1732 #ifdef VIDMODE
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)) {
1738 x += a->x;
1739 y += a->y;
1740 w = mode.hdisplay;
1741 h = mode.vdisplay;
1742 if (mode.privsize) XFree(mode.private);
1743 } else
1744 #endif
1745 {
1746 x = a->x;
1747 y = a->y;
1748 w = a->width;
1749 h = a->height;
1750 }
1751
1752 user = FALSE; /* ignore that increment etc shit when in fullscreen */
1753 } else {
1754 Rect *a;
1755
1756 a = screen_area_monitor(self->desktop, client_monitor(self));
1757
1758 /* set the size and position if maximized */
1759 if (self->max_horz) {
1760 x = a->x - self->frame->size.left;
1761 w = a->width;
1762 }
1763 if (self->max_vert) {
1764 y = a->y;
1765 h = a->height - self->frame->size.top - self->frame->size.bottom;
1766 }
1767 }
1768
1769 /* gets the client's position */
1770 frame_frame_gravity(self->frame, &x, &y);
1771
1772 /* these override the above states! if you cant move you can't move! */
1773 if (user) {
1774 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
1775 x = self->area.x;
1776 y = self->area.y;
1777 }
1778 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
1779 w = self->area.width;
1780 h = self->area.height;
1781 }
1782 }
1783
1784 if (!(w == self->area.width && h == self->area.height)) {
1785 int basew, baseh, minw, minh;
1786
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;
1791 } else {
1792 basew = self->min_size.width;
1793 baseh = self->min_size.height;
1794 }
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;
1799 } else {
1800 minw = self->base_size.width;
1801 minh = self->base_size.height;
1802 }
1803
1804 if (user) {
1805 /* for interactive resizing. have to move half an increment in each
1806 direction. */
1807
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;
1811 /* amount to add */
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;
1819 w += aw;
1820 h += ah;
1821
1822 /* if this is a user-requested resize, then check against min/max
1823 sizes */
1824
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;
1830 }
1831
1832 w -= basew;
1833 h -= baseh;
1834
1835 /* keep to the increments */
1836 w /= self->size_inc.width;
1837 h /= self->size_inc.height;
1838
1839 /* you cannot resize to nothing */
1840 if (basew + w < 1) w = 1 - basew;
1841 if (baseh + h < 1) h = 1 - baseh;
1842
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);
1847
1848 w *= self->size_inc.width;
1849 h *= self->size_inc.height;
1850
1851 w += basew;
1852 h += baseh;
1853
1854 if (user) {
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;
1859
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);
1864
1865 w += self->base_size.width;
1866 h += self->base_size.height;
1867 }
1868 }
1869
1870 switch (anchor) {
1871 case OB_CORNER_TOPLEFT:
1872 break;
1873 case OB_CORNER_TOPRIGHT:
1874 x -= w - self->area.width;
1875 break;
1876 case OB_CORNER_BOTTOMLEFT:
1877 y -= h - self->area.height;
1878 break;
1879 case OB_CORNER_BOTTOMRIGHT:
1880 x -= w - self->area.width;
1881 y -= h - self->area.height;
1882 break;
1883 }
1884
1885 moved = x != self->area.x || y != self->area.y;
1886 resized = w != self->area.width || h != self->area.height;
1887
1888 RECT_SET(self->area, x, y, w, h);
1889
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);
1896
1897 /* move/resize the frame to match the request */
1898 if (self->frame) {
1899 if (self->decorations != self->frame->decorations)
1900 moved = resized = TRUE;
1901
1902 if (moved || resized)
1903 frame_adjust_area(self->frame, moved, resized);
1904
1905 if (!resized && (force_reply || ((!user && moved) || (user && final))))
1906 {
1907 XEvent event;
1908 event.type = ConfigureNotify;
1909 event.xconfigure.display = ob_display;
1910 event.xconfigure.event = self->window;
1911 event.xconfigure.window = self->window;
1912
1913 /* root window real coords */
1914 event.xconfigure.x = self->frame->area.x + self->frame->size.left -
1915 self->border_width;
1916 event.xconfigure.y = self->frame->area.y + self->frame->size.top -
1917 self->border_width;
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);
1925 }
1926 }
1927 }
1928
1929 void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
1930 {
1931 int x, y, w, h;
1932
1933 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
1934 self->fullscreen == fs) return; /* already done */
1935
1936 self->fullscreen = fs;
1937 client_change_state(self); /* change the state hints on the client,
1938 and adjust out layer/stacking */
1939
1940 if (fs) {
1941 if (savearea) {
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;
1947
1948 PROP_SETA32(self->window, openbox_premax, cardinal,
1949 dimensions, 4);
1950 }
1951
1952 /* these are not actually used cuz client_configure will set them
1953 as appropriate when the window is fullscreened */
1954 x = y = w = h = 0;
1955 } else {
1956 guint num;
1957 gint32 *dimensions;
1958 Rect *a;
1959
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;
1964 w = a->width / 2;
1965 h = a->height / 2;
1966
1967 if (PROP_GETA32(self->window, openbox_premax, cardinal,
1968 (guint32**)&dimensions, &num)) {
1969 if (num == 4) {
1970 x = dimensions[0];
1971 y = dimensions[1];
1972 w = dimensions[2];
1973 h = dimensions[3];
1974 }
1975 g_free(dimensions);
1976 }
1977 }
1978
1979 client_setup_decor_and_functions(self);
1980
1981 client_configure(self, OB_CORNER_TOPLEFT, x, y, w, h, TRUE, TRUE);
1982
1983 /* try focus us when we go into fullscreen mode */
1984 client_focus(self);
1985 }
1986
1987 static void client_iconify_recursive(ObClient *self,
1988 gboolean iconic, gboolean curdesk)
1989 {
1990 GSList *it;
1991 gboolean changed = FALSE;
1992
1993
1994 if (self->iconic != iconic) {
1995 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
1996 self->window);
1997
1998 self->iconic = iconic;
1999
2000 if (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);
2007
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
2010 bottom'. */
2011 focus_order_to_top(self);
2012
2013 changed = TRUE;
2014 }
2015 } else {
2016 if (curdesk)
2017 client_set_desktop(self, screen_desktop, FALSE);
2018 self->wmstate = self->shaded ? IconicState : NormalState;
2019 XMapWindow(ob_display, self->window);
2020
2021 /* this puts it after the current focused window */
2022 focus_order_remove(self);
2023 focus_order_add_new(self);
2024
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
2028 of the viewport */
2029 client_reconfigure(self);
2030
2031 changed = TRUE;
2032 }
2033 }
2034
2035 if (changed) {
2036 client_change_state(self);
2037 client_showhide(self);
2038 screen_update_areas();
2039
2040 dispatch_client(iconic ? Event_Client_Unmapped : Event_Client_Mapped,
2041 self, 0, 0);
2042 }
2043
2044 /* iconify all transients */
2045 for (it = self->transients; it != NULL; it = it->next)
2046 if (it->data != self) client_iconify_recursive(it->data,
2047 iconic, curdesk);
2048 }
2049
2050 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2051 {
2052 /* move up the transient chain as far as possible first */
2053 self = client_search_top_transient(self);
2054
2055 client_iconify_recursive(client_search_top_transient(self),
2056 iconic, curdesk);
2057 }
2058
2059 void client_maximize(ObClient *self, gboolean max, int dir, gboolean savearea)
2060 {
2061 int x, y, w, h;
2062
2063 g_assert(dir == 0 || dir == 1 || dir == 2);
2064 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2065
2066 /* check if already done */
2067 if (max) {
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;
2071 } else {
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;
2075 }
2076
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;
2082
2083 if (max) {
2084 if (savearea) {
2085 gint32 dimensions[4];
2086 gint32 *readdim;
2087 guint num;
2088
2089 dimensions[0] = x;
2090 dimensions[1] = y;
2091 dimensions[2] = w;
2092 dimensions[3] = h;
2093
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)) {
2098 if (num == 4) {
2099 if (self->max_horz) {
2100 dimensions[0] = readdim[0];
2101 dimensions[2] = readdim[2];
2102 }
2103 if (self->max_vert) {
2104 dimensions[1] = readdim[1];
2105 dimensions[3] = readdim[3];
2106 }
2107 }
2108 g_free(readdim);
2109 }
2110
2111 PROP_SETA32(self->window, openbox_premax, cardinal,
2112 (guint32*)dimensions, 4);
2113 }
2114 } else {
2115 guint num;
2116 gint32 *dimensions;
2117 Rect *a;
2118
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;
2123 w = a->width / 2;
2124 }
2125 if (dir == 0 || dir == 2) { /* vert */
2126 y = a->y + a->height / 4;
2127 h = a->height / 2;
2128 }
2129
2130 if (PROP_GETA32(self->window, openbox_premax, cardinal,
2131 (guint32**)&dimensions, &num)) {
2132 if (num == 4) {
2133 if (dir == 0 || dir == 1) { /* horz */
2134 x = dimensions[0];
2135 w = dimensions[2];
2136 }
2137 if (dir == 0 || dir == 2) { /* vert */
2138 y = dimensions[1];
2139 h = dimensions[3];
2140 }
2141 }
2142 g_free(dimensions);
2143 }
2144 }
2145
2146 if (dir == 0 || dir == 1) /* horz */
2147 self->max_horz = max;
2148 if (dir == 0 || dir == 2) /* vert */
2149 self->max_vert = max;
2150
2151 if (!self->max_horz && !self->max_vert)
2152 PROP_ERASE(self->window, openbox_premax);
2153
2154 client_change_state(self); /* change the state hints on the client */
2155
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);
2159 }
2160
2161 void client_shade(ObClient *self, gboolean shade)
2162 {
2163 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2164 shade) || /* can't shade */
2165 self->shaded == shade) return; /* already done */
2166
2167 /* when we're iconic, don't change the wmstate */
2168 if (!self->iconic)
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);
2174 }
2175
2176 void client_close(ObClient *self)
2177 {
2178 XEvent ce;
2179
2180 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2181
2182 /*
2183 XXX: itd be cool to do timeouts and shit here for killing the client's
2184 process off
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
2187 explicitly killed.
2188 */
2189
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);
2201 }
2202
2203 void client_kill(ObClient *self)
2204 {
2205 XKillClient(ob_display, self->window);
2206 }
2207
2208 void client_set_desktop_recursive(ObClient *self,
2209 guint target, gboolean donthide)
2210 {
2211 guint old;
2212 GSList *it;
2213
2214 if (target != self->desktop) {
2215
2216 ob_debug("Setting desktop %u\n", target+1);
2217
2218 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2219
2220 /* remove from the old desktop(s) */
2221 focus_order_remove(self);
2222
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 */
2229 if (!donthide)
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();
2235
2236 /* add to the new desktop(s) */
2237 if (config_focus_new)
2238 focus_order_to_top(self);
2239 else
2240 focus_order_to_bottom(self);
2241
2242 dispatch_client(Event_Client_Desktop, self, target, old);
2243 }
2244
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,
2248 target, donthide);
2249 }
2250
2251 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2252 {
2253 client_set_desktop_recursive(client_search_top_transient(self),
2254 target, donthide);
2255 }
2256
2257 ObClient *client_search_modal_child(ObClient *self)
2258 {
2259 GSList *it;
2260 ObClient *ret;
2261
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;
2266 }
2267 return NULL;
2268 }
2269
2270 gboolean client_validate(ObClient *self)
2271 {
2272 XEvent e;
2273
2274 XSync(ob_display, FALSE); /* get all events on the server */
2275
2276 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2277 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2278 XPutBackEvent(ob_display, &e);
2279 return FALSE;
2280 }
2281
2282 return TRUE;
2283 }
2284
2285 void client_set_wm_state(ObClient *self, long state)
2286 {
2287 if (state == self->wmstate) return; /* no change */
2288
2289 switch (state) {
2290 case IconicState:
2291 client_iconify(self, TRUE, TRUE);
2292 break;
2293 case NormalState:
2294 client_iconify(self, FALSE, TRUE);
2295 break;
2296 }
2297 }
2298
2299 void client_set_state(ObClient *self, Atom action, long data1, long data2)
2300 {
2301 gboolean shaded = self->shaded;
2302 gboolean fullscreen = self->fullscreen;
2303 gboolean max_horz = self->max_horz;
2304 gboolean max_vert = self->max_vert;
2305 int i;
2306
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 */
2311 return;
2312
2313 for (i = 0; i < 2; ++i) {
2314 Atom state = i == 0 ? data1 : data2;
2315
2316 if (!state) continue;
2317
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;
2350 }
2351
2352 if (action == prop_atoms.net_wm_state_add) {
2353 if (state == prop_atoms.net_wm_state_modal) {
2354 /* XXX raise here or something? */
2355 self->modal = TRUE;
2356 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2357 max_vert = TRUE;
2358 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2359 max_horz = TRUE;
2360 } else if (state == prop_atoms.net_wm_state_shaded) {
2361 shaded = TRUE;
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) {
2367 fullscreen = TRUE;
2368 } else if (state == prop_atoms.net_wm_state_above) {
2369 self->above = TRUE;
2370 } else if (state == prop_atoms.net_wm_state_below) {
2371 self->below = TRUE;
2372 }
2373
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) {
2378 max_vert = FALSE;
2379 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2380 max_horz = FALSE;
2381 } else if (state == prop_atoms.net_wm_state_shaded) {
2382 shaded = FALSE;
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) {
2388 fullscreen = FALSE;
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;
2393 }
2394 }
2395 }
2396 if (max_horz != self->max_horz || max_vert != self->max_vert) {
2397 if (max_horz != self->max_horz && max_vert != self->max_vert) {
2398 /* toggling both */
2399 if (max_horz == max_vert) { /* both going the same way */
2400 client_maximize(self, max_horz, 0, TRUE);
2401 } else {
2402 client_maximize(self, max_horz, 1, TRUE);
2403 client_maximize(self, max_vert, 2, TRUE);
2404 }
2405 } else {
2406 /* toggling one */
2407 if (max_horz != self->max_horz)
2408 client_maximize(self, max_horz, 1, TRUE);
2409 else
2410 client_maximize(self, max_vert, 2, TRUE);
2411 }
2412 }
2413 /* change fullscreen state before shading, as it will affect if the window
2414 can shade or not */
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 */
2421 }
2422
2423 ObClient *client_focus_target(ObClient *self)
2424 {
2425 ObClient *child;
2426
2427 /* if we have a modal child, then focus it, not us */
2428 child = client_search_modal_child(self);
2429 if (child) return child;
2430 return self;
2431 }
2432
2433 gboolean client_can_focus(ObClient *self)
2434 {
2435 XEvent ev;
2436
2437 /* choose the correct target */
2438 self = client_focus_target(self);
2439
2440 if (!self->frame->visible)
2441 return FALSE;
2442
2443 if (!((self->can_focus || self->focus_notify) &&
2444 (self->desktop == screen_desktop ||
2445 self->desktop == DESKTOP_ALL) &&
2446 !self->iconic))
2447 return FALSE;
2448
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);
2455 return FALSE;
2456 }
2457 while (XCheckTypedWindowEvent(ob_display, self->window,
2458 UnmapNotify, &ev)) {
2459 if (self->ignore_unmaps) {
2460 self->ignore_unmaps--;
2461 } else {
2462 XPutBackEvent(ob_display, &ev);
2463 return FALSE;
2464 }
2465 }
2466
2467 return TRUE;
2468 }
2469
2470 gboolean client_focus(ObClient *self)
2471 {
2472 /* choose the correct target */
2473 self = client_focus_target(self);
2474
2475 if (!client_can_focus(self)) {
2476 if (!self->frame->visible) {
2477 /* update the focus lists */
2478 focus_order_to_top(self);
2479 }
2480 return FALSE;
2481 }
2482
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,
2488 event_lasttime);
2489
2490 if (self->focus_notify) {
2491 XEvent ce;
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);
2503 }
2504
2505 #ifdef DEBUG_FOCUS
2506 ob_debug("%sively focusing %lx at %d\n",
2507 (self->can_focus ? "act" : "pass"),
2508 self->window, (int) event_lasttime);
2509 #endif
2510
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);
2515 return TRUE;
2516 }
2517
2518 void client_unfocus(ObClient *self)
2519 {
2520 g_assert(focus_client == self);
2521 #ifdef DEBUG_FOCUS
2522 ob_debug("client_unfocus for %lx\n", self->window);
2523 #endif
2524 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
2525 }
2526
2527 void client_activate(ObClient *self)
2528 {
2529 if (client_normal(self) && screen_showing_desktop)
2530 screen_show_desktop(FALSE);
2531 if (self->iconic)
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
2538 with it */
2539 return;
2540 if (self->shaded)
2541 client_shade(self, FALSE);
2542 client_focus(self);
2543 stacking_raise(CLIENT_AS_WINDOW(self));
2544 }
2545
2546 gboolean client_focused(ObClient *self)
2547 {
2548 return self == focus_client;
2549 }
2550
2551 ObClientIcon *client_icon(ObClient *self, int w, int h)
2552 {
2553 guint i;
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;
2557
2558 if (!self->nicons) return NULL;
2559
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)) {
2563 smallest = size;
2564 si = i;
2565 }
2566 if (size > largest && size <= (unsigned)(w * h)) {
2567 largest = size;
2568 li = i;
2569 }
2570 }
2571 if (largest == 0) /* didnt find one smaller than the requested size */
2572 return &self->icons[si];
2573 return &self->icons[li];
2574 }
2575
2576 /* this be mostly ripped from fvwm */
2577 ObClient *client_find_directional(ObClient *c, ObDirection dir)
2578 {
2579 int my_cx, my_cy, his_cx, his_cy;
2580 int offset = 0;
2581 int distance = 0;
2582 int score, best_score;
2583 ObClient *best_client, *cur;
2584 GList *it;
2585
2586 if(!client_list)
2587 return NULL;
2588
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;
2592
2593 best_score = -1;
2594 best_client = NULL;
2595
2596 for(it = g_list_first(client_list); it; it = it->next) {
2597 cur = it->data;
2598
2599 /* the currently selected window isn't interesting */
2600 if(cur == c)
2601 continue;
2602 if (!client_normal(cur))
2603 continue;
2604 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2605 continue;
2606 if(cur->iconic)
2607 continue;
2608 if(client_focus_target(cur) == cur &&
2609 !(cur->can_focus || cur->focus_notify))
2610 continue;
2611
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;
2618
2619 if(dir > 3) {
2620 int tx;
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;
2628 his_cx = tx;
2629 }
2630
2631 switch(dir) {
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) ?
2639 -his_cy : his_cy);
2640 break;
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) ?
2648 -his_cx : his_cx);
2649 break;
2650 }
2651
2652 /* the target must be in the requested direction */
2653 if(distance <= 0)
2654 continue;
2655
2656 /* Calculate score for this window. The smaller the better. */
2657 score = distance + offset;
2658
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)
2663 score += 1000000;
2664
2665 if(best_score == -1 || score < best_score)
2666 best_client = cur,
2667 best_score = score;
2668 }
2669
2670 return best_client;
2671 }
2672
2673 void client_set_layer(ObClient *self, int layer)
2674 {
2675 if (layer < 0) {
2676 self->below = TRUE;
2677 self->above = FALSE;
2678 } else if (layer == 0) {
2679 self->below = self->above = FALSE;
2680 } else {
2681 self->below = FALSE;
2682 self->above = TRUE;
2683 }
2684 client_calc_layer(self);
2685 client_change_state(self); /* reflect this in the state hints */
2686 }
2687
2688 guint client_monitor(ObClient *self)
2689 {
2690 guint i;
2691
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))
2695 break;
2696 }
2697 if (i == screen_num_monitors) i = 0;
2698 g_assert(i < screen_num_monitors);
2699 return i;
2700 }
2701
2702 ObClient *client_search_top_transient(ObClient *self)
2703 {
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);
2708 } else {
2709 GSList *it;
2710
2711 for (it = self->group->members; it; it = it->next) {
2712 ObClient *c = it->data;
2713
2714 /* checking transient_for prevents infinate loops! */
2715 if (c != self && !c->transient_for)
2716 break;
2717 }
2718 if (it)
2719 return it->data;
2720 }
2721 }
2722
2723 return self;
2724 }
2725
2726 ObClient *client_search_transient(ObClient *self, ObClient *search)
2727 {
2728 GSList *sit;
2729
2730 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
2731 if (sit->data == search)
2732 return search;
2733 if (client_search_transient(sit->data, search))
2734 return search;
2735 }
2736 return NULL;
2737 }
2738
2739 gchar* client_get_sm_client_id(ObClient *self)
2740 {
2741 gchar *id = NULL;
2742
2743 if (!PROP_GETS(self->window, sm_client_id, locale, &id) && self->group)
2744 PROP_GETS(self->group->leader, sm_client_id, locale, &id);
2745 return id;
2746 }
This page took 0.149531 seconds and 3 git commands to generate.