]> Dogcows Code - chaz/openbox/blob - openbox/client.c
don't skip windows that are skip_taskbar unless they are normal typed.. i.e. if a...
[chaz/openbox] / openbox / client.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 client.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "client.h"
21 #include "debug.h"
22 #include "startupnotify.h"
23 #include "dock.h"
24 #include "xerror.h"
25 #include "screen.h"
26 #include "moveresize.h"
27 #include "place.h"
28 #include "prop.h"
29 #include "extensions.h"
30 #include "frame.h"
31 #include "session.h"
32 #include "event.h"
33 #include "grab.h"
34 #include "focus.h"
35 #include "stacking.h"
36 #include "openbox.h"
37 #include "group.h"
38 #include "config.h"
39 #include "menuframe.h"
40 #include "keyboard.h"
41 #include "mouse.h"
42 #include "render/render.h"
43
44 #ifdef HAVE_UNISTD_H
45 # include <unistd.h>
46 #endif
47
48 #include <glib.h>
49 #include <X11/Xutil.h>
50
51 /*! The event mask to grab on client windows */
52 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
53 ColormapChangeMask)
54
55 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
56 ButtonMotionMask)
57
58 typedef struct
59 {
60 ObClientCallback func;
61 gpointer data;
62 } ClientCallback;
63
64 GList *client_list = NULL;
65
66 static GSList *client_destroy_notifies = NULL;
67
68 static void client_get_all(ObClient *self, gboolean real);
69 static void client_get_startup_id(ObClient *self);
70 static void client_get_session_ids(ObClient *self);
71 static void client_get_area(ObClient *self);
72 static void client_get_desktop(ObClient *self);
73 static void client_get_state(ObClient *self);
74 static void client_get_shaped(ObClient *self);
75 static void client_get_mwm_hints(ObClient *self);
76 static void client_get_colormap(ObClient *self);
77 static void client_change_allowed_actions(ObClient *self);
78 static void client_change_state(ObClient *self);
79 static void client_change_wm_state(ObClient *self);
80 static void client_apply_startup_state(ObClient *self,
81 gint x, gint y, gint w, gint h);
82 static void client_restore_session_state(ObClient *self);
83 static gboolean client_restore_session_stacking(ObClient *self);
84 static ObAppSettings *client_get_settings_state(ObClient *self);
85 static void client_update_transient_tree(ObClient *self,
86 ObGroup *oldgroup, ObGroup *newgroup,
87 gboolean oldgtran, gboolean newgtran,
88 ObClient* oldparent,
89 ObClient *newparent);
90 static void client_present(ObClient *self, gboolean here, gboolean raise,
91 gboolean unshade);
92 static GSList *client_search_all_top_parents_internal(ObClient *self,
93 gboolean bylayer,
94 ObStackingLayer layer);
95 static void client_call_notifies(ObClient *self, GSList *list);
96
97
98 void client_startup(gboolean reconfig)
99 {
100 if (reconfig) return;
101
102 client_set_list();
103 }
104
105 void client_shutdown(gboolean reconfig)
106 {
107 if (reconfig) return;
108 }
109
110 static void client_call_notifies(ObClient *self, GSList *list)
111 {
112 GSList *it;
113
114 for (it = list; it; it = g_slist_next(it)) {
115 ClientCallback *d = it->data;
116 d->func(self, d->data);
117 }
118 }
119
120 void client_add_destroy_notify(ObClientCallback func, gpointer data)
121 {
122 ClientCallback *d = g_new(ClientCallback, 1);
123 d->func = func;
124 d->data = data;
125 client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
126 }
127
128 void client_remove_destroy_notify(ObClientCallback func)
129 {
130 GSList *it;
131
132 for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
133 ClientCallback *d = it->data;
134 if (d->func == func) {
135 g_free(d);
136 client_destroy_notifies =
137 g_slist_delete_link(client_destroy_notifies, it);
138 break;
139 }
140 }
141 }
142
143 void client_set_list(void)
144 {
145 Window *windows, *win_it;
146 GList *it;
147 guint size = g_list_length(client_list);
148
149 /* create an array of the window ids */
150 if (size > 0) {
151 windows = g_new(Window, size);
152 win_it = windows;
153 for (it = client_list; it; it = g_list_next(it), ++win_it)
154 *win_it = ((ObClient*)it->data)->window;
155 } else
156 windows = NULL;
157
158 PROP_SETA32(RootWindow(ob_display, ob_screen),
159 net_client_list, window, (gulong*)windows, size);
160
161 if (windows)
162 g_free(windows);
163
164 stacking_set_list();
165 }
166
167 void client_manage_all(void)
168 {
169 guint i, j, nchild;
170 Window w, *children;
171 XWMHints *wmhints;
172 XWindowAttributes attrib;
173
174 XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
175 &w, &w, &children, &nchild);
176
177 /* remove all icon windows from the list */
178 for (i = 0; i < nchild; i++) {
179 if (children[i] == None) continue;
180 wmhints = XGetWMHints(ob_display, children[i]);
181 if (wmhints) {
182 if ((wmhints->flags & IconWindowHint) &&
183 (wmhints->icon_window != children[i]))
184 for (j = 0; j < nchild; j++)
185 if (children[j] == wmhints->icon_window) {
186 children[j] = None;
187 break;
188 }
189 XFree(wmhints);
190 }
191 }
192
193 for (i = 0; i < nchild; ++i) {
194 if (children[i] == None)
195 continue;
196 if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
197 if (attrib.override_redirect) continue;
198
199 if (attrib.map_state != IsUnmapped)
200 client_manage(children[i]);
201 }
202 }
203 XFree(children);
204 }
205
206 void client_manage(Window window)
207 {
208 ObClient *self;
209 XEvent e;
210 XWindowAttributes attrib;
211 XSetWindowAttributes attrib_set;
212 XWMHints *wmhint;
213 gboolean activate = FALSE;
214 ObAppSettings *settings;
215 gboolean transient = FALSE;
216 Rect place, *monitor;
217 Time launch_time, map_time;
218
219 grab_server(TRUE);
220
221 /* check if it has already been unmapped by the time we started
222 mapping. the grab does a sync so we don't have to here */
223 if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
224 XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e))
225 {
226 XPutBackEvent(ob_display, &e);
227
228 ob_debug("Trying to manage unmapped window. Aborting that.\n");
229 grab_server(FALSE);
230 return; /* don't manage it */
231 }
232
233 /* make sure it isn't an override-redirect window */
234 if (!XGetWindowAttributes(ob_display, window, &attrib) ||
235 attrib.override_redirect)
236 {
237 grab_server(FALSE);
238 return; /* don't manage it */
239 }
240
241 /* is the window a docking app */
242 if ((wmhint = XGetWMHints(ob_display, window))) {
243 if ((wmhint->flags & StateHint) &&
244 wmhint->initial_state == WithdrawnState)
245 {
246 dock_add(window, wmhint);
247 grab_server(FALSE);
248 XFree(wmhint);
249 return;
250 }
251 XFree(wmhint);
252 }
253
254 ob_debug("Managing window: 0x%lx\n", window);
255
256 map_time = event_get_server_time();
257
258 /* choose the events we want to receive on the CLIENT window */
259 attrib_set.event_mask = CLIENT_EVENTMASK;
260 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
261 XChangeWindowAttributes(ob_display, window,
262 CWEventMask|CWDontPropagate, &attrib_set);
263
264 /* create the ObClient struct, and populate it from the hints on the
265 window */
266 self = g_new0(ObClient, 1);
267 self->obwin.type = Window_Client;
268 self->window = window;
269
270 /* non-zero defaults */
271 self->wmstate = WithdrawnState; /* make sure it gets updated first time */
272 self->gravity = NorthWestGravity;
273 self->desktop = screen_num_desktops; /* always an invalid value */
274
275 /* get all the stuff off the window */
276 client_get_all(self, TRUE);
277
278 ob_debug("Window type: %d\n", self->type);
279 ob_debug("Window group: 0x%x\n", self->group?self->group->leader:0);
280
281 /* specify that if we exit, the window should not be destroyed and
282 should be reparented back to root automatically */
283 XChangeSaveSet(ob_display, window, SetModeInsert);
284
285 /* create the decoration frame for the client window */
286 self->frame = frame_new(self);
287
288 frame_grab_client(self->frame);
289
290 /* we've grabbed everything and set everything that we need to at mapping
291 time now */
292 grab_server(FALSE);
293
294 /* per-app settings override stuff from client_get_all, and return the
295 settings for other uses too. the returned settings is a shallow copy,
296 that needs to be freed with g_free(). */
297 settings = client_get_settings_state(self);
298 /* the session should get the last say though */
299 client_restore_session_state(self);
300
301 /* now we have all of the window's information so we can set this up */
302 client_setup_decor_and_functions(self, FALSE);
303
304 /* tell startup notification that this app started */
305 launch_time = sn_app_started(self->startup_id, self->class);
306
307 /* do this after we have a frame.. it uses the frame to help determine the
308 WM_STATE to apply. */
309 client_change_state(self);
310
311 /* add ourselves to the focus order */
312 focus_order_add_new(self);
313
314 /* do this to add ourselves to the stacking list in a non-intrusive way */
315 client_calc_layer(self);
316
317 /* focus the new window? */
318 if (ob_state() != OB_STATE_STARTING &&
319 (!self->session || self->session->focused) &&
320 /* this means focus=true for window is same as config_focus_new=true */
321 ((config_focus_new || (settings && settings->focus == 1)) ||
322 client_search_focus_tree_full(self)) &&
323 /* this checks for focus=false for the window */
324 (!settings || settings->focus != 0) &&
325 focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE))
326 {
327 activate = TRUE;
328 }
329
330 /* remove the client's border */
331 XSetWindowBorderWidth(ob_display, self->window, 0);
332
333 /* adjust the frame to the client's size before showing or placing
334 the window */
335 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
336 frame_adjust_client_area(self->frame);
337
338 /* where the frame was placed is where the window was originally */
339 place = self->area;
340 monitor = screen_physical_area_monitor(screen_find_monitor(&place));
341
342 /* figure out placement for the window if the window is new */
343 if (ob_state() == OB_STATE_RUNNING) {
344 ob_debug("Positioned: %s @ %d %d\n",
345 (!self->positioned ? "no" :
346 (self->positioned == PPosition ? "program specified" :
347 (self->positioned == USPosition ? "user specified" :
348 (self->positioned == (PPosition | USPosition) ?
349 "program + user specified" :
350 "BADNESS !?")))), place.x, place.y);
351
352 ob_debug("Sized: %s @ %d %d\n",
353 (!self->sized ? "no" :
354 (self->sized == PSize ? "program specified" :
355 (self->sized == USSize ? "user specified" :
356 (self->sized == (PSize | USSize) ?
357 "program + user specified" :
358 "BADNESS !?")))), place.width, place.height);
359
360 /* splash screens are also returned as TRUE for transient,
361 and so will be forced on screen below */
362 transient = place_client(self, &place.x, &place.y, settings);
363
364 /* make sure the window is visible. */
365 client_find_onscreen(self, &place.x, &place.y,
366 place.width, place.height,
367 /* non-normal clients has less rules, and
368 windows that are being restored from a
369 session do also. we can assume you want
370 it back where you saved it. Clients saying
371 they placed themselves are subjected to
372 harder rules, ones that are placed by
373 place.c or by the user are allowed partially
374 off-screen and on xinerama divides (ie,
375 it is up to the placement routines to avoid
376 the xinerama divides)
377
378 splash screens get "transient" set to TRUE by
379 the place_client call
380 */
381 ob_state() == OB_STATE_RUNNING &&
382 (transient ||
383 (!((self->positioned & USPosition) ||
384 (settings && settings->pos_given)) &&
385 client_normal(self) &&
386 !self->session &&
387 /* don't move oldschool fullscreen windows to
388 fit inside the struts (fixes Acroread, which
389 makes its fullscreen window fit the screen
390 but it is not USSize'd or USPosition'd) */
391 !(self->decorations == 0 &&
392 RECT_EQUAL(place, *monitor)))));
393 }
394
395 /* if the window isn't user-sized, then make it fit inside
396 the visible screen area on its monitor. Use basically the same rules
397 for forcing the window on screen in the client_find_onscreen call.
398
399 do this after place_client, it chooses the monitor!
400
401 splash screens get "transient" set to TRUE by
402 the place_client call
403 */
404 if (ob_state() == OB_STATE_RUNNING &&
405 (transient ||
406 (!(self->sized & USSize || self->positioned & USPosition) &&
407 client_normal(self) &&
408 !self->session &&
409 /* don't shrink oldschool fullscreen windows to fit inside the
410 struts (fixes Acroread, which makes its fullscreen window
411 fit the screen but it is not USSize'd or USPosition'd) */
412 !(self->decorations == 0 && RECT_EQUAL(place, *monitor)))))
413 {
414 Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
415
416 /* get the size of the frame */
417 place.width += self->frame->size.left + self->frame->size.right;
418 place.height += self->frame->size.top + self->frame->size.bottom;
419
420 /* fit the window inside the area */
421 place.width = MIN(place.width, a->width);
422 place.height = MIN(place.height, a->height);
423
424 ob_debug("setting window size to %dx%d\n", place.width, place.height);
425
426 /* get the size of the client back */
427 place.width -= self->frame->size.left + self->frame->size.right;
428 place.height -= self->frame->size.top + self->frame->size.bottom;
429
430 g_free(a);
431 }
432
433 ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
434 "some restrictions may apply\n",
435 self->window, place.x, place.y, place.width, place.height);
436 if (self->session)
437 ob_debug(" but session requested %d, %d %d x %d instead, "
438 "overriding\n",
439 self->session->x, self->session->y,
440 self->session->w, self->session->h);
441
442 /* do this after the window is placed, so the premax/prefullscreen numbers
443 won't be all wacko!!
444
445 this also places the window
446 */
447 client_apply_startup_state(self, place.x, place.y,
448 place.width, place.height);
449
450 g_free(monitor);
451 monitor = NULL;
452
453 ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s\n",
454 activate ? "yes" : "no");
455 if (activate) {
456 gboolean raise = FALSE;
457
458 /* This is focus stealing prevention */
459 ob_debug_type(OB_DEBUG_FOCUS,
460 "Want to focus new window 0x%x at time %u "
461 "launched at %u (last user interaction time %u)\n",
462 self->window, map_time, launch_time,
463 event_last_user_time);
464
465 if (menu_frame_visible || moveresize_in_progress) {
466 activate = FALSE;
467 raise = TRUE;
468 ob_debug_type(OB_DEBUG_FOCUS,
469 "Not focusing the window because the user is inside "
470 "an Openbox menu or is move/resizing a window and "
471 "we don't want to interrupt them\n");
472 }
473
474 /* if it's on another desktop */
475 else if (!(self->desktop == screen_desktop ||
476 self->desktop == DESKTOP_ALL) &&
477 /* the timestamp is from before you changed desktops */
478 launch_time && screen_desktop_user_time &&
479 !event_time_after(launch_time, screen_desktop_user_time))
480 {
481 activate = FALSE;
482 raise = TRUE;
483 ob_debug_type(OB_DEBUG_FOCUS,
484 "Not focusing the window because its on another "
485 "desktop\n");
486 }
487 /* If something is focused, and it's not our relative... */
488 else if (focus_client && client_search_focus_tree_full(self) == NULL &&
489 client_search_focus_group_full(self) == NULL)
490 {
491 /* If the user is working in another window right now, then don't
492 steal focus */
493 if (event_last_user_time && launch_time &&
494 event_time_after(event_last_user_time, launch_time) &&
495 event_last_user_time != launch_time &&
496 event_time_after(event_last_user_time,
497 map_time - OB_EVENT_USER_TIME_DELAY))
498 {
499 activate = FALSE;
500 ob_debug_type(OB_DEBUG_FOCUS,
501 "Not focusing the window because the user is "
502 "working in another window\n");
503 }
504 /* If its a transient (and its parents aren't focused) */
505 else if (client_has_parent(self)) {
506 activate = FALSE;
507 ob_debug_type(OB_DEBUG_FOCUS,
508 "Not focusing the window because it is a "
509 "transient, and its relatives aren't focused\n");
510 }
511 /* Don't steal focus from globally active clients.
512 I stole this idea from KWin. It seems nice.
513 */
514 else if (!(focus_client->can_focus ||
515 focus_client->focus_notify))
516 {
517 activate = FALSE;
518 ob_debug_type(OB_DEBUG_FOCUS,
519 "Not focusing the window because a globally "
520 "active client has focus\n");
521 }
522 /* Don't move focus if it's not going to go to this window
523 anyway */
524 else if (client_focus_target(self) != self) {
525 activate = FALSE;
526 raise = TRUE;
527 ob_debug_type(OB_DEBUG_FOCUS,
528 "Not focusing the window because another window "
529 "would get the focus anyway\n");
530 }
531 else if (!(self->desktop == screen_desktop ||
532 self->desktop == DESKTOP_ALL))
533 {
534 activate = FALSE;
535 raise = TRUE;
536 ob_debug_type(OB_DEBUG_FOCUS,
537 "Not focusing the window because it is on "
538 "another desktop and no relatives are focused ");
539 }
540 }
541
542 if (!activate) {
543 ob_debug_type(OB_DEBUG_FOCUS,
544 "Focus stealing prevention activated for %s at "
545 "time %u (last user interactioon time %u)\n",
546 self->title, map_time, event_last_user_time);
547 /* if the client isn't focused, then hilite it so the user
548 knows it is there */
549 client_hilite(self, TRUE);
550 /* we may want to raise it even tho we're not activating it */
551 if (raise && !client_restore_session_stacking(self))
552 stacking_raise(CLIENT_AS_WINDOW(self));
553 }
554 }
555 else {
556 /* This may look rather odd. Well it's because new windows are added
557 to the stacking order non-intrusively. If we're not going to focus
558 the new window or hilite it, then we raise it to the top. This will
559 take affect for things that don't get focused like splash screens.
560 Also if you don't have focus_new enabled, then it's going to get
561 raised to the top. Legacy begets legacy I guess?
562 */
563 if (!client_restore_session_stacking(self))
564 stacking_raise(CLIENT_AS_WINDOW(self));
565 }
566
567 mouse_grab_for_client(self, TRUE);
568
569 /* this has to happen before we try focus the window, but we want it to
570 happen after the client's stacking has been determined or it looks bad
571 */
572 {
573 gulong ignore_start;
574 if (!config_focus_under_mouse)
575 ignore_start = event_start_ignore_all_enters();
576
577 client_show(self);
578
579 if (!config_focus_under_mouse)
580 event_end_ignore_all_enters(ignore_start);
581 }
582
583 if (activate) {
584 gboolean stacked = client_restore_session_stacking(self);
585 client_present(self, FALSE, !stacked, TRUE);
586 }
587
588 /* add to client list/map */
589 client_list = g_list_append(client_list, self);
590 g_hash_table_insert(window_map, &self->window, self);
591
592 /* this has to happen after we're in the client_list */
593 if (STRUT_EXISTS(self->strut))
594 screen_update_areas();
595
596 /* update the list hints */
597 client_set_list();
598
599 /* free the ObAppSettings shallow copy */
600 g_free(settings);
601
602 ob_debug("Managed window 0x%lx plate 0x%x (%s)\n",
603 window, self->frame->window, self->class);
604
605 return;
606 }
607
608
609 ObClient *client_fake_manage(Window window)
610 {
611 ObClient *self;
612 ObAppSettings *settings;
613
614 ob_debug("Pretend-managing window: %lx\n", window);
615
616 /* do this minimal stuff to figure out the client's decorations */
617
618 self = g_new0(ObClient, 1);
619 self->window = window;
620
621 client_get_all(self, FALSE);
622 /* per-app settings override stuff, and return the settings for other
623 uses too. this returns a shallow copy that needs to be freed */
624 settings = client_get_settings_state(self);
625
626 client_setup_decor_and_functions(self, FALSE);
627
628 /* create the decoration frame for the client window and adjust its size */
629 self->frame = frame_new(self);
630 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
631
632 ob_debug("gave extents left %d right %d top %d bottom %d\n",
633 self->frame->size.left, self->frame->size.right,
634 self->frame->size.top, self->frame->size.bottom);
635
636 /* free the ObAppSettings shallow copy */
637 g_free(settings);
638
639 return self;
640 }
641
642 void client_unmanage_all(void)
643 {
644 while (client_list != NULL)
645 client_unmanage(client_list->data);
646 }
647
648 void client_unmanage(ObClient *self)
649 {
650 guint j;
651 GSList *it;
652 gulong ignore_start;
653
654 ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n",
655 self->window, self->frame->window,
656 self->class, self->title ? self->title : "");
657
658 g_assert(self != NULL);
659
660 /* we dont want events no more. do this before hiding the frame so we
661 don't generate more events */
662 XSelectInput(ob_display, self->window, NoEventMask);
663
664 /* ignore enter events from the unmap so it doesnt mess with the focus */
665 if (!config_focus_under_mouse)
666 ignore_start = event_start_ignore_all_enters();
667
668 frame_hide(self->frame);
669 /* flush to send the hide to the server quickly */
670 XFlush(ob_display);
671
672 if (!config_focus_under_mouse)
673 event_end_ignore_all_enters(ignore_start);
674
675 mouse_grab_for_client(self, FALSE);
676
677 /* remove the window from our save set */
678 XChangeSaveSet(ob_display, self->window, SetModeDelete);
679
680 /* update the focus lists */
681 focus_order_remove(self);
682 if (client_focused(self)) {
683 /* don't leave an invalid focus_client */
684 focus_client = NULL;
685 }
686
687 client_list = g_list_remove(client_list, self);
688 stacking_remove(self);
689 g_hash_table_remove(window_map, &self->window);
690
691 /* once the client is out of the list, update the struts to remove its
692 influence */
693 if (STRUT_EXISTS(self->strut))
694 screen_update_areas();
695
696 client_call_notifies(self, client_destroy_notifies);
697
698 /* tell our parent(s) that we're gone */
699 for (it = self->parents; it; it = g_slist_next(it))
700 ((ObClient*)it->data)->transients =
701 g_slist_remove(((ObClient*)it->data)->transients,self);
702
703 /* tell our transients that we're gone */
704 for (it = self->transients; it; it = g_slist_next(it)) {
705 ((ObClient*)it->data)->parents =
706 g_slist_remove(((ObClient*)it->data)->parents, self);
707 /* we could be keeping our children in a higher layer */
708 client_calc_layer(it->data);
709 }
710
711 /* remove from its group */
712 if (self->group) {
713 group_remove(self->group, self);
714 self->group = NULL;
715 }
716
717 /* restore the window's original geometry so it is not lost */
718 {
719 Rect a;
720
721 a = self->area;
722
723 if (self->fullscreen)
724 a = self->pre_fullscreen_area;
725 else if (self->max_horz || self->max_vert) {
726 if (self->max_horz) {
727 a.x = self->pre_max_area.x;
728 a.width = self->pre_max_area.width;
729 }
730 if (self->max_vert) {
731 a.y = self->pre_max_area.y;
732 a.height = self->pre_max_area.height;
733 }
734 }
735
736 self->fullscreen = self->max_horz = self->max_vert = FALSE;
737 /* let it be moved and resized no matter what */
738 self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
739 self->decorations = 0; /* unmanaged windows have no decor */
740
741 /* give the client its border back */
742 XSetWindowBorderWidth(ob_display, self->window, self->border_width);
743
744 client_move_resize(self, a.x, a.y, a.width, a.height);
745 }
746
747 /* reparent the window out of the frame, and free the frame */
748 frame_release_client(self->frame);
749 frame_free(self->frame);
750 self->frame = NULL;
751
752 if (ob_state() != OB_STATE_EXITING) {
753 /* these values should not be persisted across a window
754 unmapping/mapping */
755 PROP_ERASE(self->window, net_wm_desktop);
756 PROP_ERASE(self->window, net_wm_state);
757 PROP_ERASE(self->window, wm_state);
758 } else {
759 /* if we're left in an unmapped state, the client wont be mapped.
760 this is bad, since we will no longer be managing the window on
761 restart */
762 XMapWindow(ob_display, self->window);
763 }
764
765 /* update the list hints */
766 client_set_list();
767
768 ob_debug("Unmanaged window 0x%lx\n", self->window);
769
770 /* free all data allocated in the client struct */
771 g_slist_free(self->transients);
772 for (j = 0; j < self->nicons; ++j)
773 g_free(self->icons[j].data);
774 if (self->nicons > 0)
775 g_free(self->icons);
776 g_free(self->wm_command);
777 g_free(self->title);
778 g_free(self->icon_title);
779 g_free(self->name);
780 g_free(self->class);
781 g_free(self->role);
782 g_free(self->client_machine);
783 g_free(self->sm_client_id);
784 g_free(self);
785 }
786
787 void client_fake_unmanage(ObClient *self)
788 {
789 /* this is all that got allocated to get the decorations */
790
791 frame_free(self->frame);
792 g_free(self);
793 }
794
795 /*! Returns a new structure containing the per-app settings for this client.
796 The returned structure needs to be freed with g_free. */
797 static ObAppSettings *client_get_settings_state(ObClient *self)
798 {
799 ObAppSettings *settings;
800 GSList *it;
801
802 settings = config_create_app_settings();
803
804 for (it = config_per_app_settings; it; it = g_slist_next(it)) {
805 ObAppSettings *app = it->data;
806 gboolean match = TRUE;
807
808 g_assert(app->name != NULL || app->class != NULL);
809
810 /* we know that either name or class is not NULL so it will have to
811 match to use the rule */
812 if (app->name &&
813 !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
814 match = FALSE;
815 else if (app->class &&
816 !g_pattern_match(app->class,
817 strlen(self->class), self->class, NULL))
818 match = FALSE;
819 else if (app->role &&
820 !g_pattern_match(app->role,
821 strlen(self->role), self->role, NULL))
822 match = FALSE;
823
824 if (match) {
825 ob_debug("Window matching: %s\n", app->name);
826
827 /* copy the settings to our struct, overriding the existing
828 settings if they are not defaults */
829 config_app_settings_copy_non_defaults(app, settings);
830 }
831 }
832
833 if (settings->shade != -1)
834 self->shaded = !!settings->shade;
835 if (settings->decor != -1)
836 self->undecorated = !settings->decor;
837 if (settings->iconic != -1)
838 self->iconic = !!settings->iconic;
839 if (settings->skip_pager != -1)
840 self->skip_pager = !!settings->skip_pager;
841 if (settings->skip_taskbar != -1)
842 self->skip_taskbar = !!settings->skip_taskbar;
843
844 if (settings->max_vert != -1)
845 self->max_vert = !!settings->max_vert;
846 if (settings->max_horz != -1)
847 self->max_horz = !!settings->max_horz;
848
849 if (settings->fullscreen != -1)
850 self->fullscreen = !!settings->fullscreen;
851
852 if (settings->desktop) {
853 if (settings->desktop == DESKTOP_ALL)
854 self->desktop = settings->desktop;
855 else if (settings->desktop > 0 &&
856 settings->desktop <= screen_num_desktops)
857 self->desktop = settings->desktop - 1;
858 }
859
860 if (settings->layer == -1) {
861 self->below = TRUE;
862 self->above = FALSE;
863 }
864 else if (settings->layer == 0) {
865 self->below = FALSE;
866 self->above = FALSE;
867 }
868 else if (settings->layer == 1) {
869 self->below = FALSE;
870 self->above = TRUE;
871 }
872 return settings;
873 }
874
875 static void client_restore_session_state(ObClient *self)
876 {
877 GList *it;
878
879 ob_debug_type(OB_DEBUG_SM,
880 "Restore session for client %s\n", self->title);
881
882 if (!(it = session_state_find(self))) {
883 ob_debug_type(OB_DEBUG_SM,
884 "Session data not found for client %s\n", self->title);
885 return;
886 }
887
888 self->session = it->data;
889
890 ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s\n",
891 self->title);
892
893 RECT_SET_POINT(self->area, self->session->x, self->session->y);
894 self->positioned = USPosition;
895 self->sized = USSize;
896 if (self->session->w > 0)
897 self->area.width = self->session->w;
898 if (self->session->h > 0)
899 self->area.height = self->session->h;
900 XResizeWindow(ob_display, self->window,
901 self->area.width, self->area.height);
902
903 self->desktop = (self->session->desktop == DESKTOP_ALL ?
904 self->session->desktop :
905 MIN(screen_num_desktops - 1, self->session->desktop));
906 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
907
908 self->shaded = self->session->shaded;
909 self->iconic = self->session->iconic;
910 self->skip_pager = self->session->skip_pager;
911 self->skip_taskbar = self->session->skip_taskbar;
912 self->fullscreen = self->session->fullscreen;
913 self->above = self->session->above;
914 self->below = self->session->below;
915 self->max_horz = self->session->max_horz;
916 self->max_vert = self->session->max_vert;
917 self->undecorated = self->session->undecorated;
918 }
919
920 static gboolean client_restore_session_stacking(ObClient *self)
921 {
922 GList *it, *mypos;
923
924 if (!self->session) return FALSE;
925
926 mypos = g_list_find(session_saved_state, self->session);
927 if (!mypos) return FALSE;
928
929 /* start above me and look for the first client */
930 for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
931 GList *cit;
932
933 for (cit = client_list; cit; cit = g_list_next(cit)) {
934 ObClient *c = cit->data;
935 /* found a client that was in the session, so go below it */
936 if (c->session == it->data) {
937 stacking_below(CLIENT_AS_WINDOW(self),
938 CLIENT_AS_WINDOW(cit->data));
939 return TRUE;
940 }
941 }
942 }
943 return FALSE;
944 }
945
946 void client_move_onscreen(ObClient *self, gboolean rude)
947 {
948 gint x = self->area.x;
949 gint y = self->area.y;
950 if (client_find_onscreen(self, &x, &y,
951 self->area.width,
952 self->area.height, rude)) {
953 client_move(self, x, y);
954 }
955 }
956
957 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
958 gboolean rude)
959 {
960 gint ox = *x, oy = *y;
961 gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
962 gint fw, fh;
963 Rect desired;
964 guint i;
965
966 RECT_SET(desired, *x, *y, w, h);
967 frame_rect_to_frame(self->frame, &desired);
968
969 /* get where the frame would be */
970 frame_client_gravity(self->frame, x, y);
971
972 /* get the requested size of the window with decorations */
973 fw = self->frame->size.left + w + self->frame->size.right;
974 fh = self->frame->size.top + h + self->frame->size.bottom;
975
976 /* If rudeness wasn't requested, then still be rude in a given direction
977 if the client is not moving, only resizing in that direction */
978 if (!rude) {
979 Point oldtl, oldtr, oldbl, oldbr;
980 Point newtl, newtr, newbl, newbr;
981 gboolean stationary_l, stationary_r, stationary_t, stationary_b;
982
983 POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
984 POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
985 self->frame->area.y + self->frame->area.height - 1);
986 POINT_SET(oldtr, oldbr.x, oldtl.y);
987 POINT_SET(oldbl, oldtl.x, oldbr.y);
988
989 POINT_SET(newtl, *x, *y);
990 POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
991 POINT_SET(newtr, newbr.x, newtl.y);
992 POINT_SET(newbl, newtl.x, newbr.y);
993
994 /* is it moving or just resizing from some corner? */
995 stationary_l = oldtl.x == newtl.x;
996 stationary_r = oldtr.x == newtr.x;
997 stationary_t = oldtl.y == newtl.y;
998 stationary_b = oldbl.y == newbl.y;
999
1000 /* if left edge is growing and didnt move right edge */
1001 if (stationary_r && newtl.x < oldtl.x)
1002 rudel = TRUE;
1003 /* if right edge is growing and didnt move left edge */
1004 if (stationary_l && newtr.x > oldtr.x)
1005 ruder = TRUE;
1006 /* if top edge is growing and didnt move bottom edge */
1007 if (stationary_b && newtl.y < oldtl.y)
1008 rudet = TRUE;
1009 /* if bottom edge is growing and didnt move top edge */
1010 if (stationary_t && newbl.y > oldbl.y)
1011 rudeb = TRUE;
1012 }
1013
1014 for (i = 0; i < screen_num_monitors; ++i) {
1015 Rect *a;
1016
1017 if (!screen_physical_area_monitor_contains(i, &desired)) {
1018 if (i < screen_num_monitors - 1)
1019 continue;
1020
1021 /* the window is not inside any monitor! so just use the first
1022 one */
1023 a = screen_area(self->desktop, 0, NULL);
1024 } else
1025 a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
1026
1027 /* This makes sure windows aren't entirely outside of the screen so you
1028 can't see them at all.
1029 It makes sure 10% of the window is on the screen at least. At don't
1030 let it move itself off the top of the screen, which would hide the
1031 titlebar on you. (The user can still do this if they want too, it's
1032 only limiting the application.
1033 */
1034 if (client_normal(self)) {
1035 if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
1036 *x = a->x + a->width - fw/10;
1037 if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
1038 *y = a->y + a->height - fh/10;
1039 if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
1040 *x = a->x - fw*9/10;
1041 if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
1042 *y = a->y - fh*9/10;
1043 }
1044
1045 /* This here doesn't let windows even a pixel outside the
1046 struts/screen. When called from client_manage, programs placing
1047 themselves are forced completely onscreen, while things like
1048 xterm -geometry resolution-width/2 will work fine. Trying to
1049 place it completely offscreen will be handled in the above code.
1050 Sorry for this confused comment, i am tired. */
1051 if (rudel && !self->strut.left && *x < a->x) *x = a->x;
1052 if (ruder && !self->strut.right && *x + fw > a->x + a->width)
1053 *x = a->x + MAX(0, a->width - fw);
1054
1055 if (rudet && !self->strut.top && *y < a->y) *y = a->y;
1056 if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
1057 *y = a->y + MAX(0, a->height - fh);
1058
1059 g_free(a);
1060 }
1061
1062 /* get where the client should be */
1063 frame_frame_gravity(self->frame, x, y);
1064
1065 return ox != *x || oy != *y;
1066 }
1067
1068 static void client_get_all(ObClient *self, gboolean real)
1069 {
1070 /* this is needed for the frame to set itself up */
1071 client_get_area(self);
1072
1073 /* these things can change the decor and functions of the window */
1074
1075 client_get_mwm_hints(self);
1076 /* this can change the mwmhints for special cases */
1077 client_get_type_and_transientness(self);
1078 client_get_state(self);
1079 client_update_normal_hints(self);
1080
1081 /* get the session related properties, these can change decorations
1082 from per-app settings */
1083 client_get_session_ids(self);
1084
1085 /* now we got everything that can affect the decorations */
1086 if (!real)
1087 return;
1088
1089 /* get this early so we have it for debugging */
1090 client_update_title(self);
1091
1092 client_update_protocols(self);
1093
1094 client_update_wmhints(self);
1095 /* this may have already been called from client_update_wmhints */
1096 if (!self->parents && !self->transient_for_group)
1097 client_update_transient_for(self);
1098
1099 client_get_startup_id(self);
1100 client_get_desktop(self);/* uses transient data/group/startup id if a
1101 desktop is not specified */
1102 client_get_shaped(self);
1103
1104 {
1105 /* a couple type-based defaults for new windows */
1106
1107 /* this makes sure that these windows appear on all desktops */
1108 if (self->type == OB_CLIENT_TYPE_DESKTOP)
1109 self->desktop = DESKTOP_ALL;
1110 }
1111
1112 #ifdef SYNC
1113 client_update_sync_request_counter(self);
1114 #endif
1115
1116 client_get_colormap(self);
1117 client_update_strut(self);
1118 client_update_icons(self);
1119 client_update_icon_geometry(self);
1120 }
1121
1122 static void client_get_startup_id(ObClient *self)
1123 {
1124 if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
1125 if (self->group)
1126 PROP_GETS(self->group->leader,
1127 net_startup_id, utf8, &self->startup_id);
1128 }
1129
1130 static void client_get_area(ObClient *self)
1131 {
1132 XWindowAttributes wattrib;
1133 Status ret;
1134
1135 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
1136 g_assert(ret != BadWindow);
1137
1138 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1139 POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1140 self->border_width = wattrib.border_width;
1141
1142 ob_debug("client area: %d %d %d %d bw %d\n", wattrib.x, wattrib.y,
1143 wattrib.width, wattrib.height, wattrib.border_width);
1144 }
1145
1146 static void client_get_desktop(ObClient *self)
1147 {
1148 guint32 d = screen_num_desktops; /* an always-invalid value */
1149
1150 if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
1151 if (d >= screen_num_desktops && d != DESKTOP_ALL)
1152 self->desktop = screen_num_desktops - 1;
1153 else
1154 self->desktop = d;
1155 ob_debug("client requested desktop 0x%x\n", self->desktop);
1156 } else {
1157 GSList *it;
1158 gboolean first = TRUE;
1159 guint all = screen_num_desktops; /* not a valid value */
1160
1161 /* if they are all on one desktop, then open it on the
1162 same desktop */
1163 for (it = self->parents; it; it = g_slist_next(it)) {
1164 ObClient *c = it->data;
1165
1166 if (c->desktop == DESKTOP_ALL) continue;
1167
1168 if (first) {
1169 all = c->desktop;
1170 first = FALSE;
1171 }
1172 else if (all != c->desktop)
1173 all = screen_num_desktops; /* make it invalid */
1174 }
1175 if (all != screen_num_desktops) {
1176 self->desktop = all;
1177
1178 ob_debug("client desktop set from parents: 0x%x\n",
1179 self->desktop);
1180 }
1181 /* try get from the startup-notification protocol */
1182 else if (sn_get_desktop(self->startup_id, &self->desktop)) {
1183 if (self->desktop >= screen_num_desktops &&
1184 self->desktop != DESKTOP_ALL)
1185 self->desktop = screen_num_desktops - 1;
1186 ob_debug("client desktop set from startup-notification: 0x%x\n",
1187 self->desktop);
1188 }
1189 /* defaults to the current desktop */
1190 else {
1191 self->desktop = screen_desktop;
1192 ob_debug("client desktop set to the current desktop: %d\n",
1193 self->desktop);
1194 }
1195 }
1196 }
1197
1198 static void client_get_state(ObClient *self)
1199 {
1200 guint32 *state;
1201 guint num;
1202
1203 if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
1204 gulong i;
1205 for (i = 0; i < num; ++i) {
1206 if (state[i] == prop_atoms.net_wm_state_modal)
1207 self->modal = TRUE;
1208 else if (state[i] == prop_atoms.net_wm_state_shaded)
1209 self->shaded = TRUE;
1210 else if (state[i] == prop_atoms.net_wm_state_hidden)
1211 self->iconic = TRUE;
1212 else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
1213 self->skip_taskbar = TRUE;
1214 else if (state[i] == prop_atoms.net_wm_state_skip_pager)
1215 self->skip_pager = TRUE;
1216 else if (state[i] == prop_atoms.net_wm_state_fullscreen)
1217 self->fullscreen = TRUE;
1218 else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
1219 self->max_vert = TRUE;
1220 else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
1221 self->max_horz = TRUE;
1222 else if (state[i] == prop_atoms.net_wm_state_above)
1223 self->above = TRUE;
1224 else if (state[i] == prop_atoms.net_wm_state_below)
1225 self->below = TRUE;
1226 else if (state[i] == prop_atoms.net_wm_state_demands_attention)
1227 self->demands_attention = TRUE;
1228 else if (state[i] == prop_atoms.ob_wm_state_undecorated)
1229 self->undecorated = TRUE;
1230 }
1231
1232 g_free(state);
1233 }
1234 }
1235
1236 static void client_get_shaped(ObClient *self)
1237 {
1238 self->shaped = FALSE;
1239 #ifdef SHAPE
1240 if (extensions_shape) {
1241 gint foo;
1242 guint ufoo;
1243 gint s;
1244
1245 XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
1246
1247 XShapeQueryExtents(ob_display, self->window, &s, &foo,
1248 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1249 &ufoo);
1250 self->shaped = (s != 0);
1251 }
1252 #endif
1253 }
1254
1255 void client_update_transient_for(ObClient *self)
1256 {
1257 Window t = None;
1258 ObClient *target = NULL;
1259 gboolean trangroup = FALSE;
1260
1261 if (XGetTransientForHint(ob_display, self->window, &t)) {
1262 if (t != self->window) { /* cant be transient to itself! */
1263 target = g_hash_table_lookup(window_map, &t);
1264 /* if this happens then we need to check for it*/
1265 g_assert(target != self);
1266 if (target && !WINDOW_IS_CLIENT(target)) {
1267 /* this can happen when a dialog is a child of
1268 a dockapp, for example */
1269 target = NULL;
1270 }
1271 }
1272
1273 /* Setting the transient_for to Root is actually illegal, however
1274 applications from time have done this to specify transient for
1275 their group */
1276 if (!target && self->group && t == RootWindow(ob_display, ob_screen))
1277 trangroup = TRUE;
1278 } else if (self->group && self->transient)
1279 trangroup = TRUE;
1280
1281 client_update_transient_tree(self, self->group, self->group,
1282 self->transient_for_group, trangroup,
1283 client_direct_parent(self), target);
1284 self->transient_for_group = trangroup;
1285
1286 }
1287
1288 static void client_update_transient_tree(ObClient *self,
1289 ObGroup *oldgroup, ObGroup *newgroup,
1290 gboolean oldgtran, gboolean newgtran,
1291 ObClient* oldparent,
1292 ObClient *newparent)
1293 {
1294 GSList *it, *next;
1295 ObClient *c;
1296
1297 g_assert(!oldgtran || oldgroup);
1298 g_assert(!newgtran || newgroup);
1299 g_assert((!oldgtran && !oldparent) ||
1300 (oldgtran && !oldparent) ||
1301 (!oldgtran && oldparent));
1302 g_assert((!newgtran && !newparent) ||
1303 (newgtran && !newparent) ||
1304 (!newgtran && newparent));
1305
1306 /* * *
1307 Group transient windows are not allowed to have other group
1308 transient windows as their children.
1309 * * */
1310
1311
1312 /* No change has occured */
1313 if (oldgroup == newgroup &&
1314 oldgtran == newgtran &&
1315 oldparent == newparent) return;
1316
1317 /** Remove the client from the transient tree **/
1318
1319 for (it = self->transients; it; it = next) {
1320 next = g_slist_next(it);
1321 c = it->data;
1322 self->transients = g_slist_delete_link(self->transients, it);
1323 c->parents = g_slist_remove(c->parents, self);
1324 }
1325 for (it = self->parents; it; it = next) {
1326 next = g_slist_next(it);
1327 c = it->data;
1328 self->parents = g_slist_delete_link(self->parents, it);
1329 c->transients = g_slist_remove(c->transients, self);
1330 }
1331
1332 /** Re-add the client to the transient tree **/
1333
1334 /* If we're transient for a group then we need to add ourselves to all our
1335 parents */
1336 if (newgtran) {
1337 for (it = newgroup->members; it; it = g_slist_next(it)) {
1338 c = it->data;
1339 if (c != self &&
1340 !client_search_top_direct_parent(c)->transient_for_group &&
1341 client_normal(c))
1342 {
1343 c->transients = g_slist_prepend(c->transients, self);
1344 self->parents = g_slist_prepend(self->parents, c);
1345 }
1346 }
1347 }
1348
1349 /* If we are now transient for a single window we need to add ourselves to
1350 its children
1351
1352 WARNING: Cyclical transient ness is possible if two windows are
1353 transient for eachother.
1354 */
1355 else if (newparent &&
1356 /* don't make ourself its child if it is already our child */
1357 !client_is_direct_child(self, newparent) &&
1358 client_normal(newparent))
1359 {
1360 newparent->transients = g_slist_prepend(newparent->transients, self);
1361 self->parents = g_slist_prepend(self->parents, newparent);
1362 }
1363
1364 /* Add any group transient windows to our children. But if we're transient
1365 for the group, then other group transients are not our children.
1366
1367 WARNING: Cyclical transient-ness is possible. For e.g. if:
1368 A is transient for the group
1369 B is transient for A
1370 C is transient for B
1371 A can't be transient for C or we have a cycle
1372 */
1373 if (!newgtran && newgroup &&
1374 (!newparent ||
1375 !client_search_top_direct_parent(newparent)->transient_for_group) &&
1376 client_normal(self))
1377 {
1378 for (it = newgroup->members; it; it = g_slist_next(it)) {
1379 c = it->data;
1380 if (c != self && c->transient_for_group &&
1381 /* Don't make it our child if it is already our parent */
1382 !client_is_direct_child(c, self))
1383 {
1384 self->transients = g_slist_prepend(self->transients, c);
1385 c->parents = g_slist_prepend(c->parents, self);
1386 }
1387 }
1388 }
1389
1390 /** If we change our group transient-ness, our children change their
1391 effect group transient-ness, which affects how they relate to other
1392 group windows **/
1393
1394 for (it = self->transients; it; it = g_slist_next(it)) {
1395 c = it->data;
1396 if (!c->transient_for_group)
1397 client_update_transient_tree(c, c->group, c->group,
1398 c->transient_for_group,
1399 c->transient_for_group,
1400 client_direct_parent(c),
1401 client_direct_parent(c));
1402 }
1403 }
1404
1405 static void client_get_mwm_hints(ObClient *self)
1406 {
1407 guint num;
1408 guint32 *hints;
1409
1410 self->mwmhints.flags = 0; /* default to none */
1411
1412 if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
1413 &hints, &num)) {
1414 if (num >= OB_MWM_ELEMENTS) {
1415 self->mwmhints.flags = hints[0];
1416 self->mwmhints.functions = hints[1];
1417 self->mwmhints.decorations = hints[2];
1418 }
1419 g_free(hints);
1420 }
1421 }
1422
1423 void client_get_type_and_transientness(ObClient *self)
1424 {
1425 guint num, i;
1426 guint32 *val;
1427 Window t;
1428
1429 self->type = -1;
1430 self->transient = FALSE;
1431
1432 if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
1433 /* use the first value that we know about in the array */
1434 for (i = 0; i < num; ++i) {
1435 if (val[i] == prop_atoms.net_wm_window_type_desktop)
1436 self->type = OB_CLIENT_TYPE_DESKTOP;
1437 else if (val[i] == prop_atoms.net_wm_window_type_dock)
1438 self->type = OB_CLIENT_TYPE_DOCK;
1439 else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
1440 self->type = OB_CLIENT_TYPE_TOOLBAR;
1441 else if (val[i] == prop_atoms.net_wm_window_type_menu)
1442 self->type = OB_CLIENT_TYPE_MENU;
1443 else if (val[i] == prop_atoms.net_wm_window_type_utility)
1444 self->type = OB_CLIENT_TYPE_UTILITY;
1445 else if (val[i] == prop_atoms.net_wm_window_type_splash)
1446 self->type = OB_CLIENT_TYPE_SPLASH;
1447 else if (val[i] == prop_atoms.net_wm_window_type_dialog)
1448 self->type = OB_CLIENT_TYPE_DIALOG;
1449 else if (val[i] == prop_atoms.net_wm_window_type_normal)
1450 self->type = OB_CLIENT_TYPE_NORMAL;
1451 else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
1452 /* prevent this window from getting any decor or
1453 functionality */
1454 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1455 OB_MWM_FLAG_DECORATIONS);
1456 self->mwmhints.decorations = 0;
1457 self->mwmhints.functions = 0;
1458 }
1459 if (self->type != (ObClientType) -1)
1460 break; /* grab the first legit type */
1461 }
1462 g_free(val);
1463 }
1464
1465 if (XGetTransientForHint(ob_display, self->window, &t))
1466 self->transient = TRUE;
1467
1468 if (self->type == (ObClientType) -1) {
1469 /*the window type hint was not set, which means we either classify
1470 ourself as a normal window or a dialog, depending on if we are a
1471 transient. */
1472 if (self->transient)
1473 self->type = OB_CLIENT_TYPE_DIALOG;
1474 else
1475 self->type = OB_CLIENT_TYPE_NORMAL;
1476 }
1477
1478 /* then, based on our type, we can update our transientness.. */
1479 if (self->type == OB_CLIENT_TYPE_DIALOG ||
1480 self->type == OB_CLIENT_TYPE_TOOLBAR ||
1481 self->type == OB_CLIENT_TYPE_MENU ||
1482 self->type == OB_CLIENT_TYPE_UTILITY)
1483 {
1484 self->transient = TRUE;
1485 }
1486 }
1487
1488 void client_update_protocols(ObClient *self)
1489 {
1490 guint32 *proto;
1491 guint num_return, i;
1492
1493 self->focus_notify = FALSE;
1494 self->delete_window = FALSE;
1495
1496 if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
1497 for (i = 0; i < num_return; ++i) {
1498 if (proto[i] == prop_atoms.wm_delete_window)
1499 /* this means we can request the window to close */
1500 self->delete_window = TRUE;
1501 else if (proto[i] == prop_atoms.wm_take_focus)
1502 /* if this protocol is requested, then the window will be
1503 notified whenever we want it to receive focus */
1504 self->focus_notify = TRUE;
1505 #ifdef SYNC
1506 else if (proto[i] == prop_atoms.net_wm_sync_request)
1507 /* if this protocol is requested, then resizing the
1508 window will be synchronized between the frame and the
1509 client */
1510 self->sync_request = TRUE;
1511 #endif
1512 }
1513 g_free(proto);
1514 }
1515 }
1516
1517 #ifdef SYNC
1518 void client_update_sync_request_counter(ObClient *self)
1519 {
1520 guint32 i;
1521
1522 if (PROP_GET32(self->window, net_wm_sync_request_counter, cardinal, &i)) {
1523 self->sync_counter = i;
1524 } else
1525 self->sync_counter = None;
1526 }
1527 #endif
1528
1529 void client_get_colormap(ObClient *self)
1530 {
1531 XWindowAttributes wa;
1532
1533 if (XGetWindowAttributes(ob_display, self->window, &wa))
1534 client_update_colormap(self, wa.colormap);
1535 }
1536
1537 void client_update_colormap(ObClient *self, Colormap colormap)
1538 {
1539 if (colormap == self->colormap) return;
1540
1541 ob_debug("Setting client %s colormap: 0x%x\n", self->title, colormap);
1542
1543 if (client_focused(self)) {
1544 screen_install_colormap(self, FALSE); /* uninstall old one */
1545 self->colormap = colormap;
1546 screen_install_colormap(self, FALSE); /* install new one */
1547 } else
1548 self->colormap = colormap;
1549 }
1550
1551 void client_update_normal_hints(ObClient *self)
1552 {
1553 XSizeHints size;
1554 glong ret;
1555
1556 /* defaults */
1557 self->min_ratio = 0.0f;
1558 self->max_ratio = 0.0f;
1559 SIZE_SET(self->size_inc, 1, 1);
1560 SIZE_SET(self->base_size, 0, 0);
1561 SIZE_SET(self->min_size, 0, 0);
1562 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1563
1564 /* get the hints from the window */
1565 if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
1566 /* normal windows can't request placement! har har
1567 if (!client_normal(self))
1568 */
1569 self->positioned = (size.flags & (PPosition|USPosition));
1570 self->sized = (size.flags & (PSize|USSize));
1571
1572 if (size.flags & PWinGravity)
1573 self->gravity = size.win_gravity;
1574
1575 if (size.flags & PAspect) {
1576 if (size.min_aspect.y)
1577 self->min_ratio =
1578 (gfloat) size.min_aspect.x / size.min_aspect.y;
1579 if (size.max_aspect.y)
1580 self->max_ratio =
1581 (gfloat) size.max_aspect.x / size.max_aspect.y;
1582 }
1583
1584 if (size.flags & PMinSize)
1585 SIZE_SET(self->min_size, size.min_width, size.min_height);
1586
1587 if (size.flags & PMaxSize)
1588 SIZE_SET(self->max_size, size.max_width, size.max_height);
1589
1590 if (size.flags & PBaseSize)
1591 SIZE_SET(self->base_size, size.base_width, size.base_height);
1592
1593 if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1594 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1595
1596 ob_debug("Normal hints: min size (%d %d) max size (%d %d)\n "
1597 "size inc (%d %d) base size (%d %d)\n",
1598 self->min_size.width, self->min_size.height,
1599 self->max_size.width, self->max_size.height,
1600 self->size_inc.width, self->size_inc.height,
1601 self->base_size.width, self->base_size.height);
1602 }
1603 else
1604 ob_debug("Normal hints: not set\n");
1605 }
1606
1607 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
1608 {
1609 /* start with everything (cept fullscreen) */
1610 self->decorations =
1611 (OB_FRAME_DECOR_TITLEBAR |
1612 OB_FRAME_DECOR_HANDLE |
1613 OB_FRAME_DECOR_GRIPS |
1614 OB_FRAME_DECOR_BORDER |
1615 OB_FRAME_DECOR_ICON |
1616 OB_FRAME_DECOR_ALLDESKTOPS |
1617 OB_FRAME_DECOR_ICONIFY |
1618 OB_FRAME_DECOR_MAXIMIZE |
1619 OB_FRAME_DECOR_SHADE |
1620 OB_FRAME_DECOR_CLOSE);
1621 self->functions =
1622 (OB_CLIENT_FUNC_RESIZE |
1623 OB_CLIENT_FUNC_MOVE |
1624 OB_CLIENT_FUNC_ICONIFY |
1625 OB_CLIENT_FUNC_MAXIMIZE |
1626 OB_CLIENT_FUNC_SHADE |
1627 OB_CLIENT_FUNC_CLOSE |
1628 OB_CLIENT_FUNC_BELOW |
1629 OB_CLIENT_FUNC_ABOVE |
1630 OB_CLIENT_FUNC_UNDECORATE);
1631
1632 if (!(self->min_size.width < self->max_size.width ||
1633 self->min_size.height < self->max_size.height))
1634 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1635
1636 switch (self->type) {
1637 case OB_CLIENT_TYPE_NORMAL:
1638 /* normal windows retain all of the possible decorations and
1639 functionality, and can be fullscreen */
1640 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1641 break;
1642
1643 case OB_CLIENT_TYPE_DIALOG:
1644 /* sometimes apps make dialog windows fullscreen for some reason (for
1645 e.g. kpdf does this..) */
1646 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1647 break;
1648
1649 case OB_CLIENT_TYPE_UTILITY:
1650 /* these windows don't have anything added or removed by default */
1651 break;
1652
1653 case OB_CLIENT_TYPE_MENU:
1654 case OB_CLIENT_TYPE_TOOLBAR:
1655 /* these windows can't iconify or maximize */
1656 self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
1657 OB_FRAME_DECOR_MAXIMIZE);
1658 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
1659 OB_CLIENT_FUNC_MAXIMIZE);
1660 break;
1661
1662 case OB_CLIENT_TYPE_SPLASH:
1663 /* these don't get get any decorations, and the only thing you can
1664 do with them is move them */
1665 self->decorations = 0;
1666 self->functions = OB_CLIENT_FUNC_MOVE;
1667 break;
1668
1669 case OB_CLIENT_TYPE_DESKTOP:
1670 /* these windows are not manipulated by the window manager */
1671 self->decorations = 0;
1672 self->functions = 0;
1673 break;
1674
1675 case OB_CLIENT_TYPE_DOCK:
1676 /* these windows are not manipulated by the window manager, but they
1677 can set below layer which has a special meaning */
1678 self->decorations = 0;
1679 self->functions = OB_CLIENT_FUNC_BELOW;
1680 break;
1681 }
1682
1683 /* Mwm Hints are applied subtractively to what has already been chosen for
1684 decor and functionality */
1685 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1686 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1687 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1688 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1689 {
1690 /* if the mwm hints request no handle or title, then all
1691 decorations are disabled, but keep the border if that's
1692 specified */
1693 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1694 self->decorations = OB_FRAME_DECOR_BORDER;
1695 else
1696 self->decorations = 0;
1697 }
1698 }
1699 }
1700
1701 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1702 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1703 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1704 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1705 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1706 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1707 /* dont let mwm hints kill any buttons
1708 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1709 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1710 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1711 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1712 */
1713 /* dont let mwm hints kill the close button
1714 if (! (self->mwmhints.functions & MwmFunc_Close))
1715 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1716 }
1717 }
1718
1719 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1720 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1721 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1722 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1723 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1724 self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1725
1726 /* can't maximize without moving/resizing */
1727 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1728 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1729 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1730 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1731 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1732 }
1733
1734 if (self->max_horz && self->max_vert) {
1735 /* you can't resize fully maximized windows */
1736 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1737 /* kill the handle on fully maxed windows */
1738 self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1739 }
1740
1741 /* If there are no decorations to remove, don't allow the user to try
1742 toggle the state */
1743 if (self->decorations == 0)
1744 self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
1745
1746 /* finally, the user can have requested no decorations, which overrides
1747 everything (but doesnt give it a border if it doesnt have one) */
1748 if (self->undecorated)
1749 self->decorations = 0;
1750
1751 /* if we don't have a titlebar, then we cannot shade! */
1752 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1753 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1754
1755 /* now we need to check against rules for the client's current state */
1756 if (self->fullscreen) {
1757 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1758 OB_CLIENT_FUNC_FULLSCREEN |
1759 OB_CLIENT_FUNC_ICONIFY);
1760 self->decorations = 0;
1761 }
1762
1763 client_change_allowed_actions(self);
1764
1765 if (reconfig)
1766 /* force reconfigure to make sure decorations are updated */
1767 client_reconfigure(self, TRUE);
1768 }
1769
1770 static void client_change_allowed_actions(ObClient *self)
1771 {
1772 gulong actions[12];
1773 gint num = 0;
1774
1775 /* desktop windows are kept on all desktops */
1776 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1777 actions[num++] = prop_atoms.net_wm_action_change_desktop;
1778
1779 if (self->functions & OB_CLIENT_FUNC_SHADE)
1780 actions[num++] = prop_atoms.net_wm_action_shade;
1781 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1782 actions[num++] = prop_atoms.net_wm_action_close;
1783 if (self->functions & OB_CLIENT_FUNC_MOVE)
1784 actions[num++] = prop_atoms.net_wm_action_move;
1785 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1786 actions[num++] = prop_atoms.net_wm_action_minimize;
1787 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1788 actions[num++] = prop_atoms.net_wm_action_resize;
1789 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1790 actions[num++] = prop_atoms.net_wm_action_fullscreen;
1791 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1792 actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1793 actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1794 }
1795 if (self->functions & OB_CLIENT_FUNC_ABOVE)
1796 actions[num++] = prop_atoms.net_wm_action_above;
1797 if (self->functions & OB_CLIENT_FUNC_BELOW)
1798 actions[num++] = prop_atoms.net_wm_action_below;
1799 if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
1800 actions[num++] = prop_atoms.ob_wm_action_undecorate;
1801
1802 PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1803
1804 /* make sure the window isn't breaking any rules now */
1805
1806 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1807 if (self->frame) client_shade(self, FALSE);
1808 else self->shaded = FALSE;
1809 }
1810 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1811 if (self->frame) client_iconify(self, FALSE, TRUE, FALSE);
1812 else self->iconic = FALSE;
1813 }
1814 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1815 if (self->frame) client_fullscreen(self, FALSE);
1816 else self->fullscreen = FALSE;
1817 }
1818 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1819 self->max_vert)) {
1820 if (self->frame) client_maximize(self, FALSE, 0);
1821 else self->max_vert = self->max_horz = FALSE;
1822 }
1823 }
1824
1825 void client_update_wmhints(ObClient *self)
1826 {
1827 XWMHints *hints;
1828
1829 /* assume a window takes input if it doesnt specify */
1830 self->can_focus = TRUE;
1831
1832 if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1833 gboolean ur;
1834
1835 if (hints->flags & InputHint)
1836 self->can_focus = hints->input;
1837
1838 /* only do this when first managing the window *AND* when we aren't
1839 starting up! */
1840 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1841 if (hints->flags & StateHint)
1842 self->iconic = hints->initial_state == IconicState;
1843
1844 ur = self->urgent;
1845 self->urgent = (hints->flags & XUrgencyHint);
1846 if (self->urgent && !ur)
1847 client_hilite(self, TRUE);
1848 else if (!self->urgent && ur && self->demands_attention)
1849 client_hilite(self, FALSE);
1850
1851 if (!(hints->flags & WindowGroupHint))
1852 hints->window_group = None;
1853
1854 /* did the group state change? */
1855 if (hints->window_group !=
1856 (self->group ? self->group->leader : None))
1857 {
1858 ObGroup *oldgroup = self->group;
1859
1860 /* remove from the old group if there was one */
1861 if (self->group != NULL) {
1862 group_remove(self->group, self);
1863 self->group = NULL;
1864 }
1865
1866 /* add ourself to the group if we have one */
1867 if (hints->window_group != None) {
1868 self->group = group_add(hints->window_group, self);
1869 }
1870
1871 /* Put ourselves into the new group's transient tree, and remove
1872 ourselves from the old group's */
1873 client_update_transient_tree(self, oldgroup, self->group,
1874 self->transient_for_group,
1875 self->transient_for_group,
1876 client_direct_parent(self),
1877 client_direct_parent(self));
1878
1879 /* Lastly, being in a group, or not, can change if the window is
1880 transient for anything.
1881
1882 The logic for this is:
1883 self->transient = TRUE always if the window wants to be
1884 transient for something, even if transient_for was NULL because
1885 it wasn't in a group before.
1886
1887 If parents was NULL and oldgroup was NULL we can assume
1888 that when we add the new group, it will become transient for
1889 something.
1890
1891 If transient_for_group is TRUE, then it must have already
1892 had a group. If it is getting a new group, the above call to
1893 client_update_transient_tree has already taken care of
1894 everything ! If it is losing all group status then it will
1895 no longer be transient for anything and that needs to be
1896 updated.
1897 */
1898 if (self->transient &&
1899 ((self->parents == NULL && oldgroup == NULL) ||
1900 (self->transient_for_group && !self->group)))
1901 client_update_transient_for(self);
1902 }
1903
1904 /* the WM_HINTS can contain an icon */
1905 if (hints->flags & IconPixmapHint)
1906 client_update_icons(self);
1907
1908 XFree(hints);
1909 }
1910 }
1911
1912 void client_update_title(ObClient *self)
1913 {
1914 gchar *data = NULL;
1915 gchar *visible = NULL;
1916
1917 g_free(self->title);
1918
1919 /* try netwm */
1920 if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
1921 /* try old x stuff */
1922 if (!(PROP_GETS(self->window, wm_name, locale, &data)
1923 || PROP_GETS(self->window, wm_name, utf8, &data))) {
1924 if (self->transient) {
1925 /*
1926 GNOME alert windows are not given titles:
1927 http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1928 */
1929 data = g_strdup("");
1930 } else
1931 data = g_strdup("Unnamed Window");
1932 }
1933 }
1934
1935 if (self->client_machine) {
1936 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
1937 g_free(data);
1938 } else
1939 visible = data;
1940
1941 PROP_SETS(self->window, net_wm_visible_name, visible);
1942 self->title = visible;
1943
1944 if (self->frame)
1945 frame_adjust_title(self->frame);
1946
1947 /* update the icon title */
1948 data = NULL;
1949 g_free(self->icon_title);
1950
1951 /* try netwm */
1952 if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1953 /* try old x stuff */
1954 if (!(PROP_GETS(self->window, wm_icon_name, locale, &data) ||
1955 PROP_GETS(self->window, wm_icon_name, utf8, &data)))
1956 data = g_strdup(self->title);
1957
1958 if (self->client_machine) {
1959 visible = g_strdup_printf("%s (%s)", data, self->client_machine);
1960 g_free(data);
1961 } else
1962 visible = data;
1963
1964 PROP_SETS(self->window, net_wm_visible_icon_name, visible);
1965 self->icon_title = visible;
1966 }
1967
1968 void client_update_strut(ObClient *self)
1969 {
1970 guint num;
1971 guint32 *data;
1972 gboolean got = FALSE;
1973 StrutPartial strut;
1974
1975 if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1976 &data, &num)) {
1977 if (num == 12) {
1978 got = TRUE;
1979 STRUT_PARTIAL_SET(strut,
1980 data[0], data[2], data[1], data[3],
1981 data[4], data[5], data[8], data[9],
1982 data[6], data[7], data[10], data[11]);
1983 }
1984 g_free(data);
1985 }
1986
1987 if (!got &&
1988 PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1989 if (num == 4) {
1990 Rect *a;
1991
1992 got = TRUE;
1993
1994 /* use the screen's width/height */
1995 a = screen_physical_area_all_monitors();
1996
1997 STRUT_PARTIAL_SET(strut,
1998 data[0], data[2], data[1], data[3],
1999 a->y, a->y + a->height - 1,
2000 a->x, a->x + a->width - 1,
2001 a->y, a->y + a->height - 1,
2002 a->x, a->x + a->width - 1);
2003 g_free(a);
2004 }
2005 g_free(data);
2006 }
2007
2008 if (!got)
2009 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
2010 0, 0, 0, 0, 0, 0, 0, 0);
2011
2012 if (!STRUT_EQUAL(strut, self->strut)) {
2013 self->strut = strut;
2014
2015 /* updating here is pointless while we're being mapped cuz we're not in
2016 the client list yet */
2017 if (self->frame)
2018 screen_update_areas();
2019 }
2020 }
2021
2022 void client_update_icons(ObClient *self)
2023 {
2024 guint num;
2025 guint32 *data;
2026 guint w, h, i, j;
2027
2028 for (i = 0; i < self->nicons; ++i)
2029 g_free(self->icons[i].data);
2030 if (self->nicons > 0)
2031 g_free(self->icons);
2032 self->nicons = 0;
2033
2034 if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
2035 /* figure out how many valid icons are in here */
2036 i = 0;
2037 while (num - i > 2) {
2038 w = data[i++];
2039 h = data[i++];
2040 i += w * h;
2041 if (i > num || w*h == 0) break;
2042 ++self->nicons;
2043 }
2044
2045 self->icons = g_new(ObClientIcon, self->nicons);
2046
2047 /* store the icons */
2048 i = 0;
2049 for (j = 0; j < self->nicons; ++j) {
2050 guint x, y, t;
2051
2052 w = self->icons[j].width = data[i++];
2053 h = self->icons[j].height = data[i++];
2054
2055 if (w*h == 0) continue;
2056
2057 self->icons[j].data = g_new(RrPixel32, w * h);
2058 for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
2059 if (x >= w) {
2060 x = 0;
2061 ++y;
2062 }
2063 self->icons[j].data[t] =
2064 (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2065 (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
2066 (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
2067 (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
2068 }
2069 g_assert(i <= num);
2070 }
2071
2072 g_free(data);
2073 } else {
2074 XWMHints *hints;
2075
2076 if ((hints = XGetWMHints(ob_display, self->window))) {
2077 if (hints->flags & IconPixmapHint) {
2078 self->nicons = 1;
2079 self->icons = g_new(ObClientIcon, self->nicons);
2080 xerror_set_ignore(TRUE);
2081 if (!RrPixmapToRGBA(ob_rr_inst,
2082 hints->icon_pixmap,
2083 (hints->flags & IconMaskHint ?
2084 hints->icon_mask : None),
2085 &self->icons[0].width,
2086 &self->icons[0].height,
2087 &self->icons[0].data))
2088 {
2089 g_free(self->icons);
2090 self->nicons = 0;
2091 }
2092 xerror_set_ignore(FALSE);
2093 }
2094 XFree(hints);
2095 }
2096 }
2097
2098 /* set the default icon onto the window
2099 in theory, this could be a race, but if a window doesn't set an icon
2100 or removes it entirely, it's not very likely it is going to set one
2101 right away afterwards
2102
2103 if it has parents, then one of them will have an icon already
2104 */
2105 if (self->nicons == 0 && !self->parents) {
2106 RrPixel32 *icon = ob_rr_theme->def_win_icon;
2107 gulong *data;
2108
2109 data = g_new(gulong, 48*48+2);
2110 data[0] = data[1] = 48;
2111 for (i = 0; i < 48*48; ++i)
2112 data[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2113 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
2114 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
2115 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
2116 PROP_SETA32(self->window, net_wm_icon, cardinal, data, 48*48+2);
2117 g_free(data);
2118 } else if (self->frame)
2119 /* don't draw the icon empty if we're just setting one now anyways,
2120 we'll get the property change any second */
2121 frame_adjust_icon(self->frame);
2122 }
2123
2124 void client_update_icon_geometry(ObClient *self)
2125 {
2126 guint num;
2127 guint32 *data;
2128
2129 RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2130
2131 if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num)
2132 && num == 4)
2133 {
2134 /* don't let them set it with an area < 0 */
2135 RECT_SET(self->icon_geometry, data[0], data[1],
2136 MAX(data[2],0), MAX(data[3],0));
2137 }
2138 }
2139
2140 static void client_get_session_ids(ObClient *self)
2141 {
2142 guint32 leader;
2143 gboolean got;
2144 gchar *s;
2145 gchar **ss;
2146
2147 if (!PROP_GET32(self->window, wm_client_leader, window, &leader))
2148 leader = None;
2149
2150 /* get the SM_CLIENT_ID */
2151 got = FALSE;
2152 if (leader)
2153 got = PROP_GETS(leader, sm_client_id, locale, &self->sm_client_id);
2154 if (!got)
2155 PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id);
2156
2157 /* get the WM_CLASS (name and class). make them "" if they are not
2158 provided */
2159 got = FALSE;
2160 if (leader)
2161 got = PROP_GETSS(leader, wm_class, locale, &ss);
2162 if (!got)
2163 got = PROP_GETSS(self->window, wm_class, locale, &ss);
2164
2165 if (got) {
2166 if (ss[0]) {
2167 self->name = g_strdup(ss[0]);
2168 if (ss[1])
2169 self->class = g_strdup(ss[1]);
2170 }
2171 g_strfreev(ss);
2172 }
2173
2174 if (self->name == NULL) self->name = g_strdup("");
2175 if (self->class == NULL) self->class = g_strdup("");
2176
2177 /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2178 got = FALSE;
2179 if (leader)
2180 got = PROP_GETS(leader, wm_window_role, locale, &s);
2181 if (!got)
2182 got = PROP_GETS(self->window, wm_window_role, locale, &s);
2183
2184 if (got)
2185 self->role = s;
2186 else
2187 self->role = g_strdup("");
2188
2189 /* get the WM_COMMAND */
2190 got = FALSE;
2191
2192 if (leader)
2193 got = PROP_GETSS(leader, wm_command, locale, &ss);
2194 if (!got)
2195 got = PROP_GETSS(self->window, wm_command, locale, &ss);
2196
2197 if (got) {
2198 /* merge/mash them all together */
2199 gchar *merge = NULL;
2200 gint i;
2201
2202 for (i = 0; ss[i]; ++i) {
2203 gchar *tmp = merge;
2204 if (merge)
2205 merge = g_strconcat(merge, ss[i], NULL);
2206 else
2207 merge = g_strconcat(ss[i], NULL);
2208 g_free(tmp);
2209 }
2210 g_strfreev(ss);
2211
2212 self->wm_command = merge;
2213 }
2214
2215 /* get the WM_CLIENT_MACHINE */
2216 got = FALSE;
2217 if (leader)
2218 got = PROP_GETS(leader, wm_client_machine, locale, &s);
2219 if (!got)
2220 got = PROP_GETS(self->window, wm_client_machine, locale, &s);
2221
2222 if (got) {
2223 gchar localhost[128];
2224
2225 gethostname(localhost, 127);
2226 localhost[127] = '\0';
2227 if (strcmp(localhost, s) != 0)
2228 self->client_machine = s;
2229 else
2230 g_free(s);
2231 }
2232 }
2233
2234 static void client_change_wm_state(ObClient *self)
2235 {
2236 gulong state[2];
2237 glong old;
2238
2239 old = self->wmstate;
2240
2241 if (self->shaded || self->iconic ||
2242 (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2243 {
2244 self->wmstate = IconicState;
2245 } else
2246 self->wmstate = NormalState;
2247
2248 if (old != self->wmstate) {
2249 PROP_MSG(self->window, kde_wm_change_state,
2250 self->wmstate, 1, 0, 0);
2251
2252 state[0] = self->wmstate;
2253 state[1] = None;
2254 PROP_SETA32(self->window, wm_state, wm_state, state, 2);
2255 }
2256 }
2257
2258 static void client_change_state(ObClient *self)
2259 {
2260 gulong netstate[12];
2261 guint num;
2262
2263 num = 0;
2264 if (self->modal)
2265 netstate[num++] = prop_atoms.net_wm_state_modal;
2266 if (self->shaded)
2267 netstate[num++] = prop_atoms.net_wm_state_shaded;
2268 if (self->iconic)
2269 netstate[num++] = prop_atoms.net_wm_state_hidden;
2270 if (self->skip_taskbar)
2271 netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
2272 if (self->skip_pager)
2273 netstate[num++] = prop_atoms.net_wm_state_skip_pager;
2274 if (self->fullscreen)
2275 netstate[num++] = prop_atoms.net_wm_state_fullscreen;
2276 if (self->max_vert)
2277 netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
2278 if (self->max_horz)
2279 netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
2280 if (self->above)
2281 netstate[num++] = prop_atoms.net_wm_state_above;
2282 if (self->below)
2283 netstate[num++] = prop_atoms.net_wm_state_below;
2284 if (self->demands_attention)
2285 netstate[num++] = prop_atoms.net_wm_state_demands_attention;
2286 if (self->undecorated)
2287 netstate[num++] = prop_atoms.ob_wm_state_undecorated;
2288 PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
2289
2290 if (self->frame)
2291 frame_adjust_state(self->frame);
2292 }
2293
2294 ObClient *client_search_focus_tree(ObClient *self)
2295 {
2296 GSList *it;
2297 ObClient *ret;
2298
2299 for (it = self->transients; it; it = g_slist_next(it)) {
2300 if (client_focused(it->data)) return it->data;
2301 if ((ret = client_search_focus_tree(it->data))) return ret;
2302 }
2303 return NULL;
2304 }
2305
2306 ObClient *client_search_focus_tree_full(ObClient *self)
2307 {
2308 if (self->parents) {
2309 GSList *it;
2310
2311 for (it = self->parents; it; it = g_slist_next(it)) {
2312 ObClient *c = it->data;
2313 if ((c = client_search_focus_tree_full(it->data))) return c;
2314 }
2315
2316 return NULL;
2317 }
2318 else {
2319 /* this function checks the whole tree, the client_search_focus_tree
2320 does not, so we need to check this window */
2321 if (client_focused(self))
2322 return self;
2323 return client_search_focus_tree(self);
2324 }
2325 }
2326
2327 ObClient *client_search_focus_group_full(ObClient *self)
2328 {
2329 GSList *it;
2330
2331 if (self->group) {
2332 for (it = self->group->members; it; it = g_slist_next(it)) {
2333 ObClient *c = it->data;
2334
2335 if (client_focused(c)) return c;
2336 if ((c = client_search_focus_tree(it->data))) return c;
2337 }
2338 } else
2339 if (client_focused(self)) return self;
2340 return NULL;
2341 }
2342
2343 gboolean client_has_parent(ObClient *self)
2344 {
2345 return self->parents != NULL;
2346 }
2347
2348 static ObStackingLayer calc_layer(ObClient *self)
2349 {
2350 ObStackingLayer l;
2351 Rect *monitor;
2352
2353 monitor = screen_physical_area_monitor(client_monitor(self));
2354
2355 if (self->type == OB_CLIENT_TYPE_DESKTOP)
2356 l = OB_STACKING_LAYER_DESKTOP;
2357 else if (self->type == OB_CLIENT_TYPE_DOCK) {
2358 if (self->below) l = OB_STACKING_LAYER_NORMAL;
2359 else l = OB_STACKING_LAYER_ABOVE;
2360 }
2361 else if ((self->fullscreen ||
2362 /* No decorations and fills the monitor = oldskool fullscreen.
2363 But not for maximized windows.
2364 */
2365 (self->decorations == 0 &&
2366 !(self->max_horz && self->max_vert) &&
2367 RECT_EQUAL(self->area, *monitor))) &&
2368 (client_focused(self) || client_search_focus_tree(self)))
2369 l = OB_STACKING_LAYER_FULLSCREEN;
2370 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2371 else if (self->below) l = OB_STACKING_LAYER_BELOW;
2372 else l = OB_STACKING_LAYER_NORMAL;
2373
2374 g_free(monitor);
2375
2376 return l;
2377 }
2378
2379 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2380 ObStackingLayer min)
2381 {
2382 ObStackingLayer old, own;
2383 GSList *it;
2384
2385 old = self->layer;
2386 own = calc_layer(self);
2387 self->layer = MAX(own, min);
2388
2389 if (self->layer != old) {
2390 stacking_remove(CLIENT_AS_WINDOW(self));
2391 stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2392 }
2393
2394 for (it = self->transients; it; it = g_slist_next(it))
2395 client_calc_layer_recursive(it->data, orig,
2396 self->layer);
2397 }
2398
2399 void client_calc_layer(ObClient *self)
2400 {
2401 ObClient *orig;
2402 GSList *it;
2403
2404 orig = self;
2405
2406 /* transients take on the layer of their parents */
2407 it = client_search_all_top_parents(self);
2408
2409 for (; it; it = g_slist_next(it))
2410 client_calc_layer_recursive(it->data, orig, 0);
2411 }
2412
2413 gboolean client_should_show(ObClient *self)
2414 {
2415 if (self->iconic)
2416 return FALSE;
2417 if (client_normal(self) && screen_showing_desktop)
2418 return FALSE;
2419 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2420 return TRUE;
2421
2422 return FALSE;
2423 }
2424
2425 gboolean client_show(ObClient *self)
2426 {
2427 gboolean show = FALSE;
2428
2429 if (client_should_show(self)) {
2430 frame_show(self->frame);
2431 show = TRUE;
2432
2433 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2434 it needs to be in IconicState. This includes when it is on another
2435 desktop!
2436 */
2437 client_change_wm_state(self);
2438 }
2439 return show;
2440 }
2441
2442 gboolean client_hide(ObClient *self)
2443 {
2444 gboolean hide = FALSE;
2445
2446 if (!client_should_show(self)) {
2447 if (self == focus_client) {
2448 /* if there is a grab going on, then we need to cancel it. if we
2449 move focus during the grab, applications will get
2450 NotifyWhileGrabbed events and ignore them !
2451
2452 actions should not rely on being able to move focus during an
2453 interactive grab.
2454 */
2455 event_cancel_all_key_grabs();
2456 }
2457
2458 /* We don't need to ignore enter events here.
2459 The window can hide/iconify in 3 different ways:
2460 1 - through an x message. in this case we ignore all enter events
2461 caused by responding to the x message (unless underMouse)
2462 2 - by a keyboard action. in this case we ignore all enter events
2463 caused by the action
2464 3 - by a mouse action. in this case they are doing stuff with the
2465 mouse and focus _should_ move.
2466
2467 Also in action_end, we simulate an enter event that can't be ignored
2468 so trying to ignore them is futile in case 3 anyways
2469 */
2470
2471 frame_hide(self->frame);
2472 hide = TRUE;
2473
2474 /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2475 it needs to be in IconicState. This includes when it is on another
2476 desktop!
2477 */
2478 client_change_wm_state(self);
2479 }
2480 return hide;
2481 }
2482
2483 void client_showhide(ObClient *self)
2484 {
2485 if (!client_show(self))
2486 client_hide(self);
2487 }
2488
2489 gboolean client_normal(ObClient *self) {
2490 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2491 self->type == OB_CLIENT_TYPE_DOCK ||
2492 self->type == OB_CLIENT_TYPE_SPLASH);
2493 }
2494
2495 gboolean client_helper(ObClient *self)
2496 {
2497 return (self->type == OB_CLIENT_TYPE_UTILITY ||
2498 self->type == OB_CLIENT_TYPE_MENU ||
2499 self->type == OB_CLIENT_TYPE_TOOLBAR);
2500 }
2501
2502 gboolean client_mouse_focusable(ObClient *self)
2503 {
2504 return !(self->type == OB_CLIENT_TYPE_MENU ||
2505 self->type == OB_CLIENT_TYPE_TOOLBAR ||
2506 self->type == OB_CLIENT_TYPE_SPLASH ||
2507 self->type == OB_CLIENT_TYPE_DOCK);
2508 }
2509
2510 gboolean client_enter_focusable(ObClient *self)
2511 {
2512 /* you can focus desktops but it shouldn't on enter */
2513 return (client_mouse_focusable(self) &&
2514 self->type != OB_CLIENT_TYPE_DESKTOP);
2515 }
2516
2517
2518 static void client_apply_startup_state(ObClient *self,
2519 gint x, gint y, gint w, gint h)
2520 {
2521 /* save the states that we are going to apply */
2522 gboolean iconic = self->iconic;
2523 gboolean fullscreen = self->fullscreen;
2524 gboolean undecorated = self->undecorated;
2525 gboolean shaded = self->shaded;
2526 gboolean demands_attention = self->demands_attention;
2527 gboolean max_horz = self->max_horz;
2528 gboolean max_vert = self->max_vert;
2529 Rect oldarea;
2530 gint l;
2531
2532 /* turn them all off in the client, so they won't affect the window
2533 being placed */
2534 self->iconic = self->fullscreen = self->undecorated = self->shaded =
2535 self->demands_attention = self->max_horz = self->max_vert = FALSE;
2536
2537 /* move the client to its placed position, or it it's already there,
2538 generate a ConfigureNotify telling the client where it is.
2539
2540 do this after adjusting the frame. otherwise it gets all weird and
2541 clients don't work right
2542
2543 do this before applying the states so they have the correct
2544 pre-max/pre-fullscreen values
2545 */
2546 client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
2547 ob_debug("placed window 0x%x at %d, %d with size %d x %d\n",
2548 self->window, x, y, w, h);
2549 /* save the area, and make it where it should be for the premax stuff */
2550 oldarea = self->area;
2551 RECT_SET(self->area, x, y, w, h);
2552
2553 /* apply the states. these are in a carefully crafted order.. */
2554
2555 if (iconic)
2556 client_iconify(self, TRUE, FALSE, TRUE);
2557 if (fullscreen)
2558 client_fullscreen(self, TRUE);
2559 if (undecorated)
2560 client_set_undecorated(self, TRUE);
2561 if (shaded)
2562 client_shade(self, TRUE);
2563 if (demands_attention)
2564 client_hilite(self, TRUE);
2565
2566 if (max_vert && max_horz)
2567 client_maximize(self, TRUE, 0);
2568 else if (max_vert)
2569 client_maximize(self, TRUE, 2);
2570 else if (max_horz)
2571 client_maximize(self, TRUE, 1);
2572
2573 /* if the window hasn't been configured yet, then do so now, in fact the
2574 x,y,w,h may _not_ be the same as the area rect, which can end up
2575 meaning that the client isn't properly moved/resized by the fullscreen
2576 function
2577 pho can cause this because it maps at size of the screen but not 0,0
2578 so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2579 then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2580 cuz thats where the pre-fullscreen will be. however the actual area is
2581 not, so this needs to be called even if we have fullscreened/maxed
2582 */
2583 self->area = oldarea;
2584 client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
2585
2586 /* set the desktop hint, to make sure that it always exists */
2587 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
2588
2589 /* nothing to do for the other states:
2590 skip_taskbar
2591 skip_pager
2592 modal
2593 above
2594 below
2595 */
2596 }
2597
2598 void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
2599 {
2600 /* these should be the current values. this is for when you're not moving,
2601 just resizing */
2602 g_assert(*x == self->area.x);
2603 g_assert(oldw == self->area.width);
2604
2605 /* horizontal */
2606 switch (self->gravity) {
2607 default:
2608 case NorthWestGravity:
2609 case WestGravity:
2610 case SouthWestGravity:
2611 case StaticGravity:
2612 case ForgetGravity:
2613 break;
2614 case NorthGravity:
2615 case CenterGravity:
2616 case SouthGravity:
2617 *x -= (neww - oldw) / 2;
2618 break;
2619 case NorthEastGravity:
2620 case EastGravity:
2621 case SouthEastGravity:
2622 *x -= neww - oldw;
2623 break;
2624 }
2625 }
2626
2627 void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
2628 {
2629 /* these should be the current values. this is for when you're not moving,
2630 just resizing */
2631 g_assert(*y == self->area.y);
2632 g_assert(oldh == self->area.height);
2633
2634 /* vertical */
2635 switch (self->gravity) {
2636 default:
2637 case NorthWestGravity:
2638 case NorthGravity:
2639 case NorthEastGravity:
2640 case StaticGravity:
2641 case ForgetGravity:
2642 break;
2643 case WestGravity:
2644 case CenterGravity:
2645 case EastGravity:
2646 *y -= (newh - oldh) / 2;
2647 break;
2648 case SouthWestGravity:
2649 case SouthGravity:
2650 case SouthEastGravity:
2651 *y -= newh - oldh;
2652 break;
2653 }
2654 }
2655
2656 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2657 gint *logicalw, gint *logicalh,
2658 gboolean user)
2659 {
2660 Rect desired = {*x, *y, *w, *h};
2661 frame_rect_to_frame(self->frame, &desired);
2662
2663 /* make the frame recalculate its dimentions n shit without changing
2664 anything visible for real, this way the constraints below can work with
2665 the updated frame dimensions. */
2666 frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
2667
2668 /* gets the frame's position */
2669 frame_client_gravity(self->frame, x, y);
2670
2671 /* these positions are frame positions, not client positions */
2672
2673 /* set the size and position if fullscreen */
2674 if (self->fullscreen) {
2675 Rect *a;
2676 guint i;
2677
2678 i = screen_find_monitor(&desired);
2679 a = screen_physical_area_monitor(i);
2680
2681 *x = a->x;
2682 *y = a->y;
2683 *w = a->width;
2684 *h = a->height;
2685
2686 user = FALSE; /* ignore if the client can't be moved/resized when it
2687 is fullscreening */
2688
2689 g_free(a);
2690 } else if (self->max_horz || self->max_vert) {
2691 Rect *a;
2692 guint i;
2693
2694 /* use all possible struts when maximizing to the full screen */
2695 i = screen_find_monitor(&desired);
2696 a = screen_area(self->desktop, i,
2697 (self->max_horz && self->max_vert ? NULL : &desired));
2698
2699 /* set the size and position if maximized */
2700 if (self->max_horz) {
2701 *x = a->x;
2702 *w = a->width - self->frame->size.left - self->frame->size.right;
2703 }
2704 if (self->max_vert) {
2705 *y = a->y;
2706 *h = a->height - self->frame->size.top - self->frame->size.bottom;
2707 }
2708
2709 user = FALSE; /* ignore if the client can't be moved/resized when it
2710 is maximizing */
2711
2712 g_free(a);
2713 }
2714
2715 /* gets the client's position */
2716 frame_frame_gravity(self->frame, x, y);
2717
2718 /* work within the prefered sizes given by the window */
2719 if (!(*w == self->area.width && *h == self->area.height)) {
2720 gint basew, baseh, minw, minh;
2721 gint incw, inch;
2722 gfloat minratio, maxratio;
2723
2724 incw = self->fullscreen || self->max_horz ? 1 : self->size_inc.width;
2725 inch = self->fullscreen || self->max_vert ? 1 : self->size_inc.height;
2726 minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2727 0 : self->min_ratio;
2728 maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2729 0 : self->max_ratio;
2730
2731 /* base size is substituted with min size if not specified */
2732 if (self->base_size.width || self->base_size.height) {
2733 basew = self->base_size.width;
2734 baseh = self->base_size.height;
2735 } else {
2736 basew = self->min_size.width;
2737 baseh = self->min_size.height;
2738 }
2739 /* min size is substituted with base size if not specified */
2740 if (self->min_size.width || self->min_size.height) {
2741 minw = self->min_size.width;
2742 minh = self->min_size.height;
2743 } else {
2744 minw = self->base_size.width;
2745 minh = self->base_size.height;
2746 }
2747
2748 /* if this is a user-requested resize, then check against min/max
2749 sizes */
2750
2751 /* smaller than min size or bigger than max size? */
2752 if (*w > self->max_size.width) *w = self->max_size.width;
2753 if (*w < minw) *w = minw;
2754 if (*h > self->max_size.height) *h = self->max_size.height;
2755 if (*h < minh) *h = minh;
2756
2757 *w -= basew;
2758 *h -= baseh;
2759
2760 /* keep to the increments */
2761 *w /= incw;
2762 *h /= inch;
2763
2764 /* you cannot resize to nothing */
2765 if (basew + *w < 1) *w = 1 - basew;
2766 if (baseh + *h < 1) *h = 1 - baseh;
2767
2768 /* save the logical size */
2769 *logicalw = incw > 1 ? *w : *w + basew;
2770 *logicalh = inch > 1 ? *h : *h + baseh;
2771
2772 *w *= incw;
2773 *h *= inch;
2774
2775 *w += basew;
2776 *h += baseh;
2777
2778 /* adjust the height to match the width for the aspect ratios.
2779 for this, min size is not substituted for base size ever. */
2780 *w -= self->base_size.width;
2781 *h -= self->base_size.height;
2782
2783 if (minratio)
2784 if (*h * minratio > *w) {
2785 *h = (gint)(*w / minratio);
2786
2787 /* you cannot resize to nothing */
2788 if (*h < 1) {
2789 *h = 1;
2790 *w = (gint)(*h * minratio);
2791 }
2792 }
2793 if (maxratio)
2794 if (*h * maxratio < *w) {
2795 *h = (gint)(*w / maxratio);
2796
2797 /* you cannot resize to nothing */
2798 if (*h < 1) {
2799 *h = 1;
2800 *w = (gint)(*h * minratio);
2801 }
2802 }
2803
2804 *w += self->base_size.width;
2805 *h += self->base_size.height;
2806 }
2807
2808 /* these override the above states! if you cant move you can't move! */
2809 if (user) {
2810 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
2811 *x = self->area.x;
2812 *y = self->area.y;
2813 }
2814 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
2815 *w = self->area.width;
2816 *h = self->area.height;
2817 }
2818 }
2819
2820 g_assert(*w > 0);
2821 g_assert(*h > 0);
2822 }
2823
2824
2825 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
2826 gboolean user, gboolean final, gboolean force_reply)
2827 {
2828 gint oldw, oldh;
2829 gboolean send_resize_client;
2830 gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
2831 gboolean fmoved, fresized;
2832 guint fdecor = self->frame->decorations;
2833 gboolean fhorz = self->frame->max_horz;
2834 gboolean fvert = self->frame->max_vert;
2835 gint logicalw, logicalh;
2836
2837 /* find the new x, y, width, and height (and logical size) */
2838 client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
2839
2840 /* set the logical size if things changed */
2841 if (!(w == self->area.width && h == self->area.height))
2842 SIZE_SET(self->logical_size, logicalw, logicalh);
2843
2844 /* figure out if we moved or resized or what */
2845 moved = (x != self->area.x || y != self->area.y);
2846 resized = (w != self->area.width || h != self->area.height);
2847
2848 oldw = self->area.width;
2849 oldh = self->area.height;
2850 RECT_SET(self->area, x, y, w, h);
2851
2852 /* for app-requested resizes, always resize if 'resized' is true.
2853 for user-requested ones, only resize if final is true, or when
2854 resizing in redraw mode */
2855 send_resize_client = ((!user && resized) ||
2856 (user && (final ||
2857 (resized && config_resize_redraw))));
2858
2859 /* if the client is enlarging, then resize the client before the frame */
2860 if (send_resize_client && (w > oldw || h > oldh)) {
2861 XMoveResizeWindow(ob_display, self->window,
2862 self->frame->size.left, self->frame->size.top,
2863 MAX(w, oldw), MAX(h, oldh));
2864 frame_adjust_client_area(self->frame);
2865 }
2866
2867 /* find the frame's dimensions and move/resize it */
2868 fmoved = moved;
2869 fresized = resized;
2870
2871 /* if decorations changed, then readjust everything for the frame */
2872 if (self->decorations != fdecor ||
2873 self->max_horz != fhorz || self->max_vert != fvert)
2874 {
2875 fmoved = fresized = TRUE;
2876 }
2877
2878 /* adjust the frame */
2879 if (fmoved || fresized) {
2880 gulong ignore_start;
2881 if (!user)
2882 ignore_start = event_start_ignore_all_enters();
2883
2884 frame_adjust_area(self->frame, fmoved, fresized, FALSE);
2885
2886 if (!user)
2887 event_end_ignore_all_enters(ignore_start);
2888 }
2889
2890 if (!user || final) {
2891 gint oldrx = self->root_pos.x;
2892 gint oldry = self->root_pos.y;
2893 /* we have reset the client to 0 border width, so don't include
2894 it in these coords */
2895 POINT_SET(self->root_pos,
2896 self->frame->area.x + self->frame->size.left -
2897 self->border_width,
2898 self->frame->area.y + self->frame->size.top -
2899 self->border_width);
2900 if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
2901 rootmoved = TRUE;
2902 }
2903
2904 /* This is kinda tricky and should not be changed.. let me explain!
2905
2906 When user = FALSE, then the request is coming from the application
2907 itself, and we are more strict about when to send a synthetic
2908 ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
2909 in this case (if force_reply is true)
2910
2911 When user = TRUE, then the request is coming from "us", like when we
2912 maximize a window or something. In this case we are more lenient. We
2913 used to follow the same rules as above, but _Java_ Swing can't handle
2914 this. So just to appease Swing, when user = TRUE, we always send
2915 a synthetic ConfigureNotify to give the window its root coordinates.
2916 */
2917 if ((!user && !resized && (rootmoved || force_reply)) ||
2918 (user && final && rootmoved))
2919 {
2920 XEvent event;
2921
2922 event.type = ConfigureNotify;
2923 event.xconfigure.display = ob_display;
2924 event.xconfigure.event = self->window;
2925 event.xconfigure.window = self->window;
2926
2927 ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d\n",
2928 self->title, self->root_pos.x, self->root_pos.y, w, h);
2929
2930 /* root window real coords */
2931 event.xconfigure.x = self->root_pos.x;
2932 event.xconfigure.y = self->root_pos.y;
2933 event.xconfigure.width = w;
2934 event.xconfigure.height = h;
2935 event.xconfigure.border_width = self->border_width;
2936 event.xconfigure.above = None;
2937 event.xconfigure.override_redirect = FALSE;
2938 XSendEvent(event.xconfigure.display, event.xconfigure.window,
2939 FALSE, StructureNotifyMask, &event);
2940 }
2941
2942 /* if the client is shrinking, then resize the frame before the client.
2943
2944 both of these resize sections may run, because the top one only resizes
2945 in the direction that is growing
2946 */
2947 if (send_resize_client && (w <= oldw || h <= oldh)) {
2948 frame_adjust_client_area(self->frame);
2949 XMoveResizeWindow(ob_display, self->window,
2950 self->frame->size.left, self->frame->size.top, w, h);
2951 }
2952
2953 XFlush(ob_display);
2954 }
2955
2956 void client_fullscreen(ObClient *self, gboolean fs)
2957 {
2958 gint x, y, w, h;
2959
2960 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2961 self->fullscreen == fs) return; /* already done */
2962
2963 self->fullscreen = fs;
2964 client_change_state(self); /* change the state hints on the client */
2965
2966 if (fs) {
2967 self->pre_fullscreen_area = self->area;
2968 /* if the window is maximized, its area isn't all that meaningful.
2969 save it's premax area instead. */
2970 if (self->max_horz) {
2971 self->pre_fullscreen_area.x = self->pre_max_area.x;
2972 self->pre_fullscreen_area.width = self->pre_max_area.width;
2973 }
2974 if (self->max_vert) {
2975 self->pre_fullscreen_area.y = self->pre_max_area.y;
2976 self->pre_fullscreen_area.height = self->pre_max_area.height;
2977 }
2978
2979 /* these will help configure_full figure out where to fullscreen
2980 the window */
2981 x = self->area.x;
2982 y = self->area.y;
2983 w = self->area.width;
2984 h = self->area.height;
2985 } else {
2986 g_assert(self->pre_fullscreen_area.width > 0 &&
2987 self->pre_fullscreen_area.height > 0);
2988
2989 x = self->pre_fullscreen_area.x;
2990 y = self->pre_fullscreen_area.y;
2991 w = self->pre_fullscreen_area.width;
2992 h = self->pre_fullscreen_area.height;
2993 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2994 }
2995
2996 ob_debug("Window %s going fullscreen (%d)\n",
2997 self->title, self->fullscreen);
2998
2999 client_setup_decor_and_functions(self, FALSE);
3000 client_move_resize(self, x, y, w, h);
3001
3002 /* and adjust our layer/stacking. do this after resizing the window,
3003 and applying decorations, because windows which fill the screen are
3004 considered "fullscreen" and it affects their layer */
3005 client_calc_layer(self);
3006
3007 if (fs) {
3008 /* try focus us when we go into fullscreen mode */
3009 client_focus(self);
3010 }
3011 }
3012
3013 static void client_iconify_recursive(ObClient *self,
3014 gboolean iconic, gboolean curdesk,
3015 gboolean hide_animation)
3016 {
3017 GSList *it;
3018 gboolean changed = FALSE;
3019
3020
3021 if (self->iconic != iconic) {
3022 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
3023 self->window);
3024
3025 if (iconic) {
3026 /* don't let non-normal windows iconify along with their parents
3027 or whatever */
3028 if (client_normal(self)) {
3029 self->iconic = iconic;
3030
3031 /* update the focus lists.. iconic windows go to the bottom of
3032 the list */
3033 focus_order_to_bottom(self);
3034
3035 changed = TRUE;
3036 }
3037 } else {
3038 self->iconic = iconic;
3039
3040 if (curdesk && self->desktop != screen_desktop &&
3041 self->desktop != DESKTOP_ALL)
3042 client_set_desktop(self, screen_desktop, FALSE, FALSE);
3043
3044 /* this puts it after the current focused window */
3045 focus_order_remove(self);
3046 focus_order_add_new(self);
3047
3048 changed = TRUE;
3049 }
3050 }
3051
3052 if (changed) {
3053 client_change_state(self);
3054 if (config_animate_iconify && !hide_animation)
3055 frame_begin_iconify_animation(self->frame, iconic);
3056 /* do this after starting the animation so it doesn't flash */
3057 client_showhide(self);
3058 }
3059
3060 /* iconify all direct transients, and deiconify all transients
3061 (non-direct too) */
3062 for (it = self->transients; it; it = g_slist_next(it))
3063 if (it->data != self)
3064 if (client_is_direct_child(self, it->data) || !iconic)
3065 client_iconify_recursive(it->data, iconic, curdesk,
3066 hide_animation);
3067 }
3068
3069 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3070 gboolean hide_animation)
3071 {
3072 if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3073 /* move up the transient chain as far as possible first */
3074 self = client_search_top_direct_parent(self);
3075 client_iconify_recursive(self, iconic, curdesk, hide_animation);
3076 }
3077 }
3078
3079 void client_maximize(ObClient *self, gboolean max, gint dir)
3080 {
3081 gint x, y, w, h;
3082
3083 g_assert(dir == 0 || dir == 1 || dir == 2);
3084 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
3085
3086 /* check if already done */
3087 if (max) {
3088 if (dir == 0 && self->max_horz && self->max_vert) return;
3089 if (dir == 1 && self->max_horz) return;
3090 if (dir == 2 && self->max_vert) return;
3091 } else {
3092 if (dir == 0 && !self->max_horz && !self->max_vert) return;
3093 if (dir == 1 && !self->max_horz) return;
3094 if (dir == 2 && !self->max_vert) return;
3095 }
3096
3097 /* these will help configure_full figure out which screen to fill with
3098 the window */
3099 x = self->area.x;
3100 y = self->area.y;
3101 w = self->area.width;
3102 h = self->area.height;
3103
3104 if (max) {
3105 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3106 RECT_SET(self->pre_max_area,
3107 self->area.x, self->pre_max_area.y,
3108 self->area.width, self->pre_max_area.height);
3109 }
3110 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3111 RECT_SET(self->pre_max_area,
3112 self->pre_max_area.x, self->area.y,
3113 self->pre_max_area.width, self->area.height);
3114 }
3115 } else {
3116 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3117 g_assert(self->pre_max_area.width > 0);
3118
3119 x = self->pre_max_area.x;
3120 w = self->pre_max_area.width;
3121
3122 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3123 0, self->pre_max_area.height);
3124 }
3125 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3126 g_assert(self->pre_max_area.height > 0);
3127
3128 y = self->pre_max_area.y;
3129 h = self->pre_max_area.height;
3130
3131 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3132 self->pre_max_area.width, 0);
3133 }
3134 }
3135
3136 if (dir == 0 || dir == 1) /* horz */
3137 self->max_horz = max;
3138 if (dir == 0 || dir == 2) /* vert */
3139 self->max_vert = max;
3140
3141 client_change_state(self); /* change the state hints on the client */
3142
3143 client_setup_decor_and_functions(self, FALSE);
3144 client_move_resize(self, x, y, w, h);
3145 }
3146
3147 void client_shade(ObClient *self, gboolean shade)
3148 {
3149 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3150 shade) || /* can't shade */
3151 self->shaded == shade) return; /* already done */
3152
3153 self->shaded = shade;
3154 client_change_state(self);
3155 client_change_wm_state(self); /* the window is being hidden/shown */
3156 /* resize the frame to just the titlebar */
3157 frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
3158 }
3159
3160 void client_close(ObClient *self)
3161 {
3162 XEvent ce;
3163
3164 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3165
3166 /* in the case that the client provides no means to requesting that it
3167 close, we just kill it */
3168 if (!self->delete_window)
3169 client_kill(self);
3170
3171 /*
3172 XXX: itd be cool to do timeouts and shit here for killing the client's
3173 process off
3174 like... if the window is around after 5 seconds, then the close button
3175 turns a nice red, and if this function is called again, the client is
3176 explicitly killed.
3177 */
3178
3179 ce.xclient.type = ClientMessage;
3180 ce.xclient.message_type = prop_atoms.wm_protocols;
3181 ce.xclient.display = ob_display;
3182 ce.xclient.window = self->window;
3183 ce.xclient.format = 32;
3184 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
3185 ce.xclient.data.l[1] = event_curtime;
3186 ce.xclient.data.l[2] = 0l;
3187 ce.xclient.data.l[3] = 0l;
3188 ce.xclient.data.l[4] = 0l;
3189 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
3190 }
3191
3192 void client_kill(ObClient *self)
3193 {
3194 XKillClient(ob_display, self->window);
3195 }
3196
3197 void client_hilite(ObClient *self, gboolean hilite)
3198 {
3199 if (self->demands_attention == hilite)
3200 return; /* no change */
3201
3202 /* don't allow focused windows to hilite */
3203 self->demands_attention = hilite && !client_focused(self);
3204 if (self->frame != NULL) { /* if we're mapping, just set the state */
3205 if (self->demands_attention)
3206 frame_flash_start(self->frame);
3207 else
3208 frame_flash_stop(self->frame);
3209 client_change_state(self);
3210 }
3211 }
3212
3213 void client_set_desktop_recursive(ObClient *self,
3214 guint target,
3215 gboolean donthide,
3216 gboolean dontraise)
3217 {
3218 guint old;
3219 GSList *it;
3220
3221 if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3222
3223 ob_debug("Setting desktop %u\n", target+1);
3224
3225 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3226
3227 old = self->desktop;
3228 self->desktop = target;
3229 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
3230 /* the frame can display the current desktop state */
3231 frame_adjust_state(self->frame);
3232 /* 'move' the window to the new desktop */
3233 if (!donthide)
3234 client_hide(self);
3235 client_show(self);
3236 /* raise if it was not already on the desktop */
3237 if (old != DESKTOP_ALL && !dontraise)
3238 stacking_raise(CLIENT_AS_WINDOW(self));
3239 if (STRUT_EXISTS(self->strut))
3240 screen_update_areas();
3241 else
3242 /* the new desktop's geometry may be different, so we may need to
3243 resize, for example if we are maximized */
3244 client_reconfigure(self, FALSE);
3245 }
3246
3247 /* move all transients */
3248 for (it = self->transients; it; it = g_slist_next(it))
3249 if (it->data != self)
3250 if (client_is_direct_child(self, it->data))
3251 client_set_desktop_recursive(it->data, target,
3252 donthide, dontraise);
3253 }
3254
3255 void client_set_desktop(ObClient *self, guint target,
3256 gboolean donthide, gboolean dontraise)
3257 {
3258 self = client_search_top_direct_parent(self);
3259 client_set_desktop_recursive(self, target, donthide, dontraise);
3260 }
3261
3262 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3263 {
3264 while (child != parent && (child = client_direct_parent(child)));
3265 return child == parent;
3266 }
3267
3268 ObClient *client_search_modal_child(ObClient *self)
3269 {
3270 GSList *it;
3271 ObClient *ret;
3272
3273 for (it = self->transients; it; it = g_slist_next(it)) {
3274 ObClient *c = it->data;
3275 if ((ret = client_search_modal_child(c))) return ret;
3276 if (c->modal) return c;
3277 }
3278 return NULL;
3279 }
3280
3281 gboolean client_validate(ObClient *self)
3282 {
3283 XEvent e;
3284
3285 XSync(ob_display, FALSE); /* get all events on the server */
3286
3287 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
3288 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
3289 XPutBackEvent(ob_display, &e);
3290 return FALSE;
3291 }
3292
3293 return TRUE;
3294 }
3295
3296 void client_set_wm_state(ObClient *self, glong state)
3297 {
3298 if (state == self->wmstate) return; /* no change */
3299
3300 switch (state) {
3301 case IconicState:
3302 client_iconify(self, TRUE, TRUE, FALSE);
3303 break;
3304 case NormalState:
3305 client_iconify(self, FALSE, TRUE, FALSE);
3306 break;
3307 }
3308 }
3309
3310 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3311 {
3312 gboolean shaded = self->shaded;
3313 gboolean fullscreen = self->fullscreen;
3314 gboolean undecorated = self->undecorated;
3315 gboolean max_horz = self->max_horz;
3316 gboolean max_vert = self->max_vert;
3317 gboolean modal = self->modal;
3318 gboolean iconic = self->iconic;
3319 gboolean demands_attention = self->demands_attention;
3320 gboolean above = self->above;
3321 gboolean below = self->below;
3322 gint i;
3323
3324 if (!(action == prop_atoms.net_wm_state_add ||
3325 action == prop_atoms.net_wm_state_remove ||
3326 action == prop_atoms.net_wm_state_toggle))
3327 /* an invalid action was passed to the client message, ignore it */
3328 return;
3329
3330 for (i = 0; i < 2; ++i) {
3331 Atom state = i == 0 ? data1 : data2;
3332
3333 if (!state) continue;
3334
3335 /* if toggling, then pick whether we're adding or removing */
3336 if (action == prop_atoms.net_wm_state_toggle) {
3337 if (state == prop_atoms.net_wm_state_modal)
3338 action = modal ? prop_atoms.net_wm_state_remove :
3339 prop_atoms.net_wm_state_add;
3340 else if (state == prop_atoms.net_wm_state_maximized_vert)
3341 action = self->max_vert ? prop_atoms.net_wm_state_remove :
3342 prop_atoms.net_wm_state_add;
3343 else if (state == prop_atoms.net_wm_state_maximized_horz)
3344 action = self->max_horz ? prop_atoms.net_wm_state_remove :
3345 prop_atoms.net_wm_state_add;
3346 else if (state == prop_atoms.net_wm_state_shaded)
3347 action = shaded ? prop_atoms.net_wm_state_remove :
3348 prop_atoms.net_wm_state_add;
3349 else if (state == prop_atoms.net_wm_state_skip_taskbar)
3350 action = self->skip_taskbar ?
3351 prop_atoms.net_wm_state_remove :
3352 prop_atoms.net_wm_state_add;
3353 else if (state == prop_atoms.net_wm_state_skip_pager)
3354 action = self->skip_pager ?
3355 prop_atoms.net_wm_state_remove :
3356 prop_atoms.net_wm_state_add;
3357 else if (state == prop_atoms.net_wm_state_hidden)
3358 action = self->iconic ?
3359 prop_atoms.net_wm_state_remove :
3360 prop_atoms.net_wm_state_add;
3361 else if (state == prop_atoms.net_wm_state_fullscreen)
3362 action = fullscreen ?
3363 prop_atoms.net_wm_state_remove :
3364 prop_atoms.net_wm_state_add;
3365 else if (state == prop_atoms.net_wm_state_above)
3366 action = self->above ? prop_atoms.net_wm_state_remove :
3367 prop_atoms.net_wm_state_add;
3368 else if (state == prop_atoms.net_wm_state_below)
3369 action = self->below ? prop_atoms.net_wm_state_remove :
3370 prop_atoms.net_wm_state_add;
3371 else if (state == prop_atoms.net_wm_state_demands_attention)
3372 action = self->demands_attention ?
3373 prop_atoms.net_wm_state_remove :
3374 prop_atoms.net_wm_state_add;
3375 else if (state == prop_atoms.ob_wm_state_undecorated)
3376 action = undecorated ? prop_atoms.net_wm_state_remove :
3377 prop_atoms.net_wm_state_add;
3378 }
3379
3380 if (action == prop_atoms.net_wm_state_add) {
3381 if (state == prop_atoms.net_wm_state_modal) {
3382 modal = TRUE;
3383 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
3384 max_vert = TRUE;
3385 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
3386 max_horz = TRUE;
3387 } else if (state == prop_atoms.net_wm_state_shaded) {
3388 shaded = TRUE;
3389 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
3390 self->skip_taskbar = TRUE;
3391 } else if (state == prop_atoms.net_wm_state_skip_pager) {
3392 self->skip_pager = TRUE;
3393 } else if (state == prop_atoms.net_wm_state_hidden) {
3394 iconic = TRUE;
3395 } else if (state == prop_atoms.net_wm_state_fullscreen) {
3396 fullscreen = TRUE;
3397 } else if (state == prop_atoms.net_wm_state_above) {
3398 above = TRUE;
3399 below = FALSE;
3400 } else if (state == prop_atoms.net_wm_state_below) {
3401 above = FALSE;
3402 below = TRUE;
3403 } else if (state == prop_atoms.net_wm_state_demands_attention) {
3404 demands_attention = TRUE;
3405 } else if (state == prop_atoms.ob_wm_state_undecorated) {
3406 undecorated = TRUE;
3407 }
3408
3409 } else { /* action == prop_atoms.net_wm_state_remove */
3410 if (state == prop_atoms.net_wm_state_modal) {
3411 modal = FALSE;
3412 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
3413 max_vert = FALSE;
3414 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
3415 max_horz = FALSE;
3416 } else if (state == prop_atoms.net_wm_state_shaded) {
3417 shaded = FALSE;
3418 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
3419 self->skip_taskbar = FALSE;
3420 } else if (state == prop_atoms.net_wm_state_skip_pager) {
3421 self->skip_pager = FALSE;
3422 } else if (state == prop_atoms.net_wm_state_hidden) {
3423 iconic = FALSE;
3424 } else if (state == prop_atoms.net_wm_state_fullscreen) {
3425 fullscreen = FALSE;
3426 } else if (state == prop_atoms.net_wm_state_above) {
3427 above = FALSE;
3428 } else if (state == prop_atoms.net_wm_state_below) {
3429 below = FALSE;
3430 } else if (state == prop_atoms.net_wm_state_demands_attention) {
3431 demands_attention = FALSE;
3432 } else if (state == prop_atoms.ob_wm_state_undecorated) {
3433 undecorated = FALSE;
3434 }
3435 }
3436 }
3437
3438 if (max_horz != self->max_horz || max_vert != self->max_vert) {
3439 if (max_horz != self->max_horz && max_vert != self->max_vert) {
3440 /* toggling both */
3441 if (max_horz == max_vert) { /* both going the same way */
3442 client_maximize(self, max_horz, 0);
3443 } else {
3444 client_maximize(self, max_horz, 1);
3445 client_maximize(self, max_vert, 2);
3446 }
3447 } else {
3448 /* toggling one */
3449 if (max_horz != self->max_horz)
3450 client_maximize(self, max_horz, 1);
3451 else
3452 client_maximize(self, max_vert, 2);
3453 }
3454 }
3455 /* change fullscreen state before shading, as it will affect if the window
3456 can shade or not */
3457 if (fullscreen != self->fullscreen)
3458 client_fullscreen(self, fullscreen);
3459 if (shaded != self->shaded)
3460 client_shade(self, shaded);
3461 if (undecorated != self->undecorated)
3462 client_set_undecorated(self, undecorated);
3463 if (above != self->above || below != self->below) {
3464 self->above = above;
3465 self->below = below;
3466 client_calc_layer(self);
3467 }
3468
3469 if (modal != self->modal) {
3470 self->modal = modal;
3471 /* when a window changes modality, then its stacking order with its
3472 transients needs to change */
3473 stacking_raise(CLIENT_AS_WINDOW(self));
3474
3475 /* it also may get focused. if something is focused that shouldn't
3476 be focused anymore, then move the focus */
3477 if (focus_client && client_focus_target(focus_client) != focus_client)
3478 client_focus(focus_client);
3479 }
3480
3481 if (iconic != self->iconic)
3482 client_iconify(self, iconic, FALSE, FALSE);
3483
3484 if (demands_attention != self->demands_attention)
3485 client_hilite(self, demands_attention);
3486
3487 client_change_state(self); /* change the hint to reflect these changes */
3488 }
3489
3490 ObClient *client_focus_target(ObClient *self)
3491 {
3492 ObClient *child = NULL;
3493
3494 child = client_search_modal_child(self);
3495 if (child) return child;
3496 return self;
3497 }
3498
3499 gboolean client_can_focus(ObClient *self)
3500 {
3501 /* choose the correct target */
3502 self = client_focus_target(self);
3503
3504 if (!self->frame->visible)
3505 return FALSE;
3506
3507 if (!(self->can_focus || self->focus_notify))
3508 return FALSE;
3509
3510 return TRUE;
3511 }
3512
3513 gboolean client_focus(ObClient *self)
3514 {
3515 /* we might not focus this window, so if we have modal children which would
3516 be focused instead, bring them to this desktop */
3517 client_bring_modal_windows(self);
3518
3519 /* choose the correct target */
3520 self = client_focus_target(self);
3521
3522 if (!client_can_focus(self)) {
3523 ob_debug_type(OB_DEBUG_FOCUS,
3524 "Client %s can't be focused\n", self->title);
3525 return FALSE;
3526 }
3527
3528 ob_debug_type(OB_DEBUG_FOCUS,
3529 "Focusing client \"%s\" (0x%x) at time %u\n",
3530 self->title, self->window, event_curtime);
3531
3532 /* if using focus_delay, stop the timer now so that focus doesn't
3533 go moving on us */
3534 event_halt_focus_delay();
3535
3536 /* if there is a grab going on, then we need to cancel it. if we move
3537 focus during the grab, applications will get NotifyWhileGrabbed events
3538 and ignore them !
3539
3540 actions should not rely on being able to move focus during an
3541 interactive grab.
3542 */
3543 event_cancel_all_key_grabs();
3544
3545 xerror_set_ignore(TRUE);
3546 xerror_occured = FALSE;
3547
3548 if (self->can_focus) {
3549 /* This can cause a BadMatch error with CurrentTime, or if an app
3550 passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3551 XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
3552 event_curtime);
3553 }
3554
3555 if (self->focus_notify) {
3556 XEvent ce;
3557 ce.xclient.type = ClientMessage;
3558 ce.xclient.message_type = prop_atoms.wm_protocols;
3559 ce.xclient.display = ob_display;
3560 ce.xclient.window = self->window;
3561 ce.xclient.format = 32;
3562 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
3563 ce.xclient.data.l[1] = event_curtime;
3564 ce.xclient.data.l[2] = 0l;
3565 ce.xclient.data.l[3] = 0l;
3566 ce.xclient.data.l[4] = 0l;
3567 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
3568 }
3569
3570 xerror_set_ignore(FALSE);
3571
3572 ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d\n", xerror_occured);
3573 return !xerror_occured;
3574 }
3575
3576 static void client_present(ObClient *self, gboolean here, gboolean raise,
3577 gboolean unshade)
3578 {
3579 if (client_normal(self) && screen_showing_desktop)
3580 screen_show_desktop(FALSE, self);
3581 if (self->iconic)
3582 client_iconify(self, FALSE, here, FALSE);
3583 if (self->desktop != DESKTOP_ALL &&
3584 self->desktop != screen_desktop)
3585 {
3586 if (here)
3587 client_set_desktop(self, screen_desktop, FALSE, TRUE);
3588 else
3589 screen_set_desktop(self->desktop, FALSE);
3590 } else if (!self->frame->visible)
3591 /* if its not visible for other reasons, then don't mess
3592 with it */
3593 return;
3594 if (self->shaded && unshade)
3595 client_shade(self, FALSE);
3596 if (raise)
3597 stacking_raise(CLIENT_AS_WINDOW(self));
3598
3599 client_focus(self);
3600 }
3601
3602 void client_activate(ObClient *self, gboolean here, gboolean raise,
3603 gboolean unshade, gboolean user)
3604 {
3605 client_present(self, here, raise, unshade);
3606 }
3607
3608 static void client_bring_windows_recursive(ObClient *self,
3609 guint desktop,
3610 gboolean helpers,
3611 gboolean modals,
3612 gboolean iconic)
3613 {
3614 GSList *it;
3615
3616 for (it = self->transients; it; it = g_slist_next(it))
3617 client_bring_windows_recursive(it->data, desktop,
3618 helpers, modals, iconic);
3619
3620 if (((helpers && client_helper(self)) ||
3621 (modals && self->modal)) &&
3622 ((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
3623 (iconic && self->iconic)))
3624 {
3625 if (iconic && self->iconic)
3626 client_iconify(self, FALSE, TRUE, FALSE);
3627 else
3628 client_set_desktop(self, desktop, FALSE, FALSE);
3629 }
3630 }
3631
3632 void client_bring_helper_windows(ObClient *self)
3633 {
3634 client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
3635 }
3636
3637 void client_bring_modal_windows(ObClient *self)
3638 {
3639 client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
3640 }
3641
3642 gboolean client_focused(ObClient *self)
3643 {
3644 return self == focus_client;
3645 }
3646
3647 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
3648 {
3649 guint i;
3650 gulong min_diff, min_i;
3651
3652 if (!self->nicons) {
3653 ObClientIcon *parent = NULL;
3654 GSList *it;
3655
3656 for (it = self->parents; it; it = g_slist_next(it)) {
3657 ObClient *c = it->data;
3658 if ((parent = client_icon_recursive(c, w, h)))
3659 break;
3660 }
3661
3662 return parent;
3663 }
3664
3665 /* some kind of crappy approximation to find the icon closest in size to
3666 what we requested, but icons are generally all the same ratio as
3667 eachother so it's good enough. */
3668
3669 min_diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h);
3670 min_i = 0;
3671
3672 for (i = 1; i < self->nicons; ++i) {
3673 gulong diff;
3674
3675 diff = ABS(self->icons[i].width - w) + ABS(self->icons[i].height - h);
3676 if (diff < min_diff) {
3677 min_diff = diff;
3678 min_i = i;
3679 }
3680 }
3681 return &self->icons[min_i];
3682 }
3683
3684 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
3685 {
3686 ObClientIcon *ret;
3687 static ObClientIcon deficon;
3688
3689 if (!(ret = client_icon_recursive(self, w, h))) {
3690 deficon.width = deficon.height = 48;
3691 deficon.data = ob_rr_theme->def_win_icon;
3692 ret = &deficon;
3693 }
3694 return ret;
3695 }
3696
3697 void client_set_layer(ObClient *self, gint layer)
3698 {
3699 if (layer < 0) {
3700 self->below = TRUE;
3701 self->above = FALSE;
3702 } else if (layer == 0) {
3703 self->below = self->above = FALSE;
3704 } else {
3705 self->below = FALSE;
3706 self->above = TRUE;
3707 }
3708 client_calc_layer(self);
3709 client_change_state(self); /* reflect this in the state hints */
3710 }
3711
3712 void client_set_undecorated(ObClient *self, gboolean undecorated)
3713 {
3714 if (self->undecorated != undecorated &&
3715 /* don't let it undecorate if the function is missing, but let
3716 it redecorate */
3717 (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
3718 {
3719 self->undecorated = undecorated;
3720 client_setup_decor_and_functions(self, TRUE);
3721 client_change_state(self); /* reflect this in the state hints */
3722 }
3723 }
3724
3725 guint client_monitor(ObClient *self)
3726 {
3727 return screen_find_monitor(&self->frame->area);
3728 }
3729
3730 ObClient *client_direct_parent(ObClient *self)
3731 {
3732 if (!self->parents) return NULL;
3733 if (self->transient_for_group) return NULL;
3734 return self->parents->data;
3735 }
3736
3737 ObClient *client_search_top_direct_parent(ObClient *self)
3738 {
3739 ObClient *p;
3740 while ((p = client_direct_parent(self))) self = p;
3741 return self;
3742 }
3743
3744 static GSList *client_search_all_top_parents_internal(ObClient *self,
3745 gboolean bylayer,
3746 ObStackingLayer layer)
3747 {
3748 GSList *ret;
3749 ObClient *p;
3750
3751 /* move up the direct transient chain as far as possible */
3752 while ((p = client_direct_parent(self)) &&
3753 (!bylayer || p->layer == layer))
3754 self = p;
3755
3756 if (!self->parents)
3757 ret = g_slist_prepend(NULL, self);
3758 else
3759 ret = g_slist_copy(self->parents);
3760
3761 return ret;
3762 }
3763
3764 GSList *client_search_all_top_parents(ObClient *self)
3765 {
3766 return client_search_all_top_parents_internal(self, FALSE, 0);
3767 }
3768
3769 GSList *client_search_all_top_parents_layer(ObClient *self)
3770 {
3771 return client_search_all_top_parents_internal(self, TRUE, self->layer);
3772 }
3773
3774 ObClient *client_search_focus_parent(ObClient *self)
3775 {
3776 GSList *it;
3777
3778 for (it = self->parents; it; it = g_slist_next(it))
3779 if (client_focused(it->data)) return it->data;
3780
3781 return NULL;
3782 }
3783
3784 ObClient *client_search_parent(ObClient *self, ObClient *search)
3785 {
3786 GSList *it;
3787
3788 for (it = self->parents; it; it = g_slist_next(it))
3789 if (it->data == search) return search;
3790
3791 return NULL;
3792 }
3793
3794 ObClient *client_search_transient(ObClient *self, ObClient *search)
3795 {
3796 GSList *sit;
3797
3798 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
3799 if (sit->data == search)
3800 return search;
3801 if (client_search_transient(sit->data, search))
3802 return search;
3803 }
3804 return NULL;
3805 }
3806
3807 static void detect_edge(Rect area, ObDirection dir,
3808 gint my_head, gint my_size,
3809 gint my_edge_start, gint my_edge_size,
3810 gint *dest, gboolean *near_edge)
3811 {
3812 gint edge_start, edge_size, head, tail;
3813 gboolean skip_head = FALSE, skip_tail = FALSE;
3814
3815 switch (dir) {
3816 case OB_DIRECTION_NORTH:
3817 case OB_DIRECTION_SOUTH:
3818 edge_start = area.x;
3819 edge_size = area.width;
3820 break;
3821 case OB_DIRECTION_EAST:
3822 case OB_DIRECTION_WEST:
3823 edge_start = area.y;
3824 edge_size = area.height;
3825 break;
3826 default:
3827 g_assert_not_reached();
3828 }
3829
3830 /* do we collide with this window? */
3831 if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
3832 edge_start, edge_size))
3833 return;
3834
3835 switch (dir) {
3836 case OB_DIRECTION_NORTH:
3837 head = RECT_BOTTOM(area);
3838 tail = RECT_TOP(area);
3839 break;
3840 case OB_DIRECTION_SOUTH:
3841 head = RECT_TOP(area);
3842 tail = RECT_BOTTOM(area);
3843 break;
3844 case OB_DIRECTION_WEST:
3845 head = RECT_RIGHT(area);
3846 tail = RECT_LEFT(area);
3847 break;
3848 case OB_DIRECTION_EAST:
3849 head = RECT_LEFT(area);
3850 tail = RECT_RIGHT(area);
3851 break;
3852 default:
3853 g_assert_not_reached();
3854 }
3855 switch (dir) {
3856 case OB_DIRECTION_NORTH:
3857 case OB_DIRECTION_WEST:
3858 /* check if our window is past the head of this window */
3859 if (my_head <= head + 1)
3860 skip_head = TRUE;
3861 /* check if our window's tail is past the tail of this window */
3862 if (my_head + my_size - 1 <= tail)
3863 skip_tail = TRUE;
3864 /* check if the head of this window is closer than the previously
3865 chosen edge (take into account that the previously chosen
3866 edge might have been a tail, not a head) */
3867 if (head + (*near_edge ? 0 : my_size) < *dest)
3868 skip_head = TRUE;
3869 /* check if the tail of this window is closer than the previously
3870 chosen edge (take into account that the previously chosen
3871 edge might have been a head, not a tail) */
3872 if (tail - (!*near_edge ? 0 : my_size) < *dest)
3873 skip_tail = TRUE;
3874 break;
3875 case OB_DIRECTION_SOUTH:
3876 case OB_DIRECTION_EAST:
3877 /* check if our window is past the head of this window */
3878 if (my_head >= head - 1)
3879 skip_head = TRUE;
3880 /* check if our window's tail is past the tail of this window */
3881 if (my_head - my_size + 1 >= tail)
3882 skip_tail = TRUE;
3883 /* check if the head of this window is closer than the previously
3884 chosen edge (take into account that the previously chosen
3885 edge might have been a tail, not a head) */
3886 if (head - (*near_edge ? 0 : my_size) > *dest)
3887 skip_head = TRUE;
3888 /* check if the tail of this window is closer than the previously
3889 chosen edge (take into account that the previously chosen
3890 edge might have been a head, not a tail) */
3891 if (tail + (!*near_edge ? 0 : my_size) > *dest)
3892 skip_tail = TRUE;
3893 break;
3894 default:
3895 g_assert_not_reached();
3896 }
3897
3898 ob_debug("my head %d size %d\n", my_head, my_size);
3899 ob_debug("head %d tail %d deest %d\n", head, tail, *dest);
3900 if (!skip_head) {
3901 ob_debug("using near edge %d\n", head);
3902 *dest = head;
3903 *near_edge = TRUE;
3904 }
3905 else if (!skip_tail) {
3906 ob_debug("using far edge %d\n", tail);
3907 *dest = tail;
3908 *near_edge = FALSE;
3909 }
3910 }
3911
3912 void client_find_edge_directional(ObClient *self, ObDirection dir,
3913 gint my_head, gint my_size,
3914 gint my_edge_start, gint my_edge_size,
3915 gint *dest, gboolean *near_edge)
3916 {
3917 GList *it;
3918 Rect *a, *mon;
3919 Rect dock_area;
3920 gint edge;
3921
3922 a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
3923 &self->frame->area);
3924 mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR,
3925 &self->frame->area);
3926
3927 switch (dir) {
3928 case OB_DIRECTION_NORTH:
3929 if (my_head >= RECT_TOP(*mon) + 1)
3930 edge = RECT_TOP(*mon) - 1;
3931 else
3932 edge = RECT_TOP(*a) - 1;
3933 break;
3934 case OB_DIRECTION_SOUTH:
3935 if (my_head <= RECT_BOTTOM(*mon) - 1)
3936 edge = RECT_BOTTOM(*mon) + 1;
3937 else
3938 edge = RECT_BOTTOM(*a) + 1;
3939 break;
3940 case OB_DIRECTION_EAST:
3941 if (my_head <= RECT_RIGHT(*mon) - 1)
3942 edge = RECT_RIGHT(*mon) + 1;
3943 else
3944 edge = RECT_RIGHT(*a) + 1;
3945 break;
3946 case OB_DIRECTION_WEST:
3947 if (my_head >= RECT_LEFT(*mon) + 1)
3948 edge = RECT_LEFT(*mon) - 1;
3949 else
3950 edge = RECT_LEFT(*a) - 1;
3951 break;
3952 default:
3953 g_assert_not_reached();
3954 }
3955 /* default to the far edge, then narrow it down */
3956 *dest = edge;
3957 *near_edge = TRUE;
3958
3959 for (it = client_list; it; it = g_list_next(it)) {
3960 ObClient *cur = it->data;
3961
3962 /* skip windows to not bump into */
3963 if (cur == self)
3964 continue;
3965 if (cur->iconic)
3966 continue;
3967 if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
3968 cur->desktop != screen_desktop)
3969 continue;
3970
3971 ob_debug("trying window %s\n", cur->title);
3972
3973 detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
3974 my_edge_size, dest, near_edge);
3975 }
3976 dock_get_area(&dock_area);
3977 detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
3978 my_edge_size, dest, near_edge);
3979 g_free(a);
3980 g_free(mon);
3981 }
3982
3983 void client_find_move_directional(ObClient *self, ObDirection dir,
3984 gint *x, gint *y)
3985 {
3986 gint head, size;
3987 gint e, e_start, e_size;
3988 gboolean near;
3989
3990 switch (dir) {
3991 case OB_DIRECTION_EAST:
3992 head = RECT_RIGHT(self->frame->area);
3993 size = self->frame->area.width;
3994 e_start = RECT_TOP(self->frame->area);
3995 e_size = self->frame->area.height;
3996 break;
3997 case OB_DIRECTION_WEST:
3998 head = RECT_LEFT(self->frame->area);
3999 size = self->frame->area.width;
4000 e_start = RECT_TOP(self->frame->area);
4001 e_size = self->frame->area.height;
4002 break;
4003 case OB_DIRECTION_NORTH:
4004 head = RECT_TOP(self->frame->area);
4005 size = self->frame->area.height;
4006 e_start = RECT_LEFT(self->frame->area);
4007 e_size = self->frame->area.width;
4008 break;
4009 case OB_DIRECTION_SOUTH:
4010 head = RECT_BOTTOM(self->frame->area);
4011 size = self->frame->area.height;
4012 e_start = RECT_LEFT(self->frame->area);
4013 e_size = self->frame->area.width;
4014 break;
4015 default:
4016 g_assert_not_reached();
4017 }
4018
4019 client_find_edge_directional(self, dir, head, size,
4020 e_start, e_size, &e, &near);
4021 *x = self->frame->area.x;
4022 *y = self->frame->area.y;
4023 switch (dir) {
4024 case OB_DIRECTION_EAST:
4025 if (near) e -= self->frame->area.width;
4026 else e++;
4027 *x = e;
4028 break;
4029 case OB_DIRECTION_WEST:
4030 if (near) e++;
4031 else e -= self->frame->area.width;
4032 *x = e;
4033 break;
4034 case OB_DIRECTION_NORTH:
4035 if (near) e++;
4036 else e -= self->frame->area.height;
4037 *y = e;
4038 break;
4039 case OB_DIRECTION_SOUTH:
4040 if (near) e -= self->frame->area.height;
4041 else e++;
4042 *y = e;
4043 break;
4044 default:
4045 g_assert_not_reached();
4046 }
4047 frame_frame_gravity(self->frame, x, y);
4048 }
4049
4050 void client_find_resize_directional(ObClient *self, ObDirection side,
4051 gboolean grow,
4052 gint *x, gint *y, gint *w, gint *h)
4053 {
4054 gint head;
4055 gint e, e_start, e_size, delta;
4056 gboolean near;
4057 ObDirection dir;
4058
4059 switch (side) {
4060 case OB_DIRECTION_EAST:
4061 head = RECT_RIGHT(self->frame->area) +
4062 (self->size_inc.width - 1) * (grow ? 1 : -1);
4063 e_start = RECT_TOP(self->frame->area);
4064 e_size = self->frame->area.height;
4065 dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4066 break;
4067 case OB_DIRECTION_WEST:
4068 head = RECT_LEFT(self->frame->area) -
4069 (self->size_inc.width - 1) * (grow ? 1 : -1);
4070 e_start = RECT_TOP(self->frame->area);
4071 e_size = self->frame->area.height;
4072 dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4073 break;
4074 case OB_DIRECTION_NORTH:
4075 head = RECT_TOP(self->frame->area) -
4076 (self->size_inc.height - 1) * (grow ? 1 : -1);
4077 e_start = RECT_LEFT(self->frame->area);
4078 e_size = self->frame->area.width;
4079 dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4080 break;
4081 case OB_DIRECTION_SOUTH:
4082 head = RECT_BOTTOM(self->frame->area) +
4083 (self->size_inc.height - 1) * (grow ? 1 : -1);
4084 e_start = RECT_LEFT(self->frame->area);
4085 e_size = self->frame->area.width;
4086 dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4087 break;
4088 default:
4089 g_assert_not_reached();
4090 }
4091
4092 ob_debug("head %d dir %d\n", head, dir);
4093 client_find_edge_directional(self, dir, head, 1,
4094 e_start, e_size, &e, &near);
4095 ob_debug("edge %d\n", e);
4096 *x = self->frame->area.x;
4097 *y = self->frame->area.y;
4098 *w = self->frame->area.width;
4099 *h = self->frame->area.height;
4100 switch (side) {
4101 case OB_DIRECTION_EAST:
4102 if (grow == near) --e;
4103 delta = e - RECT_RIGHT(self->frame->area);
4104 *w += delta;
4105 break;
4106 case OB_DIRECTION_WEST:
4107 if (grow == near) ++e;
4108 delta = RECT_LEFT(self->frame->area) - e;
4109 *x -= delta;
4110 *w += delta;
4111 break;
4112 case OB_DIRECTION_NORTH:
4113 if (grow == near) ++e;
4114 delta = RECT_TOP(self->frame->area) - e;
4115 *y -= delta;
4116 *h += delta;
4117 break;
4118 case OB_DIRECTION_SOUTH:
4119 if (grow == near) --e;
4120 delta = e - RECT_BOTTOM(self->frame->area);
4121 *h += delta;
4122 break;
4123 default:
4124 g_assert_not_reached();
4125 }
4126 frame_frame_gravity(self->frame, x, y);
4127 *w -= self->frame->size.left + self->frame->size.right;
4128 *h -= self->frame->size.top + self->frame->size.bottom;
4129 }
4130
4131 ObClient* client_under_pointer(void)
4132 {
4133 gint x, y;
4134 GList *it;
4135 ObClient *ret = NULL;
4136
4137 if (screen_pointer_pos(&x, &y)) {
4138 for (it = stacking_list; it; it = g_list_next(it)) {
4139 if (WINDOW_IS_CLIENT(it->data)) {
4140 ObClient *c = WINDOW_AS_CLIENT(it->data);
4141 if (c->frame->visible &&
4142 /* check the desktop, this is done during desktop
4143 switching and windows are shown/hidden status is not
4144 reliable */
4145 (c->desktop == screen_desktop ||
4146 c->desktop == DESKTOP_ALL) &&
4147 /* ignore all animating windows */
4148 !frame_iconify_animating(c->frame) &&
4149 RECT_CONTAINS(c->frame->area, x, y))
4150 {
4151 ret = c;
4152 break;
4153 }
4154 }
4155 }
4156 }
4157 return ret;
4158 }
4159
4160 gboolean client_has_group_siblings(ObClient *self)
4161 {
4162 return self->group && self->group->members->next;
4163 }
This page took 0.234638 seconds and 4 git commands to generate.