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