From e1406c3e42dc96e271727aec7aeb3587cd7a237e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Sun, 15 Apr 2007 14:39:10 +0000 Subject: [PATCH] Utility function for XGrabKeyboard(). svn path=/branches/work/kwin_composite/; revision=654227 --- client.cpp | 605 ++++++++++--------------------------------------- client.h | 1 + events.cpp | 2 +- geometry.cpp | 8 +- killwindow.cpp | 11 +- tabbox.cpp | 7 +- utils.cpp | 26 +++ utils.h | 2 + workspace.cpp | 10 +- 9 files changed, 170 insertions(+), 502 deletions(-) diff --git a/client.cpp b/client.cpp index 215db69342..dc7f1e3719 100644 --- a/client.cpp +++ b/client.cpp @@ -28,6 +28,9 @@ License. See the file "COPYING" for the exact licensing terms. #include "atoms.h" #include "notifications.h" #include "rules.h" +#include "scene.h" +#include "effects.h" +#include "deleted.h" #include #include @@ -63,15 +66,14 @@ namespace KWin is done in manage(). */ Client::Client( Workspace *ws ) - : QObject( NULL ), + : Toplevel( ws ), client( None ), wrapper( None ), - frame( None ), decoration( NULL ), - wspace( ws ), bridge( new Bridge( this )), move_faked_activity( false ), move_resize_grab_window( None ), + move_resize_has_keyboard_grab( false ), transient_for( NULL ), transient_for_id( None ), original_transient_for_id( None ), @@ -82,7 +84,7 @@ Client::Client( Workspace *ws ) process_killer( NULL ), user_time( CurrentTime ), // not known yet allowed_actions( 0 ), - postpone_geometry_updates( 0 ), + block_geometry_updates( 0 ), pending_geometry_update( false ), shade_geometry_change( false ), border_left( 0 ), @@ -111,7 +113,6 @@ Client::Client( Workspace *ws ) deleting = false; keep_above = false; keep_below = false; - is_shape = false; motif_noborder = false; motif_may_move = true; motif_may_resize = true; @@ -124,6 +125,7 @@ Client::Client( Workspace *ws ) modal = false; noborder = false; user_noborder = false; + not_obscured = false; urgency = false; ignore_focus_stealing = false; demands_attention = false; @@ -142,7 +144,7 @@ Client::Client( Workspace *ws ) cmap = None; - frame_geometry = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) + geom = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) client_size = QSize( 100, 100 ); // SELI initialize xsizehints?? @@ -155,11 +157,11 @@ Client::~Client() { assert(!moveResizeMode); assert( client == None ); - assert( frame == None && wrapper == None ); + assert( wrapper == None ); +// assert( frameId() == None ); assert( decoration == NULL ); - assert( postpone_geometry_updates == 0 ); + assert( block_geometry_updates == 0 ); assert( !check_active_modal ); - delete info; delete bridge; } @@ -176,15 +178,21 @@ void Client::releaseWindow( bool on_shutdown ) { assert( !deleting ); deleting = true; + Deleted* del = Deleted::create( this ); + if( effects ) + { + static_cast(effects)->windowClosed( effectWindow()); + scene->windowClosed( this, del ); + } + finishCompositing(); workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules StackingUpdatesBlocker blocker( workspace()); if (moveResizeMode) leaveMoveResize(); finishWindowRules(); - ++postpone_geometry_updates; - // grab X during the release to make removing of properties, setting to withdrawn state - // and repareting to root an atomic operation (http://lists.kde.org/?l=kde-devel&m=116448102901184&w=2) - grabXServer(); + ++block_geometry_updates; + if( isOnCurrentDesktop() && isShown( true )) + addWorkspaceRepaint( geometry()); setMappingState( WithdrawnState ); setModal( false ); // otherwise its mainwindow wouldn't get focus hidden = true; // so that it's not considered visible anymore (can't use hideClient(), it would set flags) @@ -222,12 +230,12 @@ void Client::releaseWindow( bool on_shutdown ) client = None; XDestroyWindow( display(), wrapper ); wrapper = None; - XDestroyWindow( display(), frame ); - frame = None; - --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry - checkNonExistentClients(); + XDestroyWindow( display(), frameId()); +// frame = None; + --block_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry + disownDataPassedToDeleted(); + del->unrefWindow(); deleteClient( this, Allowed ); - ungrabXServer(); } // like releaseWindow(), but this one is called when the window has been already destroyed @@ -236,12 +244,21 @@ void Client::destroyClient() { assert( !deleting ); deleting = true; + Deleted* del = Deleted::create( this ); + if( effects ) + { + static_cast(effects)->windowClosed( effectWindow()); + scene->windowClosed( this, del ); + } + finishCompositing(); workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules StackingUpdatesBlocker blocker( workspace()); if (moveResizeMode) leaveMoveResize(); finishWindowRules(); - ++postpone_geometry_updates; + ++block_geometry_updates; + if( isOnCurrentDesktop() && isShown( true )) + addWorkspaceRepaint( geometry()); setModal( false ); hidden = true; // so that it's not considered visible anymore workspace()->clientHidden( this ); @@ -251,10 +268,11 @@ void Client::destroyClient() client = None; // invalidate XDestroyWindow( display(), wrapper ); wrapper = None; - XDestroyWindow( display(), frame ); - frame = None; - --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry - checkNonExistentClients(); + XDestroyWindow( display(), frameId()); +// frame = None; + --block_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry + disownDataPassedToDeleted(); + del->unrefWindow(); deleteClient( this, Allowed ); } @@ -264,12 +282,12 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) || ( decoration != NULL && !noBorder()))) return; bool do_show = false; - postponeGeometryUpdates( true ); + QRect oldgeom = geometry(); + blockGeometryUpdates( true ); if( force ) destroyDecoration(); if( !noBorder()) { - setMask( QRegion()); // reset shape mask decoration = workspace()->createDecoration( bridge ); // TODO check decoration's minimum size? decoration->init(); @@ -284,12 +302,18 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) workarea_diff_x = save_workarea_diff_x; workarea_diff_y = save_workarea_diff_y; do_show = true; + if( compositing() ) + discardWindowPixmap(); + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); + if( effects != NULL ) + static_cast(effects)->windowGeometryShapeChanged( effectWindow(), oldgeom ); } else destroyDecoration(); if( check_workspace_pos ) checkWorkspacePosition(); - postponeGeometryUpdates( false ); + blockGeometryUpdates( false ); if( do_show ) decoration->widget()->show(); updateFrameExtents(); @@ -297,6 +321,7 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) void Client::destroyDecoration() { + QRect oldgeom = geometry(); if( decoration != NULL ) { delete decoration; @@ -310,6 +335,12 @@ void Client::destroyDecoration() move( grav ); workarea_diff_x = save_workarea_diff_x; workarea_diff_y = save_workarea_diff_y; + if( compositing() ) + discardWindowPixmap(); + if( scene != NULL && !deleting ) + scene->windowGeometryShapeChanged( this ); + if( effects != NULL && !deleting ) + static_cast(effects)->windowGeometryShapeChanged( effectWindow(), oldgeom ); } } @@ -322,7 +353,7 @@ void Client::checkBorderSizes() if( new_left == border_left && new_right == border_right && new_top == border_top && new_bottom == border_bottom ) return; - GeometryUpdatesPostponer blocker( this ); + GeometryUpdatesBlocker blocker( this ); move( calculateGravitation( true )); border_left = new_left; border_right = new_right; @@ -339,7 +370,7 @@ void Client::checkBorderSizes() void Client::detectNoBorder() { - if( Shape::hasShape( window())) + if( shape()) { noborder = true; return; @@ -427,47 +458,25 @@ void Client::setUserNoBorder( bool set ) void Client::updateShape() { + if ( shape() ) + XShapeCombineShape(display(), frameId(), ShapeBounding, + clientPos().x(), clientPos().y(), + window(), ShapeBounding, ShapeSet); + else + XShapeCombineMask( display(), frameId(), ShapeBounding, 0, 0, + None, ShapeSet); + if( compositing()) + addDamageFull(); + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); + if( effects != NULL ) + static_cast(effects)->windowGeometryShapeChanged( effectWindow(), geometry()); // workaround for #19644 - shaped windows shouldn't have decoration if( shape() && !noBorder()) { noborder = true; updateDecoration( true ); } - if( shape()) - { - XShapeCombineShape(display(), frameId(), ShapeBounding, - clientPos().x(), clientPos().y(), - window(), ShapeBounding, ShapeSet); - } - // !shape() mask setting is done in setMask() when the decoration - // calls it or when the decoration is created/destroyed - - if( Shape::version() >= 0x11 ) // 1.1, has input shape support - { // There appears to be no way to find out if a window has input - // shape set or not, so always propagate the input shape - // (it's the same like the bounding shape by default). - // Also, build the shape using a helper window, not directly - // in the frame window, because the sequence set-shape-to-frame, - // remove-shape-of-client, add-input-shape-of-client has the problem - // that after the second step there's a hole in the input shape - // until the real shape of the client is added and that can make - // the window lose focus (which is a problem with mouse focus policies) - static Window helper_window = None; - if( helper_window == None ) - helper_window = XCreateSimpleWindow( display(), rootWindow(), - 0, 0, 1, 1, 0, 0, 0 ); - XResizeWindow( display(), helper_window, width(), height()); - XShapeCombineShape( display(), helper_window, ShapeInput, 0, 0, - frameId(), ShapeBounding, ShapeSet ); - XShapeCombineShape( display(), helper_window, ShapeInput, - clientPos().x(), clientPos().y(), - window(), ShapeBounding, ShapeSubtract ); - XShapeCombineShape( display(), helper_window, ShapeInput, - clientPos().x(), clientPos().y(), - window(), ShapeInput, ShapeUnion ); - XShapeCombineShape( display(), frameId(), ShapeInput, 0, 0, - helper_window, ShapeInput, ShapeSet ); - } } void Client::setMask( const QRegion& reg, int mode ) @@ -496,7 +505,12 @@ void Client::setMask( const QRegion& reg, int mode ) xrects, rects.count(), ShapeSet, mode ); delete[] xrects; } - updateShape(); + if( compositing()) + addDamageFull(); + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); + if( effects != NULL ) + static_cast(effects)->windowGeometryShapeChanged( effectWindow(), geometry()); } QRegion Client::mask() const @@ -555,10 +569,6 @@ void Client::minimize( bool avoid_animation ) Notify::raise( Notify::Minimize ); - // SELI mainClients().isEmpty() ??? - and in unminimize() too - if ( mainClients().isEmpty() && isOnCurrentDesktop() && isShown( true ) && !avoid_animation ) - animateMinimizeOrUnminimize( true ); // was visible or shaded - minimized = true; updateVisibility(); @@ -566,6 +576,8 @@ void Client::minimize( bool avoid_animation ) workspace()->updateMinimizedOfTransients( this ); updateWindowRules(); workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast ); + if( effects && !avoid_animation ) // TODO shouldn't it tell effects at least about the change? + static_cast(effects)->windowMinimized( effectWindow()); } void Client::unminimize( bool avoid_animation ) @@ -575,142 +587,34 @@ void Client::unminimize( bool avoid_animation ) Notify::raise( Notify::UnMinimize ); minimized = false; - if( isOnCurrentDesktop() && isShown( true )) - { - if( mainClients().isEmpty() && !avoid_animation ) - animateMinimizeOrUnminimize( false ); - } updateVisibility(); updateAllowedActions(); workspace()->updateMinimizedOfTransients( this ); updateWindowRules(); + if( effects && !avoid_animation ) + static_cast(effects)->windowUnminimized( effectWindow()); } -extern bool blockAnimation; - -void Client::animateMinimizeOrUnminimize( bool minimize ) +QRect Client::iconGeometry() const { -#ifdef __GNUC__ - #warning implement kwin animation -#endif - if ( 1 || blockAnimation ) - return; - if ( !options->animateMinimize ) - return; - - if( decoration != NULL && decoration->animateMinimize( minimize )) - return; // decoration did it - - // the function is a bit tricky since it will ensure that an - // animation action needs always the same time regardless of the - // performance of the machine or the X-Server. - - float lf,rf,tf,bf,step; - - int speed = options->animateMinimizeSpeed; - if ( speed > 10 ) - speed = 10; - if ( speed < 0 ) - speed = 0; - - step = 40. * (11 - speed ); - NETRect r = info->iconGeometry(); - QRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height ); - if ( !icongeom.isValid() ) - return; - - QPixmap pm = animationPixmap( minimize ? width() : icongeom.width() ); - - QRect before, after; - if ( minimize ) - { - before = QRect( x(), y(), width(), pm.height() ); - after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); - } - else - { - before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); - after = QRect( x(), y(), width(), pm.height() ); - } - - lf = (after.left() - before.left())/step; - rf = (after.right() - before.right())/step; - tf = (after.top() - before.top())/step; - bf = (after.bottom() - before.bottom())/step; - - grabXServer(); - - QRect area = before; - QRect area2; - QPixmap pm2; - - QTime t; - t.start(); - float diff; - - QPainter p ( workspace()->desktopWidget() ); - bool need_to_clear = false; - QPixmap pm3; - do - { - if (area2 != area) - { - pm = animationPixmap( area.width() ); - pm2 = QPixmap::grabWindow( rootWindow(), area.x(), area.y(), area.width(), area.height() ); - p.drawPixmap( area.x(), area.y(), pm ); - if ( need_to_clear ) - { - p.drawPixmap( area2.x(), area2.y(), pm3 ); - need_to_clear = false; - } - area2 = area; - } - XFlush(display()); - XSync( display(), false ); - diff = t.elapsed(); - if (diff > step) - diff = step; - area.setLeft(before.left() + int(diff*lf)); - area.setRight(before.right() + int(diff*rf)); - area.setTop(before.top() + int(diff*tf)); - area.setBottom(before.bottom() + int(diff*bf)); - if (area2 != area ) - { - if ( area2.intersects( area ) ) - p.drawPixmap( area2.x(), area2.y(), pm2 ); - else - { // no overlap, we can clear later to avoid flicker - pm3 = pm2; - need_to_clear = true; - } - } - } while ( t.elapsed() < step); - if (area2 == area || need_to_clear ) - p.drawPixmap( area2.x(), area2.y(), pm2 ); - - p.end(); - ungrabXServer(); - } - - -/*! - The pixmap shown during (un)minimalization animation - */ -QPixmap Client::animationPixmap( int w ) + QRect geom( r.pos.x, r.pos.y, r.size.width, r.size.height ); + if( geom.isValid() ) + return geom; + else { - QFont font = options->font(isActive()); - QFontMetrics fm( font ); - QPixmap pm( w, fm.lineSpacing() ); - pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) ); - QPainter p( &pm ); - p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() )); - p.setFont(options->font(isActive())); - p.drawText( pm.rect(), Qt::AlignLeft|Qt::AlignVCenter|Qt::TextSingleLine, caption() ); - return pm; + // Check all mainwindows of this window (recursively) + foreach( Client* mainwin, mainClients() ) + { + geom = mainwin->iconGeometry(); + if( geom.isValid() ) + return geom; + } + // No mainwindow (or their parents) with icon geometry was found + return QRect(); + } } - bool Client::isShadeable() const { return !isSpecialWindow() && !noBorder(); @@ -745,19 +649,15 @@ void Client::setShade( ShadeMode mode ) } assert( decoration != NULL ); // noborder windows can't be shaded - GeometryUpdatesPostponer blocker( this ); + GeometryUpdatesBlocker blocker( this ); // decorations may turn off some borders when shaded decoration->borders( border_left, border_right, border_top, border_bottom ); - int as = options->animateShade? 10 : 1; // TODO all this unmapping, resizing etc. feels too much duplicated from elsewhere if ( isShade()) { // shade_mode == ShadeNormal - // we're about to shade, texx xcompmgr to prepare - long _shade = 1; - XChangeProperty(display(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L); + addWorkspaceRepaint( geometry()); // shade - int h = height(); shade_geometry_change = true; QSize s( sizeForClientSize( QSize( clientSize()))); s.setHeight( border_top + border_bottom ); @@ -765,21 +665,6 @@ void Client::setShade( ShadeMode mode ) XUnmapWindow( display(), wrapper ); XUnmapWindow( display(), client ); XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); - //as we hid the unmap event, xcompmgr didn't recognize the client wid has vanished, so we'll extra inform it - //done xcompmgr workaround -// FRAME repaint( false ); -// bool wasStaticContents = testWFlags( WStaticContents ); -// setWFlags( WStaticContents ); - int step = qMax( 4, QABS( h - s.height() ) / as )+1; - do - { - h -= step; - XResizeWindow( display(), frameId(), s.width(), h ); - resizeDecoration( QSize( s.width(), h )); - QApplication::syncX(); - } while ( h > s.height() + step ); -// if ( !wasStaticContents ) -// clearWFlags( WStaticContents ); plainResize( s ); shade_geometry_change = false; if( isActive()) @@ -789,43 +674,24 @@ void Client::setShade( ShadeMode mode ) else workspace()->focusToNull(); } - // tell xcompmgr shade's done - _shade = 2; - XChangeProperty(display(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L); } else { - int h = height(); shade_geometry_change = true; QSize s( sizeForClientSize( clientSize())); -// FRAME bool wasStaticContents = testWFlags( WStaticContents ); -// setWFlags( WStaticContents ); - int step = qMax( 4, QABS( h - s.height() ) / as )+1; - do - { - h += step; - XResizeWindow( display(), frameId(), s.width(), h ); - resizeDecoration( QSize( s.width(), h )); - // assume a border - // we do not have time to wait for X to send us paint events -// FRAME repaint( 0, h - step-5, width(), step+5, true); - QApplication::syncX(); - } while ( h < s.height() - step ); -// if ( !wasStaticContents ) -// clearWFlags( WStaticContents ); shade_geometry_change = false; plainResize( s ); if( shade_mode == ShadeHover || shade_mode == ShadeActivated ) setActive( true ); XMapWindow( display(), wrapperId()); XMapWindow( display(), window()); - XDeleteProperty (display(), client, atoms->net_wm_window_shade); if ( isActive() ) workspace()->requestFocus( this ); } checkMaximizeGeometry(); info->setState( isShade() ? NET::Shaded : 0, NET::Shaded ); info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden ); + discardWindowPixmap(); updateVisibility(); updateAllowedActions(); workspace()->updateMinimizedOfTransients( this ); @@ -929,8 +795,8 @@ void Client::setMappingState(int s) XChangeProperty(display(), window(), atoms->wm_state, atoms->wm_state, 32, PropModeReplace, (unsigned char *)data, 2); - if( was_unmanaged ) // manage() did postpone_geometry_updates = 1, now it's ok to finally set the geometry - postponeGeometryUpdates( false ); + if( was_unmanaged ) // manage() did block_geometry_updates = 1, now it's ok to finally set the geometry + blockGeometryUpdates( false ); } /*! @@ -941,12 +807,17 @@ void Client::rawShow() { if( decoration != NULL ) decoration->widget()->show(); // not really necessary, but let it know the state - XMapWindow( display(), frame ); + XMapWindow( display(), frameId()); if( !isShade()) { XMapWindow( display(), wrapper ); XMapWindow( display(), client ); } + // XComposite invalidates backing pixmaps on unmap (minimize, different + // virtual desktop, etc.). We kept the last known good pixmap around + // for use in effects, but now we want to have access to the new pixmap + if( compositing() ) + discardWindowPixmap(); } /*! @@ -956,6 +827,7 @@ void Client::rawShow() */ void Client::rawHide() { + addWorkspaceRepaint( geometry()); // Here it may look like a race condition, as some other client might try to unmap // the window between these two XSelectInput() calls. However, they're supposed to // use XWithdrawWindow(), which also sends a synthetic event to the root window, @@ -963,7 +835,7 @@ void Client::rawHide() // will be missed is also very minimal, so I don't think it's needed to grab the server // here. XSelectInput( display(), wrapper, ClientWinMask ); // avoid getting UnmapNotify - XUnmapWindow( display(), frame ); + XUnmapWindow( display(), frameId()); XUnmapWindow( display(), wrapper ); XUnmapWindow( display(), client ); XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); @@ -1067,8 +939,7 @@ void Client::pingWindow() void Client::gotPing( Time timestamp ) { - // just plain compare is not good enough because of 64bit and truncating and whatnot - if( NET::timestampCompare( timestamp, ping_timestamp ) != 0 ) + if( timestamp != ping_timestamp ) return; delete ping_timer; ping_timer = NULL; @@ -1198,6 +1069,17 @@ void Client::setDesktop( int desktop ) updateWindowRules(); } +/*! + Returns the virtual desktop within the workspace() the client window + is located in, 0 if it isn't located on any special desktop (not mapped yet), + or NET::OnAllDesktops. Do not use desktop() directly, use + isOnDesktop() instead. + */ +int Client::desktop() const + { + return desk; + } + void Client::setOnAllDesktops( bool b ) { if(( b && isOnAllDesktops()) @@ -1209,11 +1091,6 @@ void Client::setOnAllDesktops( bool b ) setDesktop( workspace()->currentDesktop()); } -bool Client::isOnCurrentDesktop() const - { - return isOnDesktop( workspace()->currentDesktop()); - } - // performs activation and/or raising of the window void Client::takeActivity( int flags, bool handled, allowed_t ) { @@ -1309,7 +1186,7 @@ QString Client::readName() const return KWM::readNameProperty( window(), XA_WM_NAME ); } -KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); +KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, Client, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); void Client::setCaption( const QString& _s, bool force ) { @@ -1496,125 +1373,6 @@ void Client::getWindowProtocols() } } -static int nullErrorHandler(Display *, XErrorEvent *) - { - return 0; - } - -/*! - Returns WM_WINDOW_ROLE property for a given window. - */ -QByteArray Client::staticWindowRole(WId w) - { - return getStringProperty(w, atoms->wm_window_role).toLower(); - } - -/*! - Returns SM_CLIENT_ID property for a given window. - */ -QByteArray Client::staticSessionId(WId w) - { - return getStringProperty(w, atoms->sm_client_id); - } - -/*! - Returns WM_COMMAND property for a given window. - */ -QByteArray Client::staticWmCommand(WId w) - { - return getStringProperty(w, XA_WM_COMMAND, ' '); - } - -/*! - Returns WM_CLIENT_LEADER property for a given window. - */ -Window Client::staticWmClientLeader(WId w) - { - Atom type; - int format, status; - unsigned long nitems = 0; - unsigned long extra = 0; - unsigned char *data = 0; - Window result = w; - XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler); - status = XGetWindowProperty( display(), w, atoms->wm_client_leader, 0, 10000, - false, XA_WINDOW, &type, &format, - &nitems, &extra, &data ); - XSetErrorHandler(oldHandler); - if (status == Success ) - { - if (data && nitems > 0) - result = *((Window*) data); - XFree(data); - } - return result; - } - - -void Client::getWmClientLeader() - { - wmClientLeaderWin = staticWmClientLeader(window()); - } - -/*! - Returns sessionId for this client, - taken either from its window or from the leader window. - */ -QByteArray Client::sessionId() - { - QByteArray result = staticSessionId(window()); - if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) - result = staticSessionId(wmClientLeaderWin); - return result; - } - -/*! - Returns command property for this client, - taken either from its window or from the leader window. - */ -QByteArray Client::wmCommand() - { - QByteArray result = staticWmCommand(window()); - if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) - result = staticWmCommand(wmClientLeaderWin); - return result; - } - -void Client::getWmClientMachine() - { - client_machine = getStringProperty(window(), XA_WM_CLIENT_MACHINE); - if( client_machine.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) - client_machine = getStringProperty(wmClientLeaderWin, XA_WM_CLIENT_MACHINE); - if( client_machine.isEmpty()) - client_machine = "localhost"; - } - -/*! - Returns client machine for this client, - taken either from its window or from the leader window. -*/ -QByteArray Client::wmClientMachine( bool use_localhost ) const - { - QByteArray result = client_machine; - if( use_localhost ) - { // special name for the local machine (localhost) - if( result != "localhost" && isLocalMachine( result )) - result = "localhost"; - } - return result; - } - -/*! - Returns client leader window for this client. - Returns the client window itself if no leader window is defined. -*/ -Window Client::wmClientLeader() const - { - if (wmClientLeaderWin) - return wmClientLeaderWin; - return window(); - } - bool Client::wantsTabFocus() const { return ( isNormalWindow() || isDialog()) && wantsInput(); @@ -1626,89 +1384,12 @@ bool Client::wantsInput() const return rules()->checkAcceptFocus( input || Ptakefocus ); } -bool Client::isDesktop() const - { - return windowType() == NET::Desktop; - } - -bool Client::isDock() const - { - return windowType() == NET::Dock; - } - -bool Client::isTopMenu() const - { - return windowType() == NET::TopMenu; - } - - -bool Client::isMenu() const - { - return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp. - } - -bool Client::isToolbar() const - { - return windowType() == NET::Toolbar; - } - -bool Client::isSplash() const - { - return windowType() == NET::Splash; - } - -bool Client::isUtility() const - { - return windowType() == NET::Utility; - } - -bool Client::isDialog() const - { - return windowType() == NET::Dialog; - } - -bool Client::isNormalWindow() const - { - return windowType() == NET::Normal; - } - bool Client::isSpecialWindow() const { return isDesktop() || isDock() || isSplash() || isTopMenu() || isToolbar(); // TODO } -NET::WindowType Client::windowType( bool direct, int supported_types ) const - { - NET::WindowType wt = info->windowType( supported_types ); - if( direct ) - return wt; - NET::WindowType wt2 = rules()->checkType( wt ); - if( wt != wt2 ) - { - wt = wt2; - info->setWindowType( wt ); // force hint change - } - // hacks here - if( wt == NET::Menu ) - { - // ugly hack to support the times when NET::Menu meant NET::TopMenu - // if it's as wide as the screen, not very high and has its upper-left - // corner a bit above the screen's upper-left cornet, it's a topmenu - if( x() == 0 && y() < 0 && y() > -10 && height() < 100 - && abs( width() - workspace()->clientArea( FullArea, this ).width()) < 10 ) - wt = NET::TopMenu; - } - // TODO change this to rule - const char* const oo_prefix = "openoffice.org"; // QByteArray has no startsWith() - // oo_prefix is lowercase, because resourceClass() is forced to be lowercase - if( qstrncmp( resourceClass(), oo_prefix, strlen( oo_prefix )) == 0 && wt == NET::Dialog ) - wt = NET::Normal; // see bug #66065 - if( wt == NET::Unknown ) // this is more or less suggested in NETWM spec - wt = isTransient() ? NET::Dialog : NET::Normal; - return wt; - } - /*! Sets an appropriate cursor shape for the logical mouse position \a m @@ -1805,46 +1486,10 @@ void Client::cancelAutoRaise() autoRaiseTimer = 0; } -#ifndef NDEBUG -kdbgstream& operator<<( kdbgstream& stream, const Client* cl ) +void Client::debug( kdbgstream& stream ) const { - if( cl == NULL ) - return stream << "\'NULL_CLIENT\'"; - return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'"; + stream << "\'ID:" << window() << ";WMCLASS:" << resourceClass() << ":" << resourceName() << ";Caption:" << caption() << "\'"; } -kdbgstream& operator<<( kdbgstream& stream, const ClientList& list ) - { - stream << "LIST:("; - bool first = true; - for( ClientList::ConstIterator it = list.begin(); - it != list.end(); - ++it ) - { - if( !first ) - stream << ":"; - first = false; - stream << *it; - } - stream << ")"; - return stream; - } -kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list ) - { - stream << "LIST:("; - bool first = true; - for( ConstClientList::ConstIterator it = list.begin(); - it != list.end(); - ++it ) - { - if( !first ) - stream << ":"; - first = false; - stream << *it; - } - stream << ")"; - return stream; - } -#endif QPixmap * kwin_get_menu_pix_hack() { diff --git a/client.h b/client.h index 5db2742aee..9d4b07bf89 100644 --- a/client.h +++ b/client.h @@ -377,6 +377,7 @@ class Client bool moveResizeMode; bool move_faked_activity; Window move_resize_grab_window; + bool move_resize_has_keyboard_grab; bool unrestrictedMoveResize; Position mode; diff --git a/events.cpp b/events.cpp index b90c27d79f..ca56b4c60a 100644 --- a/events.cpp +++ b/events.cpp @@ -203,7 +203,7 @@ bool Workspace::workspaceEvent( XEvent * e ) if ( mouse_emulation && (e->type == ButtonPress || e->type == ButtonRelease ) ) { mouse_emulation = false; - XUngrabKeyboard( display(), xTime() ); + ungrabXKeyboard(); } if ( e->type == PropertyNotify || e->type == ClientMessage ) diff --git a/geometry.cpp b/geometry.cpp index 10cb131289..e3c6e18e0c 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -2266,8 +2266,8 @@ bool Client::startMoveResize() ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask, GrabModeAsync, GrabModeAsync, None, cursor.handle(), xTime() ) == Success ) has_grab = true; - if( XGrabKeyboard( display(), frameId(), False, GrabModeAsync, GrabModeAsync, xTime() ) == Success ) - has_grab = true; + if( grabXKeyboard( frameId())) + has_grab = move_resize_has_keyboard_grab = true; if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize { XDestroyWindow( display(), move_resize_grab_window ); @@ -2327,7 +2327,9 @@ void Client::leaveMoveResize() if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque ) || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) ) ungrabXServer(); - XUngrabKeyboard( display(), xTime() ); + if( move_resize_has_keyboard_grab ) + ungrabXKeyboard(); + move_resize_has_keyboard_grab = false; XUngrabPointer( display(), xTime() ); XDestroyWindow( display(), move_resize_grab_window ); move_resize_grab_window = None; diff --git a/killwindow.cpp b/killwindow.cpp index d2d7eaa73e..ff1612827a 100644 --- a/killwindow.cpp +++ b/killwindow.cpp @@ -45,8 +45,7 @@ void KillWindow::start() GrabModeAsync, GrabModeAsync, None, kill_cursor, CurrentTime) == GrabSuccess) { - XGrabKeyboard(display(), rootWindow(), False, - GrabModeAsync, GrabModeAsync, CurrentTime); + grabXKeyboard(); XEvent ev; int return_pressed = 0; @@ -76,7 +75,7 @@ void KillWindow::start() mx /= 10; my /= 10; } - QCursor::setPos(QCursor::pos()+QPoint(mx, my)); + QCursor::setPos(cursorPos()+QPoint(mx, my)); } if (ev.type == ButtonRelease) @@ -87,8 +86,7 @@ void KillWindow::start() escape_pressed = true; break; } - if( ev.xbutton.button == Button1 || ev.xbutton.button == Button2 ) - workspace->killWindowId(ev.xbutton.subwindow); + workspace->killWindowId(ev.xbutton.subwindow); } continue; } @@ -104,8 +102,7 @@ void KillWindow::start() } ungrabXServer(); - - XUngrabKeyboard(display(), CurrentTime); + ungrabXKeyboard(); XUngrabPointer(display(), CurrentTime); } } diff --git a/tabbox.cpp b/tabbox.cpp index 1d7d3682b6..d2c98a38e7 100644 --- a/tabbox.cpp +++ b/tabbox.cpp @@ -870,7 +870,7 @@ void Workspace::slotWalkThroughWindows() return; if ( options->altTabStyle == Options::CDE || !options->focusPolicyIsReasonable()) { - //XUngrabKeyboard(display(), xTime()); // need that because of accelerator raw mode + //ungrabXKeyboard(); // need that because of accelerator raw mode // CDE style raise / lower CDEWalkThroughWindows( true ); } @@ -1398,8 +1398,7 @@ void Workspace::setTabBoxDesktop( int iDesktop ) bool Workspace::establishTabBoxGrab() { - if( XGrabKeyboard( display(), root, false, - GrabModeAsync, GrabModeAsync, xTime()) != GrabSuccess ) + if( !grabXKeyboard()) return false; // Don't try to establish a global mouse grab using XGrabPointer, as that would prevent // using Alt+Tab while DND (#44972). However force passive grabs on all windows @@ -1415,7 +1414,7 @@ bool Workspace::establishTabBoxGrab() void Workspace::removeTabBoxGrab() { - XUngrabKeyboard(display(), xTime()); + ungrabXKeyboard(); assert( forced_global_mouse_grab ); forced_global_mouse_grab = false; if( active_client != NULL ) diff --git a/utils.cpp b/utils.cpp index caec7d60ee..4938582ad3 100644 --- a/utils.cpp +++ b/utils.cpp @@ -354,6 +354,32 @@ bool grabbedXServer() return server_grab_count > 0; } +static bool keyboard_grabbed = false; + +bool grabXKeyboard( Window w ) + { + if( QWidget::keyboardGrabber() != NULL ) + return false; + if( keyboard_grabbed ) + return false; + if( qApp->activePopupWidget() != NULL ) + return false; + if( w == None ) + w = rootWindow(); + if( XGrabKeyboard( display(), w, False, + GrabModeAsync, GrabModeAsync, xTime()) != GrabSuccess ) + return false; + keyboard_grabbed = true; + return true; + } + +void ungrabXKeyboard() + { + assert( keyboard_grabbed ); + keyboard_grabbed = false; + XUngrabKeyboard( display(), xTime()); + } + QPoint cursorPos() { return Workspace::self()->cursorPos(); diff --git a/utils.h b/utils.h index b9ebde5577..ea270869bf 100644 --- a/utils.h +++ b/utils.h @@ -238,6 +238,8 @@ void updateXTime(); void grabXServer(); void ungrabXServer(); bool grabbedXServer(); +bool grabXKeyboard( Window w = rootWindow()); +void ungrabXKeyboard(); class Scene; extern Scene* scene; diff --git a/workspace.cpp b/workspace.cpp index f6e2425b34..43f081e150 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -1778,18 +1778,14 @@ void Workspace::slotGrabDesktop() */ void Workspace::slotMouseEmulation() { - if ( mouse_emulation ) { - XUngrabKeyboard(display(), xTime()); + ungrabXKeyboard(); mouse_emulation = false; return; } - if ( XGrabKeyboard(display(), - root, false, - GrabModeAsync, GrabModeAsync, - xTime()) == GrabSuccess ) + if( grabXKeyboard()) { mouse_emulation = true; mouse_emulation_state = 0; @@ -1987,7 +1983,7 @@ bool Workspace::keyPressMouseEmulation( XKeyEvent& ev ) } // fall through case XK_Escape: - XUngrabKeyboard(display(), xTime()); + ungrabXKeyboard(); mouse_emulation = false; return true; default: