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