~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to kwin/client.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/********************************************************************
 
2
 KWin - the KDE window manager
 
3
 This file is part of the KDE project.
 
4
 
 
5
Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
 
6
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
 
7
 
 
8
This program is free software; you can redistribute it and/or modify
 
9
it under the terms of the GNU General Public License as published by
 
10
the Free Software Foundation; either version 2 of the License, or
 
11
(at your option) any later version.
 
12
 
 
13
This program is distributed in the hope that it will be useful,
 
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
GNU General Public License for more details.
 
17
 
 
18
You should have received a copy of the GNU General Public License
 
19
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
*********************************************************************/
 
21
 
 
22
#include "client.h"
 
23
 
 
24
#include <QApplication>
 
25
#include <QPainter>
 
26
#include <QDateTime>
 
27
#include <QProcess>
 
28
#include <QPaintEngine>
 
29
#include <unistd.h>
 
30
#include <kstandarddirs.h>
 
31
#include <QWhatsThis>
 
32
#include <kwindowsystem.h>
 
33
#include <kiconloader.h>
 
34
#include <stdlib.h>
 
35
#include <signal.h>
 
36
 
 
37
#include "scripting/client.h"
 
38
#include "scripting/scripting.h"
 
39
#include "scripting/workspaceproxy.h"
 
40
 
 
41
#include "bridge.h"
 
42
#include "group.h"
 
43
#include "workspace.h"
 
44
#include "atoms.h"
 
45
#include "notifications.h"
 
46
#include "rules.h"
 
47
#include "scene.h"
 
48
#include "shadow.h"
 
49
#include "deleted.h"
 
50
#include "paintredirector.h"
 
51
#include "tabbox.h"
 
52
 
 
53
#include <X11/extensions/shape.h>
 
54
#include <QX11Info>
 
55
 
 
56
#ifdef HAVE_XSYNC
 
57
#include <X11/extensions/sync.h>
 
58
#endif
 
59
 
 
60
#ifdef HAVE_XRENDER
 
61
#include <X11/extensions/Xrender.h>
 
62
#endif
 
63
 
 
64
 
 
65
// Put all externs before the namespace statement to allow the linker
 
66
// to resolve them properly
 
67
 
 
68
namespace KWin
 
69
{
 
70
 
 
71
// Creating a client:
 
72
//  - only by calling Workspace::createClient()
 
73
//      - it creates a new client and calls manage() for it
 
74
//
 
75
// Destroying a client:
 
76
//  - destroyClient() - only when the window itself has been destroyed
 
77
//      - releaseWindow() - the window is kept, only the client itself is destroyed
 
78
 
 
79
/**
 
80
 * \class Client client.h
 
81
 * \brief The Client class encapsulates a window decoration frame.
 
82
 */
 
83
 
 
84
/**
 
85
 * This ctor is "dumb" - it only initializes data. All the real initialization
 
86
 * is done in manage().
 
87
 */
 
88
Client::Client(Workspace* ws)
 
89
    : Toplevel(ws)
 
90
    , client(None)
 
91
    , wrapper(None)
 
92
    , decoration(NULL)
 
93
    , bridge(new Bridge(this))
 
94
    , move_faked_activity(false)
 
95
    , move_resize_grab_window(None)
 
96
    , move_resize_has_keyboard_grab(false)
 
97
    , transient_for (NULL)
 
98
    , transient_for_id(None)
 
99
    , original_transient_for_id(None)
 
100
    , blocks_compositing(false)
 
101
    , autoRaiseTimer(NULL)
 
102
    , shadeHoverTimer(NULL)
 
103
    , delayedMoveResizeTimer(NULL)
 
104
    , in_group(NULL)
 
105
    , window_group(None)
 
106
    , client_group(NULL)
 
107
    , in_layer(UnknownLayer)
 
108
    , ping_timer(NULL)
 
109
    , process_killer(NULL)
 
110
    , user_time(CurrentTime)   // Not known yet
 
111
    , allowed_actions(0)
 
112
    , block_geometry_updates(0)
 
113
    , pending_geometry_update(PendingGeometryNone)
 
114
    , shade_geometry_change(false)
 
115
#ifdef HAVE_XSYNC
 
116
    , sync_counter(None)
 
117
    , sync_alarm(None)
 
118
#endif
 
119
    , sync_timeout(NULL)
 
120
    , sync_resize_pending(false)
 
121
    , border_left(0)
 
122
    , border_right(0)
 
123
    , border_top(0)
 
124
    , border_bottom(0)
 
125
    , padding_left(0)
 
126
    , padding_right(0)
 
127
    , padding_top(0)
 
128
    , padding_bottom(0)
 
129
    , sm_stacking_order(-1)
 
130
    , demandAttentionKNotifyTimer(NULL)
 
131
    , m_responsibleForDecoPixmap(false)
 
132
    , paintRedirector(0)
 
133
    , electricMaximizing(false)
 
134
    , activitiesDefined(false)
 
135
    , needsSessionInteract(false)
 
136
{
 
137
    // TODO: Do all as initialization
 
138
 
 
139
    scriptCache = new QHash<QScriptEngine*, ClientResolution>();
 
140
 
 
141
    // Set the initial mapping state
 
142
    mapping_state = Withdrawn;
 
143
    quick_tile_mode = QuickTileNone;
 
144
    geom_pretile = QRect(0, 0, 0, 0);
 
145
    desk = 0; // No desktop yet
 
146
 
 
147
    mode = PositionCenter;
 
148
    buttonDown = false;
 
149
    moveResizeMode = false;
 
150
 
 
151
    info = NULL;
 
152
 
 
153
    shade_mode = ShadeNone;
 
154
    active = false;
 
155
    deleting = false;
 
156
    keep_above = false;
 
157
    keep_below = false;
 
158
    motif_may_move = true;
 
159
    motif_may_resize = true;
 
160
    motif_may_close = true;
 
161
    fullscreen_mode = FullScreenNone;
 
162
    skip_taskbar = false;
 
163
    original_skip_taskbar = false;
 
164
    minimized = false;
 
165
    hidden = false;
 
166
    modal = false;
 
167
    noborder = false;
 
168
    app_noborder = false;
 
169
    motif_noborder = false;
 
170
    urgency = false;
 
171
    ignore_focus_stealing = false;
 
172
    demands_attention = false;
 
173
    check_active_modal = false;
 
174
 
 
175
    Pdeletewindow = 0;
 
176
    Ptakefocus = 0;
 
177
    Ptakeactivity = 0;
 
178
    Pcontexthelp = 0;
 
179
    Pping = 0;
 
180
    input = false;
 
181
    skip_pager = false;
 
182
 
 
183
    max_mode = MaximizeRestore;
 
184
    maxmode_restore = MaximizeRestore;
 
185
 
 
186
    cmap = None;
 
187
 
 
188
    //Client to workspace connections require that each
 
189
    //client constructed be connected to the workspace wrapper
 
190
 
 
191
    // TabBoxClient
 
192
    m_tabBoxClient = new TabBox::TabBoxClientImpl();
 
193
    m_tabBoxClient->setClient(this);
 
194
 
 
195
    geom = QRect(0, 0, 100, 100);   // So that decorations don't start with size being (0,0)
 
196
    client_size = QSize(100, 100);
 
197
#if defined(HAVE_XSYNC) || defined(HAVE_XDAMAGE)
 
198
    ready_for_painting = false; // wait for first damage or sync reply
 
199
#endif
 
200
 
 
201
    connect(this, SIGNAL(clientGeometryShapeChanged(KWin::Client*,QRect)), SIGNAL(geometryChanged()));
 
202
    connect(this, SIGNAL(clientMaximizedStateChanged(KWin::Client*,KDecorationDefines::MaximizeMode)), SIGNAL(geometryChanged()));
 
203
    connect(this, SIGNAL(clientStepUserMovedResized(KWin::Client*,QRect)), SIGNAL(geometryChanged()));
 
204
 
 
205
    // SELI TODO: Initialize xsizehints??
 
206
}
 
207
 
 
208
/**
 
209
 * "Dumb" destructor.
 
210
 */
 
211
Client::~Client()
 
212
{
 
213
    //SWrapper::Client::clientRelease(this);
 
214
#ifdef HAVE_XSYNC
 
215
    if (sync_alarm != None)
 
216
        XSyncDestroyAlarm(display(), sync_alarm);
 
217
#endif
 
218
    assert(!moveResizeMode);
 
219
    assert(client == None);
 
220
    assert(wrapper == None);
 
221
    //assert( frameId() == None );
 
222
    assert(decoration == NULL);
 
223
    assert(block_geometry_updates == 0);
 
224
    assert(!check_active_modal);
 
225
    delete bridge;
 
226
    delete m_tabBoxClient;
 
227
    delete scriptCache;
 
228
}
 
229
 
 
230
// Use destroyClient() or releaseWindow(), Client instances cannot be deleted directly
 
231
void Client::deleteClient(Client* c, allowed_t)
 
232
{
 
233
    delete c;
 
234
}
 
235
 
 
236
/**
 
237
 * Releases the window. The client has done its job and the window is still existing.
 
238
 */
 
239
void Client::releaseWindow(bool on_shutdown)
 
240
{
 
241
    assert(!deleting);
 
242
    deleting = true;
 
243
    Deleted* del = Deleted::create(this);
 
244
    emit clientClosed(this);
 
245
    if (scene) {
 
246
        scene->windowClosed(this, del);
 
247
    }
 
248
    finishCompositing();
 
249
    workspace()->discardUsedWindowRules(this, true);   // Remove ForceTemporarily rules
 
250
    StackingUpdatesBlocker blocker(workspace());
 
251
    if (moveResizeMode)
 
252
        leaveMoveResize();
 
253
    finishWindowRules();
 
254
    ++block_geometry_updates;
 
255
    if (isOnCurrentDesktop() && isShown(true))
 
256
        addWorkspaceRepaint(visibleRect());
 
257
    // Grab X during the release to make removing of properties, setting to withdrawn state
 
258
    // and repareting to root an atomic operation (http://lists.kde.org/?l=kde-devel&m=116448102901184&w=2)
 
259
    grabXServer();
 
260
    exportMappingState(WithdrawnState);
 
261
    setModal(false);   // Otherwise its mainwindow wouldn't get focus
 
262
    hidden = true; // So that it's not considered visible anymore (can't use hideClient(), it would set flags)
 
263
    if (!on_shutdown)
 
264
        workspace()->clientHidden(this);
 
265
    XUnmapWindow(display(), frameId());  // Destroying decoration would cause ugly visual effect
 
266
    destroyDecoration();
 
267
    cleanGrouping();
 
268
    if (clientGroup())
 
269
        clientGroup()->remove(this, QRect(), true);
 
270
    if (!on_shutdown) {
 
271
        workspace()->removeClient(this, Allowed);
 
272
        // Only when the window is being unmapped, not when closing down KWin (NETWM sections 5.5,5.7)
 
273
        info->setDesktop(0);
 
274
        desk = 0;
 
275
        info->setState(0, info->state());  // Reset all state flags
 
276
    }
 
277
    XDeleteProperty(display(), client, atoms->kde_net_wm_user_creation_time);
 
278
    XDeleteProperty(display(), client, atoms->net_frame_extents);
 
279
    XDeleteProperty(display(), client, atoms->kde_net_wm_frame_strut);
 
280
    XReparentWindow(display(), client, rootWindow(), x(), y());
 
281
    XRemoveFromSaveSet(display(), client);
 
282
    XSelectInput(display(), client, NoEventMask);
 
283
    if (on_shutdown)
 
284
        // Map the window, so it can be found after another WM is started
 
285
        XMapWindow(display(), client);
 
286
    // TODO: Preserve minimized, shaded etc. state?
 
287
    else // Make sure it's not mapped if the app unmapped it (#65279). The app
 
288
        // may do map+unmap before we initially map the window by calling rawShow() from manage().
 
289
        XUnmapWindow(display(), client);
 
290
    client = None;
 
291
    XDestroyWindow(display(), wrapper);
 
292
    wrapper = None;
 
293
    XDestroyWindow(display(), frameId());
 
294
    //frame = None;
 
295
    --block_geometry_updates; // Don't use GeometryUpdatesBlocker, it would now set the geometry
 
296
    disownDataPassedToDeleted();
 
297
    del->unrefWindow();
 
298
    checkNonExistentClients();
 
299
    deleteClient(this, Allowed);
 
300
    ungrabXServer();
 
301
}
 
302
 
 
303
/**
 
304
 * Like releaseWindow(), but this one is called when the window has been already destroyed
 
305
 * (E.g. The application closed it)
 
306
 */
 
307
void Client::destroyClient()
 
308
{
 
309
    assert(!deleting);
 
310
    deleting = true;
 
311
    Deleted* del = Deleted::create(this);
 
312
    emit clientClosed(this);
 
313
    if (scene) {
 
314
        scene->windowClosed(this, del);
 
315
    }
 
316
    finishCompositing();
 
317
    workspace()->discardUsedWindowRules(this, true);   // Remove ForceTemporarily rules
 
318
    StackingUpdatesBlocker blocker(workspace());
 
319
    if (moveResizeMode)
 
320
        leaveMoveResize();
 
321
    finishWindowRules();
 
322
    ++block_geometry_updates;
 
323
    if (isOnCurrentDesktop() && isShown(true))
 
324
        addWorkspaceRepaint(visibleRect());
 
325
    setModal(false);
 
326
    hidden = true; // So that it's not considered visible anymore
 
327
    workspace()->clientHidden(this);
 
328
    destroyDecoration();
 
329
    cleanGrouping();
 
330
    if (clientGroup())
 
331
        clientGroup()->remove(this, QRect(), true);
 
332
    workspace()->removeClient(this, Allowed);
 
333
    client = None; // invalidate
 
334
    XDestroyWindow(display(), wrapper);
 
335
    wrapper = None;
 
336
    XDestroyWindow(display(), frameId());
 
337
    //frame = None;
 
338
    --block_geometry_updates; // Don't use GeometryUpdatesBlocker, it would now set the geometry
 
339
    disownDataPassedToDeleted();
 
340
    del->unrefWindow();
 
341
    checkNonExistentClients();
 
342
    deleteClient(this, Allowed);
 
343
}
 
344
 
 
345
void Client::updateDecoration(bool check_workspace_pos, bool force)
 
346
{
 
347
    if (!force &&
 
348
            ((decoration == NULL && noBorder()) || (decoration != NULL && !noBorder())))
 
349
        return;
 
350
    QRect oldgeom = geometry();
 
351
    blockGeometryUpdates(true);
 
352
    if (force)
 
353
        destroyDecoration();
 
354
    if (!noBorder()) {
 
355
        setMask(QRegion());  // Reset shape mask
 
356
        decoration = workspace()->createDecoration(bridge);
 
357
        // TODO: Check decoration's minimum size?
 
358
        decoration->init();
 
359
        decoration->widget()->installEventFilter(this);
 
360
        XReparentWindow(display(), decoration->widget()->winId(), frameId(), 0, 0);
 
361
        decoration->widget()->lower();
 
362
        decoration->borders(border_left, border_right, border_top, border_bottom);
 
363
        padding_left = padding_right = padding_top = padding_bottom = 0;
 
364
        if (KDecorationUnstable *deco2 = dynamic_cast<KDecorationUnstable*>(decoration))
 
365
            deco2->padding(padding_left, padding_right, padding_top, padding_bottom);
 
366
        XMoveWindow(display(), decoration->widget()->winId(), -padding_left, -padding_top);
 
367
        move(calculateGravitation(false));
 
368
        plainResize(sizeForClientSize(clientSize()), ForceGeometrySet);
 
369
        paintRedirector = new PaintRedirector(decoration->widget());
 
370
        connect(paintRedirector, SIGNAL(paintPending()), SLOT(repaintDecorationPending()));
 
371
        resizeDecorationPixmaps();
 
372
        if (compositing())
 
373
            discardWindowPixmap();
 
374
        if (scene != NULL)
 
375
            scene->windowGeometryShapeChanged(this);
 
376
        emit clientGeometryShapeChanged(this, oldgeom);
 
377
    } else
 
378
        destroyDecoration();
 
379
    if (check_workspace_pos)
 
380
        checkWorkspacePosition();
 
381
    blockGeometryUpdates(false);
 
382
    if (!noBorder())
 
383
        decoration->widget()->show();
 
384
    updateFrameExtents();
 
385
}
 
386
 
 
387
void Client::destroyDecoration()
 
388
{
 
389
    QRect oldgeom = geometry();
 
390
    if (decoration != NULL) {
 
391
        delete decoration;
 
392
        decoration = NULL;
 
393
        QPoint grav = calculateGravitation(true);
 
394
        border_left = border_right = border_top = border_bottom = 0;
 
395
        setMask(QRegion());  // Reset shape mask
 
396
        plainResize(sizeForClientSize(clientSize()), ForceGeometrySet);
 
397
        move(grav);
 
398
        delete paintRedirector;
 
399
        paintRedirector = NULL;
 
400
        if (m_responsibleForDecoPixmap) {
 
401
            XFreePixmap(display(), decorationPixmapTop.handle());
 
402
            XFreePixmap(display(), decorationPixmapBottom.handle());
 
403
            XFreePixmap(display(), decorationPixmapLeft.handle());
 
404
            XFreePixmap(display(), decorationPixmapRight.handle());
 
405
            m_responsibleForDecoPixmap = false;
 
406
        }
 
407
        decorationPixmapLeft = decorationPixmapRight = decorationPixmapTop = decorationPixmapBottom = QPixmap();
 
408
        if (compositing())
 
409
            discardWindowPixmap();
 
410
        if (scene != NULL && !deleting)
 
411
            scene->windowGeometryShapeChanged(this);
 
412
        if (!deleting) {
 
413
            emit clientGeometryShapeChanged(this, oldgeom);
 
414
        }
 
415
    }
 
416
}
 
417
 
 
418
bool Client::checkBorderSizes(bool also_resize)
 
419
{
 
420
    if (decoration == NULL)
 
421
        return false;
 
422
 
 
423
    int new_left = 0, new_right = 0, new_top = 0, new_bottom = 0;
 
424
    if (KDecorationUnstable *deco2 = dynamic_cast<KDecorationUnstable*>(decoration))
 
425
        deco2->padding(new_left, new_right, new_top, new_bottom);
 
426
    if (padding_left != new_left || padding_top != new_top)
 
427
        XMoveWindow(display(), decoration->widget()->winId(), -new_left, -new_top);
 
428
    padding_left = new_left;
 
429
    padding_right = new_right;
 
430
    padding_top = new_top;
 
431
    padding_bottom = new_bottom;
 
432
    decoration->borders(new_left, new_right, new_top, new_bottom);
 
433
    if (new_left == border_left && new_right == border_right &&
 
434
            new_top == border_top && new_bottom == border_bottom)
 
435
        return false;
 
436
    if (!also_resize) {
 
437
        border_left = new_left;
 
438
        border_right = new_right;
 
439
        border_top = new_top;
 
440
        border_bottom = new_bottom;
 
441
        return true;
 
442
    }
 
443
    GeometryUpdatesBlocker blocker(this);
 
444
    move(calculateGravitation(true));
 
445
    border_left = new_left;
 
446
    border_right = new_right;
 
447
    border_top = new_top;
 
448
    border_bottom = new_bottom;
 
449
    move(calculateGravitation(false));
 
450
    plainResize(sizeForClientSize(clientSize()), ForceGeometrySet);
 
451
    checkWorkspacePosition();
 
452
    return true;
 
453
}
 
454
 
 
455
void Client::triggerDecorationRepaint()
 
456
{
 
457
    if (decoration != NULL)
 
458
        decoration->widget()->update();
 
459
}
 
460
 
 
461
void Client::layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom, Client::CoordinateMode mode) const
 
