]> Dogcows Code - chaz/openbox/blob - openbox/client.c
kill the prints again for now
[chaz/openbox] / openbox / client.c
1 #include "client.h"
2 #include "screen.h"
3 #include "prop.h"
4 #include "extensions.h"
5 #include "frame.h"
6 #include "engine.h"
7 #include "event.h"
8 #include "grab.h"
9 #include "focus.h"
10 #include "stacking.h"
11 #include "dispatch.h"
12
13 #include <glib.h>
14 #include <X11/Xutil.h>
15
16 /*! The event mask to grab on client windows */
17 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
18 StructureNotifyMask)
19
20 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
21 ButtonMotionMask)
22
23 GSList *client_list = NULL;
24 GHashTable *client_map = NULL;
25
26 static Window *client_startup_stack_order = NULL;
27 static gulong client_startup_stack_size = 0;
28
29 static void client_get_all(Client *self);
30 static void client_toggle_border(Client *self, gboolean show);
31 static void client_get_area(Client *self);
32 static void client_get_desktop(Client *self);
33 static void client_get_state(Client *self);
34 static void client_get_shaped(Client *self);
35 static void client_get_mwm_hints(Client *self);
36 static void client_get_gravity(Client *self);
37 static void client_showhide(Client *self);
38 static void client_change_allowed_actions(Client *self);
39 static void client_change_state(Client *self);
40 static Client *search_focus_tree(Client *node, Client *skip);
41 static void client_apply_startup_state(Client *self);
42 static Client *search_modal_tree(Client *node, Client *skip);
43
44 static guint map_hash(Window *w) { return *w; }
45 static gboolean map_key_comp(Window *w1, Window *w2) { return *w1 == *w2; }
46
47 void client_startup()
48 {
49 client_map = g_hash_table_new((GHashFunc)map_hash,
50 (GEqualFunc)map_key_comp);
51
52 /* save the stacking order on startup! */
53 PROP_GET32U(ob_root, net_client_list_stacking, window,
54 client_startup_stack_order, client_startup_stack_size);
55
56 client_set_list();
57 }
58
59 void client_shutdown()
60 {
61 g_hash_table_destroy(client_map);
62 }
63
64 void client_set_list()
65 {
66 Window *windows, *win_it;
67 GSList *it;
68 guint size = g_slist_length(client_list);
69
70 /* create an array of the window ids */
71 if (size > 0) {
72 windows = g_new(Window, size);
73 win_it = windows;
74 for (it = client_list; it != NULL; it = it->next, ++win_it)
75 *win_it = ((Client*)it->data)->window;
76 } else
77 windows = NULL;
78
79 PROP_SET32A(ob_root, net_client_list, window, windows, size);
80
81 if (windows)
82 g_free(windows);
83
84 stacking_set_list();
85 }
86
87 void client_manage_all()
88 {
89 unsigned int i, j, nchild;
90 Window w, *children;
91 XWMHints *wmhints;
92 XWindowAttributes attrib;
93
94 XQueryTree(ob_display, ob_root, &w, &w, &children, &nchild);
95
96 /* remove all icon windows from the list */
97 for (i = 0; i < nchild; i++) {
98 if (children[i] == None) continue;
99 wmhints = XGetWMHints(ob_display, children[i]);
100 if (wmhints) {
101 if ((wmhints->flags & IconWindowHint) &&
102 (wmhints->icon_window != children[i]))
103 for (j = 0; j < nchild; j++)
104 if (children[j] == wmhints->icon_window) {
105 children[j] = None;
106 break;
107 }
108 XFree(wmhints);
109 }
110 }
111
112 for (i = 0; i < nchild; ++i) {
113 if (children[i] == None)
114 continue;
115 if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
116 if (attrib.override_redirect) continue;
117
118 if (attrib.map_state != IsUnmapped)
119 client_manage(children[i]);
120 }
121 }
122 XFree(children);
123
124 /* stack them as they were on startup!
125 why with stacking_lower? Why, because then windows who aren't in the
126 stacking list are on the top where you can see them instead of buried
127 at the bottom! */
128 for (i = client_startup_stack_size; i > 0; --i) {
129 Client *c;
130
131 w = client_startup_stack_order[i-1];
132 c = g_hash_table_lookup(client_map, &w);
133 if (c) stacking_lower(c);
134 }
135 g_free(client_startup_stack_order);
136 client_startup_stack_order = NULL;
137 client_startup_stack_size = 0;
138 }
139
140 void client_manage(Window window)
141 {
142 Client *client;
143 XEvent e;
144 XWindowAttributes attrib;
145 XSetWindowAttributes attrib_set;
146 /* XWMHints *wmhint; */
147 guint i;
148
149 grab_server(TRUE);
150
151 /* check if it has already been unmapped by the time we started mapping
152 the grab does a sync so we don't have to here */
153 if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
154 XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) {
155 XPutBackEvent(ob_display, &e);
156
157 grab_server(FALSE);
158 return; /* don't manage it */
159 }
160
161 /* make sure it isn't an override-redirect window */
162 if (!XGetWindowAttributes(ob_display, window, &attrib) ||
163 attrib.override_redirect) {
164 grab_server(FALSE);
165 return; /* don't manage it */
166 }
167
168 /* /\* is the window a docking app *\/
169 if ((wmhint = XGetWMHints(ob_display, window))) {
170 if ((wmhint->flags & StateHint) &&
171 wmhint->initial_state == WithdrawnState) {
172 /\* XXX: make dock apps work! *\/
173 grab_server(FALSE);
174 XFree(wmhint);
175 return;
176 }
177 XFree(wmhint);
178 }
179 */
180
181 /* choose the events we want to receive on the CLIENT window */
182 attrib_set.event_mask = CLIENT_EVENTMASK;
183 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
184 XChangeWindowAttributes(ob_display, window,
185 CWEventMask|CWDontPropagate, &attrib_set);
186
187
188 /* create the Client struct, and populate it from the hints on the
189 window */
190 client = g_new(Client, 1);
191 client->window = window;
192 client_get_all(client);
193
194 /* remove the client's border (and adjust re gravity) */
195 client_toggle_border(client, FALSE);
196
197 /* specify that if we exit, the window should not be destroyed and should
198 be reparented back to root automatically */
199 XChangeSaveSet(ob_display, window, SetModeInsert);
200
201 /* create the decoration frame for the client window */
202 client->frame = engine_frame_new();
203
204 engine_frame_grab_client(client->frame, client);
205
206 client_apply_startup_state(client);
207
208 grab_server(FALSE);
209
210 client_list = g_slist_append(client_list, client);
211 stacking_list = g_list_append(stacking_list, client);
212 g_assert(!g_hash_table_lookup(client_map, &client->window));
213 g_hash_table_insert(client_map, &client->window, client);
214
215 /* update the focus lists */
216 if (client->desktop == DESKTOP_ALL) {
217 for (i = 0; i < screen_num_desktops; ++i)
218 focus_order[i] = g_list_append(focus_order[i], client);
219 } else {
220 i = client->desktop;
221 focus_order[i] = g_list_append(focus_order[i], client);
222 }
223
224 stacking_raise(client);
225
226 screen_update_struts();
227
228 dispatch_client(Event_Client_New, client, 0, 0);
229
230 client_showhide(client);
231
232 dispatch_client(Event_Client_Mapped, client, 0, 0);
233
234 /* update the list hints */
235 client_set_list();
236
237 /* g_message("Managed window 0x%lx", window);*/
238 }
239
240 void client_unmanage_all()
241 {
242 while (client_list != NULL)
243 client_unmanage(client_list->data);
244 }
245
246 void client_unmanage(Client *client)
247 {
248 guint i;
249 int j;
250 GSList *it;
251
252 /* g_message("Unmanaging window: %lx", client->window);*/
253
254 dispatch_client(Event_Client_Destroy, client, 0, 0);
255 g_assert(client != NULL);
256
257 /* remove the window from our save set */
258 XChangeSaveSet(ob_display, client->window, SetModeDelete);
259
260 /* we dont want events no more */
261 XSelectInput(ob_display, client->window, NoEventMask);
262
263 engine_frame_hide(client->frame);
264
265 client_list = g_slist_remove(client_list, client);
266 stacking_list = g_list_remove(stacking_list, client);
267 g_hash_table_remove(client_map, &client->window);
268
269 /* update the focus lists */
270 if (client->desktop == DESKTOP_ALL) {
271 for (i = 0; i < screen_num_desktops; ++i)
272 focus_order[i] = g_list_remove(focus_order[i], client);
273 } else {
274 i = client->desktop;
275 focus_order[i] = g_list_remove(focus_order[i], client);
276 }
277
278 /* once the client is out of the list, update the struts to remove it's
279 influence */
280 screen_update_struts();
281
282 /* tell our parent that we're gone */
283 if (client->transient_for != NULL)
284 client->transient_for->transients =
285 g_slist_remove(client->transient_for->transients, client);
286
287 /* tell our transients that we're gone */
288 for (it = client->transients; it != NULL; it = it->next) {
289 ((Client*)it->data)->transient_for = NULL;
290 client_calc_layer(it->data);
291 }
292
293 /* dispatch the unmapped event */
294 dispatch_client(Event_Client_Unmapped, client, 0, 0);
295 g_assert(client != NULL);
296
297 /* unfocus the client (dispatchs the focus event) (we're out of the
298 transient lists already, so being modal doesn't matter) */
299 if (client_focused(client))
300 client_unfocus(client);
301
302 /* give the client its border back */
303 client_toggle_border(client, TRUE);
304
305 /* reparent the window out of the frame, and free the frame */
306 engine_frame_release_client(client->frame, client);
307 client->frame = NULL;
308
309 if (ob_state != State_Exiting) {
310 /* these values should not be persisted across a window
311 unmapping/mapping */
312 prop_erase(client->window, prop_atoms.net_wm_desktop);
313 prop_erase(client->window, prop_atoms.net_wm_state);
314 } else {
315 /* if we're left in an iconic state, the client wont be mapped. this is
316 bad, since we will no longer be managing the window on restart */
317 if (client->iconic)
318 XMapWindow(ob_display, client->window);
319 }
320
321 /* free all data allocated in the client struct */
322 g_slist_free(client->transients);
323 for (j = 0; j < client->nicons; ++j)
324 g_free(client->icons[j].data);
325 if (client->nicons > 0)
326 g_free(client->icons);
327 g_free(client->title);
328 g_free(client->icon_title);
329 g_free(client->name);
330 g_free(client->class);
331 g_free(client->role);
332 g_free(client);
333
334 /* update the list hints */
335 client_set_list();
336 }
337
338 static void client_toggle_border(Client *self, gboolean show)
339 {
340 /* adjust our idea of where the client is, based on its border. When the
341 border is removed, the client should now be considered to be in a
342 different position.
343 when re-adding the border to the client, the same operation needs to be
344 reversed. */
345 int oldx = self->area.x, oldy = self->area.y;
346 int x = oldx, y = oldy;
347 switch(self->gravity) {
348 default:
349 case NorthWestGravity:
350 case WestGravity:
351 case SouthWestGravity:
352 break;
353 case NorthEastGravity:
354 case EastGravity:
355 case SouthEastGravity:
356 if (show) x -= self->border_width * 2;
357 else x += self->border_width * 2;
358 break;
359 case NorthGravity:
360 case SouthGravity:
361 case CenterGravity:
362 case ForgetGravity:
363 case StaticGravity:
364 if (show) x -= self->border_width;
365 else x += self->border_width;
366 break;
367 }
368 switch(self->gravity) {
369 default:
370 case NorthWestGravity:
371 case NorthGravity:
372 case NorthEastGravity:
373 break;
374 case SouthWestGravity:
375 case SouthGravity:
376 case SouthEastGravity:
377 if (show) y -= self->border_width * 2;
378 else y += self->border_width * 2;
379 break;
380 case WestGravity:
381 case EastGravity:
382 case CenterGravity:
383 case ForgetGravity:
384 case StaticGravity:
385 if (show) y -= self->border_width;
386 else y += self->border_width;
387 break;
388 }
389 self->area.x = x;
390 self->area.y = y;
391
392 if (show) {
393 XSetWindowBorderWidth(ob_display, self->window, self->border_width);
394
395 /* move the client so it is back it the right spot _with_ its
396 border! */
397 if (x != oldx || y != oldy)
398 XMoveWindow(ob_display, self->window, x, y);
399 } else
400 XSetWindowBorderWidth(ob_display, self->window, 0);
401 }
402
403
404 static void client_get_all(Client *self)
405 {
406 /* update EVERYTHING!! */
407
408 self->ignore_unmaps = 0;
409
410 /* defaults */
411 self->frame = NULL;
412 self->title = self->icon_title = NULL;
413 self->name = self->class = self->role = NULL;
414 self->wmstate = NormalState;
415 self->transient = FALSE;
416 self->transients = NULL;
417 self->transient_for = NULL;
418 self->layer = -1;
419 self->urgent = FALSE;
420 self->positioned = FALSE;
421 self->disabled_decorations = 0;
422 self->group = None;
423 self->nicons = 0;
424
425 client_get_area(self);
426 client_get_desktop(self);
427 client_get_state(self);
428 client_get_shaped(self);
429
430 client_update_transient_for(self);
431 client_get_mwm_hints(self);
432 client_get_type(self);/* this can change the mwmhints for special cases */
433
434 client_update_protocols(self);
435
436 client_get_gravity(self); /* get the attribute gravity */
437 client_update_normal_hints(self); /* this may override the attribute
438 gravity */
439
440 /* got the type, the mwmhints, the protocols, and the normal hints
441 (min/max sizes), so we're ready to set up the decorations/functions */
442 client_setup_decor_and_functions(self);
443
444 client_update_wmhints(self);
445 client_update_title(self);
446 client_update_icon_title(self);
447 client_update_class(self);
448 client_update_strut(self);
449 client_update_icons(self);
450 client_update_kwm_icon(self);
451
452 /* this makes sure that these windows appear on all desktops */
453 if (self->type == Type_Desktop)
454 self->desktop = DESKTOP_ALL;
455
456 /* set the desktop hint, to make sure that it always exists, and to
457 reflect any changes we've made here */
458 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
459
460 client_change_state(self);
461 }
462
463 static void client_get_area(Client *self)
464 {
465 XWindowAttributes wattrib;
466 Status ret;
467
468 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
469 g_assert(ret != BadWindow);
470
471 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
472 self->border_width = wattrib.border_width;
473 }
474
475 static void client_get_desktop(Client *self)
476 {
477 unsigned int d;
478
479 if (PROP_GET32(self->window, net_wm_desktop, cardinal, d)) {
480 if (d >= screen_num_desktops && d != DESKTOP_ALL)
481 d = screen_num_desktops - 1;
482 self->desktop = d;
483 } else {
484 /* defaults to the current desktop */
485 self->desktop = screen_desktop;
486 }
487 }
488
489 static void client_get_state(Client *self)
490 {
491 gulong *state;
492 gulong num;
493
494 self->modal = self->shaded = self->max_horz = self->max_vert =
495 self->fullscreen = self->above = self->below = self->iconic =
496 self->skip_taskbar = self->skip_pager = FALSE;
497
498 if (PROP_GET32U(self->window, net_wm_state, atom, state, num)) {
499 gulong i;
500 for (i = 0; i < num; ++i) {
501 if (state[i] == prop_atoms.net_wm_state_modal)
502 self->modal = TRUE;
503 else if (state[i] == prop_atoms.net_wm_state_shaded)
504 self->shaded = TRUE;
505 else if (state[i] == prop_atoms.net_wm_state_hidden)
506 self->iconic = TRUE;
507 else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
508 self->skip_taskbar = TRUE;
509 else if (state[i] == prop_atoms.net_wm_state_skip_pager)
510 self->skip_pager = TRUE;
511 else if (state[i] == prop_atoms.net_wm_state_fullscreen)
512 self->fullscreen = TRUE;
513 else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
514 self->max_vert = TRUE;
515 else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
516 self->max_horz = TRUE;
517 else if (state[i] == prop_atoms.net_wm_state_above)
518 self->above = TRUE;
519 else if (state[i] == prop_atoms.net_wm_state_below)
520 self->below = TRUE;
521 }
522
523 g_free(state);
524 }
525 }
526
527 static void client_get_shaped(Client *self)
528 {
529 self->shaped = FALSE;
530 #ifdef SHAPE
531 if (extensions_shape) {
532 int foo;
533 guint ufoo;
534 int s;
535
536 XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
537
538 XShapeQueryExtents(ob_display, self->window, &s, &foo,
539 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
540 &ufoo);
541 self->shaped = (s != 0);
542 }
543 #endif
544 }
545
546 void client_update_transient_for(Client *self)
547 {
548 Window t = None;
549 Client *c = NULL;
550
551 if (XGetTransientForHint(ob_display, self->window, &t) &&
552 t != self->window) { /* cant be transient to itself! */
553 self->transient = TRUE;
554 c = g_hash_table_lookup(client_map, &t);
555 g_assert(c != self);/* if this happens then we need to check for it*/
556
557 if (!c /*XXX: && _group*/) {
558 /* not transient to a client, see if it is transient for a
559 group */
560 if (/*t == _group->leader() || */
561 t == None ||
562 t == ob_root) {
563 /* window is a transient for its group! */
564 /* XXX: for now this is treated as non-transient.
565 this needs to be fixed! */
566 }
567 }
568 } else
569 self->transient = FALSE;
570
571 /* if anything has changed... */
572 if (c != self->transient_for) {
573 if (self->transient_for)
574 /* remove from old parent */
575 self->transient_for->transients =
576 g_slist_remove(self->transient_for->transients, self);
577 self->transient_for = c;
578 if (self->transient_for)
579 /* add to new parent */
580 self->transient_for->transients =
581 g_slist_append(self->transient_for->transients, self);
582 }
583 }
584
585 static void client_get_mwm_hints(Client *self)
586 {
587 unsigned long num;
588 unsigned long *hints;
589
590 self->mwmhints.flags = 0; /* default to none */
591
592 if (PROP_GET32U(self->window, motif_wm_hints, motif_wm_hints, hints, num)) {
593 if (num >= MWM_ELEMENTS) {
594 self->mwmhints.flags = hints[0];
595 self->mwmhints.functions = hints[1];
596 self->mwmhints.decorations = hints[2];
597 }
598 g_free(hints);
599 }
600 }
601
602 void client_get_type(Client *self)
603 {
604 gulong *val, num, i;
605
606 self->type = -1;
607
608 if (PROP_GET32U(self->window, net_wm_window_type, atom, val, num)) {
609 /* use the first value that we know about in the array */
610 for (i = 0; i < num; ++i) {
611 if (val[i] == prop_atoms.net_wm_window_type_desktop)
612 self->type = Type_Desktop;
613 else if (val[i] == prop_atoms.net_wm_window_type_dock)
614 self->type = Type_Dock;
615 else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
616 self->type = Type_Toolbar;
617 else if (val[i] == prop_atoms.net_wm_window_type_menu)
618 self->type = Type_Menu;
619 else if (val[i] == prop_atoms.net_wm_window_type_utility)
620 self->type = Type_Utility;
621 else if (val[i] == prop_atoms.net_wm_window_type_splash)
622 self->type = Type_Splash;
623 else if (val[i] == prop_atoms.net_wm_window_type_dialog)
624 self->type = Type_Dialog;
625 else if (val[i] == prop_atoms.net_wm_window_type_normal)
626 self->type = Type_Normal;
627 else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
628 /* prevent this window from getting any decor or
629 functionality */
630 self->mwmhints.flags &= (MwmFlag_Functions |
631 MwmFlag_Decorations);
632 self->mwmhints.decorations = 0;
633 self->mwmhints.functions = 0;
634 }
635 if (self->type != (WindowType) -1)
636 break; /* grab the first legit type */
637 }
638 g_free(val);
639 }
640
641 if (self->type == (WindowType) -1) {
642 /*the window type hint was not set, which means we either classify
643 ourself as a normal window or a dialog, depending on if we are a
644 transient. */
645 if (self->transient)
646 self->type = Type_Dialog;
647 else
648 self->type = Type_Normal;
649 }
650 }
651
652 void client_update_protocols(Client *self)
653 {
654 Atom *proto;
655 gulong num_return, i;
656
657 self->focus_notify = FALSE;
658 self->delete_window = FALSE;
659
660 if (PROP_GET32U(self->window, wm_protocols, atom, proto, num_return)) {
661 for (i = 0; i < num_return; ++i) {
662 if (proto[i] == prop_atoms.wm_delete_window) {
663 /* this means we can request the window to close */
664 self->delete_window = TRUE;
665 } else if (proto[i] == prop_atoms.wm_take_focus)
666 /* if this protocol is requested, then the window will be
667 notified whenever we want it to receive focus */
668 self->focus_notify = TRUE;
669 }
670 g_free(proto);
671 }
672 }
673
674 static void client_get_gravity(Client *self)
675 {
676 XWindowAttributes wattrib;
677 Status ret;
678
679 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
680 g_assert(ret != BadWindow);
681 self->gravity = wattrib.win_gravity;
682 }
683
684 void client_update_normal_hints(Client *self)
685 {
686 XSizeHints size;
687 long ret;
688 int oldgravity = self->gravity;
689
690 /* defaults */
691 self->min_ratio = 0.0f;
692 self->max_ratio = 0.0f;
693 SIZE_SET(self->size_inc, 1, 1);
694 SIZE_SET(self->base_size, 0, 0);
695 SIZE_SET(self->min_size, 0, 0);
696 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
697
698 /* get the hints from the window */
699 if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
700 self->positioned = (size.flags & (PPosition|USPosition));
701
702 if (size.flags & PWinGravity) {
703 self->gravity = size.win_gravity;
704
705 /* if the client has a frame, i.e. has already been mapped and
706 is changing its gravity */
707 if (self->frame && self->gravity != oldgravity) {
708 /* move our idea of the client's position based on its new
709 gravity */
710 self->area.x = self->frame->area.x;
711 self->area.y = self->frame->area.y;
712 frame_frame_gravity(self->frame, &self->area.x, &self->area.y);
713 }
714 }
715
716 if (size.flags & PAspect) {
717 if (size.min_aspect.y)
718 self->min_ratio = (float)size.min_aspect.x / size.min_aspect.y;
719 if (size.max_aspect.y)
720 self->max_ratio = (float)size.max_aspect.x / size.max_aspect.y;
721 }
722
723 if (size.flags & PMinSize)
724 SIZE_SET(self->min_size, size.min_width, size.min_height);
725
726 if (size.flags & PMaxSize)
727 SIZE_SET(self->max_size, size.max_width, size.max_height);
728
729 if (size.flags & PBaseSize)
730 SIZE_SET(self->base_size, size.base_width, size.base_height);
731
732 if (size.flags & PResizeInc)
733 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
734 }
735 }
736
737 void client_setup_decor_and_functions(Client *self)
738 {
739 /* start with everything (cept fullscreen) */
740 self->decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
741 Decor_Icon | Decor_AllDesktops | Decor_Iconify | Decor_Maximize;
742 self->functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
743 Func_Shade;
744 if (self->delete_window) {
745 self->decorations |= Decor_Close;
746 self->functions |= Func_Close;
747 }
748
749 if (!(self->min_size.width < self->max_size.width ||
750 self->min_size.height < self->max_size.height)) {
751 self->decorations &= ~(Decor_Maximize | Decor_Handle);
752 self->functions &= ~(Func_Resize | Func_Maximize);
753 }
754
755 switch (self->type) {
756 case Type_Normal:
757 /* normal windows retain all of the possible decorations and
758 functionality, and are the only windows that you can fullscreen */
759 self->functions |= Func_Fullscreen;
760 break;
761
762 case Type_Dialog:
763 /* dialogs cannot be maximized */
764 self->decorations &= ~Decor_Maximize;
765 self->functions &= ~Func_Maximize;
766 break;
767
768 case Type_Menu:
769 case Type_Toolbar:
770 case Type_Utility:
771 /* these windows get less functionality */
772 self->decorations &= ~(Decor_Iconify | Decor_Handle);
773 self->functions &= ~(Func_Iconify | Func_Resize);
774 break;
775
776 case Type_Desktop:
777 case Type_Dock:
778 case Type_Splash:
779 /* none of these windows are manipulated by the window manager */
780 self->decorations = 0;
781 self->functions = 0;
782 break;
783 }
784
785 /* Mwm Hints are applied subtractively to what has already been chosen for
786 decor and functionality */
787 if (self->mwmhints.flags & MwmFlag_Decorations) {
788 if (! (self->mwmhints.decorations & MwmDecor_All)) {
789 if (! (self->mwmhints.decorations & MwmDecor_Border))
790 self->decorations &= ~Decor_Border;
791 if (! (self->mwmhints.decorations & MwmDecor_Handle))
792 self->decorations &= ~Decor_Handle;
793 if (! (self->mwmhints.decorations & MwmDecor_Title))
794 self->decorations &= ~Decor_Titlebar;
795 if (! (self->mwmhints.decorations & MwmDecor_Iconify))
796 self->decorations &= ~Decor_Iconify;
797 if (! (self->mwmhints.decorations & MwmDecor_Maximize))
798 self->decorations &= ~Decor_Maximize;
799 }
800 }
801
802 if (self->mwmhints.flags & MwmFlag_Functions) {
803 if (! (self->mwmhints.functions & MwmFunc_All)) {
804 if (! (self->mwmhints.functions & MwmFunc_Resize))
805 self->functions &= ~Func_Resize;
806 if (! (self->mwmhints.functions & MwmFunc_Move))
807 self->functions &= ~Func_Move;
808 if (! (self->mwmhints.functions & MwmFunc_Iconify))
809 self->functions &= ~Func_Iconify;
810 if (! (self->mwmhints.functions & MwmFunc_Maximize))
811 self->functions &= ~Func_Maximize;
812 /* dont let mwm hints kill the close button
813 if (! (self->mwmhints.functions & MwmFunc_Close))
814 self->functions &= ~Func_Close; */
815 }
816 }
817
818 /* can't maximize without moving/resizing */
819 if (!((self->functions & Func_Move) && (self->functions & Func_Resize)))
820 self->functions &= ~(Func_Maximize | Func_Fullscreen);
821
822 /* finally, user specified disabled decorations are applied to subtract
823 decorations */
824 if (self->disabled_decorations & Decor_Titlebar)
825 self->decorations &= ~Decor_Titlebar;
826 if (self->disabled_decorations & Decor_Handle)
827 self->decorations &= ~Decor_Handle;
828 if (self->disabled_decorations & Decor_Border)
829 self->decorations &= ~Decor_Border;
830 if (self->disabled_decorations & Decor_Iconify)
831 self->decorations &= ~Decor_Iconify;
832 if (self->disabled_decorations & Decor_Maximize)
833 self->decorations &= ~Decor_Maximize;
834 if (self->disabled_decorations & Decor_AllDesktops)
835 self->decorations &= ~Decor_AllDesktops;
836 if (self->disabled_decorations & Decor_Close)
837 self->decorations &= ~Decor_Close;
838
839 /* if we don't have a titlebar, then we cannot shade! */
840 if (!(self->decorations & Decor_Titlebar))
841 self->functions &= ~Func_Shade;
842
843 client_change_allowed_actions(self);
844
845 if (self->frame) {
846 /* change the decors on the frame, and with more/less decorations,
847 we may also need to be repositioned */
848 engine_frame_adjust_area(self->frame, TRUE, TRUE);
849 /* with new decor, the window's maximized size may change */
850 client_remaximize(self);
851 }
852 }
853
854 static void client_change_allowed_actions(Client *self)
855 {
856 Atom actions[9];
857 int num = 0;
858
859 actions[num++] = prop_atoms.net_wm_action_change_desktop;
860
861 if (self->functions & Func_Shade)
862 actions[num++] = prop_atoms.net_wm_action_shade;
863 if (self->functions & Func_Close)
864 actions[num++] = prop_atoms.net_wm_action_close;
865 if (self->functions & Func_Move)
866 actions[num++] = prop_atoms.net_wm_action_move;
867 if (self->functions & Func_Iconify)
868 actions[num++] = prop_atoms.net_wm_action_minimize;
869 if (self->functions & Func_Resize)
870 actions[num++] = prop_atoms.net_wm_action_resize;
871 if (self->functions & Func_Fullscreen)
872 actions[num++] = prop_atoms.net_wm_action_fullscreen;
873 if (self->functions & Func_Maximize) {
874 actions[num++] = prop_atoms.net_wm_action_maximize_horz;
875 actions[num++] = prop_atoms.net_wm_action_maximize_vert;
876 }
877
878 PROP_SET32A(self->window, net_wm_allowed_actions, atom, actions, num);
879
880 /* make sure the window isn't breaking any rules now */
881
882 if (!(self->functions & Func_Shade) && self->shaded) {
883 if (self->frame) client_shade(self, FALSE);
884 else self->shaded = FALSE;
885 }
886 if (!(self->functions & Func_Iconify) && self->iconic) {
887 if (self->frame) client_iconify(self, FALSE, TRUE);
888 else self->iconic = FALSE;
889 }
890 if (!(self->functions & Func_Fullscreen) && self->fullscreen) {
891 if (self->frame) client_fullscreen(self, FALSE, TRUE);
892 else self->fullscreen = FALSE;
893 }
894 if (!(self->functions & Func_Maximize) && (self->max_horz ||
895 self->max_vert)) {
896 if (self->frame) client_maximize(self, FALSE, 0, TRUE);
897 else self->max_vert = self->max_horz = FALSE;
898 }
899 }
900
901 void client_remaximize(Client *self)
902 {
903 int dir;
904 if (self->max_horz && self->max_vert)
905 dir = 0;
906 else if (self->max_horz)
907 dir = 1;
908 else if (self->max_vert)
909 dir = 2;
910 else
911 return; /* not maximized */
912 self->max_horz = self->max_vert = FALSE;
913 client_maximize(self, TRUE, dir, FALSE);
914 }
915
916 void client_update_wmhints(Client *self)
917 {
918 XWMHints *hints;
919 gboolean ur = FALSE;
920
921 /* assume a window takes input if it doesnt specify */
922 self->can_focus = TRUE;
923
924 if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
925 if (hints->flags & InputHint)
926 self->can_focus = hints->input;
927
928 /* only do this when starting! */
929 if (ob_state == State_Starting && (hints->flags & StateHint))
930 self->iconic = hints->initial_state == IconicState;
931
932 if (hints->flags & XUrgencyHint)
933 ur = TRUE;
934
935 if (hints->flags & WindowGroupHint) {
936 if (hints->window_group != self->group) {
937 /* XXX: remove from the old group if there was one */
938 self->group = hints->window_group;
939 /* XXX: do stuff with the group */
940 }
941 } else /* no group! */
942 self->group = None;
943
944 if (hints->flags & IconPixmapHint) {
945 client_update_kwm_icon(self);
946 /* try get the kwm icon first, this is a fallback only */
947 if (self->pixmap_icon == None) {
948 self->pixmap_icon = hints->icon_pixmap;
949 if (hints->flags & IconMaskHint)
950 self->pixmap_icon_mask = hints->icon_mask;
951 else
952 self->pixmap_icon_mask = None;
953
954 if (self->frame)
955 engine_frame_adjust_icon(self->frame);
956 }
957 }
958
959 XFree(hints);
960 }
961
962 if (ur != self->urgent) {
963 self->urgent = ur;
964 g_message("Urgent Hint for 0x%lx: %s", self->window,
965 ur ? "ON" : "OFF");
966 /* fire the urgent callback if we're mapped, otherwise, wait until
967 after we're mapped */
968 if (self->frame)
969 dispatch_client(Event_Client_Urgent, self, self->urgent, 0);
970 }
971 }
972
973 void client_update_title(Client *self)
974 {
975 gchar *data = NULL;
976
977 g_free(self->title);
978
979 /* try netwm */
980 if (!PROP_GETS(self->window, net_wm_name, utf8, data)) {
981 /* try old x stuff */
982 if (PROP_GETS(self->window, wm_name, string, data)) {
983 /* convert it to UTF-8 */
984 gsize r, w;
985 gchar *u;
986
987 u = g_locale_to_utf8(data, -1, &r, &w, NULL);
988 if (u == NULL) {
989 g_warning("Unable to convert string to UTF-8");
990 } else {
991 g_free(data);
992 data = u;
993 }
994 }
995 if (data == NULL)
996 data = g_strdup("Unnamed Window");
997
998 PROP_SETS(self->window, net_wm_visible_name, utf8, data);
999 }
1000
1001 self->title = data;
1002
1003 if (self->frame)
1004 engine_frame_adjust_title(self->frame);
1005 }
1006
1007 void client_update_icon_title(Client *self)
1008 {
1009 gchar *data = NULL;
1010
1011 g_free(self->icon_title);
1012
1013 /* try netwm */
1014 if (!PROP_GETS(self->window, net_wm_icon_name, utf8, data)) {
1015 /* try old x stuff */
1016 if (PROP_GETS(self->window, wm_icon_name, string, data)) {
1017 /* convert it to UTF-8 */
1018 gsize r, w;
1019 gchar *u;
1020
1021 u = g_locale_to_utf8(data, -1, &r, &w, NULL);
1022 if (u == NULL) {
1023 g_warning("Unable to convert string to UTF-8");
1024 } else {
1025 g_free(data);
1026 data = u;
1027 }
1028 }
1029 if (data == NULL)
1030 data = g_strdup("Unnamed Window");
1031
1032 PROP_SETS(self->window, net_wm_visible_icon_name, utf8, data);
1033 }
1034
1035 self->icon_title = data;
1036 }
1037
1038 void client_update_class(Client *self)
1039 {
1040 GPtrArray *data;
1041 gchar *s;
1042 guint i;
1043
1044 if (self->name) g_free(self->name);
1045 if (self->class) g_free(self->class);
1046 if (self->role) g_free(self->role);
1047
1048 self->name = self->class = self->role = NULL;
1049
1050 data = g_ptr_array_new();
1051
1052 if (PROP_GETSA(self->window, wm_class, string, data)) {
1053 if (data->len > 0)
1054 self->name = g_strdup(g_ptr_array_index(data, 0));
1055 if (data->len > 1)
1056 self->class = g_strdup(g_ptr_array_index(data, 1));
1057 }
1058
1059 for (i = 0; i < data->len; ++i)
1060 g_free(g_ptr_array_index(data, i));
1061 g_ptr_array_free(data, TRUE);
1062
1063 if (PROP_GETS(self->window, wm_window_role, string, s))
1064 self->role = g_strdup(s);
1065
1066 if (self->name == NULL) self->name = g_strdup("");
1067 if (self->class == NULL) self->class = g_strdup("");
1068 if (self->role == NULL) self->role = g_strdup("");
1069 }
1070
1071 void client_update_strut(Client *self)
1072 {
1073 gulong *data;
1074
1075 if (PROP_GET32A(self->window, net_wm_strut, cardinal, data, 4)) {
1076 STRUT_SET(self->strut, data[0], data[1], data[2], data[3]);
1077 g_free(data);
1078 } else
1079 STRUT_SET(self->strut, 0, 0, 0, 0);
1080
1081 /* updating here is pointless while we're being mapped cuz we're not in
1082 the client list yet */
1083 if (self->frame)
1084 screen_update_struts();
1085 }
1086
1087 void client_update_icons(Client *self)
1088 {
1089 unsigned long num;
1090 unsigned long *data;
1091 unsigned long w, h, i;
1092 int j;
1093
1094 for (j = 0; j < self->nicons; ++j)
1095 g_free(self->icons[j].data);
1096 if (self->nicons > 0)
1097 g_free(self->icons);
1098 self->nicons = 0;
1099
1100 if (PROP_GET32U(self->window, net_wm_icon, cardinal, data, num)) {
1101 /* figure out how many valid icons are in here */
1102 i = 0;
1103 while (num - i > 2) {
1104 w = data[i++];
1105 h = data[i++];
1106 i += w * h;
1107 if (i > num) break;
1108 ++self->nicons;
1109 }
1110
1111 self->icons = g_new(Icon, self->nicons);
1112
1113 /* store the icons */
1114 i = 0;
1115 for (j = 0; j < self->nicons; ++j) {
1116 w = self->icons[j].width = data[i++];
1117 h = self->icons[j].height = data[i++];
1118 self->icons[j].data =
1119 g_memdup(&data[i], w * h * sizeof(gulong));
1120 i += w * h;
1121 g_assert(i <= num);
1122 }
1123
1124 g_free(data);
1125 }
1126
1127 if (self->frame)
1128 engine_frame_adjust_icon(self->frame);
1129 }
1130
1131 void client_update_kwm_icon(Client *self)
1132 {
1133 Pixmap *data;
1134
1135 if (PROP_GET32A(self->window, kwm_win_icon, kwm_win_icon, data, 2)) {
1136 self->pixmap_icon = data[0];
1137 self->pixmap_icon_mask = data[1];
1138 g_free(data);
1139 } else {
1140 self->pixmap_icon = self->pixmap_icon_mask = None;
1141 }
1142 if (self->frame)
1143 engine_frame_adjust_icon(self->frame);
1144 }
1145
1146 static void client_change_state(Client *self)
1147 {
1148 unsigned long state[2];
1149 Atom netstate[10];
1150 int num;
1151
1152 state[0] = self->wmstate;
1153 state[1] = None;
1154 PROP_SET32A(self->window, wm_state, wm_state, state, 2);
1155
1156 num = 0;
1157 if (self->modal)
1158 netstate[num++] = prop_atoms.net_wm_state_modal;
1159 if (self->shaded)
1160 netstate[num++] = prop_atoms.net_wm_state_shaded;
1161 if (self->iconic)
1162 netstate[num++] = prop_atoms.net_wm_state_hidden;
1163 if (self->skip_taskbar)
1164 netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1165 if (self->skip_pager)
1166 netstate[num++] = prop_atoms.net_wm_state_skip_pager;
1167 if (self->fullscreen)
1168 netstate[num++] = prop_atoms.net_wm_state_fullscreen;
1169 if (self->max_vert)
1170 netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1171 if (self->max_horz)
1172 netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1173 if (self->above)
1174 netstate[num++] = prop_atoms.net_wm_state_above;
1175 if (self->below)
1176 netstate[num++] = prop_atoms.net_wm_state_below;
1177 PROP_SET32A(self->window, net_wm_state, atom, netstate, num);
1178
1179 client_calc_layer(self);
1180
1181 if (self->frame)
1182 engine_frame_adjust_state(self->frame);
1183 }
1184
1185 static Client *search_focus_tree(Client *node, Client *skip)
1186 {
1187 GSList *it;
1188 Client *ret;
1189
1190 for (it = node->transients; it != NULL; it = it->next) {
1191 Client *c = it->data;
1192 if (c == skip) continue; /* circular? */
1193 if ((ret = search_focus_tree(c, skip))) return ret;
1194 if (client_focused(c)) return c;
1195 }
1196 return NULL;
1197 }
1198
1199 void client_calc_layer(Client *self)
1200 {
1201 StackLayer l;
1202 gboolean fs;
1203 Client *c;
1204
1205 /* are we fullscreen, or do we have a fullscreen transient parent? */
1206 c = self;
1207 fs = FALSE;
1208 while (c) {
1209 if (c->fullscreen) {
1210 fs = TRUE;
1211 break;
1212 }
1213 c = c->transient_for;
1214 }
1215 if (!fs && self->fullscreen) {
1216 /* is one of our transients focused? */
1217 c = search_focus_tree(self, self);
1218 if (c != NULL) fs = TRUE;
1219 }
1220
1221 if (self->iconic) l = Layer_Icon;
1222 else if (fs) l = Layer_Fullscreen;
1223 else if (self->type == Type_Desktop) l = Layer_Desktop;
1224 else if (self->type == Type_Dock) {
1225 if (!self->below) l = Layer_Top;
1226 else l = Layer_Normal;
1227 }
1228 else if (self->above) l = Layer_Above;
1229 else if (self->below) l = Layer_Below;
1230 else l = Layer_Normal;
1231
1232 if (l != self->layer) {
1233 self->layer = l;
1234 if (self->frame)
1235 stacking_raise(self);
1236 }
1237 }
1238
1239 gboolean client_should_show(Client *self)
1240 {
1241 if (self->iconic) return FALSE;
1242 else if (!(self->desktop == screen_desktop ||
1243 self->desktop == DESKTOP_ALL)) return FALSE;
1244 else if (client_normal(self) && screen_showing_desktop) return FALSE;
1245
1246 return TRUE;
1247 }
1248
1249 static void client_showhide(Client *self)
1250 {
1251
1252 if (client_should_show(self))
1253 engine_frame_show(self->frame);
1254 else
1255 engine_frame_hide(self->frame);
1256 }
1257
1258 gboolean client_normal(Client *self) {
1259 return ! (self->type == Type_Desktop || self->type == Type_Dock ||
1260 self->type == Type_Splash);
1261 }
1262
1263 static void client_apply_startup_state(Client *self)
1264 {
1265 /* these are in a carefully crafted order.. */
1266
1267 if (self->iconic) {
1268 self->iconic = FALSE;
1269 client_iconify(self, TRUE, FALSE);
1270 }
1271 if (self->fullscreen) {
1272 self->fullscreen = FALSE;
1273 client_fullscreen(self, TRUE, FALSE);
1274 }
1275 if (self->shaded) {
1276 self->shaded = FALSE;
1277 client_shade(self, TRUE);
1278 }
1279 if (self->urgent)
1280 dispatch_client(Event_Client_Urgent, self, self->urgent, 0);
1281
1282 if (self->max_vert && self->max_horz) {
1283 self->max_vert = self->max_horz = FALSE;
1284 client_maximize(self, TRUE, 0, FALSE);
1285 } else if (self->max_vert) {
1286 self->max_vert = FALSE;
1287 client_maximize(self, TRUE, 2, FALSE);
1288 } else if (self->max_horz) {
1289 self->max_horz = FALSE;
1290 client_maximize(self, TRUE, 1, FALSE);
1291 }
1292
1293 /* nothing to do for the other states:
1294 skip_taskbar
1295 skip_pager
1296 modal
1297 above
1298 below
1299 */
1300 }
1301
1302 void client_configure(Client *self, Corner anchor, int x, int y, int w, int h,
1303 gboolean user, gboolean final)
1304 {
1305 gboolean moved = FALSE, resized = FALSE;
1306
1307 /* set the size and position if fullscreen */
1308 if (self->fullscreen) {
1309 x = 0;
1310 y = 0;
1311 w = screen_physical_size.width;
1312 h = screen_physical_size.height;
1313 } else {
1314 /* set the size and position if maximized */
1315 if (self->max_horz) {
1316 x = screen_area(self->desktop)->x - self->frame->size.left;
1317 w = screen_area(self->desktop)->x +
1318 screen_area(self->desktop)->width;
1319 }
1320 if (self->max_vert) {
1321 y = screen_area(self->desktop)->y;
1322 h = screen_area(self->desktop)->y +
1323 screen_area(self->desktop)->height -
1324 self->frame->size.top - self->frame->size.bottom;
1325 }
1326 }
1327
1328 /* these override the above states! if you cant move you can't move! */
1329 if (user) {
1330 if (!(self->functions & Func_Move)) {
1331 x = self->area.x;
1332 y = self->area.y;
1333 }
1334 if (!(self->functions & Func_Resize)) {
1335 w = self->area.width;
1336 h = self->area.height;
1337 }
1338 }
1339
1340 if (!(w == self->area.width && h == self->area.height)) {
1341 w -= self->base_size.width;
1342 h -= self->base_size.height;
1343
1344 if (user) {
1345 /* for interactive resizing. have to move half an increment in each
1346 direction. */
1347
1348 /* how far we are towards the next size inc */
1349 int mw = w % self->size_inc.width;
1350 int mh = h % self->size_inc.height;
1351 /* amount to add */
1352 int aw = self->size_inc.width / 2;
1353 int ah = self->size_inc.height / 2;
1354 /* don't let us move into a new size increment */
1355 if (mw + aw >= self->size_inc.width)
1356 aw = self->size_inc.width - mw - 1;
1357 if (mh + ah >= self->size_inc.height)
1358 ah = self->size_inc.height - mh - 1;
1359 w += aw;
1360 h += ah;
1361
1362 /* if this is a user-requested resize, then check against min/max
1363 sizes and aspect ratios */
1364
1365 /* smaller than min size or bigger than max size? */
1366 if (w > self->max_size.width) w = self->max_size.width;
1367 if (w < self->min_size.width) w = self->min_size.width;
1368 if (h > self->max_size.height) h = self->max_size.height;
1369 if (h < self->min_size.height) h = self->min_size.height;
1370
1371 /* adjust the height ot match the width for the aspect ratios */
1372 if (self->min_ratio)
1373 if (h * self->min_ratio > w) h = (int)(w / self->min_ratio);
1374 if (self->max_ratio)
1375 if (h * self->max_ratio < w) h = (int)(w / self->max_ratio);
1376 }
1377
1378 /* keep to the increments */
1379 w /= self->size_inc.width;
1380 h /= self->size_inc.height;
1381
1382 /* you cannot resize to nothing */
1383 if (w < 1) w = 1;
1384 if (h < 1) h = 1;
1385
1386 /* store the logical size */
1387 SIZE_SET(self->logical_size, w, h);
1388
1389 w *= self->size_inc.width;
1390 h *= self->size_inc.height;
1391
1392 w += self->base_size.width;
1393 h += self->base_size.height;
1394 }
1395
1396 switch (anchor) {
1397 case Corner_TopLeft:
1398 break;
1399 case Corner_TopRight:
1400 x -= w - self->area.width;
1401 break;
1402 case Corner_BottomLeft:
1403 y -= h - self->area.height;
1404 break;
1405 case Corner_BottomRight:
1406 x -= w - self->area.width;
1407 y -= h - self->area.height;
1408 break;
1409 }
1410
1411 moved = x != self->area.x || y != self->area.y;
1412 resized = w != self->area.width || h != self->area.height;
1413
1414 RECT_SET(self->area, x, y, w, h);
1415
1416 if (resized)
1417 XResizeWindow(ob_display, self->window, w, h);
1418
1419 /* move/resize the frame to match the request */
1420 if (self->frame) {
1421 if (moved || resized)
1422 engine_frame_adjust_area(self->frame, moved, resized);
1423
1424 if (!user || final) {
1425 XEvent event;
1426 event.type = ConfigureNotify;
1427 event.xconfigure.display = ob_display;
1428 event.xconfigure.event = self->window;
1429 event.xconfigure.window = self->window;
1430
1431 /* root window coords with border in mind */
1432 event.xconfigure.x = x - self->border_width +
1433 self->frame->size.left;
1434 event.xconfigure.y = y - self->border_width +
1435 self->frame->size.top;
1436
1437 event.xconfigure.width = self->area.width;
1438 event.xconfigure.height = self->area.height;
1439 event.xconfigure.border_width = self->border_width;
1440 event.xconfigure.above = self->frame->plate;
1441 event.xconfigure.override_redirect = FALSE;
1442 XSendEvent(event.xconfigure.display, event.xconfigure.window,
1443 FALSE, StructureNotifyMask, &event);
1444 }
1445 }
1446 }
1447
1448 void client_fullscreen(Client *self, gboolean fs, gboolean savearea)
1449 {
1450 int x, y, w, h;
1451
1452 if (!(self->functions & Func_Fullscreen) || /* can't */
1453 self->fullscreen == fs) return; /* already done */
1454
1455 self->fullscreen = fs;
1456 client_change_state(self); /* change the state hints on the client */
1457
1458 if (fs) {
1459 /* save the functions and remove them */
1460 self->pre_fs_func = self->functions;
1461 self->functions &= (Func_Close | Func_Fullscreen |
1462 Func_Iconify);
1463 /* save the decorations and remove them */
1464 self->pre_fs_decor = self->decorations;
1465 self->decorations = 0;
1466 if (savearea) {
1467 long dimensions[4];
1468 dimensions[0] = self->area.x;
1469 dimensions[1] = self->area.y;
1470 dimensions[2] = self->area.width;
1471 dimensions[3] = self->area.height;
1472
1473 PROP_SET32A(self->window, openbox_premax, cardinal,
1474 dimensions, 4);
1475 }
1476
1477 /* these are not actually used cuz client_configure will set them
1478 as appropriate when the window is fullscreened */
1479 x = y = w = h = 0;
1480 } else {
1481 long *dimensions;
1482
1483 self->functions = self->pre_fs_func;
1484 self->decorations = self->pre_fs_decor;
1485
1486 if (PROP_GET32A(self->window, openbox_premax, cardinal,
1487 dimensions, 4)) {
1488 x = dimensions[0];
1489 y = dimensions[1];
1490 w = dimensions[2];
1491 h = dimensions[3];
1492 g_free(dimensions);
1493 } else {
1494 /* pick some fallbacks... */
1495 x = screen_area(self->desktop)->x +
1496 screen_area(self->desktop)->width / 4;
1497 y = screen_area(self->desktop)->y +
1498 screen_area(self->desktop)->height / 4;
1499 w = screen_area(self->desktop)->width / 2;
1500 h = screen_area(self->desktop)->height / 2;
1501 }
1502 }
1503
1504 client_change_allowed_actions(self); /* based on the new _functions */
1505
1506 /* when fullscreening, don't obey things like increments, fill the
1507 screen */
1508 client_configure(self, Corner_TopLeft, x, y, w, h, !fs, TRUE);
1509
1510 /* raise (back) into our stacking layer */
1511 stacking_raise(self);
1512
1513 /* try focus us when we go into fullscreen mode */
1514 client_focus(self);
1515 }
1516
1517 void client_iconify(Client *self, gboolean iconic, gboolean curdesk)
1518 {
1519 if (self->iconic == iconic) return; /* nothing to do */
1520
1521 g_message("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
1522 self->window);
1523
1524 self->iconic = iconic;
1525
1526 if (iconic) {
1527 self->wmstate = IconicState;
1528 self->ignore_unmaps++;
1529 /* we unmap the client itself so that we can get MapRequest events,
1530 and because the ICCCM tells us to! */
1531 XUnmapWindow(ob_display, self->window);
1532 } else {
1533 if (curdesk)
1534 client_set_desktop(self, screen_desktop);
1535 self->wmstate = self->shaded ? IconicState : NormalState;
1536 XMapWindow(ob_display, self->window);
1537 }
1538 client_change_state(self);
1539 client_showhide(self);
1540 screen_update_struts();
1541
1542 dispatch_client(iconic ? Event_Client_Unmapped : Event_Client_Mapped,
1543 self, 0, 0);
1544 }
1545
1546 void client_maximize(Client *self, gboolean max, int dir, gboolean savearea)
1547 {
1548 int x, y, w, h;
1549
1550 g_assert(dir == 0 || dir == 1 || dir == 2);
1551 if (!(self->functions & Func_Maximize)) return; /* can't */
1552
1553 /* check if already done */
1554 if (max) {
1555 if (dir == 0 && self->max_horz && self->max_vert) return;
1556 if (dir == 1 && self->max_horz) return;
1557 if (dir == 2 && self->max_vert) return;
1558 } else {
1559 if (dir == 0 && !self->max_horz && !self->max_vert) return;
1560 if (dir == 1 && !self->max_horz) return;
1561 if (dir == 2 && !self->max_vert) return;
1562 }
1563
1564 /* work with the frame's coords */
1565 x = self->frame->area.x;
1566 y = self->frame->area.y;
1567 w = self->area.width;
1568 h = self->area.height;
1569
1570 if (max) {
1571 if (savearea) {
1572 long dimensions[4];
1573 long *readdim;
1574
1575 dimensions[0] = x;
1576 dimensions[1] = y;
1577 dimensions[2] = w;
1578 dimensions[3] = h;
1579
1580 /* get the property off the window and use it for the dimensions
1581 we are already maxed on */
1582 if (PROP_GET32A(self->window, openbox_premax, cardinal,
1583 readdim, 4)) {
1584 if (self->max_horz) {
1585 dimensions[0] = readdim[0];
1586 dimensions[2] = readdim[2];
1587 }
1588 if (self->max_vert) {
1589 dimensions[1] = readdim[1];
1590 dimensions[3] = readdim[3];
1591 }
1592 g_free(readdim);
1593 }
1594
1595 PROP_SET32A(self->window, openbox_premax, cardinal,
1596 dimensions, 4);
1597 }
1598
1599 /* pass the client's current position info. the client_configure
1600 will move/size stuff as appropriate for a maximized window */
1601 x = self->area.x;
1602 y = self->area.y;
1603 w = self->area.width;
1604 h = self->area.height;
1605 } else {
1606 long *dimensions;
1607
1608 if (PROP_GET32A(self->window, openbox_premax, cardinal,
1609 dimensions, 4)) {
1610 if (dir == 0 || dir == 1) { /* horz */
1611 x = dimensions[0];
1612 w = dimensions[2];
1613 }
1614 if (dir == 0 || dir == 2) { /* vert */
1615 y = dimensions[1];
1616 h = dimensions[3];
1617 }
1618 g_free(dimensions);
1619 } else {
1620 /* pick some fallbacks... */
1621 if (dir == 0 || dir == 1) { /* horz */
1622 x = screen_area(self->desktop)->x +
1623 screen_area(self->desktop)->width / 4;
1624 w = screen_area(self->desktop)->width / 2;
1625 }
1626 if (dir == 0 || dir == 2) { /* vert */
1627 y = screen_area(self->desktop)->y +
1628 screen_area(self->desktop)->height / 4;
1629 h = screen_area(self->desktop)->height / 2;
1630 }
1631 }
1632 }
1633
1634 if (dir == 0 || dir == 1) /* horz */
1635 self->max_horz = max;
1636 if (dir == 0 || dir == 2) /* vert */
1637 self->max_vert = max;
1638
1639 if (!self->max_horz && !self->max_vert)
1640 PROP_ERASE(self->window, openbox_premax);
1641
1642 client_change_state(self); /* change the state hints on the client */
1643
1644 /* figure out where the client should be going */
1645 frame_frame_gravity(self->frame, &x, &y);
1646 client_configure(self, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
1647 }
1648
1649 void client_shade(Client *self, gboolean shade)
1650 {
1651 if (!(self->functions & Func_Shade) || /* can't */
1652 self->shaded == shade) return; /* already done */
1653
1654 /* when we're iconic, don't change the wmstate */
1655 if (!self->iconic)
1656 self->wmstate = shade ? IconicState : NormalState;
1657 self->shaded = shade;
1658 client_change_state(self);
1659 /* resize the frame to just the titlebar */
1660 engine_frame_adjust_area(self->frame, FALSE, FALSE);
1661 }
1662
1663 void client_close(Client *self)
1664 {
1665 XEvent ce;
1666
1667 if (!(self->functions & Func_Close)) return;
1668
1669 /*
1670 XXX: itd be cool to do timeouts and shit here for killing the client's
1671 process off
1672 like... if the window is around after 5 seconds, then the close button
1673 turns a nice red, and if this function is called again, the client is
1674 explicitly killed.
1675 */
1676
1677 ce.xclient.type = ClientMessage;
1678 ce.xclient.message_type = prop_atoms.wm_protocols;
1679 ce.xclient.display = ob_display;
1680 ce.xclient.window = self->window;
1681 ce.xclient.format = 32;
1682 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
1683 ce.xclient.data.l[1] = event_lasttime;
1684 ce.xclient.data.l[2] = 0l;
1685 ce.xclient.data.l[3] = 0l;
1686 ce.xclient.data.l[4] = 0l;
1687 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
1688 }
1689
1690 void client_kill(Client *self)
1691 {
1692 XKillClient(ob_display, self->window);
1693 }
1694
1695 void client_set_desktop(Client *self, guint target)
1696 {
1697 guint old, i;
1698
1699 if (target == self->desktop) return;
1700
1701 g_message("Setting desktop %u\n", target);
1702
1703 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
1704
1705 old = self->desktop;
1706 self->desktop = target;
1707 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
1708 /* the frame can display the current desktop state */
1709 engine_frame_adjust_state(self->frame);
1710 /* 'move' the window to the new desktop */
1711 client_showhide(self);
1712 screen_update_struts();
1713
1714 /* update the focus lists */
1715 if (old == DESKTOP_ALL) {
1716 for (i = 0; i < screen_num_desktops; ++i)
1717 focus_order[i] = g_list_remove(focus_order[i], self);
1718 focus_order[target] = g_list_prepend(focus_order[target], self);
1719 } else {
1720 focus_order[old] = g_list_remove(focus_order[old], self);
1721 if (target == DESKTOP_ALL)
1722 for (i = 0; i < screen_num_desktops; ++i)
1723 focus_order[i] = g_list_prepend(focus_order[i], self);
1724 }
1725
1726 dispatch_client(Event_Client_Desktop, self, target, old);
1727 }
1728
1729 static Client *search_modal_tree(Client *node, Client *skip)
1730 {
1731 GSList *it;
1732 Client *ret;
1733
1734 for (it = node->transients; it != NULL; it = it->next) {
1735 Client *c = it->data;
1736 if (c == skip) continue; /* circular? */
1737 if ((ret = search_modal_tree(c, skip))) return ret;
1738 if (c->modal) return c;
1739 }
1740 return NULL;
1741 }
1742
1743 Client *client_find_modal_child(Client *self)
1744 {
1745 return search_modal_tree(self, self);
1746 }
1747
1748 gboolean client_validate(Client *self)
1749 {
1750 XEvent e;
1751
1752 XSync(ob_display, FALSE); /* get all events on the server */
1753
1754 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
1755 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
1756 XPutBackEvent(ob_display, &e);
1757 return FALSE;
1758 }
1759
1760 return TRUE;
1761 }
1762
1763 void client_set_wm_state(Client *self, long state)
1764 {
1765 if (state == self->wmstate) return; /* no change */
1766
1767 switch (state) {
1768 case IconicState:
1769 client_iconify(self, TRUE, TRUE);
1770 break;
1771 case NormalState:
1772 client_iconify(self, FALSE, TRUE);
1773 break;
1774 }
1775 }
1776
1777 void client_set_state(Client *self, Atom action, long data1, long data2)
1778 {
1779 gboolean shaded = self->shaded;
1780 gboolean fullscreen = self->fullscreen;
1781 gboolean max_horz = self->max_horz;
1782 gboolean max_vert = self->max_vert;
1783 int i;
1784
1785 if (!(action == prop_atoms.net_wm_state_add ||
1786 action == prop_atoms.net_wm_state_remove ||
1787 action == prop_atoms.net_wm_state_toggle))
1788 /* an invalid action was passed to the client message, ignore it */
1789 return;
1790
1791 for (i = 0; i < 2; ++i) {
1792 Atom state = i == 0 ? data1 : data2;
1793
1794 if (!state) continue;
1795
1796 /* if toggling, then pick whether we're adding or removing */
1797 if (action == prop_atoms.net_wm_state_toggle) {
1798 if (state == prop_atoms.net_wm_state_modal)
1799 action = self->modal ? prop_atoms.net_wm_state_remove :
1800 prop_atoms.net_wm_state_add;
1801 else if (state == prop_atoms.net_wm_state_maximized_vert)
1802 action = self->max_vert ? prop_atoms.net_wm_state_remove :
1803 prop_atoms.net_wm_state_add;
1804 else if (state == prop_atoms.net_wm_state_maximized_horz)
1805 action = self->max_horz ? prop_atoms.net_wm_state_remove :
1806 prop_atoms.net_wm_state_add;
1807 else if (state == prop_atoms.net_wm_state_shaded)
1808 action = self->shaded ? prop_atoms.net_wm_state_remove :
1809 prop_atoms.net_wm_state_add;
1810 else if (state == prop_atoms.net_wm_state_skip_taskbar)
1811 action = self->skip_taskbar ?
1812 prop_atoms.net_wm_state_remove :
1813 prop_atoms.net_wm_state_add;
1814 else if (state == prop_atoms.net_wm_state_skip_pager)
1815 action = self->skip_pager ?
1816 prop_atoms.net_wm_state_remove :
1817 prop_atoms.net_wm_state_add;
1818 else if (state == prop_atoms.net_wm_state_fullscreen)
1819 action = self->fullscreen ?
1820 prop_atoms.net_wm_state_remove :
1821 prop_atoms.net_wm_state_add;
1822 else if (state == prop_atoms.net_wm_state_above)
1823 action = self->above ? prop_atoms.net_wm_state_remove :
1824 prop_atoms.net_wm_state_add;
1825 else if (state == prop_atoms.net_wm_state_below)
1826 action = self->below ? prop_atoms.net_wm_state_remove :
1827 prop_atoms.net_wm_state_add;
1828 }
1829
1830 if (action == prop_atoms.net_wm_state_add) {
1831 if (state == prop_atoms.net_wm_state_modal) {
1832 /* XXX raise here or something? */
1833 self->modal = TRUE;
1834 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
1835 max_vert = TRUE;
1836 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
1837 max_horz = TRUE;
1838 } else if (state == prop_atoms.net_wm_state_shaded) {
1839 shaded = TRUE;
1840 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
1841 self->skip_taskbar = TRUE;
1842 } else if (state == prop_atoms.net_wm_state_skip_pager) {
1843 self->skip_pager = TRUE;
1844 } else if (state == prop_atoms.net_wm_state_fullscreen) {
1845 fullscreen = TRUE;
1846 } else if (state == prop_atoms.net_wm_state_above) {
1847 self->above = TRUE;
1848 } else if (state == prop_atoms.net_wm_state_below) {
1849 self->below = TRUE;
1850 }
1851
1852 } else { /* action == prop_atoms.net_wm_state_remove */
1853 if (state == prop_atoms.net_wm_state_modal) {
1854 self->modal = FALSE;
1855 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
1856 max_vert = FALSE;
1857 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
1858 max_horz = FALSE;
1859 } else if (state == prop_atoms.net_wm_state_shaded) {
1860 shaded = FALSE;
1861 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
1862 self->skip_taskbar = FALSE;
1863 } else if (state == prop_atoms.net_wm_state_skip_pager) {
1864 self->skip_pager = FALSE;
1865 } else if (state == prop_atoms.net_wm_state_fullscreen) {
1866 fullscreen = FALSE;
1867 } else if (state == prop_atoms.net_wm_state_above) {
1868 self->above = FALSE;
1869 } else if (state == prop_atoms.net_wm_state_below) {
1870 self->below = FALSE;
1871 }
1872 }
1873 }
1874 if (max_horz != self->max_horz || max_vert != self->max_vert) {
1875 if (max_horz != self->max_horz && max_vert != self->max_vert) {
1876 /* toggling both */
1877 if (max_horz == max_vert) { /* both going the same way */
1878 client_maximize(self, max_horz, 0, TRUE);
1879 } else {
1880 client_maximize(self, max_horz, 1, TRUE);
1881 client_maximize(self, max_vert, 2, TRUE);
1882 }
1883 } else {
1884 /* toggling one */
1885 if (max_horz != self->max_horz)
1886 client_maximize(self, max_horz, 1, TRUE);
1887 else
1888 client_maximize(self, max_vert, 2, TRUE);
1889 }
1890 }
1891 /* change fullscreen state before shading, as it will affect if the window
1892 can shade or not */
1893 if (fullscreen != self->fullscreen)
1894 client_fullscreen(self, fullscreen, TRUE);
1895 if (shaded != self->shaded)
1896 client_shade(self, shaded);
1897 client_calc_layer(self);
1898 client_change_state(self); /* change the hint to relect these changes */
1899 }
1900
1901 gboolean client_focus(Client *self)
1902 {
1903 XEvent ev;
1904 Client *child;
1905
1906 /* if we have a modal child, then focus it, not us */
1907 child = client_find_modal_child(self);
1908 if (child)
1909 return client_focus(child);
1910
1911 /* won't try focus if the client doesn't want it, or if the window isn't
1912 visible on the screen */
1913 if (!(self->frame->visible &&
1914 (self->can_focus || self->focus_notify)))
1915 return FALSE;
1916
1917 /* do a check to see if the window has already been unmapped or destroyed
1918 do this intelligently while watching out for unmaps we've generated
1919 (ignore_unmaps > 0) */
1920 if (XCheckTypedWindowEvent(ob_display, self->window,
1921 DestroyNotify, &ev)) {
1922 XPutBackEvent(ob_display, &ev);
1923 return FALSE;
1924 }
1925 while (XCheckTypedWindowEvent(ob_display, self->window,
1926 UnmapNotify, &ev)) {
1927 if (self->ignore_unmaps) {
1928 self->ignore_unmaps--;
1929 } else {
1930 XPutBackEvent(ob_display, &ev);
1931 return FALSE;
1932 }
1933 }
1934
1935 if (self->can_focus)
1936 XSetInputFocus(ob_display, self->window, RevertToNone,
1937 CurrentTime);
1938
1939 if (self->focus_notify) {
1940 XEvent ce;
1941 ce.xclient.type = ClientMessage;
1942 ce.xclient.message_type = prop_atoms.wm_protocols;
1943 ce.xclient.display = ob_display;
1944 ce.xclient.window = self->window;
1945 ce.xclient.format = 32;
1946 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
1947 ce.xclient.data.l[1] = CurrentTime;
1948 ce.xclient.data.l[2] = 0l;
1949 ce.xclient.data.l[3] = 0l;
1950 ce.xclient.data.l[4] = 0l;
1951 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
1952 }
1953
1954 /* XSync(ob_display, FALSE); XXX Why sync? */
1955 return TRUE;
1956 }
1957
1958 void client_unfocus(Client *self)
1959 {
1960 g_assert(focus_client == self);
1961 client_set_focused(self, FALSE);
1962 }
1963
1964 gboolean client_focused(Client *self)
1965 {
1966 return self == focus_client;
1967 }
1968
1969 void client_set_focused(Client *self, gboolean focused)
1970 {
1971 if (focused) {
1972 if (focus_client != self)
1973 focus_set_client(self);
1974 } else {
1975 if (focus_client == self)
1976 focus_set_client(NULL);
1977 }
1978
1979 /* focus state can affect the stacking layer */
1980 client_calc_layer(self);
1981
1982 engine_frame_adjust_focus(self->frame);
1983 }
1984
1985 Icon *client_icon(Client *self, int w, int h)
1986 {
1987 int i;
1988 /* si is the smallest image >= req */
1989 /* li is the largest image < req */
1990 unsigned long size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
1991
1992 if (!self->nicons) return NULL;
1993
1994 for (i = 0; i < self->nicons; ++i) {
1995 size = self->icons[i].width * self->icons[i].height;
1996 if (size < smallest && size >= (unsigned)(w * h)) {
1997 smallest = size;
1998 si = i;
1999 }
2000 if (size > largest && size <= (unsigned)(w * h)) {
2001 largest = size;
2002 li = i;
2003 }
2004 }
2005 if (largest == 0) /* didnt find one smaller than the requested size */
2006 return &self->icons[si];
2007 return &self->icons[li];
2008 }
This page took 0.121524 seconds and 5 git commands to generate.