~ubuntu-branches/ubuntu/wily/openbox/wily-proposed

« back to all changes in this revision

Viewing changes to .pc/openbox-3.5.0-title-matching.patch/openbox/client.c

  • Committer: Package Import Robot
  • Author(s): Mateusz Łukasik
  • Date: 2013-08-30 18:32:43 UTC
  • mfrom: (1.1.12)
  • Revision ID: package-import@ubuntu.com-20130830183243-o45g9l2xdljdu0rg
Tags: 3.5.2-1
* New upstream release (LP: #957808):
  + Fix Stacking of dock doesn't work correctly (Closes: #680664),
  + Fix Windows do not refresh anymore when lauching epdfview when
    awn is running (Closes: #683556),
  + Fix xmodmap freezes openbox in lxde (Closes: #678993).
* debian/control:
  + Rename libobrender27 to libobrender29 to reflect ABI change,
  + Rename libobt0 to libobt2 to reflect ABI change,
  + Add librsvg2-dev and libxi-dev to Build-Depends,
  + Add gnome-panel-control binary package,
  + Tidy up Build-Depends and Depends.
  + Fix wording in short description.
* Remove from debian/patches:
  + 04_xsession.desktop_translation.patch,
  + 05_simplify_gnome_session.patch,
  + 90_fix_link_obt.patch,
  + 675991_fix_crash_from_gtk3_apps.patch,
  + 666676_wrong_undecorated_window_placement.patch,
  + clever-rectangle-picking.patch,
  + use-nearest-monitor.patch,
  + 91_fix_loose_focus.patch,
  + 658081_fix_kde_menu.patch,
  + add_automake1.11_support.patch -- All included upstream.
* Rename in debian/patches:
  + 07_fix_xml_load_file.patch to 04_fix_xml_load_file.patch,
  + openbox-3.5.0-title-matching.patch to
    05_openbox-3.5.0-title-matching.patch,
  + openbox-3.5.0-which-2.20.patch to 06_openbox-3.5.0-which-2.20.patch.
* Update symbols files.
* debian/rules:
  + override_dh_auto_configure to configure with librsvg support,
  + enable all hardening flags.
* Add debian/patches/07_update_desktop.patch to provide keywords
  in desktop file.
* Add debian/patches/719620_fix_show_startup_notification.patch
  (Closes: #719620, LP: #862662).
* Update debian/NEWS.Debian.
* Add debian/patches/08_fix_causing_submenuShowDelay.patch from
  upstream.

Show diffs side-by-side

added added

removed removed

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