462
{
 
463
    QRect r = decoration->widget()->rect();
 
464
    if (mode == WindowRelative)
 
465
        r.translate(-padding_left, -padding_top);
 
466
 
 
467
    NETStrut strut = info->frameOverlap();
 
468
 
 
469
    // Ignore the overlap strut when compositing is disabled
 
470
    if (!compositing() || !Workspace::self()->decorationSupportsFrameOverlap())
 
471
        strut.left = strut.top = strut.right = strut.bottom = 0;
 
472
    else if (strut.left == -1 && strut.top == -1 && strut.right == -1 && strut.bottom == -1) {
 
473
        top = QRect(r.x(), r.y(), r.width(), r.height() / 3);
 
474
        left = QRect(r.x(), r.y() + top.height(), width() / 2, r.height() / 3);
 
475
        right = QRect(r.x() + left.width(), r.y() + top.height(), r.width() - left.width(), left.height());
 
476
        bottom = QRect(r.x(), r.y() + top.height() + left.height(), r.width(), r.height() - left.height() - top.height());
 
477
        return;
 
478
    }
 
479
 
 
480
    top = QRect(r.x(), r.y(), r.width(), padding_top + border_top + strut.top);
 
481
    bottom = QRect(r.x(), r.y() + r.height() - padding_bottom - border_bottom - strut.bottom,
 
482
                   r.width(), padding_bottom + border_bottom + strut.bottom);
 
483
    left = QRect(r.x(), r.y() + top.height(),
 
484
                 padding_left + border_left + strut.left, r.height() - top.height() - bottom.height());
 
485
    right = QRect(r.x() + r.width() - padding_right - border_right - strut.right, r.y() + top.height(),
 
486
                  padding_right + border_right + strut.right, r.height() - top.height() - bottom.height());
 
487
}
 
488
 
 
489
QRegion Client::decorationPendingRegion() const
 
490
{
 
491
    if (!paintRedirector)
 
492
        return QRegion();
 
493
    return paintRedirector->scheduledRepaintRegion().translated(x() - padding_left, y() - padding_top);
 
494
}
 
495
 
 
496
void Client::repaintDecorationPending()
 
497
{
 
498
    if (compositing()) {
 
499
        // The scene will update the decoration pixmaps in the next painting pass
 
500
        // if it has not been already repainted before
 
501
        const QRegion r = paintRedirector->scheduledRepaintRegion();
 
502
        if (!r.isEmpty())
 
503
            Workspace::self()->addRepaint(r.translated(x() - padding_left, y() - padding_top));
 
504
    } else
 
505
        ensureDecorationPixmapsPainted();
 
506
}
 
507
 
 
508
bool Client::decorationPixmapRequiresRepaint()
 
509
{
 
510
    if (!paintRedirector)
 
511
        return false;
 
512
    QRegion r = paintRedirector->pendingRegion();
 
513
    return !r.isEmpty();
 
514
}
 
515
 
 
516
void Client::ensureDecorationPixmapsPainted()
 
517
{
 
518
    if (!paintRedirector)
 
519
        return;
 
520
 
 
521
    QRegion r = paintRedirector->pendingRegion();
 
522
    if (r.isEmpty())
 
523
        return;
 
524
 
 
525
    QPixmap p = paintRedirector->performPendingPaint();
 
526
 
 
527
    QRect lr, rr, tr, br;
 
528
    layoutDecorationRects(lr, tr, rr, br, DecorationRelative);
 
529
 
 
530
    repaintDecorationPixmap(decorationPixmapLeft, lr, p, r);
 
531
    repaintDecorationPixmap(decorationPixmapRight, rr, p, r);
 
532
    repaintDecorationPixmap(decorationPixmapTop, tr, p, r);
 
533
    repaintDecorationPixmap(decorationPixmapBottom, br, p, r);
 
534
 
 
535
    if (!compositing()) {
 
536
        // Blit the pixmaps to the frame window
 
537
        layoutDecorationRects(lr, tr, rr, br, WindowRelative);
 
538
#ifdef HAVE_XRENDER
 
539
        if (Extensions::renderAvailable()) {
 
540
            XRenderPictFormat* format = XRenderFindVisualFormat(display(), visual());
 
541
            XRenderPictureAttributes pa;
 
542
            pa.subwindow_mode = IncludeInferiors;
 
543
            Picture pic = XRenderCreatePicture(display(), frameId(), format, CPSubwindowMode, &pa);
 
544
            XRenderComposite(display(), PictOpSrc, decorationPixmapLeft.x11PictureHandle(), None, pic,
 
545
                             0, 0, 0, 0, lr.x(), lr.y(), lr.width(), lr.height());
 
546
            XRenderComposite(display(), PictOpSrc, decorationPixmapRight.x11PictureHandle(), None, pic,
 
547
                             0, 0, 0, 0, rr.x(), rr.y(), rr.width(), rr.height());
 
548
            XRenderComposite(display(), PictOpSrc, decorationPixmapTop.x11PictureHandle(), None, pic,
 
549
                             0, 0, 0, 0, tr.x(), tr.y(), tr.width(), tr.height());
 
550
            XRenderComposite(display(), PictOpSrc, decorationPixmapBottom.x11PictureHandle(), None, pic,
 
551
                             0, 0, 0, 0, br.x(), br.y(), br.width(), br.height());
 
552
            XRenderFreePicture(display(), pic);   // TODO don't recreate pictures all the time?
 
553
        } else
 
554
#endif
 
555
        {
 
556
            XGCValues values;
 
557
            values.subwindow_mode = IncludeInferiors;
 
558
            GC gc = XCreateGC(display(), rootWindow(), GCSubwindowMode, &values);
 
559
            XCopyArea(display(), decorationPixmapLeft.handle(), frameId(), gc, 0, 0,
 
560
                      lr.width(), lr.height(), lr.x(), lr.y());
 
561
            XCopyArea(display(), decorationPixmapRight.handle(), frameId(), gc, 0, 0,
 
562
                      rr.width(), rr.height(), rr.x(), rr.y());
 
563
            XCopyArea(display(), decorationPixmapTop.handle(), frameId(), gc, 0, 0,
 
564
                      tr.width(), tr.height(), tr.x(), tr.y());
 
565
            XCopyArea(display(), decorationPixmapBottom.handle(), frameId(), gc, 0, 0,
 
566
                      br.width(), br.height(), br.x(), br.y());
 
567
            XFreeGC(display(), gc);
 
568
        }
 
569
    } else
 
570
        XSync(display(), false);
 
571
}
 
