1
/********************************************************************
2
KWin - the KDE window manager
3
This file is part of the KDE project.
5
Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
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.
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.
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
*********************************************************************/
24
#include <QApplication>
28
#include <QPaintEngine>
30
#include <kstandarddirs.h>
32
#include <kwindowsystem.h>
33
#include <kiconloader.h>
37
#include "scripting/client.h"
38
#include "scripting/scripting.h"
39
#include "scripting/workspaceproxy.h"
43
#include "workspace.h"
45
#include "notifications.h"
50
#include "paintredirector.h"
53
#include <X11/extensions/shape.h>
57
#include <X11/extensions/sync.h>
61
#include <X11/extensions/Xrender.h>
65
// Put all externs before the namespace statement to allow the linker
66
// to resolve them properly
72
// - only by calling Workspace::createClient()
73
// - it creates a new client and calls manage() for it
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
80
* \class Client client.h
81
* \brief The Client class encapsulates a window decoration frame.
85
* This ctor is "dumb" - it only initializes data. All the real initialization
86
* is done in manage().
88
Client::Client(Workspace* ws)
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)
107
, in_layer(UnknownLayer)
109
, process_killer(NULL)
110
, user_time(CurrentTime) // Not known yet
112
, block_geometry_updates(0)
113
, pending_geometry_update(PendingGeometryNone)
114
, shade_geometry_change(false)
120
, sync_resize_pending(false)
129
, sm_stacking_order(-1)
130
, demandAttentionKNotifyTimer(NULL)
131
, m_responsibleForDecoPixmap(false)
133
, electricMaximizing(false)
134
, activitiesDefined(false)
135
, needsSessionInteract(false)
137
// TODO: Do all as initialization
139
scriptCache = new QHash<QScriptEngine*, ClientResolution>();
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
147
mode = PositionCenter;
149
moveResizeMode = false;
153
shade_mode = ShadeNone;
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;
168
app_noborder = false;
169
motif_noborder = false;
171
ignore_focus_stealing = false;
172
demands_attention = false;
173
check_active_modal = false;
183
max_mode = MaximizeRestore;
184
maxmode_restore = MaximizeRestore;
188
//Client to workspace connections require that each
189
//client constructed be connected to the workspace wrapper
192
m_tabBoxClient = new TabBox::TabBoxClientImpl();
193
m_tabBoxClient->setClient(this);
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
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()));
205
// SELI TODO: Initialize xsizehints??
213
//SWrapper::Client::clientRelease(this);
215
if (sync_alarm != None)
216
XSyncDestroyAlarm(display(), sync_alarm);
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);
226
delete m_tabBoxClient;
230
// Use destroyClient() or releaseWindow(), Client instances cannot be deleted directly
231
void Client::deleteClient(Client* c, allowed_t)
237
* Releases the window. The client has done its job and the window is still existing.
239
void Client::releaseWindow(bool on_shutdown)
243
Deleted* del = Deleted::create(this);
244
emit clientClosed(this);
246
scene->windowClosed(this, del);
249
workspace()->discardUsedWindowRules(this, true); // Remove ForceTemporarily rules
250
StackingUpdatesBlocker blocker(workspace());
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)
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)
264
workspace()->clientHidden(this);
265
XUnmapWindow(display(), frameId()); // Destroying decoration would cause ugly visual effect
269
clientGroup()->remove(this, QRect(), true);
271
workspace()->removeClient(this, Allowed);
272
// Only when the window is being unmapped, not when closing down KWin (NETWM sections 5.5,5.7)
275
info->setState(0, info->state()); // Reset all state flags
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);
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);
291
XDestroyWindow(display(), wrapper);
293
XDestroyWindow(display(), frameId());
295
--block_geometry_updates; // Don't use GeometryUpdatesBlocker, it would now set the geometry
296
disownDataPassedToDeleted();
298
checkNonExistentClients();
299
deleteClient(this, Allowed);
304
* Like releaseWindow(), but this one is called when the window has been already destroyed
305
* (E.g. The application closed it)
307
void Client::destroyClient()
311
Deleted* del = Deleted::create(this);
312
emit clientClosed(this);
314
scene->windowClosed(this, del);
317
workspace()->discardUsedWindowRules(this, true); // Remove ForceTemporarily rules
318
StackingUpdatesBlocker blocker(workspace());
322
++block_geometry_updates;
323
if (isOnCurrentDesktop() && isShown(true))
324
addWorkspaceRepaint(visibleRect());
326
hidden = true; // So that it's not considered visible anymore
327
workspace()->clientHidden(this);
331
clientGroup()->remove(this, QRect(), true);
332
workspace()->removeClient(this, Allowed);
333
client = None; // invalidate
334
XDestroyWindow(display(), wrapper);
336
XDestroyWindow(display(), frameId());
338
--block_geometry_updates; // Don't use GeometryUpdatesBlocker, it would now set the geometry
339
disownDataPassedToDeleted();
341
checkNonExistentClients();
342
deleteClient(this, Allowed);
345
void Client::updateDecoration(bool check_workspace_pos, bool force)
348
((decoration == NULL && noBorder()) || (decoration != NULL && !noBorder())))
350
QRect oldgeom = geometry();
351
blockGeometryUpdates(true);
355
setMask(QRegion()); // Reset shape mask
356
decoration = workspace()->createDecoration(bridge);
357
// TODO: Check decoration's minimum size?
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();
373
discardWindowPixmap();
375
scene->windowGeometryShapeChanged(this);
376
emit clientGeometryShapeChanged(this, oldgeom);
379
if (check_workspace_pos)
380
checkWorkspacePosition();
381
blockGeometryUpdates(false);
383
decoration->widget()->show();
384
updateFrameExtents();
387
void Client::destroyDecoration()
389
QRect oldgeom = geometry();
390
if (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);
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;
407
decorationPixmapLeft = decorationPixmapRight = decorationPixmapTop = decorationPixmapBottom = QPixmap();
409
discardWindowPixmap();
410
if (scene != NULL && !deleting)
411
scene->windowGeometryShapeChanged(this);
413
emit clientGeometryShapeChanged(this, oldgeom);
418
bool Client::checkBorderSizes(bool also_resize)
420
if (decoration == NULL)
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)
437
border_left = new_left;
438
border_right = new_right;
439
border_top = new_top;
440
border_bottom = new_bottom;
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();
455
void Client::triggerDecorationRepaint()
457
if (decoration != NULL)
458
decoration->widget()->update();
461
void Client::layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom, Client::CoordinateMode mode) const
463
QRect r = decoration->widget()->rect();
464
if (mode == WindowRelative)
465
r.translate(-padding_left, -padding_top);
467
NETStrut strut = info->frameOverlap();
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());
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());
489
QRegion Client::decorationPendingRegion() const
491
if (!paintRedirector)
493
return paintRedirector->scheduledRepaintRegion().translated(x() - padding_left, y() - padding_top);
496
void Client::repaintDecorationPending()
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();
503
Workspace::self()->addRepaint(r.translated(x() - padding_left, y() - padding_top));
505
ensureDecorationPixmapsPainted();
508
bool Client::decorationPixmapRequiresRepaint()
510
if (!paintRedirector)
512
QRegion r = paintRedirector->pendingRegion();
516
void Client::ensureDecorationPixmapsPainted()
518
if (!paintRedirector)
521
QRegion r = paintRedirector->pendingRegion();
525
QPixmap p = paintRedirector->performPendingPaint();
527
QRect lr, rr, tr, br;
528
layoutDecorationRects(lr, tr, rr, br, DecorationRelative);
530
repaintDecorationPixmap(decorationPixmapLeft, lr, p, r);
531
repaintDecorationPixmap(decorationPixmapRight, rr, p, r);
532
repaintDecorationPixmap(decorationPixmapTop, tr, p, r);
533
repaintDecorationPixmap(decorationPixmapBottom, br, p, r);
535
if (!compositing()) {
536
// Blit the pixmaps to the frame window
537
layoutDecorationRects(lr, tr, rr, br, WindowRelative);
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?
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);
570
XSync(display(), false);
573
void Client::repaintDecorationPixmap(QPixmap& pix, const QRect& r, const QPixmap& src, QRegion reg)
577
QRect b = reg.boundingRect();
582
pt.translate(-r.topLeft());
583
pt.setCompositionMode(QPainter::CompositionMode_Source);
584
pt.setClipRegion(reg);
585
pt.drawPixmap(b.topLeft(), src);
589
void Client::resizeDecorationPixmaps()
591
QRect lr, rr, tr, br;
592
layoutDecorationRects(lr, tr, rr, br, DecorationRelative);
594
if (decorationPixmapTop.size() != tr.size()) {
595
if (m_responsibleForDecoPixmap && !decorationPixmapTop.isNull() &&
596
decorationPixmapTop.paintEngine()->type() == QPaintEngine::X11) {
597
XFreePixmap(display(), decorationPixmapTop.handle());
600
if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) {
601
decorationPixmapTop = QPixmap(tr.size());
602
m_responsibleForDecoPixmap = false;
604
Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(),
605
tr.size().width(), tr.height(),
607
decorationPixmapTop = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared);
608
decorationPixmapTop.fill(Qt::transparent);
609
m_responsibleForDecoPixmap= true;
613
if (decorationPixmapBottom.size() != br.size()) {
614
if (m_responsibleForDecoPixmap && !decorationPixmapBottom.isNull() &&
615
decorationPixmapBottom.paintEngine()->type() == QPaintEngine::X11) {
616
XFreePixmap(display(), decorationPixmapBottom.handle());
619
if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) {
620
decorationPixmapBottom = QPixmap(br.size());
621
m_responsibleForDecoPixmap = false;
623
Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(),
624
br.size().width(), br.height(),
626
decorationPixmapBottom = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared);
627
decorationPixmapBottom.fill(Qt::transparent);
628
m_responsibleForDecoPixmap = true;
632
if (decorationPixmapLeft.size() != lr.size()) {
633
if (m_responsibleForDecoPixmap && !decorationPixmapLeft.isNull() &&
634
decorationPixmapLeft.paintEngine()->type() == QPaintEngine::X11) {
635
XFreePixmap(display(), decorationPixmapLeft.handle());
638
if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) {
639
decorationPixmapLeft = QPixmap(lr.size());
640
m_responsibleForDecoPixmap = false;
642
Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(),
643
lr.size().width(), lr.height(),
645
decorationPixmapLeft = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared);
646
decorationPixmapLeft.fill(Qt::transparent);
647
m_responsibleForDecoPixmap = true;
651
if (decorationPixmapRight.size() != rr.size()) {
652
if (m_responsibleForDecoPixmap && !decorationPixmapRight.isNull() &&
653
decorationPixmapRight.paintEngine()->type() == QPaintEngine::X11) {
654
XFreePixmap(display(), decorationPixmapRight.handle());
657
if (workspace()->compositingActive() && effects->compositingType() == OpenGLCompositing) {
658
decorationPixmapRight = QPixmap(rr.size());
659
m_responsibleForDecoPixmap = false;
661
Pixmap xpix = XCreatePixmap(QX11Info::display(), rootWindow(),
662
rr.size().width(), rr.height(),
664
decorationPixmapRight = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared);
665
decorationPixmapRight.fill(Qt::transparent);
666
m_responsibleForDecoPixmap = true;
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);
679
triggerDecorationRepaint();
682
QRect Client::transparentRect() const
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)
694
const QRect r = QRect(clientPos(), clientSize())
695
.adjusted(strut.left, strut.top, -strut.right, -strut.bottom);
702
void Client::detectNoBorder()
709
switch(windowType()) {
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) {
737
void Client::updateFrameExtents()
740
strut.left = border_left;
741
strut.right = border_right;
742
strut.top = border_top;
743
strut.bottom = border_bottom;
744
info->setFrameExtents(strut);
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).
754
void Client::resizeDecoration(const QSize& s)
756
if (decoration == NULL)
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();
769
bool Client::noBorder() const
771
return !workspace()->hasDecorationPlugin() || noborder || isFullScreen();
774
bool Client::userCanSetNoBorder() const
776
return !isFullScreen() && !isShade() && (clientGroup() == NULL || !(clientGroup()->items().count() > 1));
779
void Client::setNoBorder(bool set)
781
if (!userCanSetNoBorder())
783
set = rules()->checkNoBorder(set);
787
updateDecoration(true, false);
791
void Client::checkNoBorder()
793
setNoBorder(app_noborder);
796
void Client::updateShape()
799
// Workaround for #19644 - Shaped windows shouldn't have decoration
801
// Only when shape is detected for the first time, still let the user to override
804
updateDecoration(true);
807
if (shape() && noBorder())
808
XShapeCombineShape(display(), frameId(), ShapeBounding,
809
clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeSet);
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
816
addWorkspaceRepaint(visibleRect()); // In case shape change removes part of this window
819
scene->windowGeometryShapeChanged(this);
820
emit clientGeometryShapeChanged(this, geometry());
823
static Window shape_helper_window = None;
825
void Client::updateInputShape()
827
if (hiddenPreview()) // Sets it to none, don't change
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);
855
void Client::setMask(const QRegion& reg, int mode)
857
QRegion r = reg.translated(-padding_left, -padding_right) & QRect(0, 0, width(), height());
861
Window shape_window = frameId();
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;
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);
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();
882
XShapeCombineRectangles(display(), shape_window, ShapeBounding, 0, 0,
883
xrects, rects.count(), ShapeSet, mode);
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);
897
scene->windowGeometryShapeChanged(this);
898
emit clientGeometryShapeChanged(this, geometry());
902
QRegion Client::mask() const
905
return QRegion(0, 0, width(), height());
909
void Client::hideClient(bool hide)
918
* Returns whether the window is minimizable or not
920
bool Client::isMinimizable() const
922
if (isSpecialWindow())
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();
931
if ((*it)->isShown(true))
932
shown_mainwindow = true;
933
if (!shown_mainwindow)
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)
945
if (!wantsTabFocus()) // SELI, TODO: - NET::Utility? why wantsTabFocus() - skiptaskbar? ?
951
* Minimizes this client plus its transients
953
void Client::minimize(bool avoid_animation)
955
if (!isMinimizable() || isMinimized())
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();
963
ws_wrap->sl_clientMinimized(this);
968
Notify::raise(Notify::Minimize);
973
updateAllowedActions();
974
workspace()->updateMinimizedOfTransients(this);
976
workspace()->updateFocusChains(this, Workspace::FocusChainMakeLast);
977
// TODO: merge signal with s_minimized
978
emit clientMinimized(this, !avoid_animation);
980
// when tiling, request a rearrangement
981
workspace()->notifyTilingWindowMinimizeToggled(this);
983
// Update states of all other windows in this group
985
clientGroup()->updateStates(this);
988
void Client::unminimize(bool avoid_animation)
993
SWrapper::WorkspaceProxy* ws_wrap = SWrapper::WorkspaceProxy::instance();
995
ws_wrap->sl_clientUnminimized(this);
998
emit s_unminimized();
1000
Notify::raise(Notify::UnMinimize);
1003
updateAllowedActions();
1004
workspace()->updateMinimizedOfTransients(this);
1005
updateWindowRules();
1006
workspace()->updateAllTiles();
1007
emit clientUnminimized(this, !avoid_animation);
1009
// when tiling, request a rearrangement
1010
workspace()->notifyTilingWindowMinimizeToggled(this);
1012
// Update states of all other windows in this group
1014
clientGroup()->updateStates(this);
1017
QRect Client::iconGeometry() const
1019
NETRect r = info->iconGeometry();
1020
QRect geom(r.pos.x, r.pos.y, r.size.width, r.size.height);
1024
// Check all mainwindows of this window (recursively)
1025
foreach (Client * mainwin, mainClients()) {
1026
geom = mainwin->iconGeometry();
1030
// No mainwindow (or their parents) with icon geometry was found
1035
bool Client::isShadeable() const
1037
return !isSpecialWindow() && !noBorder();
1040
void Client::setShade(ShadeMode mode)
1044
mode = rules()->checkShade(mode);
1045
if (shade_mode == mode)
1047
bool was_shade = isShade();
1048
ShadeMode was_shade_mode = shade_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
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);
1064
assert(decoration != NULL); // noborder windows can't be shaded
1065
GeometryUpdatesBlocker blocker(this);
1066
// Decorations may turn off some borders when shaded
1068
decoration->borders(border_left, border_right, border_top, border_bottom);
1070
// TODO: All this unmapping, resizing etc. feels too much duplicated from elsewhere
1072
// shade_mode == ShadeNormal
1073
addWorkspaceRepaint(visibleRect());
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);
1083
shade_geometry_change = false;
1085
if (was_shade_mode == ShadeHover)
1086
workspace()->activateNextClient(this);
1088
workspace()->focusToNull();
1091
shade_geometry_change = true;
1092
QSize s(sizeForClientSize(clientSize()));
1093
shade_geometry_change = false;
1095
if (shade_mode == ShadeHover || shade_mode == ShadeActivated)
1097
XMapWindow(display(), wrapperId());
1098
XMapWindow(display(), window());
1100
workspace()->requestFocus(this);
1102
info->setState(isShade() ? NET::Shaded : 0, NET::Shaded);
1103
info->setState(isShown(false) ? 0 : NET::Hidden, NET::Hidden);
1104
discardWindowPixmap();
1106
updateAllowedActions();
1108
decoration->shadeChange();
1109
updateWindowRules();
1111
// Update states of all other windows in this group
1113
clientGroup()->updateStates(this);
1116
void Client::shadeHover()
1118
setShade(ShadeHover);
1119
cancelShadeHoverTimer();
1122
void Client::shadeUnhover()
1124
setShade(ShadeNormal);
1125
cancelShadeHoverTimer();
1128
void Client::cancelShadeHoverTimer()
1130
delete shadeHoverTimer;
1131
shadeHoverTimer = 0;
1134
void Client::toggleShade()
1136
// If the mode is ShadeHover or ShadeActive, cancel shade too
1137
setShade(shade_mode == ShadeNone ? ShadeNormal : ShadeNone);
1140
void Client::updateVisibility()
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);
1150
internalHide(Allowed);
1153
if (clientGroup() == NULL || clientGroup()->visible() == this)
1154
setSkipTaskbar(original_skip_taskbar, false); // Reset from 'hidden'
1156
info->setState(NET::Hidden, NET::Hidden);
1157
if (compositing() && options->hiddenPreviews == HiddenPreviewsAlways)
1158
internalKeep(Allowed);
1160
internalHide(Allowed);
1163
info->setState(0, NET::Hidden);
1164
if (!isOnCurrentDesktop()) {
1165
if (compositing() && options->hiddenPreviews != HiddenPreviewsNever)
1166
internalKeep(Allowed);
1168
internalHide(Allowed);
1171
if (!isOnCurrentActivity()) {
1172
if (compositing() && options->hiddenPreviews != HiddenPreviewsNever)
1173
internalKeep(Allowed);
1175
internalHide(Allowed);
1178
bool belongs_to_desktop = false;
1179
for (ClientList::ConstIterator it = group()->members().constBegin();
1180
it != group()->members().constEnd();
1182
if ((*it)->isDesktop()) {
1183
belongs_to_desktop = true;
1186
if (!belongs_to_desktop && workspace()->showingDesktop())
1187
workspace()->resetShowingDesktop(true);
1188
internalShow(Allowed);
1192
* Sets the client window's mapping state. Possible values are
1193
* WithdrawnState, IconicState, NormalState.
1195
void Client::exportMappingState(int s)
1197
assert(client != None);
1198
assert(!deleting || s == WithdrawnState);
1199
if (s == WithdrawnState) {
1200
XDeleteProperty(display(), window(), atoms->wm_state);
1203
assert(s == NormalState || s == IconicState);
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);
1212
void Client::internalShow(allowed_t)
1214
if (mapping_state == Mapped)
1216
MappingState old = mapping_state;
1217
mapping_state = Mapped;
1218
if (old == Unmapped || old == Withdrawn)
1221
updateHiddenPreview();
1222
workspace()->checkUnredirect();
1225
void Client::internalHide(allowed_t)
1227
if (mapping_state == Unmapped)
1229
MappingState old = mapping_state;
1230
mapping_state = Unmapped;
1231
if (old == Mapped || old == Kept)
1234
updateHiddenPreview();
1235
addWorkspaceRepaint(visibleRect());
1236
workspace()->clientHidden(this);
1237
workspace()->checkUnredirect();
1240
void Client::internalKeep(allowed_t)
1242
assert(compositing());
1243
if (mapping_state == Kept)
1245
MappingState old = mapping_state;
1246
mapping_state = Kept;
1247
if (old == Unmapped || old == Withdrawn)
1249
updateHiddenPreview();
1250
addWorkspaceRepaint(visibleRect());
1251
workspace()->clientHidden(this);
1252
workspace()->checkUnredirect();
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).
1260
void Client::map(allowed_t)
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
1266
discardWindowPixmap();
1267
if (decoration != NULL)
1268
decoration->widget()->show(); // Not really necessary, but let it know the state
1269
XMapWindow(display(), frameId());
1271
XMapWindow(display(), wrapper);
1272
XMapWindow(display(), client);
1273
exportMappingState(NormalState);
1275
exportMappingState(IconicState);
1279
* Unmaps the client. Again, this is about the frame.
1281
void Client::unmap(allowed_t)
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
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);
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.
1310
void Client::updateHiddenPreview()
1312
if (hiddenPreview()) {
1313
workspace()->forceRestacking();
1314
if (Extensions::shapeInputAvailable())
1315
XShapeCombineRectangles(display(), frameId(), ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted);
1317
workspace()->forceRestacking();
1322
void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3)
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;
1338
if (w == rootWindow())
1339
mask = SubstructureRedirectMask; // Magic!
1340
XSendEvent(display(), w, False, mask, &ev);
1344
* Returns whether the window may be closed (have a close button)
1346
bool Client::isCloseable() const
1348
return rules()->checkCloseable(motif_may_close && !isSpecialWindow());
1352
* Closes the window by either sending a delete_window message or using XKill.
1354
void Client::closeWindow()
1359
// Update user time, because the window may create a confirming dialog.
1362
if (Pdeletewindow) {
1363
Notify::raise(Notify::Close);
1364
sendClientMessage(window(), atoms->wm_protocols, atoms->wm_delete_window);
1366
} else // Client will not react on wm_delete_window. We have not choice
1367
// but destroy his connection to the XServer.
1373
* Kills the window via XKill
1375
void Client::killWindow()
1377
kDebug(1212) << "Client::killWindow():" << caption();
1379
// Not sure if we need an Notify::Kill or not.. until then, use
1381
Notify::raise(Notify::Close);
1384
Notify::raise(Notify::TransDelete);
1385
if (isNormalWindow())
1386
Notify::raise(Notify::Delete);
1388
XKillClient(display(), window()); // Always kill this client at the server
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.
1396
void Client::pingWindow()
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);
1412
void Client::gotPing(Time timestamp)
1414
// Just plain compare is not good enough because of 64bit and truncating and whatnot
1415
if (NET::timestampCompare(timestamp, ping_timestamp) != 0)
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;
1429
void Client::pingTimeout()
1431
kDebug(1212) << "Ping timeout:" << caption();
1432
ping_timer->deleteLater();
1434
killProcess(true, ping_timestamp);
1437
void Client::killProcess(bool ask, Time timestamp)
1439
if (process_killer != NULL)
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
1446
kDebug(1212) << "Kill process:" << pid << "(" << machine << ")";
1448
if (machine != "localhost") {
1450
lst << machine << "kill" << QString::number(pid);
1451
QProcess::startDetached("xon", lst);
1453
::kill(pid, SIGTERM);
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));
1467
void Client::processKillerExited()
1469
kDebug(1212) << "Killer exited";
1470
delete process_killer;
1471
process_killer = NULL;
1474
void Client::setSkipTaskbar(bool b, bool from_outside)
1476
int was_wants_tab_focus = wantsTabFocus();
1478
b = rules()->checkSkipTaskbar(b);
1479
original_skip_taskbar = b;
1481
if (b == skipTaskbar())
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);
1491
void Client::setSkipPager(bool b)
1493
b = rules()->checkSkipPager(b);
1494
if (b == skipPager())
1497
info->setState(b ? NET::SkipPager : 0, NET::SkipPager);
1498
updateWindowRules();
1501
void Client::setSkipSwitcher(bool set)
1503
set = rules()->checkSkipSwitcher(set);
1504
if (set == skipSwitcher())
1506
skip_switcher = set;
1507
updateWindowRules();
1510
void Client::setModal(bool m)
1512
// Qt-3.2 can have even modal normal windows :(
1518
// Changing modality for a mapped window is weird (?)
1519
// _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG
1522
void Client::setDesktop(int desktop)
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)
1530
int was_desk = desk;
1532
info->setDesktop(desktop);
1533
if ((was_desk == NET::OnAllDesktops) != (desktop == NET::OnAllDesktops)) {
1534
// onAllDesktops changed
1536
Notify::raise(isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops);
1537
workspace()->updateOnAllDesktopsOfTransients(this);
1539
if (decoration != NULL)
1540
decoration->desktopChange();
1542
ClientList transients_stacking_order = workspace()->ensureStackingOrder(transients());
1543
for (ClientList::ConstIterator it = transients_stacking_order.constBegin();
1544
it != transients_stacking_order.constEnd();
1546
(*it)->setDesktop(desktop);
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
1552
foreach (Client * c2, mainClients())
1553
c2->setDesktop(desktop);
1556
workspace()->updateFocusChains(this, Workspace::FocusChainMakeFirst);
1558
updateWindowRules();
1560
// Update states of all other windows in this group
1562
clientGroup()->updateStates(this);
1566
* Sets whether the client is on @p activity.
1567
* If you remove it from its last activity, then it's on all activities.
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.
1572
void Client::setOnActivity(const QString &activity, bool enable)
1574
QStringList newActivitiesList = activities();
1575
if (newActivitiesList.contains(activity) == enable) //nothing to do
1578
QStringList allActivities = workspace()->activityList();
1579
if (!allActivities.contains(activity)) //bogus ID
1581
newActivitiesList.append(activity);
1583
newActivitiesList.removeOne(activity);
1584
setOnActivities(newActivitiesList);
1588
* set exactly which activities this client is on
1590
void Client::setOnActivities(QStringList newActivitiesList)
1592
QStringList allActivities = workspace()->activityList();
1593
if (newActivitiesList.size() == allActivities.size() || newActivitiesList.isEmpty()) {
1594
setOnAllActivities(true);
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());
1604
updateActivities(false);
1608
* update after activities changed
1610
void Client::updateActivities(bool includeTransients)
1612
/* FIXME do I need this?
1613
if ( decoration != NULL )
1614
decoration->desktopChange();
1616
if (includeTransients)
1617
workspace()->updateOnAllActivitiesOfTransients(this);
1618
workspace()->updateFocusChains(this, Workspace::FocusChainMakeFirst);
1620
updateWindowRules();
1622
// Update states of all other windows in this group
1624
clientGroup()->updateStates(this);
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.
1633
int Client::desktop() const
1635
if (needsSessionInteract) {
1636
return NET::OnAllDesktops;
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)
1646
QStringList Client::activities() const
1648
if (needsSessionInteract) {
1649
return QStringList();
1651
return activityList;
1654
void Client::setOnAllDesktops(bool b)
1656
if ((b && isOnAllDesktops()) ||
1657
(!b && !isOnAllDesktops()))
1660
setDesktop(NET::OnAllDesktops);
1662
setDesktop(workspace()->currentDesktop());
1664
// Update states of all other windows in this group
1666
clientGroup()->updateStates(this);
1670
* if @p on is true, sets on all activities.
1671
* if it's false, sets it to only be on the current activity
1673
void Client::setOnAllActivities(bool on)
1675
if (on == isOnAllActivities())
1678
activityList.clear();
1679
XChangeProperty(display(), window(), atoms->activities, XA_STRING, 8,
1680
PropModeReplace, (const unsigned char *)"ALL", 3);
1681
updateActivities(true);
1683
setOnActivity(Workspace::self()->currentActivity(), true);
1684
workspace()->updateOnAllActivitiesOfTransients(this);
1689
* Performs activation and/or raising of the window
1691
void Client::takeActivity(int flags, bool handled, allowed_t)
1693
if (!handled || !Ptakeactivity) {
1694
if (flags & ActivityFocus)
1696
if (flags & ActivityRaise)
1697
workspace()->raiseClient(this);
1702
static Time previous_activity_timestamp;
1703
static Client* previous_client;
1705
//if ( previous_activity_timestamp == xTime() && previous_client != this )
1707
// kDebug( 1212 ) << "Repeated use of the same X timestamp for activity";
1708
// kDebug( 1212 ) << kBacktrace();
1711
previous_activity_timestamp = xTime();
1712
previous_client = this;
1715
workspace()->sendTakeActivity(this, xTime(), flags);
1719
* Performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS
1721
void Client::takeFocus(allowed_t)
1724
static Time previous_focus_timestamp;
1725
static Client* previous_client;
1727
//if ( previous_focus_timestamp == xTime() && previous_client != this )
1729
// kDebug( 1212 ) << "Repeated use of the same X timestamp for focus";
1730
// kDebug( 1212 ) << kBacktrace();
1733
previous_focus_timestamp = xTime();
1734
previous_client = this;
1736
if (rules()->checkAcceptFocus(input))
1737
XSetInputFocus(display(), window(), RevertToPointerRoot, xTime());
1739
sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus);
1740
workspace()->setShouldGetFocus(this);
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.
1750
bool Client::providesContextHelp() const
1752
return Pcontexthelp;
1756
* Invokes context help on the window. Only works if the window
1757
* actually provides context help.
1759
* \sa providesContextHelp()
1761
void Client::showContextHelp()
1764
sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help);
1765
QWhatsThis::enterWhatsThisMode(); // SELI TODO: ?
1770
* Fetches the window's caption (WM_NAME property). It will be
1771
* stored in the client's caption().
1773
void Client::fetchName()
1775
setCaption(readName());
1778
QString Client::readName() const
1780
if (info->name() && info->name()[0] != '\0')
1781
return QString::fromUtf8(info->name());
1783
return KWindowSystem::readNameProperty(window(), XA_WM_NAME);
1786
KWIN_COMPARE_PREDICATE(FetchNameInternalPredicate, Client, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption());
1788
// The list is taken from http://www.unicode.org/reports/tr9/ (#154840)
1797
void Client::setCaption(const QString& _s, bool force)
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())
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))) {
1815
cap_suffix = machine_suffix + " <" + QString::number(i) + '>' + LRM + shortcut_suffix;
1817
} while (workspace()->findClient(FetchNameInternalPredicate(this)));
1818
info->setVisibleName(caption().toUtf8());
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());
1829
if (isManaged() && decoration != NULL) {
1831
client_group->updateItems();
1832
decoration->captionChange();
1837
void Client::updateCaption()
1839
setCaption(cap_normal, true);
1842
void Client::fetchIconicName()
1845
if (info->iconName() && info->iconName()[0] != '\0')
1846
s = QString::fromUtf8(info->iconName());
1848
s = KWindowSystem::readNameProperty(window(), XA_WM_ICON_NAME);
1849
if (s != cap_iconic) {
1850
bool was_set = !cap_iconic.isEmpty();
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());
1856
info->setVisibleIconName("");
1864
QString Client::caption(bool full) const
1866
return full ? cap_normal + cap_suffix : cap_normal;
1869
void Client::dontMoveResize()
1872
stopDelayedMoveResize();
1874
finishMoveResize(false);
1877
void Client::setClientShown(bool shown)
1880
return; // Don't change shown status if this client is being deleted
1881
if (shown && hidden) {
1884
//updateVisibility();
1885
//updateAllowedActions();
1886
if (options->inactiveTabsSkipTaskbar)
1887
setSkipTaskbar(false, false);
1890
workspace()->updateFocusChains(this, Workspace::FocusChainMakeFirst);
1892
if (!shown && !hidden) {
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());
1906
void Client::getWMHints()
1908
XWMHints* hints = XGetWMHints(display(), window());
1910
window_group = None;
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);
1922
updateAllowedActions(); // Group affects isMinimizable()
1925
void Client::sl_activated()
1930
void Client::getMotifHints()
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.
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)
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;
1949
motif_may_resize = motif_may_move = true;
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
1955
updateDecoration(true); // Check if noborder state has changed
1958
void Client::readIcons(Window win, QPixmap* icon, QPixmap* miniicon, QPixmap* bigicon, QPixmap* hugeicon)
1960
// Get the icons, allow scaling
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);
1967
*miniicon = QPixmap();
1969
if (bigicon != NULL) {
1970
if (icon == NULL || !icon->isNull())
1971
*bigicon = KWindowSystem::icon(win, 64, 64, false, KWindowSystem::NETWM | KWindowSystem::WMHints);
1973
*bigicon = QPixmap();
1975
if (hugeicon != NULL) {
1976
if (icon == NULL || !icon->isNull())
1977
*hugeicon = KWindowSystem::icon(win, 128, 128, false, KWindowSystem::NETWM | KWindowSystem::WMHints);
1979
*hugeicon = QPixmap();
1983
void Client::getIcons()
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();
1994
if (icon_pix.isNull() && isTransient()) {
1996
ClientList mainclients = mainClients();
1997
for (ClientList::ConstIterator it = mainclients.constBegin();
1998
it != mainclients.constEnd() && icon_pix.isNull();
2000
icon_pix = (*it)->icon();
2001
miniicon_pix = (*it)->miniIcon();
2002
bigicon_pix = (*it)->bigIcon();
2003
hugeicon_pix = (*it)->hugeIcon();
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);
2013
if (isManaged() && decoration != NULL)
2014
decoration->iconChange();
2017
QPixmap Client::icon(const QSize& size) const
2019
const int iconSize = qMin(size.width(), size.height());
2022
else if (iconSize <= 32)
2030
void Client::getWindowProtocols()
2041
if (XGetWMProtocols(display(), window(), &p, &n)) {
2042
for (i = 0; i < n; ++i) {
2043
if (p[i] == atoms->wm_delete_window)
2045
else if (p[i] == atoms->wm_take_focus)
2047
else if (p[i] == atoms->net_wm_take_activity)
2049
else if (p[i] == atoms->net_wm_context_help)
2051
else if (p[i] == atoms->net_wm_ping)
2059
void Client::getSyncCounter()
2062
if (!Extensions::syncAvailable())
2066
unsigned long nItemRet;
2067
unsigned long byteRet;
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);
2073
if (ret == Success && formatRet == 32) {
2074
sync_counter = *(long*)(propRet);
2075
XSyncIntToValue(&sync_counter_value, 0);
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,
2098
* Send the client a _NET_SYNC_REQUEST
2100
void Client::sendSyncRequest()
2103
if (sync_counter == None)
2106
// We increment before the notify so that after the notify
2107
// syncCounterSerial will equal the value we are expecting
2108
// in the acknowledgement
2111
XSyncIntToValue(&one, 1);
2112
#undef XSyncValueAdd // It causes a warning :-/
2113
XSyncValueAdd(&sync_counter_value, sync_counter_value, one, &overflow);
2115
// Send the message to client
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);
2131
bool Client::wantsTabFocus() const
2133
return (isNormalWindow() || isDialog()) && wantsInput();
2136
bool Client::wantsInput() const
2138
return rules()->checkAcceptFocus(input || Ptakefocus);
2141
bool Client::isSpecialWindow() const
2144
return isDesktop() || isDock() || isSplash() || isTopMenu() || isToolbar();
2148
* Sets an appropriate cursor shape for the logical mouse position \a m
2150
void Client::updateCursor()
2153
if (!isResizable() || isShade())
2157
case PositionTopLeft:
2158
case PositionBottomRight:
2159
c = Qt::SizeFDiagCursor;
2161
case PositionBottomLeft:
2162
case PositionTopRight:
2163
c = Qt::SizeBDiagCursor;
2166
case PositionBottom:
2167
c = Qt::SizeVerCursor;
2171
c = Qt::SizeHorCursor;
2175
c = Qt::SizeAllCursor;
2177
c = Qt::ArrowCursor;
2180
if (c.handle() == cursor.handle())
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());
2192
void Client::updateCompositeBlocking(bool readProperty)
2194
const bool usedToBlock = blocks_compositing;
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());
2201
blocks_compositing = rules()->checkBlockCompositing(blocks_compositing);
2202
if (usedToBlock != blocks_compositing)
2203
workspace()->updateCompositeBlocking(blocks_compositing ? this : 0);
2206
Client::Position Client::mousePosition(const QPoint& p) const
2208
if (decoration != NULL)
2209
return decoration->mousePosition(p);
2210
return PositionCenter;
2213
void Client::updateAllowedActions(bool force)
2215
if (!isManaged() && !force)
2217
unsigned long old_allowed_actions = allowed_actions;
2218
allowed_actions = 0;
2220
allowed_actions |= NET::ActionMove;
2222
allowed_actions |= NET::ActionResize;
2223
if (isMinimizable())
2224
allowed_actions |= NET::ActionMinimize;
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.)
2234
allowed_actions |= NET::ActionClose;
2235
if (old_allowed_actions == allowed_actions)
2237
// TODO: This could be delayed and compressed - It's only for pagers etc. anyway
2238
info->setAllowedActions(allowed_actions);
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);
2246
void Client::autoRaise()
2248
workspace()->raiseClient(this);
2252
void Client::cancelAutoRaise()
2254
delete autoRaiseTimer;
2258
void Client::debug(QDebug& stream) const
2260
stream << "\'ID:" << window() << ";WMCLASS:" << resourceClass() << ":"
2261
<< resourceName() << ";Caption:" << caption() << "\'";
2264
QPixmap* kwin_get_menu_pix_hack()
2268
p = SmallIcon("bx2");
2272
void Client::checkActivities()
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);
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);
2294
newActivitiesList = QString(prop).split(',');
2295
if (newActivitiesList == activityList)
2296
return; //expected change, it's ok.
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
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--);
2311
setOnActivities(newActivitiesList);
2314
void Client::setSessionInteract(bool needed)
2316
needsSessionInteract = needed;
2319
QRect Client::decorationRect() const
2321
if (decoration && decoration->widget()) {
2322
return decoration->widget()->rect().translated(-padding_left, -padding_top);
2323
} else if (hasShadow()) {
2324
return shadow()->shadowRegion().boundingRect();
2326
return QRect(0, 0, width(), height());
2332
#include "client.moc"