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