572
 
 
573
void Client::repaintDecorationPixmap(QPixmap& pix, const QRect& r, const QPixmap& src, QRegion reg)
 
574
{
 
575
    if (!r.isValid())
 
576
        return;
 
577
    QRect b = reg.boundingRect();
 
578
    reg &= r;
 
579
    if (reg.isEmpty())
 
580
        return;
 
581
    QPainter pt(&pix);
 
582
    pt.translate(-r.topLeft());
 
583
    pt.setCompositionMode(QPainter::CompositionMode_Source);
 
584
    pt.setClipRegion(reg);
 
585
    pt.drawPixmap(b.topLeft(), src);
 
586
    pt.end();
 
587
}
 
588
 
 
589
void Client::resizeDecorationPixmaps()
 
590
{
 
591
    QRect lr, rr, tr, br;
 
592
    layoutDecorationRects(lr, tr, rr, br, DecorationRelative);
 
593
 
 
594
    if (decorationPixmapTop.size() != tr.size()) {
 
595
        if (m_responsibleForDecoPixmap && !decorationPixmapTop.isNull() &&
 
596
              decorationPixmapTop.paintEngine()->type() == QPaintEngine::X11) {
 
597
            XFreePixmap(display(), decorationPixmapTop.handle());
 
598
        }
 
599
 
 
600
        if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) {
 
601
            decorationPixmapTop = QPixmap(tr.size());
 
602
            m_responsibleForDecoPixmap = false;
 
603
        } else {
 
604
            Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(),
 
605
                                        tr.size().width(), tr.height(),
 
606
                                        32);
 
607
            decorationPixmapTop = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared);
 
608
            decorationPixmapTop.fill(Qt::transparent);
 
609
            m_responsibleForDecoPixmap= true;
 
610
        }
 
611
    }
 
612
 
 
613
    if (decorationPixmapBottom.size() != br.size()) {
 
614
        if (m_responsibleForDecoPixmap && !decorationPixmapBottom.isNull() &&
 
615
              decorationPixmapBottom.paintEngine()->type() == QPaintEngine::X11) {
 
616
            XFreePixmap(display(), decorationPixmapBottom.handle());
 
617
        }
 
618
 
 
619
        if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) {
 
620
            decorationPixmapBottom = QPixmap(br.size());
 
621
            m_responsibleForDecoPixmap = false;
 
622
        } else {
 
623
            Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(),
 
624
                                        br.size().width(), br.height(),
 
625
                                        32);
 
626
            decorationPixmapBottom = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared);
 
627
            decorationPixmapBottom.fill(Qt::transparent);
 
628
            m_responsibleForDecoPixmap = true;
 
629
        }
 
630
    }
 
631
 
 
632
    if (decorationPixmapLeft.size() != lr.size()) {
 
633
        if (m_responsibleForDecoPixmap && !decorationPixmapLeft.isNull() &&
 
634
              decorationPixmapLeft.paintEngine()->type() == QPaintEngine::X11) {
 
635
            XFreePixmap(display(), decorationPixmapLeft.handle());
 
636
        }
 
637
 
 
638
        if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) {
 
639
            decorationPixmapLeft = QPixmap(lr.size());
 
640
            m_responsibleForDecoPixmap = false;
 
641
        } else {
 
642
            Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(),
 
643
                                        lr.size().width(), lr.height(),
 
644
                                        32);
 
645
            decorationPixmapLeft = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared);
 
646
            decorationPixmapLeft.fill(Qt::transparent);
 
647
            m_responsibleForDecoPixmap = true;
 
648
        }
 
649
    }
 
650
 
 
651
    if (decorationPixmapRight.size() != rr.size()) {
 
652
        if (m_responsibleForDecoPixmap && !decorationPixmapRight.isNull() &&
 
653
              decorationPixmapRight.paintEngine()->type() == QPaintEngine::X11) {
 
654
            XFreePixmap(display(), decorationPixmapRight.handle());
 
655
        }
 
656
 
 
657
        if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) {
 
658
            decorationPixmapRight = QPixmap(rr.size());
 
659
            m_responsibleForDecoPixmap = false;
 
660
        } else {
 
661
            Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(),
 
662
                                        rr.size().width(), rr.height(),
 
663
                                        32);
 
664
            decorationPixmapRight = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared);
 
665
            decorationPixmapRight.fill(Qt::transparent);
 
666
            m_responsibleForDecoPixmap = true;
 
667
        }
 
668
    }
 
669
 
 
670
#ifdef HAVE_XRENDER
 
671
    if (Extensions::renderAvailable()) {
 
672
        // Make sure the pixmaps are created with alpha channels
 
673
        decorationPixmapLeft.fill(Qt::transparent);
 
674
        decorationPixmapRight.fill(Qt::transparent);
 
675
        decorationPixmapTop.fill(Qt::transparent);
 
676
        decorationPixmapBottom.fill(Qt::transparent);
 
677
    }
 
678
#endif
 
679
    triggerDecorationRepaint();
 
680
}
 
681
 
 
682
QRect Client::transparentRect() const
 
683
{
 
684
    if (isShade())
 
685
        return QRect();
 
686
 
 
687
    NETStrut strut = info->frameOverlap();
 
688
    // Ignore the strut when compositing is disabled or the decoration doesn't support it
 
689
    if (!compositing() || !Workspace::self()->decorationSupportsFrameOverlap())
 
690
        strut.left = strut.top = strut.right = strut.bottom = 0;
 
691
    else if (strut.left == -1 && strut.top == -1 && strut.right == -1 && strut.bottom == -1)
 
692
        return QRect();
 
693
 
 
694
    const QRect r = QRect(clientPos(), clientSize())
 
695
                    .adjusted(strut.left, strut.top, -strut.right, -strut.bottom);
 
696
    if (r.isValid())
 
697
        return r;
 
698
 
 
699
    return QRect();
 
700
}
 
701
 
 
702
void Client::detectNoBorder()
 
703
{
 
704
    if (shape()) {
 
705
        noborder = true;
 
706
        app_noborder = true;
 
707
        return;
 
708
    }
 
709
    switch(windowType()) {
 
710
    case NET::Desktop :
 
711
    case NET::Dock :
 
712
    case NET::TopMenu :
 
713
    case NET::Splash :
 
714
        noborder = true;
 
715
        app_noborder = true;
 
716
        break;
 
717
    case NET::Unknown :
 
718
    case NET::Normal :
 
719
    case NET::Toolbar :
 
720
    case NET::Menu :
 
721
    case NET::Dialog :
 
722
    case NET::Utility :
 
723
        noborder = false;
 
724
        break;
 
725
    default:
 
726
        abort();
 
727
    }
 
728
    // NET::Override is some strange beast without clear definition, usually
 
729
    // just meaning "noborder", so let's treat it only as such flag, and ignore it as
 
730
    // a window type otherwise (SUPPORTED_WINDOW_TYPES_MASK doesn't include it)
 
731
    if (info->windowType(SUPPORTED_MANAGED_WINDOW_TYPES_MASK | NET::OverrideMask) == NET::Override) {
 
732
        noborder = true;
 
733
        app_noborder = true;
 
734
    }
 
735
}
 
736
 
 
737
void Client::updateFrameExtents()
 
738
{
 
739
    NETStrut strut;
 
740
    strut.left = border_left;
 
741
    strut.right = border_right;
 
742
    strut.top = border_top;
 
743
    strut.bottom = border_bottom;
 
744
    info->setFrameExtents(strut);
 
745
}
 
746
 
 
747
/**
 
748
 * Resizes the decoration, and makes sure the decoration widget gets resize event
 
749
 * even if the size hasn't changed. This is needed to make sure the decoration
 
750
 * re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes,
 
751
 * the decoration may turn on/off some borders, but the actual size
 
752
 * of the decoration stays the same).
 
753
 */
 
754
void Client::resizeDecoration(const QSize& s)
 
755
{
 
756
    if (decoration == NULL)
 
757
        return;
 
758
    QSize newSize = s + QSize(padding_left + padding_right, padding_top + padding_bottom);
 
759
    QSize oldSize = decoration->widget()->size();
 
760
    decoration->resize(newSize);
 
761
    if (oldSize == newSize) {
 
762
        QResizeEvent e(newSize, oldSize);
 
763
        QApplication::sendEvent(decoration->widget(), &e);
 
764
    } else { // oldSize != newSize
 
765
        resizeDecorationPixmaps();
 
766
    }
 
767
}
 
768
 
 
769
bool Client::noBorder() const
 
770
{
 
771
    return !workspace()->hasDecorationPlugin() || noborder || isFullScreen();
 
772
}
 
773
 
 
774
bool Client::userCanSetNoBorder() const
 
775
{
 
776
    return !isFullScreen() && !isShade() && (clientGroup() == NULL || !(clientGroup()->items().count() > 1));
 
777
}
 
778
 
 
779
void Client::setNoBorder(bool set)
 
780
{
 
781
    if (!userCanSetNoBorder())
 
782
        return;
 
783
    set = rules()->checkNoBorder(set);
 
784
    if (noborder == set)
 
785
        return;
 
786
    noborder = set;
 
787
    updateDecoration(true, false);
 
788
    updateWindowRules();
 
789
}
 
790
 
 
791
void Client::checkNoBorder()
 
792
{
 
793
    setNoBorder(app_noborder);
 
794
}
 
795
 
 
796
void Client::updateShape()
 
797
{
 
798
    if (shape()) {
 
799
        // Workaround for #19644 - Shaped windows shouldn't have decoration
 
800
        if (!app_noborder) {
 
801
            // Only when shape is detected for the first time, still let the user to override
 
802
            app_noborder = true;
 
803
            noborder = true;
 
804
            updateDecoration(true);
 
805
        }
 
806
    }
 
807
    if (shape() && noBorder())
 
808
        XShapeCombineShape(display(), frameId(), ShapeBounding,
 
809
                           clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeSet);
 
810
 
 
811
    // Decoration mask (i.e. 'else' here) setting is done in setMask()
 
812
    // when the decoration calls it or when the decoration is created/destroyed
 
813
    updateInputShape();
 
814
    if (compositing()) {
 
815
        addRepaintFull();
 
816
        addWorkspaceRepaint(visibleRect());   // In case shape change removes part of this window
 
817
    }
 
818
    if (scene != NULL)
 
819
        scene->windowGeometryShapeChanged(this);
 
820
    emit clientGeometryShapeChanged(this, geometry());
 
821
}
 
822
 
 
823
static Window shape_helper_window = None;
 
824
 
 
825
void Client::updateInputShape()
 
826
{
 
827
    if (hiddenPreview())   // Sets it to none, don't change
 
828
        return;
 
829
    if (Extensions::shapeInputAvailable()) {
 
830
        // There appears to be no way to find out if a window has input
 
831
        // shape set or not, so always propagate the input shape
 
832
        // (it's the same like the bounding shape by default).
 
833
        // Also, build the shape using a helper window, not directly
 
834
        // in the frame window, because the sequence set-shape-to-frame,
 
835
        // remove-shape-of-client, add-input-shape-of-client has the problem
 
836
        // that after the second step there's a hole in the input shape
 
837
        // until the real shape of the client is added and that can make
 
838
        // the window lose focus (which is a problem with mouse focus policies)
 
839
        // TODO: It seems there is, after all - XShapeGetRectangles() - but maybe this is better
 
840
        if (shape_helper_window == None)
 
841
            shape_helper_window = XCreateSimpleWindow(display(), rootWindow(),
 
842
                                  0, 0, 1, 1, 0, 0, 0);
 
843
        XResizeWindow(display(), shape_helper_window, width(), height());
 
844
        XShapeCombineShape(display(), shape_helper_window, ShapeInput, 0, 0,
 
845
                           frameId(), ShapeBounding, ShapeSet);
 
846
        XShapeCombineShape(display(), shape_helper_window, ShapeInput,
 
847
                           clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeSubtract);
 
848
        XShapeCombineShape(display(), shape_helper_window, ShapeInput,
 
849
                           clientPos().x(), clientPos().y(), window(), ShapeInput, ShapeUnion);
 
850
        XShapeCombineShape(display(), frameId(), ShapeInput, 0, 0,
 
851
                           shape_helper_window, ShapeInput, ShapeSet);
 
852
    }
 
853
}
 
854
 
 
855
void Client::setMask(const QRegion& reg, int mode)
 
