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
*********************************************************************/
22
// This file contains things relevant to handling incoming events.
26
#include <kstartupinfo.h>
28
#include <X11/extensions/shape.h>
30
#include "notifications.h"
35
#include "scripting/workspaceproxy.h"
41
* Manages the clients. This means handling the very first maprequest:
42
* reparenting, initial geometry, initial state, placement, etc.
43
* Returns false if KWin is not going to manage this window.
45
bool Client::manage(Window w, bool isMapped)
47
StackingUpdatesBlocker stacking_blocker(workspace());
49
//Scripting call. Does not use a signal/slot mechanism
50
//as ensuring connections was a bit difficult between
51
//so many clients and the workspace
52
SWrapper::WorkspaceProxy* ws_wrap = SWrapper::WorkspaceProxy::instance();
54
ws_wrap->sl_clientManaging(this);
59
XWindowAttributes attr;
60
if (!XGetWindowAttributes(display(), w, &attr)) {
65
// From this place on, manage() must not return false
66
block_geometry_updates = 1;
67
pending_geometry_update = PendingGeometryForced; // Force update when finishing with geometry changes
72
bit_depth = attr.depth;
74
// SELI TODO: Order all these things in some sane manner
76
bool init_minimize = false;
77
XWMHints* hints = XGetWMHints(display(), w);
78
if (hints && (hints->flags & StateHint) && hints->initial_state == IconicState)
83
init_minimize = false; // If it's already mapped, ignore hint
85
unsigned long properties[2];
86
properties[WinInfo::PROTOCOLS] =
97
properties[WinInfo::PROTOCOLS2] =
100
NET::WM2ExtendedStrut |
102
NET::WM2FullscreenMonitors |
103
NET::WM2FrameOverlap |
106
info = new WinInfo(this, display(), client, rootWindow(), properties, 2);
108
cmap = attr.colormap;
113
getWmClientMachine();
115
// First only read the caption text, so that setupWindowRules() can use it for matching,
116
// and only then really set the caption using setCaption(), which checks for duplicates etc.
117
// and also relies on rules already existing
118
cap_normal = readName();
119
setupWindowRules(false);
120
ignore_focus_stealing = options->checkIgnoreFocusStealing(this); // TODO: Change to rules
121
setCaption(cap_normal, true);
123
if (Extensions::shapeAvailable())
124
XShapeSelectInput(display(), window(), ShapeNotifyMask);
125
detectShape(window());
128
getWMHints(); // Needs to be done before readTransient() because of reading the group
129
modal = (info->state() & NET::Modal) != 0; // Needs to be valid before handling groups
132
getWindowProtocols();
133
getWmNormalHints(); // Get xSizeHint
136
// TODO: Try to obey all state information from info->state()
138
original_skip_taskbar = skip_taskbar = (info->state() & NET::SkipTaskbar) != 0;
139
skip_pager = (info->state() & NET::SkipPager) != 0;
143
KStartupInfoId asn_id;
144
KStartupInfoData asn_data;
145
bool asn_valid = workspace()->checkStartupNotification(window(), asn_id, asn_data);
147
workspace()->updateClientLayer(this);
149
SessionInfo* session = workspace()->takeSessionInfo(this);
151
init_minimize = session->minimized;
152
noborder = session->noBorder;
155
setShortcut(rules()->checkShortcut(session ? session->shortcut : QString(), true));
157
init_minimize = rules()->checkMinimize(init_minimize, !isMapped);
158
noborder = rules()->checkNoBorder(noborder, !isMapped);
162
// Initial desktop placement
164
desk = session->desktop;
165
if (session->onAllDesktops)
166
desk = NET::OnAllDesktops;
167
setOnActivities(session->activities);
169
// If this window is transient, ensure that it is opened on the
170
// same window as its parent. this is necessary when an application
171
// starts up on a different desktop than is currently displayed
173
ClientList mainclients = mainClients();
174
bool on_current = false;
176
Client* maincl = NULL;
177
// This is slightly duplicated from Placement::placeOnMainWindow()
178
for (ClientList::ConstIterator it = mainclients.constBegin();
179
it != mainclients.constEnd();
181
if (mainclients.count() > 1 && (*it)->isSpecialWindow())
182
continue; // Don't consider toolbars etc when placing
184
if ((*it)->isOnCurrentDesktop())
186
if ((*it)->isOnAllDesktops())
190
desk = NET::OnAllDesktops;
192
desk = workspace()->currentDesktop();
193
else if (maincl != NULL)
194
desk = maincl->desktop();
197
setOnActivities(maincl->activities());
200
desk = info->desktop(); // Window had the initial desktop property, force it
201
if (desktop() == 0 && asn_valid && asn_data.desktop() != 0)
202
desk = asn_data.desktop();
203
if (!isMapped && !noborder && isNormalWindow() && !activitiesDefined) {
204
//a new, regular window, when we're not recovering from a crash,
205
//and it hasn't got an activity. let's try giving it the current one.
206
//TODO: decide whether to keep this before the 4.6 release
207
//TODO: if we are keeping it (at least as an option), replace noborder checking
208
//with a public API for setting windows to be on all activities.
209
//something like KWindowSystem::setOnAllActivities or
210
//KActivityConsumer::setOnAllActivities
211
setOnActivity(Workspace::self()->currentActivity(), true);
214
if (desk == 0) // Assume window wants to be visible on the current desktop
215
desk = workspace()->currentDesktop();
216
desk = rules()->checkDesktop(desk, !isMapped);
217
if (desk != NET::OnAllDesktops) // Do range check
218
desk = qMax(1, qMin(workspace()->numberOfDesktops(), desk));
219
info->setDesktop(desk);
220
workspace()->updateOnAllDesktopsOfTransients(this); // SELI TODO
221
//onAllDesktopsChange(); // Decoration doesn't exist here yet
223
QRect geom(attr.x, attr.y, attr.width, attr.height);
224
bool placementDone = false;
227
geom = session->geometry;
230
bool partial_keep_in_area = isMapped || session;
231
if (isMapped || session)
232
area = workspace()->clientArea(FullArea, geom.center(), desktop());
233
else if (options->xineramaPlacementEnabled) {
234
int screen = options->xineramaPlacementScreen;
235
if (screen == -1) // Active screen
236
screen = asn_data.xinerama() == -1 ? workspace()->activeScreen() : asn_data.xinerama();
237
area = workspace()->clientArea(PlacementArea, workspace()->screenGeometry(screen).center(), desktop());
239
area = workspace()->clientArea(PlacementArea, cursorPos(), desktop());
241
if (int type = checkFullScreenHack(geom)) {
242
fullscreen_mode = FullScreenHack;
243
if (rules()->checkStrictGeometry(false)) {
244
geom = type == 2 // 1 = It's xinerama-aware fullscreen hack, 2 = It's full area
245
? workspace()->clientArea(FullArea, geom.center(), desktop())
246
: workspace()->clientArea(ScreenArea, geom.center(), desktop());
248
geom = workspace()->clientArea(FullScreenArea, geom.center(), desktop());
249
placementDone = true;
253
// KWin doesn't manage desktop windows
254
placementDone = true;
256
bool usePosition = false;
257
if (isMapped || session || placementDone)
258
placementDone = true; // Use geometry
259
else if (isTransient() && !isUtility() && !isDialog() && !isSplash())
261
else if (isTransient() && !hasNETSupport())
263
else if (isDialog() && hasNETSupport()) {
264
// If the dialog is actually non-NETWM transient window, don't try to apply placement to it,
265
// it breaks with too many things (xmms, display)
266
if (mainClients().count() >= 1) {
268
// #78082 - Ok, it seems there are after all some cases when an application has a good
269
// reason to specify a position for its dialog. Too bad other WMs have never bothered
270
// with placement for dialogs, so apps always specify positions for their dialogs,
271
// including such silly positions like always centered on the screen or under mouse.
272
// Using ignoring requested position in window-specific settings helps, and now
273
// there's also _NET_WM_FULL_PLACEMENT.
276
; // Force using placement policy
280
} else if (isSplash())
281
; // Force using placement policy
284
if (!rules()->checkIgnoreGeometry(!usePosition)) {
285
bool ignorePPosition = options->ignorePositionClasses.contains(
286
QString::fromLatin1(resourceClass()));
288
if (((xSizeHint.flags & PPosition) && !ignorePPosition) ||
289
(xSizeHint.flags & USPosition)) {
290
placementDone = true;
291
// Disobey xinerama placement option for now (#70943)
292
area = workspace()->clientArea(PlacementArea, geom.center(), desktop());
295
//if ( true ) // Size is always obeyed for now, only with constraints applied
296
// if (( xSizeHint.flags & USSize ) || ( xSizeHint.flags & PSize ))
298
// // Keep in mind that we now actually have a size :-)
301
if (xSizeHint.flags & PMaxSize)
302
geom.setSize(geom.size().boundedTo(
303
rules()->checkMaxSize(QSize(xSizeHint.max_width, xSizeHint.max_height))));
304
if (xSizeHint.flags & PMinSize)
305
geom.setSize(geom.size().expandedTo(
306
rules()->checkMinSize(QSize(xSizeHint.min_width, xSizeHint.min_height))));
308
if (isMovable() && (geom.x() > area.right() || geom.y() > area.bottom()))
309
placementDone = false; // Weird, do not trust.
312
move(geom.x(), geom.y()); // Before gravitating
314
// Create client group if the window will have a decoration
315
bool dontKeepInArea = false;
318
// Automatically add to previous groups on session restore
319
if (session && session->clientGroupClient && session->clientGroupClient != this)
320
session->clientGroupClient->clientGroup()->add(this, -1, true);
322
// If the window is already mapped (Restarted KWin) add any windows that already have the
323
// same geometry to the same client group. (May incorrectly handle maximized windows)
324
foreach (ClientGroup * group, workspace()->clientGroups)
325
if (geom == QRect(group->visible()->pos(), group->visible()->clientSize()) &&
326
desk == group->visible()->desktop() &&
327
activities() == group->visible()->activities() &&
328
group->visible()->maximizeMode() != MaximizeFull) {
329
group->add(this, -1, true);
332
if (!client_group && !isMapped && !session) {
333
// Attempt to automatically group similar windows
334
const Client* similar = workspace()->findSimilarClient(this);
335
if (similar && similar->clientGroup() && !similar->noBorder()) {
336
geom = QRect(similar->pos() + similar->clientPos(), similar->clientSize());
337
updateDecoration(false);
338
similar->clientGroup()->add(this, -1,
339
rules()->checkAutogroupInForeground(options->autogroupInForeground));
340
// Don't move entire group
341
geom = QRect(similar->pos() + similar->clientPos(), similar->clientSize());
342
placementDone = true;
343
dontKeepInArea = true;
347
client_group = new ClientGroup(this);
350
updateDecoration(false); // Also gravitates
351
// TODO: Is CentralGravity right here, when resizing is done after gravitating?
352
plainResize(rules()->checkSize(sizeForClientSize(geom.size()), !isMapped));
354
QPoint forced_pos = rules()->checkPosition(invalidPoint, !isMapped);
355
if (forced_pos != invalidPoint) {
357
placementDone = true;
358
// Don't keep inside workarea if the window has specially configured position
359
partial_keep_in_area = true;
360
area = workspace()->clientArea(FullArea, geom.center(), desktop());
362
if (!placementDone) {
363
// Placement needs to be after setting size
364
workspace()->place(this, area);
365
placementDone = true;
368
if ((!isSpecialWindow() || isToolbar()) && isMovable() && !dontKeepInArea)
369
keepInArea(area, partial_keep_in_area);
373
// CT: Extra check for stupid jdk 1.3.1. But should make sense in general
374
// if client has initial state set to Iconic and is transient with a parent
375
// window that is not Iconic, set init_state to Normal
376
if (init_minimize && isTransient()) {
377
ClientList mainclients = mainClients();
378
for (ClientList::ConstIterator it = mainclients.constBegin();
379
it != mainclients.constEnd();
381
if ((*it)->isShown(true))
382
init_minimize = false; // SELI TODO: Even e.g. for NET::Utility?
384
// If a dialog is shown for minimized window, minimize it too
385
if (!init_minimize && isTransient() && mainClients().count() > 0) {
386
bool visible_parent = false;
387
// Use allMainClients(), to include also main clients of group transients
388
// that have been optimized out in Client::checkGroupTransients()
389
ClientList mainclients = allMainClients();
390
for (ClientList::ConstIterator it = mainclients.constBegin();
391
it != mainclients.constEnd();
393
if ((*it)->isShown(true))
394
visible_parent = true;
395
if (!visible_parent) {
396
init_minimize = true;
402
minimize(true); // No animation
405
// SELI TODO: This seems to be mainly for kstart and ksystraycmd
406
// probably should be replaced by something better
407
bool doNotShow = false;
408
if (workspace()->isNotManaged(caption()))
411
// Other settings from the previous session
413
// Session restored windows are not considered to be new windows WRT rules,
414
// I.e. obey only forcing rules
415
setKeepAbove(session->keepAbove);
416
setKeepBelow(session->keepBelow);
417
setSkipTaskbar(session->skipTaskbar, true);
418
setSkipPager(session->skipPager);
419
setSkipSwitcher(session->skipSwitcher);
420
setShade(session->shaded ? ShadeNormal : ShadeNone);
421
setOpacity(session->opacity);
422
if (session->maximized != MaximizeRestore) {
423
maximize(MaximizeMode(session->maximized));
424
geom_restore = session->restore;
426
if (session->fullscreen == FullScreenHack)
427
; // Nothing, this should be already set again above
428
else if (session->fullscreen != FullScreenNone) {
429
setFullScreen(true, false);
430
geom_fs_restore = session->fsrestore;
433
geom_restore = geometry(); // Remember restore geometry
434
if (isMaximizable() && (width() >= area.width() || height() >= area.height())) {
435
// Window is too large for the screen, maximize in the
436
// directions necessary
437
if (width() >= area.width() && height() >= area.height()) {
438
maximize(Client::MaximizeFull);
439
geom_restore = QRect(); // Use placement when unmaximizing
440
} else if (width() >= area.width()) {
441
maximize(Client::MaximizeHorizontal);
442
geom_restore = QRect(); // Use placement when unmaximizing
443
geom_restore.setY(y()); // But only for horizontal direction
444
geom_restore.setHeight(height());
445
} else if (height() >= area.height()) {
446
maximize(Client::MaximizeVertical);
447
geom_restore = QRect(); // Use placement when unmaximizing
448
geom_restore.setX(x()); // But only for vertical direction
449
geom_restore.setWidth(width());
453
// Window may want to be maximized
454
// done after checking that the window isn't larger than the workarea, so that
455
// the restore geometry from the checks above takes precedence, and window
456
// isn't restored larger than the workarea
457
MaximizeMode maxmode = static_cast<MaximizeMode>(
458
((info->state() & NET::MaxVert) ? MaximizeVertical : 0) |
459
((info->state() & NET::MaxHoriz) ? MaximizeHorizontal : 0));
460
MaximizeMode forced_maxmode = rules()->checkMaximize(maxmode, !isMapped);
462
// Either hints were set to maximize, or is forced to maximize,
463
// or is forced to non-maximize and hints were set to maximize
464
if (forced_maxmode != MaximizeRestore || maxmode != MaximizeRestore)
465
maximize(forced_maxmode);
467
// Read other initial states
468
setShade(rules()->checkShade(info->state() & NET::Shaded ? ShadeNormal : ShadeNone, !isMapped));
469
setKeepAbove(rules()->checkKeepAbove(info->state() & NET::KeepAbove, !isMapped));
470
setKeepBelow(rules()->checkKeepBelow(info->state() & NET::KeepBelow, !isMapped));
471
setSkipTaskbar(rules()->checkSkipTaskbar(info->state() & NET::SkipTaskbar, !isMapped), true);
472
setSkipPager(rules()->checkSkipPager(info->state() & NET::SkipPager, !isMapped));
473
setSkipSwitcher(rules()->checkSkipSwitcher(false, !isMapped));
474
if (info->state() & NET::DemandsAttention)
476
if (info->state() & NET::Modal)
478
if (fullscreen_mode != FullScreenHack && isFullScreenable())
479
setFullScreen(rules()->checkFullScreen(info->state() & NET::FullScreen, !isMapped), false);
482
updateAllowedActions(true);
484
// Set initial user time directly
485
user_time = readUserTimeMapTimestamp(asn_valid ? &asn_id : NULL, asn_valid ? &asn_data : NULL, session);
486
group()->updateUserTime(user_time); // And do what Client::updateUserTime() does
488
if (isTopMenu()) // They're shown in Workspace::addClient() if their mainwindow
489
hideClient(true); // Is the active one
491
// This should avoid flicker, because real restacking is done
492
// only after manage() finishes because of blocking, but the window is shown sooner
493
XLowerWindow(display(), frameId());
494
if (session && session->stackingOrder != -1) {
495
sm_stacking_order = session->stackingOrder;
496
workspace()->restoreSessionStackingOrder(this);
500
// Sending ConfigureNotify is done when setting mapping state below,
501
// Getting the first sync response means window is ready for compositing
504
if (isShown(true) && !doNotShow) {
506
Notify::raise(Notify::TransNew);
507
if (isNormalWindow())
508
Notify::raise(Notify::New);
512
allow = session->active &&
513
(!workspace()->wasUserInteraction() || workspace()->activeClient() == NULL ||
514
workspace()->activeClient()->isDesktop());
516
allow = workspace()->allowClientActivation(this, userTime(), false);
518
if (!(isMapped || session)) {
519
if (workspace()->sessionSaving()) {
521
* If we get a new window during session saving, we assume it's some 'save file?' dialog
522
* which the user really needs to see (to know why logout's stalled).
524
* Given the current session management protocol, I can't see a nicer way of doing this.
525
* Someday I'd like to see a protocol that tells the windowmanager who's doing SessionInteract.
527
needsSessionInteract = true;
528
//show the parent too
529
ClientList mainclients = mainClients();
530
for (ClientList::ConstIterator it = mainclients.constBegin();
531
it != mainclients.constEnd(); ++it) {
532
(*it)->setSessionInteract(true);
535
// also force if activation is allowed
536
if (!isOnCurrentDesktop()) {
537
workspace()->setCurrentDesktop(desktop());
539
/*if (!isOnCurrentActivity()) {
540
workspace()->setCurrentActivity( activities().first() );
541
} FIXME no such method*/
545
bool belongs_to_desktop = false;
546
for (ClientList::ConstIterator it = group()->members().constBegin();
547
it != group()->members().constEnd();
549
if ((*it)->isDesktop()) {
550
belongs_to_desktop = true;
553
if (!belongs_to_desktop && workspace()->showingDesktop())
554
workspace()->resetShowingDesktop(options->showDesktopIsMinimizeAll);
556
if (isOnCurrentDesktop() && !isMapped && !allow && (!session || session->stackingOrder < 0))
557
workspace()->restackClientUnderActive(this);
562
if (allow && isOnCurrentDesktop()) {
563
if (!isSpecialWindow())
564
if (options->focusPolicyIsReasonable() && wantsTabFocus())
565
workspace()->requestFocus(this);
566
} else if (!session && !isSpecialWindow())
569
} else if (!doNotShow) // if ( !isShown( true ) && !doNotShow )
572
hideClient(true); // SELI HACK !!!
573
assert(mapping_state != Withdrawn);
574
blockGeometryUpdates(false);
576
if (user_time == CurrentTime || user_time == -1U) {
577
// No known user time, set something old
578
user_time = xTime() - 1000000;
579
if (user_time == CurrentTime || user_time == -1U) // Let's be paranoid
580
user_time = xTime() - 1000000 + 10;
583
//sendSyntheticConfigureNotify(); // Done when setting mapping state
589
client_rules.discardTemporary();
590
applyWindowRules(); // Just in case
591
workspace()->discardUsedWindowRules(this, false); // Remove ApplyNow rules
592
updateWindowRules(); // Was blocked while !isManaged()
594
updateCompositeBlocking(true);
596
// TODO: there's a small problem here - isManaged() depends on the mapping state,
597
// but this client is not yet in Workspace's client list at this point, will
598
// be only done in addClient()
602
// Called only from manage()
603
void Client::embedClient(Window w, const XWindowAttributes& attr)
605
assert(client == None);
606
assert(frameId() == None);
607
assert(wrapper == None);
610
// We don't want the window to be destroyed when we are destroyed
611
XAddToSaveSet(display(), client);
612
XSelectInput(display(), client, NoEventMask);
613
XUnmapWindow(display(), client);
614
XWindowChanges wc; // Set the border width to 0
615
wc.border_width = 0; // TODO: Possibly save this, and also use it for initial configuring of the window
616
XConfigureWindow(display(), client, CWBorderWidth, &wc);
618
XSetWindowAttributes swa;
619
swa.colormap = attr.colormap;
620
swa.background_pixmap = None;
621
swa.border_pixel = 0;
623
Window frame = XCreateWindow(display(), rootWindow(), 0, 0, 1, 1, 0,
624
attr.depth, InputOutput, attr.visual, CWColormap | CWBackPixmap | CWBorderPixel, &swa);
625
setWindowHandles(client, frame);
626
wrapper = XCreateWindow(display(), frame, 0, 0, 1, 1, 0,
627
attr.depth, InputOutput, attr.visual, CWColormap | CWBackPixmap | CWBorderPixel, &swa);
629
XDefineCursor(display(), frame, QCursor(Qt::ArrowCursor).handle());
630
// Some apps are stupid and don't define their own cursor - set the arrow one for them
631
XDefineCursor(display(), wrapper, QCursor(Qt::ArrowCursor).handle());
632
XReparentWindow(display(), client, wrapper, 0, 0);
633
XSelectInput(display(), frame,
634
KeyPressMask | KeyReleaseMask |
635
ButtonPressMask | ButtonReleaseMask |
639
EnterWindowMask | LeaveWindowMask |
643
StructureNotifyMask | SubstructureRedirectMask);
644
XSelectInput(display(), wrapper, ClientWinMask | SubstructureNotifyMask);
645
XSelectInput(display(), client,
649
EnterWindowMask | LeaveWindowMask |
650
KeyPressMask | KeyReleaseMask