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