856
{
 
857
    QRegion r = reg.translated(-padding_left, -padding_right) & QRect(0, 0, width(), height());
 
858
    if (_mask == r)
 
859
        return;
 
860
    _mask = r;
 
861
    Window shape_window = frameId();
 
862
    if (shape()) {
 
863
        // The same way of applying a shape without strange intermediate states like above
 
864
        if (shape_helper_window == None)
 
865
            shape_helper_window = XCreateSimpleWindow(display(), rootWindow(),
 
866
                                  0, 0, 1, 1, 0, 0, 0);
 
867
        shape_window = shape_helper_window;
 
868
    }
 
869
    if (_mask.isEmpty())
 
870
        XShapeCombineMask(display(), shape_window, ShapeBounding, 0, 0, None, ShapeSet);
 
871
    else if (mode == X::Unsorted)
 
872
        XShapeCombineRegion(display(), shape_window, ShapeBounding, 0, 0, _mask.handle(), ShapeSet);
 
873
    else {
 
874
        QVector< QRect > rects = _mask.rects();
 
875
        XRectangle* xrects = new XRectangle[rects.count()];
 
876
        for (int i = 0; i < rects.count(); ++i) {
 
877
            xrects[i].x = rects[i].x();
 
878
            xrects[i].y = rects[i].y();
 
879
            xrects[i].width = rects[i].width();
 
880
            xrects[i].height = rects[i].height();
 
881
        }
 
882
        XShapeCombineRectangles(display(), shape_window, ShapeBounding, 0, 0,
 
883
                                xrects, rects.count(), ShapeSet, mode);
 
884
        delete[] xrects;
 
885
    }
 
886
    if (shape()) {
 
887
        // The rest of the applyign using a temporary window
 
888
        XRectangle rec = { 0, 0, clientSize().width(), clientSize().height() };
 
889
        XShapeCombineRectangles(display(), shape_helper_window, ShapeBounding,
 
890
                                clientPos().x(), clientPos().y(), &rec, 1, ShapeSubtract, Unsorted);
 
891
        XShapeCombineShape(display(), shape_helper_window, ShapeBounding,
 
892
                           clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeUnion);
 
893
        XShapeCombineShape(display(), frameId(), ShapeBounding, 0, 0,
 
894
                           shape_helper_window, ShapeBounding, ShapeSet);
 
895
    }
 
896
    if (scene != NULL)
 
897
        scene->windowGeometryShapeChanged(this);
 
898
    emit clientGeometryShapeChanged(this, geometry());
 
899
    updateShape();
 
900
}
 
901
 
 
902
QRegion Client::mask() const
 
903
{
 
904
    if (_mask.isEmpty())
 
905
        return QRegion(0, 0, width(), height());
 
906
    return _mask;
 
907
}
 
908
 
 
909
void Client::hideClient(bool hide)
 
910
{
 
911
    if (hidden == hide)
 
912
        return;
 
913
    hidden = hide;
 
914
    updateVisibility();
 
915
}
 
916
 
 
917
/**
 
918
 * Returns whether the window is minimizable or not
 
919
 */
 
920
bool Client::isMinimizable() const
 
921
{
 
922
    if (isSpecialWindow())
 
923
        return false;
 
924
    if (isTransient()) {
 
925
        // #66868 - Let other xmms windows be minimized when the mainwindow is minimized
 
926
        bool shown_mainwindow = false;
 
927
        ClientList mainclients = mainClients();
 
928
        for (ClientList::ConstIterator it = mainclients.constBegin();
 
929
                it != mainclients.constEnd();
 
930
                ++it)
 
931
            if ((*it)->isShown(true))
 
932
                shown_mainwindow = true;
 
933
        if (!shown_mainwindow)
 
934
            return true;
 
935
    }
 
936
#if 0
 
937
    // This is here because kicker's taskbar doesn't provide separate entries
 
938
    // for windows with an explicitly given parent
 
939
    // TODO: perhaps this should be redone
 
940
    // Disabled for now, since at least modal dialogs should be minimizable
 
941
    // (resulting in the mainwindow being minimized too).
 
942
    if (transientFor() != NULL)
 
943
        return false;
 
944
#endif
 
945
    if (!wantsTabFocus())   // SELI, TODO: - NET::Utility? why wantsTabFocus() - skiptaskbar? ?
 
946
        return false;
 
947
    return true;
 
948
}
 
949
 
 
950
/**
 
951
 * Minimizes this client plus its transients
 
952
 */
 
953
void Client::minimize(bool avoid_animation)
 
954
{
 
955
    if (!isMinimizable() || isMinimized())
 
956
        return;
 
957
 
 
958
    //Scripting call. Does not use a signal/slot mechanism
 
959
    //as ensuring connections was a bit difficult between
 
960
    //so many clients and the workspace
 
961
    SWrapper::WorkspaceProxy* ws_wrap = SWrapper::WorkspaceProxy::instance();
 
962
    if (ws_wrap != 0) {
 
963
        ws_wrap->sl_clientMinimized(this);
 
964
    }
 
965
 
 
966
    emit s_minimized();
 
967
 
 
968
    Notify::raise(Notify::Minimize);
 
969
 
 
970
    minimized = true;
 
971
 
 
972
    updateVisibility();
 
973
    updateAllowedActions();
 
974
    workspace()->updateMinimizedOfTransients(this);
 
975
    updateWindowRules();
 
976
    workspace()->updateFocusChains(this, Workspace::FocusChainMakeLast);
 
977
    // TODO: merge signal with s_minimized
 
978
    emit clientMinimized(this, !avoid_animation);
 
979
 
 
980
    // when tiling, request a rearrangement
 
981
    workspace()->notifyTilingWindowMinimizeToggled(this);
 
982
 
 
983
    // Update states of all other windows in this group
 
984
    if (clientGroup())
 
985
        clientGroup()->updateStates(this);
 
986
}
 
987
 
 
988
void Client::unminimize(bool avoid_animation)
 
989
{
 
990
    if (!isMinimized())
 
991
        return;
 
992
 
 
993
    SWrapper::WorkspaceProxy* ws_wrap = SWrapper::WorkspaceProxy::instance();
 
994
    if (ws_wrap != 0) {
 
995
        ws_wrap->sl_clientUnminimized(this);
 
996
    }
 
997
 
 
998
    emit s_unminimized();
 
999
 
 
1000
    Notify::raise(Notify::UnMinimize);
 
1001
    minimized = false;
 
1002
    updateVisibility();
 
1003
    updateAllowedActions();
 
1004
    workspace()->updateMinimizedOfTransients(this);
 
1005
    updateWindowRules();
 
1006
    workspace()->updateAllTiles();
 
1007
    emit clientUnminimized(this, !avoid_animation);
 
1008
 
 
1009
    // when tiling, request a rearrangement
 
1010
    workspace()->notifyTilingWindowMinimizeToggled(this);
 
1011
 
 
1012
    // Update states of all other windows in this group
 
1013
    if (clientGroup())
 
1014
        clientGroup()->updateStates(this);
 
1015
}
 
1016
 
 
1017
QRect Client::iconGeometry() const
 
1018
{
 
1019
    NETRect r = info->iconGeometry();
 
1020
    QRect geom(r.pos.x, r.pos.y, r.size.width, r.size.height);
 
1021
    if (geom.isValid())
 
1022
        return geom;
 
1023
    else {
 
1024
        // Check all mainwindows of this window (recursively)
 
1025
        foreach (Client * mainwin, mainClients()) {
 
1026
            geom = mainwin->iconGeometry();
 
1027
            if (geom.isValid())
 
1028
                return geom;
 
1029
        }
 
1030
        // No mainwindow (or their parents) with icon geometry was found
 
1031
        return QRect();
 
1032
    }
 
1033
}
 
1034
 
 
1035
bool Client::isShadeable() const
 
1036
{
 
1037
    return !isSpecialWindow() && !noBorder();
 
1038
}
 
1039
 
 
1040
void Client::setShade(ShadeMode mode)
 
1041
{
 
1042
    if (!isShadeable())
 
1043
        return;
 
1044
    mode = rules()->checkShade(mode);
 
1045
    if (shade_mode == mode)
 
1046
        return;
 
1047
    bool was_shade = isShade();
 
1048
    ShadeMode was_shade_mode = shade_mode;
 
1049
    shade_mode = mode;
 
1050
    if (was_shade == isShade()) {
 
1051
        if (decoration != NULL)   // Decoration may want to update after e.g. hover-shade changes
 
1052
            decoration->shadeChange();
 
1053
        return; // No real change in shaded state
 
1054
    }
 
1055
 
 
1056
    if (shade_mode == ShadeNormal) {
 
1057
        if (isShown(true) && isOnCurrentDesktop())
 
1058
            Notify::raise(Notify::ShadeUp);
 
1059
    } else if (shade_mode == ShadeNone) {
 
1060
        if (isShown(true) && isOnCurrentDesktop())
 
1061
            Notify::raise(Notify::ShadeDown);
 
1062
    }
 
1063
 
 
1064
    assert(decoration != NULL);   // noborder windows can't be shaded
 
1065
    GeometryUpdatesBlocker blocker(this);
 
1066
    // Decorations may turn off some borders when shaded
 
1067
    if (decoration)
 
1068
        decoration->borders(border_left, border_right, border_top, border_bottom);
 
1069
 
 
1070
    // TODO: All this unmapping, resizing etc. feels too much duplicated from elsewhere
 
1071
    if (isShade()) {
 
1072
        // shade_mode == ShadeNormal
 
1073
        addWorkspaceRepaint(visibleRect());
 
1074
        // Shade
 
1075
        shade_geometry_change = true;
 
1076
        QSize s(sizeForClientSize(QSize(clientSize())));
 
1077
        s.setHeight(border_top + border_bottom);
 
1078
        XSelectInput(display(), wrapper, ClientWinMask);   // Avoid getting UnmapNotify
 
1079
        XUnmapWindow(display(), wrapper);
 
1080
        XUnmapWindow(display(), client);
 
1081
        XSelectInput(display(), wrapper, ClientWinMask | SubstructureNotifyMask);
 
1082
        plainResize(s);
 
1083
        shade_geometry_change = false;
 
1084
        if (isActive()) {
 
1085
            if (was_shade_mode == ShadeHover)
 
1086
                workspace()->activateNextClient(this);
 
1087
            else
 
1088
                workspace()->focusToNull();
 
1089
        }
 
1090
    } else {
 
1091
        shade_geometry_change = true;
 
1092
        QSize s(sizeForClientSize(clientSize()));
 
1093
        shade_geometry_change = false;
 
1094
        plainResize(s);
 
1095
        if (shade_mode == ShadeHover || shade_mode == ShadeActivated)
 
1096
            setActive(true);
 
1097
        XMapWindow(display(), wrapperId());
 
1098
        XMapWindow(display(), window());
 
1099
        if (isActive())
 
1100
            workspace()->requestFocus(this);
 
1101
    }
 
1102
    info->setState(isShade() ? NET::Shaded : 0, NET::Shaded);
 
1103
    info->setState(isShown(false) ? 0 : NET::Hidden, NET::Hidden);
 
1104
    discardWindowPixmap();
 
1105
    updateVisibility();
 
1106
    updateAllowedActions();
 
1107
    if (decoration)
 
1108
        decoration->shadeChange();
 
1109
    updateWindowRules();
 
1110
 
 
1111
    // Update states of all other windows in this group
 
1112
    if (clientGroup())
 
1113
        clientGroup()->updateStates(this);
 
1114
}
 
1115
 
 
1116
void Client::shadeHover()
 
1117
{
 
1118
    setShade(ShadeHover);
 
1119
    cancelShadeHoverTimer();
 
1120
}
 
1121
 
 
1122
void Client::shadeUnhover()
 
1123
{
 
1124
    setShade(ShadeNormal);
 
1125
    cancelShadeHoverTimer();
 
1126
}
 
1127
 
 
1128
void Client::cancelShadeHoverTimer()
 
1129
{
 
1130
    delete shadeHoverTimer;
 
1131
    shadeHoverTimer = 0;
 
1132
}
 
1133
 
 
1134
void Client::toggleShade()
 
1135
{
 
1136
    // If the mode is ShadeHover or ShadeActive, cancel shade too
 
1137
    setShade(shade_mode == ShadeNone ? ShadeNormal : ShadeNone);
 
1138
}
 
1139
 
 
1140
void Client::updateVisibility()
 
