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
You can Freely distribute this program under the GNU General Public
9
License. See the file "COPYING" for the exact licensing terms.
10
******************************************************************/
14
This file contains things relevant to handling incoming events.
20
#include <kstartupinfo.h>
22
#include <X11/extensions/shape.h>
24
#include "notifications.h"
33
Manages the clients. This means handling the very first maprequest:
34
reparenting, initial geometry, initial state, placement, etc.
35
Returns false if KWin is not going to manage this window.
37
bool Client::manage( Window w, bool isMapped )
39
StackingUpdatesBlocker stacking_blocker( workspace());
43
XWindowAttributes attr;
44
if( !XGetWindowAttributes(display(), w, &attr))
50
// from this place on, manage() mustn't return false
51
block_geometry_updates = 1;
52
pending_geometry_update = PendingGeometryForced; // force update when finishing with geometry changes
54
embedClient( w, attr );
57
bit_depth = attr.depth;
61
// SELI order all these things in some sane manner
63
bool init_minimize = false;
64
XWMHints * hints = XGetWMHints(display(), w );
65
if (hints && (hints->flags & StateHint) && hints->initial_state == IconicState)
70
init_minimize = false; // if it's already mapped, ignore hint
72
unsigned long properties[ 2 ];
73
properties[ WinInfo::PROTOCOLS ] =
84
properties[ WinInfo::PROTOCOLS2 ] =
87
NET::WM2ExtendedStrut |
91
info = new WinInfo( this, display(), client, rootWindow(), properties, 2 );
100
// first only read the caption text, so that setupWindowRules() can use it for matching,
101
// and only then really set the caption using setCaption(), which checks for duplicates etc.
102
// and also relies on rules already existing
103
cap_normal = readName();
104
setupWindowRules( false );
105
ignore_focus_stealing = options->checkIgnoreFocusStealing( this ); // TODO change to rules
106
setCaption( cap_normal, true );
108
if( Extensions::shapeAvailable())
109
XShapeSelectInput( display(), window(), ShapeNotifyMask );
110
detectShape( window());
113
getWMHints(); // needs to be done before readTransient() because of reading the group
114
modal = ( info->state() & NET::Modal ) != 0; // needs to be valid before handling groups
117
getWindowProtocols();
118
getWmNormalHints(); // get xSizeHint
121
// TODO try to obey all state information from info->state()
123
original_skip_taskbar = skip_taskbar = ( info->state() & NET::SkipTaskbar) != 0;
124
skip_pager = ( info->state() & NET::SkipPager) != 0;
126
KStartupInfoId asn_id;
127
KStartupInfoData asn_data;
128
bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data );
130
workspace()->updateClientLayer( this );
132
SessionInfo* session = workspace()->takeSessionInfo( this );
136
if ( session->minimized )
137
init_minimize = true;
138
if( session->userNoBorder )
139
setUserNoBorder( true );
142
setShortcut( rules()->checkShortcut( session ? session->shortcut : QString(), true ));
144
init_minimize = rules()->checkMinimize( init_minimize, !isMapped );
145
if( rules()->checkNoBorder( false, !isMapped ))
146
setUserNoBorder( true );
148
// initial desktop placement
151
desk = session->desktop;
152
if( session->onAllDesktops )
153
desk = NET::OnAllDesktops;
157
// if this window is transient, ensure that it is opened on the
158
// same window as its parent. this is necessary when an application
159
// starts up on a different desktop than is currently displayed
162
ClientList mainclients = mainClients();
163
bool on_current = false;
164
Client* maincl = NULL;
165
// this is slightly duplicated from Placement::placeOnMainWindow()
166
for( ClientList::ConstIterator it = mainclients.begin();
167
it != mainclients.end();
170
if( mainclients.count() > 1 && (*it)->isSpecialWindow())
171
continue; // don't consider toolbars etc when placing
173
if( (*it)->isOnCurrentDesktop())
177
desk = workspace()->currentDesktop();
178
else if( maincl != NULL )
179
desk = maincl->desktop();
181
if ( info->desktop() )
182
desk = info->desktop(); // window had the initial desktop property, force it
183
if( desktop() == 0 && asn_valid && asn_data.desktop() != 0 )
184
desk = asn_data.desktop();
186
if ( desk == 0 ) // assume window wants to be visible on the current desktop
187
desk = workspace()->currentDesktop();
188
desk = rules()->checkDesktop( desk, !isMapped );
189
if( desk != NET::OnAllDesktops ) // do range check
190
desk = qMax( 1, qMin( workspace()->numberOfDesktops(), desk ));
191
info->setDesktop( desk );
192
workspace()->updateOnAllDesktopsOfTransients( this ); // SELI
193
// onAllDesktopsChange(); decoration doesn't exist here yet
195
QRect geom( attr.x, attr.y, attr.width, attr.height );
196
bool placementDone = false;
199
geom = session->geometry;
202
bool partial_keep_in_area = isMapped || session;
203
if( isMapped || session )
204
area = workspace()->clientArea( FullArea, geom.center(), desktop());
205
else if( options->xineramaPlacementEnabled )
207
int screen = options->xineramaPlacementScreen;
208
if( screen == -1 ) // active screen
209
screen = asn_data.xinerama() == -1 ? workspace()->activeScreen() : asn_data.xinerama();
210
area = workspace()->clientArea( PlacementArea, workspace()->screenGeometry( screen ).center(), desktop());
213
area = workspace()->clientArea( PlacementArea, cursorPos(), desktop());
215
if( int type = checkFullScreenHack( geom ))
217
fullscreen_mode = FullScreenHack;
218
if( rules()->checkStrictGeometry( false ))
220
geom = type == 2 // 1 - it's xinerama-aware fullscreen hack, 2 - it's full area
221
? workspace()->clientArea( FullArea, geom.center(), desktop())
222
: workspace()->clientArea( ScreenArea, geom.center(), desktop());
225
geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop());
226
placementDone = true;
231
// desktops are treated slightly special
232
geom = workspace()->clientArea( FullArea, geom.center(), desktop());
233
placementDone = true;
236
bool usePosition = false;
237
if ( isMapped || session || placementDone )
238
placementDone = true; // use geometry
239
else if( isTransient() && !isUtility() && !isDialog() && !isSplash())
241
else if( isTransient() && !hasNETSupport())
243
else if( isDialog() && hasNETSupport())
244
// if the dialog is actually non-NETWM transient window, don't try to apply placement to it,
245
// it breaks with too many things (xmms, display)
247
if( mainClients().count() >= 1 )
250
// #78082 - Ok, it seems there are after all some cases when an application has a good
251
// reason to specify a position for its dialog. Too bad other WMs have never bothered
252
// with placement for dialogs, so apps always specify positions for their dialogs,
253
// including such silly positions like always centered on the screen or under mouse.
254
// Using ignoring requested position in window-specific settings helps, and now
255
// there's also _NET_WM_FULL_PLACEMENT.
258
; // force using placement policy
265
; // force using placement policy
268
if( !rules()->checkIgnoreGeometry( !usePosition ))
270
bool ignorePPosition = ( options->ignorePositionClasses.contains(QString::fromLatin1(resourceClass())));
272
if ( ( (xSizeHint.flags & PPosition) && !ignorePPosition ) ||
273
(xSizeHint.flags & USPosition) )
275
placementDone = true;
276
// disobey xinerama placement option for now (#70943)
277
area = workspace()->clientArea( PlacementArea, geom.center(), desktop());
280
if( true ) // size is always obeyed for now, only with constraints applied
281
if ( (xSizeHint.flags & USSize) || (xSizeHint.flags & PSize) )
283
// keep in mind that we now actually have a size :-)
286
if (xSizeHint.flags & PMaxSize)
287
geom.setSize( geom.size().boundedTo(
288
rules()->checkMaxSize( QSize(xSizeHint.max_width, xSizeHint.max_height ) ) ) );
289
if (xSizeHint.flags & PMinSize)
290
geom.setSize( geom.size().expandedTo(
291
rules()->checkMinSize( QSize(xSizeHint.min_width, xSizeHint.min_height ) ) ) );
295
if( geom.x() > area.right() || geom.y() > area.bottom())
296
placementDone = false; // weird, do not trust.
300
move( geom.x(), geom.y() ); // before gravitating
302
updateDecoration( false ); // also gravitates
303
// TODO is CentralGravity right here, when resizing is done after gravitating?
304
plainResize( rules()->checkSize( sizeForClientSize( geom.size()), !isMapped ));
306
QPoint forced_pos = rules()->checkPosition( invalidPoint, !isMapped );
307
if( forced_pos != invalidPoint )
310
placementDone = true;
311
// don't keep inside workarea if the window has specially configured position
312
partial_keep_in_area = true;
313
area = workspace()->clientArea( FullArea, geom.center(), desktop());
316
{ // placement needs to be after setting size
317
workspace()->place( this, area );
318
placementDone = true;
321
if(( !isSpecialWindow() || isToolbar()) && isMovable())
322
keepInArea( area, partial_keep_in_area );
327
//CT extra check for stupid jdk 1.3.1. But should make sense in general
328
// if client has initial state set to Iconic and is transient with a parent
329
// window that is not Iconic, set init_state to Normal
330
if( init_minimize && isTransient())
332
ClientList mainclients = mainClients();
333
for( ClientList::ConstIterator it = mainclients.begin();
334
it != mainclients.end();
336
if( (*it)->isShown( true ))
337
init_minimize = false; // SELI even e.g. for NET::Utility?
341
minimize( true ); // no animation
343
// SELI this seems to be mainly for kstart and ksystraycmd
344
// probably should be replaced by something better
345
bool doNotShow = false;
346
if ( workspace()->isNotManaged( caption() ) )
349
// other settings from the previous session
352
// session restored windows are not considered to be new windows WRT rules,
353
// i.e. obey only forcing rules
354
setKeepAbove( session->keepAbove );
355
setKeepBelow( session->keepBelow );
356
setSkipTaskbar( session->skipTaskbar, true );
357
setSkipPager( session->skipPager );
358
setShade( session->shaded ? ShadeNormal : ShadeNone );
359
if( session->maximized != MaximizeRestore )
361
maximize( (MaximizeMode) session->maximized );
362
geom_restore = session->restore;
364
if( session->fullscreen == FullScreenHack )
365
; // nothing, this should be already set again above
366
else if( session->fullscreen != FullScreenNone )
368
setFullScreen( true, false );
369
geom_fs_restore = session->fsrestore;
374
geom_restore = geometry(); // remember restore geometry
376
&& ( width() >= area.width() || height() >= area.height() ) )
378
// window is too large for the screen, maximize in the
379
// directions necessary
380
if ( width() >= area.width() && height() >= area.height() )
382
maximize( Client::MaximizeFull );
383
geom_restore = QRect(); // use placement when unmaximizing
385
else if ( width() >= area.width() )
387
maximize( Client::MaximizeHorizontal );
388
geom_restore = QRect(); // use placement when unmaximizing
389
geom_restore.setY( y()); // but only for horizontal direction
390
geom_restore.setHeight( height());
392
else if ( height() >= area.height() )
394
maximize( Client::MaximizeVertical );
395
geom_restore = QRect(); // use placement when unmaximizing
396
geom_restore.setX( x()); // but only for vertical direction
397
geom_restore.setWidth( width());
400
// window may want to be maximized
401
// done after checking that the window isn't larger than the workarea, so that
402
// the restore geometry from the checks above takes precedence, and window
403
// isn't restored larger than the workarea
404
MaximizeMode maxmode = static_cast< MaximizeMode >
405
((( info->state() & NET::MaxVert ) ? MaximizeVertical : 0 )
406
| (( info->state() & NET::MaxHoriz ) ? MaximizeHorizontal : 0 ));
407
MaximizeMode forced_maxmode = rules()->checkMaximize( maxmode, !isMapped );
408
// either hints were set to maximize, or is forced to maximize,
409
// or is forced to non-maximize and hints were set to maximize
410
if( forced_maxmode != MaximizeRestore || maxmode != MaximizeRestore )
411
maximize( forced_maxmode );
413
// read other initial states
414
setShade( rules()->checkShade( info->state() & NET::Shaded ? ShadeNormal : ShadeNone, !isMapped ));
415
setKeepAbove( rules()->checkKeepAbove( info->state() & NET::KeepAbove, !isMapped ));
416
setKeepBelow( rules()->checkKeepBelow( info->state() & NET::KeepBelow, !isMapped ));
417
setSkipTaskbar( rules()->checkSkipTaskbar( info->state() & NET::SkipTaskbar, !isMapped ), true );
418
setSkipPager( rules()->checkSkipPager( info->state() & NET::SkipPager, !isMapped ));
419
if( info->state() & NET::DemandsAttention )
421
if( info->state() & NET::Modal )
423
if( fullscreen_mode != FullScreenHack && isFullScreenable())
424
setFullScreen( rules()->checkFullScreen( info->state() & NET::FullScreen, !isMapped ), false );
427
updateAllowedActions( true );
429
// set initial user time directly
430
user_time = readUserTimeMapTimestamp( asn_valid ? &asn_id : NULL, asn_valid ? &asn_data : NULL, session );
431
group()->updateUserTime( user_time ); // and do what Client::updateUserTime() does
433
if( isTopMenu()) // they're shown in Workspace::addClient() if their mainwindow
434
hideClient( true ); // is the active one
436
// this should avoid flicker, because real restacking is done
437
// only after manage() finishes because of blocking, but the window is shown sooner
438
XLowerWindow( display(), frameId());
439
if( session && session->stackingOrder != -1 )
441
sm_stacking_order = session->stackingOrder;
442
workspace()->restoreSessionStackingOrder( this );
445
if( compositing()) // sending ConfigureNotify is done when setting mapping state below,
446
sendSyncRequest(); // getting the first sync response means window is ready for compositing
448
if( isShown( true ) && !doNotShow )
451
Notify::raise( Notify::TransNew );
452
if( isNormalWindow())
453
Notify::raise( Notify::New );
457
allow = session->active
458
&& ( !workspace()->wasUserInteraction()
459
|| workspace()->activeClient() == NULL || workspace()->activeClient()->isDesktop());
461
allow = workspace()->allowClientActivation( this, userTime(), false );
463
// if session saving, force showing new windows (i.e. "save file?" dialogs etc.)
464
// also force if activation is allowed
465
if( !isOnCurrentDesktop() && !isMapped && !session && ( allow || workspace()->sessionSaving()))
466
workspace()->setCurrentDesktop( desktop());
468
bool belongs_to_desktop = false;
469
for( ClientList::ConstIterator it = group()->members().begin();
470
it != group()->members().end();
472
if( (*it)->isDesktop())
474
belongs_to_desktop = true;
477
if( !belongs_to_desktop && workspace()->showingDesktop())
478
workspace()->resetShowingDesktop( options->showDesktopIsMinimizeAll );
480
if( isOnCurrentDesktop() && !isMapped && !allow && (!session || session->stackingOrder < 0 ))
481
workspace()->restackClientUnderActive( this );
487
if( allow && isOnCurrentDesktop())
489
if( !isSpecialWindow())
490
if ( options->focusPolicyIsReasonable() && wantsTabFocus() )
491
workspace()->requestFocus( this );
495
if( !session && !isSpecialWindow())
500
else if( !doNotShow ) // if( !isShown( true ) && !doNotShow )
507
setMappingState( IconicState );
509
assert( mappingState() != WithdrawnState );
511
if( user_time == CurrentTime || user_time == -1U ) // no known user time, set something old
513
user_time = xTime() - 1000000;
514
if( user_time == CurrentTime || user_time == -1U ) // let's be paranoid
515
user_time = xTime() - 1000000 + 10;
518
updateWorkareaDiffs();
520
// sendSyntheticConfigureNotify(); done when setting mapping state
526
client_rules.discardTemporary();
527
applyWindowRules(); // just in case
528
workspace()->discardUsedWindowRules( this, false ); // remove ApplyNow rules
529
updateWindowRules(); // was blocked while !isManaged()
531
// TODO there's a small problem here - isManaged() depends on the mapping state,
532
// but this client is not yet in Workspace's client list at this point, will
533
// be only done in addClient()
537
// called only from manage()
538
void Client::embedClient( Window w, const XWindowAttributes &attr )
540
assert( client == None );
541
assert( frameId() == None );
542
assert( wrapper == None );
544
// we don't want the window to be destroyed when we are destroyed
545
XAddToSaveSet( display(), client );
546
XSelectInput( display(), client, NoEventMask );
547
XUnmapWindow( display(), client );
548
XWindowChanges wc; // set the border width to 0
549
wc.border_width = 0; // TODO possibly save this, and also use it for initial configuring of the window
550
XConfigureWindow( display(), client, CWBorderWidth, &wc );
552
XSetWindowAttributes swa;
553
swa.colormap = attr.colormap;
554
swa.background_pixmap = None;
555
swa.border_pixel = 0;
557
Window frame = XCreateWindow( display(), rootWindow(), 0, 0, 1, 1, 0,
558
attr.depth, InputOutput, attr.visual,
559
CWColormap | CWBackPixmap | CWBorderPixel, &swa );
560
setWindowHandles( client, frame );
561
wrapper = XCreateWindow( display(), frame, 0, 0, 1, 1, 0,
562
attr.depth, InputOutput, attr.visual,
563
CWColormap | CWBackPixmap | CWBorderPixel, &swa );
565
XDefineCursor( display(), frame, QCursor( Qt::ArrowCursor ).handle());
566
// some apps are stupid and don't define their own cursor - set the arrow one for them
567
XDefineCursor( display(), wrapper, QCursor( Qt::ArrowCursor ).handle());
568
XReparentWindow( display(), client, wrapper, 0, 0 );
569
XSelectInput( display(), frame,
570
KeyPressMask | KeyReleaseMask |
571
ButtonPressMask | ButtonReleaseMask |
575
EnterWindowMask | LeaveWindowMask |
579
StructureNotifyMask | SubstructureRedirectMask );
580
XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask );
581
XSelectInput( display(), client,
585
EnterWindowMask | LeaveWindowMask |
586
KeyPressMask | KeyReleaseMask