1141
{
 
1142
    if (deleting)
 
1143
        return;
 
1144
    if (hidden && (clientGroup() == NULL || clientGroup()->visible() == this)) {
 
1145
        info->setState(NET::Hidden, NET::Hidden);
 
1146
        setSkipTaskbar(true, false);   // Also hide from taskbar
 
1147
        if (compositing() && options->hiddenPreviews == HiddenPreviewsAlways)
 
1148
            internalKeep(Allowed);
 
1149
        else
 
1150
            internalHide(Allowed);
 
1151
        return;
 
1152
    }
 
1153
    if (clientGroup() == NULL || clientGroup()->visible() == this)
 
1154
        setSkipTaskbar(original_skip_taskbar, false);   // Reset from 'hidden'
 
1155
    if (minimized) {
 
1156
        info->setState(NET::Hidden, NET::Hidden);
 
1157
        if (compositing() && options->hiddenPreviews == HiddenPreviewsAlways)
 
1158
            internalKeep(Allowed);
 
1159
        else
 
1160
            internalHide(Allowed);
 
1161
        return;
 
1162
    }
 
1163
    info->setState(0, NET::Hidden);
 
1164
    if (!isOnCurrentDesktop()) {
 
1165
        if (compositing() && options->hiddenPreviews != HiddenPreviewsNever)
 
1166
            internalKeep(Allowed);
 
1167
        else
 
1168
            internalHide(Allowed);
 
1169
        return;
 
1170
    }
 
1171
    if (!isOnCurrentActivity()) {
 
1172
        if (compositing() && options->hiddenPreviews != HiddenPreviewsNever)
 
1173
            internalKeep(Allowed);
 
1174
        else
 
1175
            internalHide(Allowed);
 
1176
        return;
 
1177
    }
 
1178
    bool belongs_to_desktop = false;
 
1179
    for (ClientList::ConstIterator it = group()->members().constBegin();
 
1180
            it != group()->members().constEnd();
 
1181
            ++it)
 
1182
        if ((*it)->isDesktop()) {
 
1183
            belongs_to_desktop = true;
 
1184
            break;
 
1185
        }
 
1186
    if (!belongs_to_desktop && workspace()->showingDesktop())
 
1187
        workspace()->resetShowingDesktop(true);
 
1188
    internalShow(Allowed);
 
1189
}
 
1190
 
 
1191
/**
 
1192
 * Sets the client window's mapping state. Possible values are
 
1193
 * WithdrawnState, IconicState, NormalState.
 
1194
 */
 
1195
void Client::exportMappingState(int s)
 
1196
{
 
1197
    assert(client != None);
 
1198
    assert(!deleting || s == WithdrawnState);
 
1199
    if (s == WithdrawnState) {
 
1200
        XDeleteProperty(display(), window(), atoms->wm_state);
 
1201
        return;
 
1202
    }
 
1203
    assert(s == NormalState || s == IconicState);
 
1204
 
 
1205
    unsigned long data[2];
 
1206
    data[0] = (unsigned long) s;
 
1207
    data[1] = (unsigned long) None;
 
1208
    XChangeProperty(display(), window(), atoms->wm_state, atoms->wm_state, 32,
 
1209
                    PropModeReplace, (unsigned char*)(data), 2);
 
1210
}
 
1211
 
 
1212
void Client::internalShow(allowed_t)
 
1213
{
 
1214
    if (mapping_state == Mapped)
 
1215
        return;
 
1216
    MappingState old = mapping_state;
 
1217
    mapping_state = Mapped;
 
1218
    if (old == Unmapped || old == Withdrawn)
 
1219
        map(Allowed);
 
1220
    if (old == Kept)
 
1221
        updateHiddenPreview();
 
1222
    workspace()->checkUnredirect();
 
1223
}
 
1224
 
 
1225
void Client::internalHide(allowed_t)
 
1226
{
 
1227
    if (mapping_state == Unmapped)
 
1228
        return;
 
1229
    MappingState old = mapping_state;
 
1230
    mapping_state = Unmapped;
 
1231
    if (old == Mapped || old == Kept)
 
1232
        unmap(Allowed);
 
1233
    if (old == Kept)
 
1234
        updateHiddenPreview();
 
1235
    addWorkspaceRepaint(visibleRect());
 
1236
    workspace()->clientHidden(this);
 
1237
    workspace()->checkUnredirect();
 
1238
}
 
1239
 
 
1240
void Client::internalKeep(allowed_t)
 
1241
{
 
1242
    assert(compositing());
 
1243
    if (mapping_state == Kept)
 
1244
        return;
 
1245
    MappingState old = mapping_state;
 
1246
    mapping_state = Kept;
 
1247
    if (old == Unmapped || old == Withdrawn)
 
1248
        map(Allowed);
 
1249
    updateHiddenPreview();
 
1250
    addWorkspaceRepaint(visibleRect());
 
1251
    workspace()->clientHidden(this);
 
1252
    workspace()->checkUnredirect();
 
1253
}
 
1254
 
 
1255
/**
 
1256
 * Maps (shows) the client. Note that it is mapping state of the frame,
 
1257
 * not necessarily the client window itself (i.e. a shaded window is here
 
1258
 * considered mapped, even though it is in IconicState).
 
1259
 */
 
1260
void Client::map(allowed_t)
 
1261
{
 
1262
    // XComposite invalidates backing pixmaps on unmap (minimize, different
 
1263
    // virtual desktop, etc.).  We kept the last known good pixmap around
 
1264
    // for use in effects, but now we want to have access to the new pixmap
 
1265
    if (compositing())
 
1266
        discardWindowPixmap();
 
1267
    if (decoration != NULL)
 
1268
        decoration->widget()->show(); // Not really necessary, but let it know the state
 
1269
    XMapWindow(display(), frameId());
 
1270
    if (!isShade()) {
 
1271
        XMapWindow(display(), wrapper);
 
1272
        XMapWindow(display(), client);
 
1273
        exportMappingState(NormalState);
 
1274
    } else
 
1275
        exportMappingState(IconicState);
 
1276
}
 
1277
 
 
1278
/**
 
1279
 * Unmaps the client. Again, this is about the frame.
 
1280
 */
 
1281
void Client::unmap(allowed_t)
 
1282
{
 
1283
    // Here it may look like a race condition, as some other client might try to unmap
 
1284
    // the window between these two XSelectInput() calls. However, they're supposed to
 
1285
    // use XWithdrawWindow(), which also sends a synthetic event to the root window,
 
1286
    // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify
 
1287
    // will be missed is also very minimal, so I don't think it's needed to grab the server
 
1288
    // here.
 
1289
    XSelectInput(display(), wrapper, ClientWinMask);   // Avoid getting UnmapNotify
 
1290
    XUnmapWindow(display(), frameId());
 
1291
    XUnmapWindow(display(), wrapper);
 
1292
    XUnmapWindow(display(), client);
 
1293
    XSelectInput(display(), wrapper, ClientWinMask | SubstructureNotifyMask);
 
1294
    if (decoration != NULL)
 
1295
        decoration->widget()->hide(); // Not really necessary, but let it know the state
 
1296
    exportMappingState(IconicState);
 
1297
}
 
1298
 
 
1299
/**
 
1300
 * XComposite doesn't keep window pixmaps of unmapped windows, which means
 
1301
 * there wouldn't be any previews of windows that are minimized or on another
 
1302
 * virtual desktop. Therefore rawHide() actually keeps such windows mapped.
 
1303
 * However special care needs to be taken so that such windows don't interfere.
 
1304
 * Therefore they're put very low in the stacking order and they have input shape
 
1305
 * set to none, which hopefully is enough. If there's no input shape available,
 
1306
 * then it's hoped that there will be some other desktop above it *shrug*.
 
1307
 * Using normal shape would be better, but that'd affect other things, e.g. painting
 
1308
 * of the actual preview.
 
1309
 */
 
1310
void Client::updateHiddenPreview()
 
1311
{
 
1312
    if (hiddenPreview()) {
 
1313
        workspace()->forceRestacking();
 
1314
        if (Extensions::shapeInputAvailable())
 
1315
            XShapeCombineRectangles(display(), frameId(), ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted);
 
1316
    } else {
 
1317
        workspace()->forceRestacking();
 
1318
        updateInputShape();
 
1319
    }
 
1320
}
 
1321
 
 
1322
void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3)
 
1323
{
 
1324
    XEvent ev;
 
1325
    long mask;
 
1326
 
 
1327
    memset(&ev, 0, sizeof(ev));
 
1328
    ev.xclient.type = ClientMessage;
 
1329
    ev.xclient.window = w;
 
1330
    ev.xclient.message_type = a;
 
1331
    ev.xclient.format = 32;
 
1332
    ev.xclient.data.l[0] = protocol;
 
1333
    ev.xclient.data.l[1] = xTime();
 
1334
    ev.xclient.data.l[2] = data1;
 
1335
    ev.xclient.data.l[3] = data2;
 
1336
    ev.xclient.data.l[4] = data3;
 
1337
    mask = 0L;
 
1338
    if (w == rootWindow())
 
1339
        mask = SubstructureRedirectMask; // Magic!
 
1340
    XSendEvent(display(), w, False, mask, &ev);
 
1341
}
 
1342
 
 
1343
/**
 
1344
 * Returns whether the window may be closed (have a close button)
 
1345
 */
 
1346
bool Client::isCloseable() const
 
1347
{
 
1348
    return rules()->checkCloseable(motif_may_close && !isSpecialWindow());
 
1349
}
 
1350
 
 
1351
/**
 
1352
 * Closes the window by either sending a delete_window message or using XKill.
 
1353
 */
 
1354
void Client::closeWindow()
 
1355
{
 
1356
    if (!isCloseable())
 
1357
        return;
 
1358
 
 
1359
    // Update user time, because the window may create a confirming dialog.
 
1360
    updateUserTime();
 
1361
 
 
1362
    if (Pdeletewindow) {
 
1363
        Notify::raise(Notify::Close);
 
1364
        sendClientMessage(window(), atoms->wm_protocols, atoms->wm_delete_window);
 
1365
        pingWindow();
 
1366
    } else // Client will not react on wm_delete_window. We have not choice
 
1367
        // but destroy his connection to the XServer.
 
1368
        killWindow();
 
1369
}
 
1370
 
 
1371
 
 
1372
/**
 
1373
 * Kills the window via XKill
 
1374
 */
 
1375
void Client::killWindow()
 
1376
{
 
1377
    kDebug(1212) << "Client::killWindow():" << caption();
 
1378
 
 
1379
    // Not sure if we need an Notify::Kill or not.. until then, use
 
1380
    // Notify::Close
 
1381
    Notify::raise(Notify::Close);
 
1382
 
 
1383
    if (isDialog())
 
1384
        Notify::raise(Notify::TransDelete);
 
1385
    if (isNormalWindow())
 
1386
        Notify::raise(Notify::Delete);
 
1387
    killProcess(false);
 
1388
    XKillClient(display(), window());  // Always kill this client at the server
 
1389
    destroyClient();
 
1390
}
 
1391
 
 
1392
/**
 
1393
 * Send a ping to the window using _NET_WM_PING if possible if it
 
1394
 * doesn't respond within a reasonable time, it will be killed.
 
1395
 */
 
1396
void Client::pingWindow()
 
1397
{
 
1398
    if (!Pping)
 
1399
        return; // Can't ping :(
 
1400
    if (options->killPingTimeout == 0)
 
1401
        return; // Turned off
 
1402
    if (ping_timer != NULL)
 
1403
        return; // Pinging already
 
1404
    ping_timer = new QTimer(this);
 
1405
    connect(ping_timer, SIGNAL(timeout()), SLOT(pingTimeout()));
 
1406
    ping_timer->setSingleShot(true);
 
1407
    ping_timer->start(options->killPingTimeout);
 
1408
    ping_timestamp = xTime();
 
1409
    workspace()->sendPingToWindow(window(), ping_timestamp);
 
1410
}
 
1411
 
 
1412
void Client::gotPing(Time timestamp)
 
1413
{
 
1414
    // Just plain compare is not good enough because of 64bit and truncating and whatnot
 
1415
    if (NET::timestampCompare(timestamp, ping_timestamp) != 0)
 
1416
        return;
 
1417
    delete ping_timer;
 
1418
    ping_timer = NULL;
 
1419
    if (process_killer != NULL) {
 
1420
        process_killer->kill();
 
1421
        // Recycle when the process manager has noticed that the process exited
 
1422
        // a delete process_killer here sometimes causes a hang in waitForFinished
 
1423
        connect(process_killer, SIGNAL(finished(int, QProcess::ExitStatus)),
 
1424
                process_killer, SLOT(deleteLater()));
 
1425
        process_killer = NULL;
 
1426
    }
 
1427
}
 
1428
 
 
1429
void Client::pingTimeout()
 
1430
{
 
1431
    kDebug(1212) << "Ping timeout:" << caption();
 
1432
    ping_timer->deleteLater();
 
1433
    ping_timer = NULL;
 
1434
    killProcess(true, ping_timestamp);
 
1435
}
 
1436
 
 
1437
void Client::killProcess(bool ask, Time timestamp)
 
1438
{
 
1439
    if (process_killer != NULL)
 
1440
        return;
 
1441
    Q_ASSERT(!ask || timestamp != CurrentTime);
 
1442
    QByteArray machine = wmClientMachine(true);
 
1443
    pid_t pid = info->pid();
 
1444
    if (pid <= 0 || machine.isEmpty())  // Needed properties missing
 
1445
        return;
 
1446
    kDebug(1212) << "Kill process:" << pid << "(" << machine << ")";
 
1447
    if (!ask) {
 
1448
        if (machine != "localhost") {
 
1449
            QStringList lst;
 
1450
            lst << machine << "kill" << QString::number(pid);
 
1451
            QProcess::startDetached("xon", lst);
 
1452
        } else
 
1453
            ::kill(pid, SIGTERM);
 
1454
    } else {
 
1455
        process_killer = new QProcess(this);
 
1456
        connect(process_killer, SIGNAL(error(QProcess::ProcessError)), SLOT(processKillerExited()));
 
1457
        connect(process_killer, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processKillerExited()));
 
1458
        process_killer->start(KStandardDirs::findExe("kwin_killer_helper"),
 
1459
                              QStringList() << "--pid" << QByteArray().setNum(unsigned(pid)) << "--hostname" << machine
 
1460
                              << "--windowname" << caption()
 
1461
                              << "--applicationname" << resourceClass()
 
1462
                              << "--wid" << QString::number(window())
 
1463
                              << "--timestamp" << QString::number(timestamp));
 
1464
    }
 
1465
}
 
1466
 
 
1467
void Client::processKillerExited()
 
1468
{
 
1469
    kDebug(1212) << "Killer exited";
 
1470
    delete process_killer;
 
1471
    process_killer = NULL;
 
1472
}
 
1473
 
 
1474
void Client::setSkipTaskbar(bool b, bool from_outside)
 
1475
{
 
1476
    int was_wants_tab_focus = wantsTabFocus();
 
1477
    if (from_outside) {
 
1478
        b = rules()->checkSkipTaskbar(b);
 
1479
        original_skip_taskbar = b;
 
1480
    }
 
1481
    if (b == skipTaskbar())
 
1482
        return;
 
1483
    skip_taskbar = b;
 
1484
    info->setState(b ? NET::SkipTaskbar : 0, NET::SkipTaskbar);
 
1485
    updateWindowRules();
 
1486
    if (was_wants_tab_focus != wantsTabFocus())
 
1487
        workspace()->updateFocusChains(this,
 
1488
                                       isActive() ? Workspace::FocusChainMakeFirst : Workspace::FocusChainUpdate);
 
1489
}
 
1490
 
 
1491
void Client::setSkipPager(bool b)
 
1492
{
 
1493
    b = rules()->checkSkipPager(b);
 
1494
    if (b == skipPager())
 
1495
        return;
 
1496
    skip_pager = b;
 
1497
    info->setState(b ? NET::SkipPager : 0, NET::SkipPager);
 
1498
    updateWindowRules();
 
1499
}
 
1500
 
 
1501
void Client::setSkipSwitcher(bool set)
 
1502
{
 
1503
    set = rules()->checkSkipSwitcher(set);
 
1504
    if (set == skipSwitcher())
 
1505
        return;
 
1506
    skip_switcher = set;
 
1507
    updateWindowRules();
 
1508
}
 
1509
 
 
1510
void Client::setModal(bool m)
 
1511
{
 
1512
    // Qt-3.2 can have even modal normal windows :(
 
1513
    if (modal == m)
 
1514
        return;
 
1515
    modal = m;
 
1516
    if (!modal)
 
1517
        return;
 
1518
    // Changing modality for a mapped window is weird (?)
 
1519
    // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG
 
1520
}
 
1521
 
 
1522
void Client::setDesktop(int desktop)
 
1523
{
 
1524
    if (desktop != NET::OnAllDesktops)   // Do range check
 
1525
        desktop = qMax(1, qMin(workspace()->numberOfDesktops(), desktop));
 
1526
    desktop = qMin(workspace()->numberOfDesktops(), rules()->checkDesktop(desktop));
 
1527
    if (desk == desktop)
 
1528
        return;
 
1529
 
 
1530
    int was_desk = desk;
 
1531
    desk = desktop;
 
1532
    info->setDesktop(desktop);
 
1533
    if ((was_desk == NET::OnAllDesktops) != (desktop == NET::OnAllDesktops)) {
 
1534
        // onAllDesktops changed
 
1535
        if (isShown(true))
 
1536
            Notify::raise(isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops);
 
1537
        workspace()->updateOnAllDesktopsOfTransients(this);
 
1538
    }
 
1539
    if (decoration != NULL)
 
1540
        decoration->desktopChange();
 
1541
 
 
1542
    ClientList transients_stacking_order = workspace()->ensureStackingOrder(transients());
 
1543
    for (ClientList::ConstIterator it = transients_stacking_order.constBegin();
 
1544
            it != transients_stacking_order.constEnd();
 
1545
            ++it)
 
1546
        (*it)->setDesktop(desktop);
 
1547
 
 
1548
    if (isModal())  // if a modal dialog is moved, move the mainwindow with it as otherwise
 
1549
        // the (just moved) modal dialog will confusingly return to the mainwindow with
 
1550
        // the next desktop change
 
1551
    {
 
1552
        foreach (Client * c2, mainClients())
 
1553
        c2->setDesktop(desktop);
 
1554
    }
 
1555
 
 
1556
    workspace()->updateFocusChains(this, Workspace::FocusChainMakeFirst);
 
1557
    updateVisibility();
 
1558
    updateWindowRules();
 
1559
 
 
1560
    // Update states of all other windows in this group
 
1561
    if (clientGroup())
 
1562
        clientGroup()->updateStates(this);
 
1563
}
 
1564
 
 
1565
/**
 
1566
 * Sets whether the client is on @p activity.
 
1567
 * If you remove it from its last activity, then it's on all activities.
 
1568
 *
 
1569
 * Note: If it was on all activities and you try to remove it from one, nothing will happen;
 
1570
 * I don't think that's an important enough use case to handle here.
 
1571
 */
 
1572
void Client::setOnActivity(const QString &activity, bool enable)
 
1573
{
 
1574
    QStringList newActivitiesList = activities();
 
1575
    if (newActivitiesList.contains(activity) == enable)   //nothing to do
 
1576
        return;
 
1577
    if (enable) {
 
1578
        QStringList allActivities = workspace()->activityList();
 
1579
        if (!allActivities.contains(activity))   //bogus ID
 
1580
            return;
 
1581
        newActivitiesList.append(activity);
 
1582
    } else
 
1583
        newActivitiesList.removeOne(activity);
 
1584
    setOnActivities(newActivitiesList);
 
1585
}
 
1586
 
 
1587
/**
 
1588
 * set exactly which activities this client is on
 
1589
 */
 
1590
void Client::setOnActivities(QStringList newActivitiesList)
 
1591
{
 
1592
    QStringList allActivities = workspace()->activityList();
 
1593
    if (newActivitiesList.size() == allActivities.size() || newActivitiesList.isEmpty()) {
 
1594
        setOnAllActivities(true);
 
1595
        return;
 
1596
    }
 
1597
 
 
1598
    QByteArray joined = newActivitiesList.join(",").toAscii();
 
1599
    char *data = joined.data();
 
1600
    activityList = newActivitiesList;
 
1601
    XChangeProperty(display(), window(), atoms->activities, XA_STRING, 8,
 
1602
                    PropModeReplace, (unsigned char *)data, joined.size());
 
1603
 
 
1604
    updateActivities(false);
 
1605
}
 
1606
 
 
1607
/**
 
1608
 * update after activities changed
 
1609
 */
 
1610
void Client::updateActivities(bool includeTransients)
 
1611
{
 
1612
    /* FIXME do I need this?
 
1613
    if ( decoration != NULL )
 
1614
        decoration->desktopChange();
 
1615
        */
 
1616
    if (includeTransients)
 
1617
        workspace()->updateOnAllActivitiesOfTransients(this);
 
1618
    workspace()->updateFocusChains(this, Workspace::FocusChainMakeFirst);
 
1619
    updateVisibility();
 
1620
    updateWindowRules();
 
1621
 
 
1622
    // Update states of all other windows in this group
 
1623
    if (clientGroup())
 
1624
        clientGroup()->updateStates(this);
 
1625
}
 
1626
 
 
1627
/**
 
1628
 * Returns the virtual desktop within the workspace() the client window
 
1629
 * is located in, 0 if it isn't located on any special desktop (not mapped yet),
 
1630
 * or NET::OnAllDesktops. Do not use desktop() directly, use
 
1631
 * isOnDesktop() instead.
 
1632
 */
 
1633
int Client::desktop() const
 
1634
{
 
1635
    if (needsSessionInteract) {
 
1636
        return NET::OnAllDesktops;
 
1637
    }
 
1638
    return desk;
 
1639
}
 
1640
 
 
1641
/**
 
1642
 * Returns the list of activities the client window is on.
 
1643
 * if it's on all activities, the list will be empty.
 
1644
 * Don't use this, use isOnActivity() and friends (from class Toplevel)
 
1645
 */
 
1646
QStringList Client::activities() const
 
1647
{
 
1648
    if (needsSessionInteract) {
 
1649
        return QStringList();
 
1650
    }
 
1651
    return activityList;
 
1652
}
 
1653
 
 
1654
void Client::setOnAllDesktops(bool b)
 
1655
{
 
1656
    if ((b && isOnAllDesktops()) ||
 
1657
            (!b && !isOnAllDesktops()))
 
1658
        return;
 
1659
    if (b)
 
1660
        setDesktop(NET::OnAllDesktops);
 
1661
    else
 
1662
        setDesktop(workspace()->currentDesktop());
 
1663
 
 
1664
    // Update states of all other windows in this group
 
1665
    if (clientGroup())
 
1666
        clientGroup()->updateStates(this);
 
1667
}
 
1668
 
 
1669
/**
 
1670
 * if @p on is true, sets on all activities.
 
1671
 * if it's false, sets it to only be on the current activity
 
1672
 */
 
1673
void Client::setOnAllActivities(bool on)
 
1674
{
 
1675
    if (on == isOnAllActivities())
 
1676
        return;
 
1677
    if (on) {
 
1678
        activityList.clear();
 
1679
        XChangeProperty(display(), window(), atoms->activities, XA_STRING, 8,
 
1680
                        PropModeReplace, (const unsigned char *)"ALL", 3);
 
1681
        updateActivities(true);
 
1682
    } else {
 
1683
        setOnActivity(Workspace::self()->currentActivity(), true);
 
1684
        workspace()->updateOnAllActivitiesOfTransients(this);
 
1685
    }
 
1686
}
 
1687
 
 
1688
/**
 
1689
 * Performs activation and/or raising of the window
 
1690
 */
 
1691
void Client::takeActivity(int flags, bool handled, allowed_t)
 
1692
{
 
1693
    if (!handled || !Ptakeactivity) {
 
1694
        if (flags & ActivityFocus)
 
1695
            takeFocus(Allowed);
 
1696
        if (flags & ActivityRaise)
 
1697
            workspace()->raiseClient(this);
 
1698
        return;
 
1699
    }
 
1700
 
 
1701
#ifndef NDEBUG
 
1702
    static Time previous_activity_timestamp;
 
1703
    static Client* previous_client;
 
1704
 
 
1705
    //if ( previous_activity_timestamp == xTime() && previous_client != this )
 
1706
    //    {
 
1707
    //    kDebug( 1212 ) << "Repeated use of the same X timestamp for activity";
 
1708
    //    kDebug( 1212 ) << kBacktrace();
 
1709
    //    }
 
1710
 
 
1711
    previous_activity_timestamp = xTime();
 
1712
    previous_client = this;
 
1713
#endif
 
1714
 
 
1715
    workspace()->sendTakeActivity(this, xTime(), flags);
 
1716
}
 
1717
 
 
1718
/**
 
1719
 * Performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS
 
1720
 */
 
1721
void Client::takeFocus(allowed_t)
 
1722
{
 
1723
#ifndef NDEBUG
 
1724
    static Time previous_focus_timestamp;
 
1725
    static Client* previous_client;
 
1726
 
 
1727
    //if ( previous_focus_timestamp == xTime() && previous_client != this )
 
1728
    //    {
 
1729
    //    kDebug( 1212 ) << "Repeated use of the same X timestamp for focus";
 
1730
    //    kDebug( 1212 ) << kBacktrace();
 
1731
    //    }
 
1732
 
 
1733
    previous_focus_timestamp = xTime();
 
1734
    previous_client = this;
 
1735
#endif
 
1736
    if (rules()->checkAcceptFocus(input))
 
1737
        XSetInputFocus(display(), window(), RevertToPointerRoot, xTime());
 
1738
    if (Ptakefocus)
 
1739
        sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus);
 
1740
    workspace()->setShouldGetFocus(this);
 
1741
}
 
1742
 
 
1743
/**
 
1744
 * Returns whether the window provides context help or not. If it does,
 
1745
 * you should show a help menu item or a help button like '?' and call
 
1746
 * contextHelp() if this is invoked.
 
1747
 *
 
1748
 * \sa contextHelp()
 
1749
 */
 
1750
bool Client::providesContextHelp() const
 
1751
{
 
1752
    return Pcontexthelp;
 
1753
}
 
1754
 
 
1755
/**
 
1756
 * Invokes context help on the window. Only works if the window
 
1757
 * actually provides context help.
 
1758
 *
 
1759
 * \sa providesContextHelp()
 
1760
 */
 
1761
void Client::showContextHelp()
 
1762
{
 
1763
    if (Pcontexthelp) {
 
1764
        sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help);
 
1765
        QWhatsThis::enterWhatsThisMode(); // SELI TODO: ?
 
1766
    }
 
1767
}
 
1768
 
 
1769
/**
 
1770
 * Fetches the window's caption (WM_NAME property). It will be
 
1771
 * stored in the client's caption().
 
1772
 */
 
1773
void Client::fetchName()
 
1774
{
 
1775
    setCaption(readName());
 
1776
}
 
1777
 
 
1778
QString Client::readName() const
 
1779
{
 
1780
    if (info->name() && info->name()[0] != '\0')
 
1781
        return QString::fromUtf8(info->name());
 
1782
    else
 
1783
        return KWindowSystem::readNameProperty(window(), XA_WM_NAME);
 
1784
}
 
1785
 
 
1786
KWIN_COMPARE_PREDICATE(FetchNameInternalPredicate, Client, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption());
 
1787
 
 
1788
// The list is taken from http://www.unicode.org/reports/tr9/ (#154840)
 
1789
QChar LRM(0x200E);
 
1790
QChar RLM(0x200F);
 
1791
QChar LRE(0x202A);
 
1792
QChar RLE(0x202B);
 
1793
QChar LRO(0x202D);
 
1794
QChar RLO(0x202E);
 
1795
QChar PDF(0x202C);
 
1796
 
 
1797
void Client::setCaption(const QString& _s, bool force)
 
1798
{
 
1799
    QString s = _s;
 
1800
    if (s != cap_normal || force) {
 
1801
        bool reset_name = force;
 
1802
        for (int i = 0; i < s.length(); ++i)
 
1803
            if (!s[i].isPrint())
 
1804
                s[i] = QChar(' ');
 
1805
        cap_normal = s;
 
1806
        bool was_suffix = (!cap_suffix.isEmpty());
 
1807
        QString machine_suffix;
 
1808
        if (wmClientMachine(false) != "localhost" && !isLocalMachine(wmClientMachine(false)))
 
1809
            machine_suffix = QString(" <@") + wmClientMachine(true) + '>' + LRM;
 
1810
        QString shortcut_suffix = !shortcut().isEmpty() ? (" {" + shortcut().toString() + '}') : QString();
 
1811
        cap_suffix = machine_suffix + shortcut_suffix;
 
1812
        if ((!isSpecialWindow() || isToolbar()) && workspace()->findClient(FetchNameInternalPredicate(this))) {
 
1813
            int i = 2;
 
1814
            do {
 
1815
                cap_suffix = machine_suffix + " <" + QString::number(i) + '>' + LRM + shortcut_suffix;
 
1816
                i++;
 
1817
            } while (workspace()->findClient(FetchNameInternalPredicate(this)));
 
1818
            info->setVisibleName(caption().toUtf8());
 
1819
            reset_name = false;
 
1820
        }
 
1821
        if ((was_suffix && cap_suffix.isEmpty()) || reset_name) {
 
1822
            // If it was new window, it may have old value still set, if the window is reused
 
1823
            info->setVisibleName("");
 
1824
            info->setVisibleIconName("");
 
1825
        } else if (!cap_suffix.isEmpty() && !cap_iconic.isEmpty())
 
1826
            // Keep the same suffix in iconic name if it's set
 
1827
            info->setVisibleIconName(QString(cap_iconic + cap_suffix).toUtf8());
 
1828
 
 
1829
        if (isManaged() && decoration != NULL) {
 
1830
            if (client_group)
 
1831
                client_group->updateItems();
 
1832
            decoration->captionChange();
 
1833
        }
 
1834
    }
 
1835
}
 
1836
 
 
1837
void Client::updateCaption()
 
1838
{
 
1839
    setCaption(cap_normal, true);
 
1840
}
 
1841
 
 
1842
void Client::fetchIconicName()
 
1843
{
 
1844
    QString s;
 
1845
    if (info->iconName() && info->iconName()[0] != '\0')
 
1846
        s = QString::fromUtf8(info->iconName());
 
1847
    else
 
1848
        s = KWindowSystem::readNameProperty(window(), XA_WM_ICON_NAME);
 
1849
    if (s != cap_iconic) {
 
1850
        bool was_set = !cap_iconic.isEmpty();
 
1851
        cap_iconic = s;
 
1852
        if (!cap_suffix.isEmpty()) {
 
1853
            if (!cap_iconic.isEmpty())  // Keep the same suffix in iconic name if it's set
 
1854
                info->setVisibleIconName(QString(s + cap_suffix).toUtf8());
 
1855
            else if (was_set)
 
1856
                info->setVisibleIconName("");
 
1857
        }
 
1858
    }
 
1859
}
 
1860
 
 
1861
/**
 
1862
 * \reimp
 
1863
 */
 
1864
QString Client::caption(bool full) const
 
1865
{
 
1866
    return full ? cap_normal + cap_suffix : cap_normal;
 
1867
}
 
1868
 
 
1869
void Client::dontMoveResize()
 
1870
{
 
1871
    buttonDown = false;
 
1872
    stopDelayedMoveResize();
 
1873
    if (moveResizeMode)
 
1874
        finishMoveResize(false);
 
1875
}
 
1876
 
 
1877
void Client::setClientShown(bool shown)
 
1878
{
 
1879
    if (deleting)
 
1880
        return; // Don't change shown status if this client is being deleted
 
1881
    if (shown && hidden) {
 
1882
        map(Allowed);
 
1883
        hidden = false;
 
1884
        //updateVisibility();
 
1885
        //updateAllowedActions();
 
1886
        if (options->inactiveTabsSkipTaskbar)
 
1887
            setSkipTaskbar(false, false);
 
1888
        takeFocus(Allowed);
 
1889
        autoRaise();
 
1890
        workspace()->updateFocusChains(this, Workspace::FocusChainMakeFirst);
 
1891
    }
 
1892
    if (!shown && !hidden) {
 
1893
        unmap(Allowed);
 
1894
        hidden = true;
 
1895
        //updateVisibility();
 
1896
        //updateAllowedActions();
 
1897
        if (options->inactiveTabsSkipTaskbar)
 
1898
            setSkipTaskbar(true, false);   // TODO: Causes reshuffle of the taskbar
 
1899
        // Don't move tabs to the end of the list when another tab get's activated
 
1900
        if (!clientGroup() || clientGroup()->visible() == this)
 
1901
            workspace()->updateFocusChains(this, Workspace::FocusChainMakeLast);
 
1902
        addWorkspaceRepaint(visibleRect());
 
1903
    }
 
1904
}
 
1905
 
 
1906
void Client::getWMHints()
 
1907
{
 
1908
    XWMHints* hints = XGetWMHints(display(), window());
 
1909
    input = true;
 
1910
    window_group = None;
 
1911
    urgency = false;
 
1912
    if (hints) {
 
1913
        if (hints->flags & InputHint)
 
1914
            input = hints->input;
 
1915
        if (hints->flags & WindowGroupHint)
 
1916
            window_group = hints->window_group;
 
1917
        urgency = !!(hints->flags & UrgencyHint);   // Need boolean, it's a uint bitfield
 
1918
        XFree((char*)hints);
 
1919
    }
 
1920
    checkGroup();
 
1921
    updateUrgency();
 
1922
    updateAllowedActions(); // Group affects isMinimizable()
 
1923
}
 
1924
 
 
1925
void Client::sl_activated()
 
1926
{
 
1927
    emit s_activated();
 
1928
}
 
1929
 
 
1930
void Client::getMotifHints()
 
1931
{
 
1932
    bool mgot_noborder, mnoborder, mresize, mmove, mminimize, mmaximize, mclose;
 
1933
    Motif::readFlags(client, mgot_noborder, mnoborder, mresize, mmove, mminimize, mmaximize, mclose);
 
1934
    if (mgot_noborder) {
 
1935
        motif_noborder = mnoborder;
 
1936
        // If we just got a hint telling us to hide decorations, we do so.
 
1937
        if (motif_noborder)
 
1938
            noborder = true;
 
1939
        // If the Motif hint is now telling us to show decorations, we only do so if the app didn't
 
1940
        // instruct us to hide decorations in some other way, though.
 
1941
        else if (!motif_noborder && !app_noborder)
 
1942
            noborder = false;
 
1943
    }
 
1944
    if (!hasNETSupport()) {
 
1945
        // NETWM apps should set type and size constraints
 
1946
        motif_may_resize = mresize; // This should be set using minsize==maxsize, but oh well
 
1947
        motif_may_move = mmove;
 
1948
    } else
 
1949
        motif_may_resize = motif_may_move = true;
 
1950
 
 
1951
    // mminimize; - Ignore, bogus - E.g. shading or sending to another desktop is "minimizing" too
 
1952
    // mmaximize; - Ignore, bogus - Maximizing is basically just resizing
 
1953
    motif_may_close = mclose; // Motif apps like to crash when they set this hint and WM closes them anyway
 
1954
    if (isManaged())
 
1955
        updateDecoration(true);   // Check if noborder state has changed
 
1956
}
 
1957
 
 
1958
void Client::readIcons(Window win, QPixmap* icon, QPixmap* miniicon, QPixmap* bigicon, QPixmap* hugeicon)
 
1959
{
 
1960
    // Get the icons, allow scaling
 
1961
    if (icon != NULL)
 
1962
        *icon = KWindowSystem::icon(win, 32, 32, true, KWindowSystem::NETWM | KWindowSystem::WMHints);
 
1963
    if (miniicon != NULL) {
 
1964
        if (icon == NULL || !icon->isNull())
 
1965
            *miniicon = KWindowSystem::icon(win, 16, 16, true, KWindowSystem::NETWM | KWindowSystem::WMHints);
 
1966
        else
 
1967
            *miniicon = QPixmap();
 
1968
    }
 
1969
    if (bigicon != NULL) {
 
1970
        if (icon == NULL || !icon->isNull())
 
1971
            *bigicon = KWindowSystem::icon(win, 64, 64, false, KWindowSystem::NETWM | KWindowSystem::WMHints);
 
1972
        else
 
1973
            *bigicon = QPixmap();
 
1974
    }
 
1975
    if (hugeicon != NULL) {
 
1976
        if (icon == NULL || !icon->isNull())
 
1977
            *hugeicon = KWindowSystem::icon(win, 128, 128, false, KWindowSystem::NETWM | KWindowSystem::WMHints);
 
1978
        else
 
1979
            *hugeicon = QPixmap();
 
1980
    }
 
1981
}
 
1982
 
 
1983
void Client::getIcons()
 
1984
{
 
1985
    // First read icons from the window itself
 
1986
    readIcons(window(), &icon_pix, &miniicon_pix, &bigicon_pix, &hugeicon_pix);
 
1987
    if (icon_pix.isNull()) {
 
1988
        // Then try window group
 
1989
        icon_pix = group()->icon();
 
1990
        miniicon_pix = group()->miniIcon();
 
1991
        bigicon_pix = group()->bigIcon();
 
1992
        hugeicon_pix = group()->hugeIcon();
 
1993
    }
 
1994
    if (icon_pix.isNull() && isTransient()) {
 
1995
        // Then mainclients
 
1996
        ClientList mainclients = mainClients();
 
1997
        for (ClientList::ConstIterator it = mainclients.constBegin();
 
1998
                it != mainclients.constEnd() && icon_pix.isNull();
 
1999
                ++it) {
 
2000
            icon_pix = (*it)->icon();
 
2001
            miniicon_pix = (*it)->miniIcon();
 
2002
            bigicon_pix = (*it)->bigIcon();
 
2003
            hugeicon_pix = (*it)->hugeIcon();
 
2004
        }
 
2005
    }
 
2006
    if (icon_pix.isNull()) {
 
2007
        // And if nothing else, load icon from classhint or xapp icon
 
2008
        icon_pix = KWindowSystem::icon(window(), 32, 32, true, KWindowSystem::ClassHint | KWindowSystem::XApp);
 
2009
        miniicon_pix = KWindowSystem::icon(window(), 16, 16, true, KWindowSystem::ClassHint | KWindowSystem::XApp);
 
2010
        bigicon_pix = KWindowSystem::icon(window(), 64, 64, false, KWindowSystem::ClassHint | KWindowSystem::XApp);
 
2011
        hugeicon_pix = KWindowSystem::icon(window(), 128, 128, false, KWindowSystem::ClassHint | KWindowSystem::XApp);
 
2012
    }
 
2013
    if (isManaged() && decoration != NULL)
 
2014
        decoration->iconChange();
 
2015
}
 
2016
 
 
2017
QPixmap Client::icon(const QSize& size) const
 
2018
{
 
2019
    const int iconSize = qMin(size.width(), size.height());
 
2020
    if (iconSize <= 16)
 
2021
        return miniIcon();
 
2022
    else if (iconSize <= 32)
 
2023
        return icon();
 
2024
    if (iconSize <= 64)
 
2025
        return bigIcon();
 
2026
    else
 
2027
        return hugeIcon();
 
2028
}
 
2029
 
 
2030
void Client::getWindowProtocols()
 
2031
{
 
2032
    Atom* p;
 
2033
    int i, n;
 
2034
 
 
2035
    Pdeletewindow = 0;
 
2036
    Ptakefocus = 0;
 
2037
    Ptakeactivity = 0;
 
2038
    Pcontexthelp = 0;
 
2039
    Pping = 0;
 
2040
 
 
2041
    if (XGetWMProtocols(display(), window(), &p, &n)) {
 
2042
        for (i = 0; i < n; ++i) {
 
2043
            if (p[i] == atoms->wm_delete_window)
 
2044
                Pdeletewindow = 1;
 
2045
            else if (p[i] == atoms->wm_take_focus)
 
2046
                Ptakefocus = 1;
 
2047
            else if (p[i] == atoms->net_wm_take_activity)
 
2048
                Ptakeactivity = 1;
 
2049
            else if (p[i] == atoms->net_wm_context_help)
 
2050
                Pcontexthelp = 1;
 
2051
            else if (p[i] == atoms->net_wm_ping)
 
2052
                Pping = 1;
 
2053
        }
 
2054
        if (n > 0)
 
2055
            XFree(p);
 
2056
    }
 
2057
}
 
2058
 
 
2059
void Client::getSyncCounter()
 
2060
{
 
2061
#ifdef HAVE_XSYNC
 
2062
    if (!Extensions::syncAvailable())
 
2063
        return;
 
2064
 
 
2065
    Atom retType;
 
2066
    unsigned long nItemRet;
 
2067
    unsigned long byteRet;
 
2068
    int formatRet;
 
2069
    unsigned char* propRet;
 
2070
    int ret = XGetWindowProperty(display(), window(), atoms->net_wm_sync_request_counter,
 
2071
                                 0, 1, false, XA_CARDINAL, &retType, &formatRet, &nItemRet, &byteRet, &propRet);
 
2072
 
 
2073
    if (ret == Success && formatRet == 32) {
 
2074
        sync_counter = *(long*)(propRet);
 
2075
        XSyncIntToValue(&sync_counter_value, 0);
 
2076
        XSyncValue zero;
 
2077
        XSyncIntToValue(&zero, 0);
 
2078
        XSyncSetCounter(display(), sync_counter, zero);
 
2079
        if (sync_alarm == None) {
 
2080
            XSyncAlarmAttributes attrs;
 
2081
            attrs.trigger.counter = sync_counter;
 
2082
            attrs.trigger.value_type = XSyncRelative;
 
2083
            attrs.trigger.test_type = XSyncPositiveTransition;
 
2084
            XSyncIntToValue(&attrs.trigger.wait_value, 1);
 
2085
            XSyncIntToValue(&attrs.delta, 1);
 
2086
            sync_alarm = XSyncCreateAlarm(display(),
 
2087
                                          XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCADelta | XSyncCAValue,
 
2088
                                          &attrs);
 
2089
        }
 
2090
    }
 
2091
 
 
2092
    if (ret == Success)
 
2093
        XFree(propRet);
 
2094
#endif
 
2095
}
 
2096
 
 
2097
/**
 
2098
 * Send the client a _NET_SYNC_REQUEST
 
2099
 */
 
2100
void Client::sendSyncRequest()
 
2101
{
 
2102
#ifdef HAVE_XSYNC
 
2103
    if (sync_counter == None)
 
2104
        return;
 
2105
 
 
2106
    // We increment before the notify so that after the notify
 
2107
    // syncCounterSerial will equal the value we are expecting
 
2108
    // in the acknowledgement
 
2109
    int overflow;
 
2110
    XSyncValue one;
 
2111
    XSyncIntToValue(&one, 1);
 
2112
#undef XSyncValueAdd // It causes a warning :-/
 
2113
    XSyncValueAdd(&sync_counter_value, sync_counter_value, one, &overflow);
 
2114
 
 
2115
    // Send the message to client
 
2116
    XEvent ev;
 
2117
    ev.xclient.type = ClientMessage;
 
2118
    ev.xclient.window = window();
 
2119
    ev.xclient.format = 32;
 
2120
    ev.xclient.message_type = atoms->wm_protocols;
 
2121
    ev.xclient.data.l[0] = atoms->net_wm_sync_request;
 
2122
    ev.xclient.data.l[1] = xTime();
 
2123
    ev.xclient.data.l[2] = XSyncValueLow32(sync_counter_value);
 
2124
    ev.xclient.data.l[3] = XSyncValueHigh32(sync_counter_value);
 
2125
    ev.xclient.data.l[4] = 0;
 
2126
    XSendEvent(display(), window(), False, NoEventMask, &ev);
 
2127
    XSync(display(), false);
 
2128
#endif
 
2129
}
 
2130
 
 
2131
bool Client::wantsTabFocus() const
 
2132
{
 
2133
    return (isNormalWindow() || isDialog()) && wantsInput();
 
2134
}
 
2135
 
 
2136
bool Client::wantsInput() const
 
2137
{
 
2138
    return rules()->checkAcceptFocus(input || Ptakefocus);
 
2139
}
 
2140
 
 
2141
bool Client::isSpecialWindow() const
 
2142
{
 
2143
    // TODO
 
2144
    return isDesktop() || isDock() || isSplash() || isTopMenu() || isToolbar();
 
2145
}
 
2146
 
 
2147
/**
 
2148
 * Sets an appropriate cursor shape for the logical mouse position \a m
 
2149
 */
 
2150
void Client::updateCursor()
 
2151
{
 
2152
    Position m = mode;
 
2153
    if (!isResizable() || isShade())
 
2154
        m = PositionCenter;
 
2155
    QCursor c;
 
2156
    switch(m) {
 
2157
    case PositionTopLeft:
 
2158
    case PositionBottomRight:
 
2159
        c = Qt::SizeFDiagCursor;
 
2160
        break;
 
2161
    case PositionBottomLeft:
 
2162
    case PositionTopRight:
 
2163
        c = Qt::SizeBDiagCursor;
 
2164
        break;
 
2165
    case PositionTop:
 
2166
    case PositionBottom:
 
2167
        c = Qt::SizeVerCursor;
 
2168
        break;
 
2169
    case PositionLeft:
 
2170
    case PositionRight:
 
2171
        c = Qt::SizeHorCursor;
 
2172
        break;
 
2173
    default:
 
2174
        if (moveResizeMode)
 
2175
            c = Qt::SizeAllCursor;
 
2176
        else
 
2177
            c = Qt::ArrowCursor;
 
2178
        break;
 
2179
    }
 
2180
    if (c.handle() == cursor.handle())
 
2181
        return;
 
2182
    cursor = c;
 
2183
    if (decoration != NULL)
 
2184
        decoration->widget()->setCursor(cursor);
 
2185
    XDefineCursor(display(), frameId(), cursor.handle());
 
2186
    if (moveResizeMode)   // XDefineCursor doesn't change cursor if there's pointer grab active
 
2187
        XChangeActivePointerGrab(display(),
 
2188
                                 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
 
2189
                                 cursor.handle(), xTime());
 
2190
}
 
2191
 
 
2192
void Client::updateCompositeBlocking(bool readProperty)
 
2193
{
 
2194
    const bool usedToBlock = blocks_compositing;
 
2195
    if (readProperty) {
 
2196
        const unsigned long properties[2] = {0, NET::WM2BlockCompositing};
 
2197
        NETWinInfo2 i(QX11Info::display(), window(), rootWindow(), properties, 2);
 
2198
        blocks_compositing = rules()->checkBlockCompositing(i.isBlockingCompositing());
 
2199
    }
 
2200
    else
 
2201
        blocks_compositing = rules()->checkBlockCompositing(blocks_compositing);
 
2202
    if (usedToBlock != blocks_compositing)
 
2203
        workspace()->updateCompositeBlocking(blocks_compositing ? this : 0);
 
2204
}
 
2205
 
 
2206
Client::Position Client::mousePosition(const QPoint& p) const
 
2207
{
 
2208
    if (decoration != NULL)
 
2209
        return decoration->mousePosition(p);
 
2210
    return PositionCenter;
 
2211
}
 
2212
 
 
2213
void Client::updateAllowedActions(bool force)
 
2214
{
 
2215
    if (!isManaged() && !force)
 
2216
        return;
 
2217
    unsigned long old_allowed_actions = allowed_actions;
 
2218
    allowed_actions = 0;
 
2219
    if (isMovable())
 
2220
        allowed_actions |= NET::ActionMove;
 
2221
    if (isResizable())
 
2222
        allowed_actions |= NET::ActionResize;
 
2223
    if (isMinimizable())
 
2224
        allowed_actions |= NET::ActionMinimize;
 
2225
    if (isShadeable())
 
2226
        allowed_actions |= NET::ActionShade;
 
2227
    // Sticky state not supported
 
2228
    if (isMaximizable())
 
2229
        allowed_actions |= NET::ActionMax;
 
2230
    if (userCanSetFullScreen())
 
2231
        allowed_actions |= NET::ActionFullScreen;
 
2232
    allowed_actions |= NET::ActionChangeDesktop; // Always (Pagers shouldn't show Docks etc.)
 
2233
    if (isCloseable())
 
2234
        allowed_actions |= NET::ActionClose;
 
2235
    if (old_allowed_actions == allowed_actions)
 
2236
        return;
 
2237
    // TODO: This could be delayed and compressed - It's only for pagers etc. anyway
 
2238
    info->setAllowedActions(allowed_actions);
 
2239
 
 
2240
    // ONLY if relevant features have changed (and the window didn't just get/loose moveresize for maximization state changes)
 
2241
    const unsigned long relevant = ~(NET::ActionMove|NET::ActionResize);
 
2242
    if (decoration && (allowed_actions & relevant) != (old_allowed_actions & relevant))
 
2243
        decoration->reset(KDecoration::SettingButtons);
 
2244
}
 
2245
 
 
2246
void Client::autoRaise()
 
2247
{
 
2248
    workspace()->raiseClient(this);
 
2249
    cancelAutoRaise();
 
2250
}
 
2251
 
 
2252
void Client::cancelAutoRaise()
 
2253
{
 
2254
    delete autoRaiseTimer;
 
2255
    autoRaiseTimer = 0;
 
2256
}
 
2257
 
 
2258
void Client::debug(QDebug& stream) const
 
2259
{
 
2260
    stream << "\'ID:" << window() << ";WMCLASS:" << resourceClass() << ":"
 
2261
           << resourceName() << ";Caption:" << caption() << "\'";
 
2262
}
 
2263
 
 
2264
QPixmap* kwin_get_menu_pix_hack()
 
2265
{
 
2266
    static QPixmap p;
 
2267
    if (p.isNull())
 
2268
        p = SmallIcon("bx2");
 
2269
    return &p;
 
2270
}
 
2271
 
 
2272
void Client::checkActivities()
 
2273
{
 
2274
    QStringList newActivitiesList;
 
2275
    QByteArray prop = getStringProperty(window(), atoms->activities);
 
2276
    activitiesDefined = !prop.isEmpty();
 
2277
    if (prop == "ALL") {
 
2278
        //copied from setOnAllActivities to avoid a redundant XChangeProperty.
 
2279
        if (!activityList.isEmpty()) {
 
2280
            activityList.clear();
 
2281
            updateActivities(true);
 
2282
        }
 
2283
        return;
 
2284
    }
 
2285
    if (prop.isEmpty()) {
 
2286
        //note: this makes it *act* like it's on all activities but doesn't set the property to 'ALL'
 
2287
        if (!activityList.isEmpty()) {
 
2288
            activityList.clear();
 
2289
            updateActivities(true);
 
2290
        }
 
2291
        return;
 
2292
    }
 
2293
 
 
2294
    newActivitiesList = QString(prop).split(',');
 
2295
    if (newActivitiesList == activityList)
 
2296
        return; //expected change, it's ok.
 
2297
 
 
2298
    //otherwise, somebody else changed it. we need to validate before reacting
 
2299
    QStringList allActivities = workspace()->activityList();
 
2300
    if (allActivities.isEmpty()) {
 
2301
        kDebug() << "no activities!?!?";
 
2302
        //don't touch anything, there's probably something bad going on and we don't wanna make it worse
 
2303
        return;
 
2304
    }
 
2305
    for (int i = 0; i < newActivitiesList.size(); ++i) {
 
2306
        if (! allActivities.contains(newActivitiesList.at(i))) {
 
2307
            kDebug() << "invalid:" << newActivitiesList.at(i);
 
2308
            newActivitiesList.removeAt(i--);
 
2309
        }
 
2310
    }
 
2311
    setOnActivities(newActivitiesList);
 
2312
}
 
2313
 
 
2314
void Client::setSessionInteract(bool needed)
 
2315
{
 
2316
    needsSessionInteract = needed;
 
2317
}
 
2318
 
 
2319
QRect Client::decorationRect() const
 
2320
{
 
2321
    if (decoration && decoration->widget()) {
 
2322
        return decoration->widget()->rect().translated(-padding_left, -padding_top);
 
2323
    } else if (hasShadow()) {
 
2324
        return shadow()->shadowRegion().boundingRect();
 
2325
    } else {
 
2326
        return QRect(0, 0, width(), height());
 
2327
    }
 
2328
}
 
2329
 
 
2330
} // namespace
 
2331
 
 
2332
#include "client.moc"