diff options
Diffstat (limited to 'kwin/client.cpp')
-rw-r--r-- | kwin/client.cpp | 2845 |
1 files changed, 0 insertions, 2845 deletions
diff --git a/kwin/client.cpp b/kwin/client.cpp deleted file mode 100644 index fbd2d75aa..000000000 --- a/kwin/client.cpp +++ /dev/null @@ -1,2845 +0,0 @@ -/***************************************************************** - KWin - the KDE window manager - This file is part of the KDE project. - -Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> -Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> - -You can Freely distribute this program under the GNU General Public -License. See the file "COPYING" for the exact licensing terms. -******************************************************************/ - -#include "client.h" - -#include <math.h> - -#include <tqapplication.h> -#include <tqpainter.h> -#include <tqdatetime.h> -#include <tqimage.h> -#include <kprocess.h> -#include <unistd.h> -#include <kstandarddirs.h> -#include <tqwhatsthis.h> -#include <twin.h> -#include <kiconloader.h> -#include <stdlib.h> - -#include "bridge.h" -#include "group.h" -#include "workspace.h" -#include "atoms.h" -#include "notifications.h" -#include "rules.h" - -#include <X11/extensions/shape.h> - -// put all externs before the namespace statement to allow the linker -// to resolve them properly - -extern Atom qt_wm_state; -extern Atom qt_window_role; -extern Atom qt_sm_client_id; - -// wait 200 ms before drawing shadow after move/resize -static const int SHADOW_DELAY = 200; - -namespace KWinInternal -{ - -/* TODO: Remove this once X has real translucency. - * - * A list of the regions covered by all shadows and the Clients to which they - * belong. Used to redraw shadows when a window overlapping or underlying a - * shadow is moved, resized, or hidden. - */ -struct ShadowRegion - { - TQRegion region; - Client *client; - }; -static TQValueList<ShadowRegion> shadowRegions; - -/* - - Creating a client: - - only by calling Workspace::createClient() - - it creates a new client and calls manage() for it - - Destroying a client: - - destroyClient() - only when the window itself has been destroyed - - releaseWindow() - the window is kept, only the client itself is destroyed - -*/ - - -/*! - \class Client client.h - - \brief The Client class encapsulates a window decoration frame. - -*/ - -/*! - This ctor is "dumb" - it only initializes data. All the real initialization - is done in manage(). - */ -Client::Client( Workspace *ws ) - : TQObject( NULL ), - client( None ), - wrapper( None ), - frame( None ), - decoration( NULL ), - wspace( ws ), - bridge( new Bridge( this )), - move_faked_activity( false ), - move_resize_grab_window( None ), - transient_for( NULL ), - transient_for_id( None ), - original_transient_for_id( None ), - in_group( NULL ), - window_group( None ), - in_layer( UnknownLayer ), - ping_timer( NULL ), - process_killer( NULL ), - user_time( CurrentTime ), // not known yet - allowed_actions( 0 ), - postpone_geometry_updates( 0 ), - pending_geometry_update( false ), - shade_geometry_change( false ), - border_left( 0 ), - border_right( 0 ), - border_top( 0 ), - border_bottom( 0 ), - opacity_( 0 ), - demandAttentionKNotifyTimer( NULL ) -// SELI do all as initialization - { - autoRaiseTimer = 0; - shadeHoverTimer = 0; - - shadowDelayTimer = new TQTimer(this); - opacityCache = &activeOpacityCache; - shadowAfterClient = NULL; - shadowWidget = NULL; - shadowMe = true; - connect(shadowDelayTimer, TQT_SIGNAL(timeout()), TQT_SLOT(drawShadow())); - - // set the initial mapping state - mapping_state = WithdrawnState; - desk = 0; // no desktop yet - - mode = PositionCenter; - buttonDown = FALSE; - moveResizeMode = FALSE; - - info = NULL; - - shade_mode = ShadeNone; - active = FALSE; - deleting = false; - keep_above = FALSE; - keep_below = FALSE; - is_tqshape = FALSE; - motif_noborder = false; - motif_may_move = TRUE; - motif_may_resize = TRUE; - motif_may_close = TRUE; - fullscreen_mode = FullScreenNone; - skip_taskbar = FALSE; - original_skip_taskbar = false; - minimized = false; - hidden = false; - modal = false; - noborder = false; - user_noborder = false; - urgency = false; - ignore_focus_stealing = false; - demands_attention = false; - check_active_modal = false; - - Pdeletewindow = 0; - Ptakefocus = 0; - Ptakeactivity = 0; - Pcontexthelp = 0; - Pping = 0; - input = FALSE; - skip_pager = FALSE; - - max_mode = MaximizeRestore; - maxmode_restore = MaximizeRestore; - - cmap = None; - - frame_geometry = TQRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) - client_size = TQSize( 100, 100 ); - custom_opacity = false; - rule_opacity_active = 0;; //translucency rules - rule_opacity_inactive = 0; //dito. - - // SELI initialize xsizehints?? - } - -/*! - "Dumb" destructor. - */ -Client::~Client() - { - assert(!moveResizeMode); - assert( client == None ); - assert( frame == None && wrapper == None ); - assert( decoration == NULL ); - assert( postpone_geometry_updates == 0 ); - assert( !check_active_modal ); - delete info; - delete bridge; - } - -// use destroyClient() or releaseWindow(), Client instances cannot be deleted directly -void Client::deleteClient( Client* c, allowed_t ) - { - delete c; - } - -/*! - Releases the window. The client has done its job and the window is still existing. - */ -void Client::releaseWindow( bool on_shutdown ) - { - assert( !deleting ); - deleting = true; - workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules - StackingUpdatesBlocker blocker( workspace()); - if (!custom_opacity) setOpacity(FALSE); - if (moveResizeMode) - leaveMoveResize(); - removeShadow(); - drawIntersectingShadows(); - 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(); - 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) - if( !on_shutdown ) - workspace()->clientHidden( this ); - XUnmapWindow( qt_xdisplay(), frameId()); // destroying decoration would cause ugly visual effect - destroyDecoration(); - cleanGrouping(); - if( !on_shutdown ) - { - workspace()->removeClient( this, Allowed ); - // only when the window is being unmapped, not when closing down KWin - // (NETWM sections 5.5,5.7) - info->setDesktop( 0 ); - desk = 0; - info->setState( 0, info->state()); // reset all state flags - } - XDeleteProperty( qt_xdisplay(), client, atoms->kde_net_wm_user_creation_time); - XDeleteProperty( qt_xdisplay(), client, atoms->net_frame_extents ); - XDeleteProperty( qt_xdisplay(), client, atoms->kde_net_wm_frame_strut ); - XReparentWindow( qt_xdisplay(), client, workspace()->rootWin(), x(), y()); - XRemoveFromSaveSet( qt_xdisplay(), client ); - XSelectInput( qt_xdisplay(), client, NoEventMask ); - if( on_shutdown ) - { // map the window, so it can be found after another WM is started - XMapWindow( qt_xdisplay(), client ); - // TODO preserve minimized, shaded etc. state? - } - else - { - // Make sure it's not mapped if the app unmapped it (#65279). The app - // may do map+unmap before we initially map the window by calling rawShow() from manage(). - XUnmapWindow( qt_xdisplay(), client ); - } - client = None; - XDestroyWindow( qt_xdisplay(), wrapper ); - wrapper = None; - XDestroyWindow( qt_xdisplay(), frame ); - frame = None; - --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry - checkNonExistentClients(); - deleteClient( this, Allowed ); - ungrabXServer(); - } - -// like releaseWindow(), but this one is called when the window has been already destroyed -// (e.g. the application closed it) -void Client::destroyClient() - { - assert( !deleting ); - deleting = true; - workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules - StackingUpdatesBlocker blocker( workspace()); - if (moveResizeMode) - leaveMoveResize(); - removeShadow(); - drawIntersectingShadows(); - finishWindowRules(); - ++postpone_geometry_updates; - setModal( false ); - hidden = true; // so that it's not considered visible anymore - workspace()->clientHidden( this ); - destroyDecoration(); - cleanGrouping(); - workspace()->removeClient( this, Allowed ); - client = None; // tqinvalidate - XDestroyWindow( qt_xdisplay(), wrapper ); - wrapper = None; - XDestroyWindow( qt_xdisplay(), frame ); - frame = None; - --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry - checkNonExistentClients(); - deleteClient( this, Allowed ); - } - -void Client::updateDecoration( bool check_workspace_pos, bool force ) - { - if( !force && (( decoration == NULL && noBorder()) - || ( decoration != NULL && !noBorder()))) - return; - bool do_show = false; - postponeGeometryUpdates( true ); - if( force ) - destroyDecoration(); - if( !noBorder()) - { - setMask( TQRegion()); // reset tqshape mask - decoration = workspace()->createDecoration( bridge ); - // TODO check decoration's minimum size? - decoration->init(); - decoration->widget()->installEventFilter( this ); - XReparentWindow( qt_xdisplay(), decoration->widget()->winId(), frameId(), 0, 0 ); - decoration->widget()->lower(); - decoration->borders( border_left, border_right, border_top, border_bottom ); - options->onlyDecoTranslucent ? - setDecoHashProperty(border_top, border_right, border_bottom, border_left): - unsetDecoHashProperty(); - int save_workarea_diff_x = workarea_diff_x; - int save_workarea_diff_y = workarea_diff_y; - move( calculateGravitation( false )); - plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); - workarea_diff_x = save_workarea_diff_x; - workarea_diff_y = save_workarea_diff_y; - do_show = true; - } - else - destroyDecoration(); - if( check_workspace_pos ) - checkWorkspacePosition(); - postponeGeometryUpdates( false ); - if( do_show ) - decoration->widget()->show(); - updateFrameExtents(); - updateOpacityCache(); - } - -void Client::destroyDecoration() - { - if( decoration != NULL ) - { - delete decoration; - decoration = NULL; - TQPoint grav = calculateGravitation( true ); - border_left = border_right = border_top = border_bottom = 0; - setMask( TQRegion()); // reset tqshape mask - int save_workarea_diff_x = workarea_diff_x; - int save_workarea_diff_y = workarea_diff_y; - plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); - move( grav ); - workarea_diff_x = save_workarea_diff_x; - workarea_diff_y = save_workarea_diff_y; - } - } - -void Client::checkBorderSizes() - { - if( decoration == NULL ) - return; - int new_left, new_right, new_top, new_bottom; - decoration->borders( new_left, new_right, new_top, new_bottom ); - if( new_left == border_left && new_right == border_right - && new_top == border_top && new_bottom == border_bottom ) - return; - GeometryUpdatesPostponer blocker( this ); - move( calculateGravitation( true )); - border_left = new_left; - border_right = new_right; - border_top = new_top; - border_bottom = new_bottom; - if (border_left != new_left || - border_right != new_right || - border_top != new_top || - border_bottom != new_bottom) - options->onlyDecoTranslucent ? - setDecoHashProperty(new_top, new_right, new_bottom, new_left): - unsetDecoHashProperty(); - move( calculateGravitation( false )); - plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); - checkWorkspacePosition(); - } - -void Client::detectNoBorder() - { - if( Shape::hasShape( window())) - { - noborder = true; - return; - } - switch( windowType()) - { - case NET::Desktop : - case NET::Dock : - case NET::TopMenu : - case NET::Splash : - noborder = true; - break; - case NET::Unknown : - case NET::Normal : - case NET::Toolbar : - case NET::Menu : - case NET::Dialog : - case NET::Utility : - noborder = false; - break; - default: - assert( false ); - } - // NET::Override is some strange beast without clear definition, usually - // just meaning "noborder", so let's treat it only as such flag, and ignore it as - // a window type otherwise (SUPPORTED_WINDOW_TYPES_MASK doesn't include it) - if( info->windowType( SUPPORTED_WINDOW_TYPES_MASK | NET::OverrideMask ) == NET::Override ) - noborder = true; - } - -void Client::detectShapable() - { - if( Shape::hasShape( window())) - return; - switch( windowType()) - { - case NET::Desktop : - case NET::Dock : - case NET::TopMenu : - case NET::Splash : - break; - case NET::Unknown : - case NET::Normal : - case NET::Toolbar : - case NET::Menu : - case NET::Dialog : - case NET::Utility : - setShapable(FALSE); - break; - default: - assert( false ); - } - } - -void Client::updateFrameExtents() - { - NETStrut strut; - strut.left = border_left; - strut.right = border_right; - strut.top = border_top; - strut.bottom = border_bottom; - info->setFrameExtents( strut ); - } - -// Resizes the decoration, and makes sure the decoration widget gets resize event -// even if the size hasn't changed. This is needed to make sure the decoration -// re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes, -// the decoration may turn on/off some borders, but the actual size -// of the decoration stays the same). -void Client::resizeDecoration( const TQSize& s ) - { - if( decoration == NULL ) - return; - TQSize oldsize = decoration->widget()->size(); - decoration->resize( s ); - if( oldsize == s ) - { - TQResizeEvent e( s, oldsize ); - TQApplication::sendEvent( decoration->widget(), &e ); - } - if (!moveResizeMode && options->shadowEnabled(isActive())) - { - // If the user is manually resizing, let Client::leaveMoveResize() - // decide when to redraw the shadow - updateOpacityCache(); - } - } - -bool Client::noBorder() const - { - return noborder || isFullScreen() || user_noborder || motif_noborder; - } - -bool Client::userCanSetNoBorder() const - { - return !noborder && !isFullScreen() && !isShade(); - } - -bool Client::isUserNoBorder() const - { - return user_noborder; - } - -void Client::setUserNoBorder( bool set ) - { - if( !userCanSetNoBorder()) - return; - set = rules()->checkNoBorder( set ); - if( user_noborder == set ) - return; - user_noborder = set; - updateDecoration( true, false ); - updateWindowRules(); - } - -bool Client::isModalSystemNotification() const - { - unsigned char *data = 0; - Atom actual; - int format, result; - unsigned long n, left; - result = XGetWindowProperty(qt_xdisplay(), window(), atoms->net_wm_system_modal_notification, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, /*(unsigned char **)*/ &data); - if (result == Success && data != None && format == 32 ) - { - return TRUE; - } - return FALSE; - } - -void Client::updateShape() - { - // workaround for #19644 - tqshaped windows shouldn't have decoration - if( tqshape() && !noBorder()) - { - noborder = true; - updateDecoration( true ); - } - updateOpacityCache(); - if ( tqshape() ) - { - XShapeCombineShape(qt_xdisplay(), frameId(), ShapeBounding, - clientPos().x(), clientPos().y(), - window(), ShapeBounding, ShapeSet); - setShapable(TRUE); - } - // !tqshape() 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 tqshape support - { // There appears to be no way to find out if a window has input - // tqshape set or not, so always propagate the input tqshape - // (it's the same like the bounding tqshape by default). - // Also, build the tqshape using a helper window, not directly - // in the frame window, because the sequence set-tqshape-to-frame, - // remove-tqshape-of-client, add-input-tqshape-of-client has the problem - // that after the second step there's a hole in the input tqshape - // until the real tqshape 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( qt_xdisplay(), qt_xrootwin(), - 0, 0, 1, 1, 0, 0, 0 ); - XResizeWindow( qt_xdisplay(), helper_window, width(), height()); - XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput, 0, 0, - frameId(), ShapeBounding, ShapeSet ); - XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput, - clientPos().x(), clientPos().y(), - window(), ShapeBounding, ShapeSubtract ); - XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput, - clientPos().x(), clientPos().y(), - window(), ShapeInput, ShapeUnion ); - XShapeCombineShape( qt_xdisplay(), frameId(), ShapeInput, 0, 0, - helper_window, ShapeInput, ShapeSet ); - } - } - -void Client::setMask( const TQRegion& reg, int mode ) - { - _mask = reg; - if( reg.isNull()) - XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, - None, ShapeSet ); - else if( mode == X::Unsorted ) - XShapeCombineRegion( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, - reg.handle(), ShapeSet ); - else - { - TQMemArray< TQRect > rects = reg.tqrects(); - XRectangle* xrects = new XRectangle[ rects.count() ]; - for( unsigned int i = 0; - i < rects.count(); - ++i ) - { - xrects[ i ].x = rects[ i ].x(); - xrects[ i ].y = rects[ i ].y(); - xrects[ i ].width = rects[ i ].width(); - xrects[ i ].height = rects[ i ].height(); - } - XShapeCombineRectangles( qt_xdisplay(), frameId(), ShapeBounding, 0, 0, - xrects, rects.count(), ShapeSet, mode ); - delete[] xrects; - } - updateShape(); - } - -TQRegion Client::mask() const - { - if( _mask.isEmpty()) - return TQRegion( 0, 0, width(), height()); - return _mask; - } - -void Client::setShapable(bool b) - { - long tmp = b?1:0; - XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shapable, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &tmp, 1L); - } - -void Client::hideClient( bool hide ) - { - if( hidden == hide ) - return; - hidden = hide; - updateVisibility(); - } - -/*! - Returns whether the window is minimizable or not - */ -bool Client::isMinimizable() const - { - if( isSpecialWindow()) - return false; - if( isModalSystemNotification()) - return false; - if( isTransient()) - { // #66868 - let other xmms windows be minimized when the mainwindow is minimized - bool shown_mainwindow = false; - ClientList mainclients = mainClients(); - for( ClientList::ConstIterator it = mainclients.begin(); - it != mainclients.end(); - ++it ) - { - if( (*it)->isShown( true )) - shown_mainwindow = true; - } - if( !shown_mainwindow ) - return true; - } - // this is here because kicker's taskbar doesn't provide separate entries - // for windows with an explicitly given parent - // TODO perhaps this should be redone - if( transientFor() != NULL ) - return false; - if( !wantsTabFocus()) // SELI - NET::Utility? why wantsTabFocus() - skiptaskbar? ? - return false; - return true; - } - -/*! - Returns whether the window is kept above or not - */ -bool Client::keepAbove() const - { - if( isModalSystemNotification()) - return true; - return keep_above; - } - -/*! - Minimizes this client plus its transients - */ -void Client::minimize( bool avoid_animation ) - { - if ( !isMinimizable() || isMinimized()) - return; - - 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(); - updateAllowedActions(); - workspace()->updateMinimizedOfTransients( this ); - updateWindowRules(); - workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast ); - } - -void Client::unminimize( bool avoid_animation ) - { - if( !isMinimized()) - return; - - Notify::raise( Notify::UnMinimize ); - minimized = false; - if( isOnCurrentDesktop() && isShown( true )) - { - if( mainClients().isEmpty() && !avoid_animation ) - animateMinimizeOrUnminimize( FALSE ); - } - updateVisibility(); - updateAllowedActions(); - workspace()->updateMinimizedOfTransients( this ); - updateWindowRules(); - } - -extern bool blockAnimation; - -void Client::animateMinimizeOrUnminimize( bool minimize ) - { - if ( 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(); - TQRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height ); - if ( !icongeom.isValid() ) - return; - - TQPixmap pm = animationPixmap( minimize ? width() : icongeom.width() ); - - TQRect before, after; - if ( minimize ) - { - before = TQRect( x(), y(), width(), pm.height() ); - after = TQRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); - } - else - { - before = TQRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); - after = TQRect( 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(); - - TQRect area = before; - TQRect area2; - TQPixmap pm2; - - TQTime t; - t.start(); - float diff; - - TQPainter p ( workspace()->desktopWidget() ); - bool need_to_clear = FALSE; - TQPixmap pm3; - do - { - if (area2 != area) - { - pm = animationPixmap( area.width() ); - pm2 = TQPixmap::grabWindow( qt_xrootwin(), 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(qt_xdisplay()); - XSync( qt_xdisplay(), 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 - */ -TQPixmap Client::animationPixmap( int w ) - { - TQFont font = options->font(isActive()); - TQFontMetrics fm( font ); - TQPixmap pm( w, fm.lineSpacing() ); - pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) ); - TQPainter p( &pm ); - p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() )); - p.setFont(options->font(isActive())); - p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() ); - return pm; - } - - -bool Client::isShadeable() const - { - return !isSpecialWindow() && !noBorder(); - } - -void Client::setShade( ShadeMode mode ) - { - if( !isShadeable()) - return; - if( isModalSystemNotification()) - return; - mode = rules()->checkShade( mode ); - if( shade_mode == mode ) - return; - bool was_shade = isShade(); - ShadeMode was_shade_mode = shade_mode; - shade_mode = mode; - if( was_shade == isShade()) - { - if( decoration != NULL ) // decoration may want to update after e.g. hover-shade changes - decoration->shadeChange(); - return; // no real change in shaded state - } - - if( shade_mode == ShadeNormal ) - { - if ( isShown( true ) && isOnCurrentDesktop()) - Notify::raise( Notify::ShadeUp ); - } - else if( shade_mode == ShadeNone ) - { - if( isShown( true ) && isOnCurrentDesktop()) - Notify::raise( Notify::ShadeDown ); - } - - assert( decoration != NULL ); // noborder windows can't be shaded - GeometryUpdatesPostponer 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(qt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L); - // shade - int h = height(); - shade_geometry_change = true; - TQSize s( sizeForClientSize( TQSize( clientSize()))); - s.setHeight( border_top + border_bottom ); - XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify - XUnmapWindow( qt_xdisplay(), wrapper ); - XUnmapWindow( qt_xdisplay(), client ); - XSelectInput( qt_xdisplay(), 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 tqrepaint( FALSE ); -// bool wasStaticContents = testWFlags( WStaticContents ); -// setWFlags( WStaticContents ); - int step = QMAX( 4, QABS( h - s.height() ) / as )+1; - do - { - h -= step; - XResizeWindow( qt_xdisplay(), frameId(), s.width(), h ); - resizeDecoration( TQSize( s.width(), h )); - TQApplication::syncX(); - } while ( h > s.height() + step ); -// if ( !wasStaticContents ) -// clearWFlags( WStaticContents ); - plainResize( s ); - shade_geometry_change = false; - if( isActive()) - { - if( was_shade_mode == ShadeHover ) - workspace()->activateNextClient( this ); - else - workspace()->focusToNull(); - } - // tell xcompmgr shade's done - _shade = 2; - XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L); - } - else - { - int h = height(); - shade_geometry_change = true; - TQSize s( sizeForClientSize( clientSize())); -// FRAME bool wasStaticContents = testWFlags( WStaticContents ); -// setWFlags( WStaticContents ); - int step = QMAX( 4, QABS( h - s.height() ) / as )+1; - do - { - h += step; - XResizeWindow( qt_xdisplay(), frameId(), s.width(), h ); - resizeDecoration( TQSize( s.width(), h )); - // assume a border - // we do not have time to wait for X to send us paint events -// FRAME tqrepaint( 0, h - step-5, width(), step+5, TRUE); - TQApplication::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( qt_xdisplay(), wrapperId()); - XMapWindow( qt_xdisplay(), window()); - XDeleteProperty (qt_xdisplay(), client, atoms->net_wm_window_shade); - if (options->shadowEnabled(false)) - { - for (ClientList::ConstIterator it = transients().begin(); - it != transients().end(); ++it) - { - (*it)->removeShadow(); - (*it)->drawDelayedShadow(); - } - } - - if ( isActive() ) - workspace()->requestFocus( this ); - } - checkMaximizeGeometry(); - info->setState( isShade() ? NET::Shaded : 0, NET::Shaded ); - info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden ); - updateVisibility(); - updateAllowedActions(); - workspace()->updateMinimizedOfTransients( this ); - decoration->shadeChange(); - updateWindowRules(); - } - -void Client::shadeHover() - { - setShade( ShadeHover ); - cancelShadeHover(); - } - -void Client::cancelShadeHover() - { - delete shadeHoverTimer; - shadeHoverTimer = 0; - } - -void Client::toggleShade() - { - // if the mode is ShadeHover or ShadeActive, cancel shade too - setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone ); - } - -void Client::updateVisibility() - { - if( deleting ) - return; - bool show = true; - if( hidden ) - { - setMappingState( IconicState ); - info->setState( NET::Hidden, NET::Hidden ); - setSkipTaskbar( true, false ); // also hide from taskbar - rawHide(); - show = false; - } - else - { - setSkipTaskbar( original_skip_taskbar, false ); - } - if( minimized ) - { - setMappingState( IconicState ); - info->setState( NET::Hidden, NET::Hidden ); - rawHide(); - show = false; - } - if( show ) - info->setState( 0, NET::Hidden ); - if( !isOnCurrentDesktop()) - { - setMappingState( IconicState ); - rawHide(); - show = false; - } - if( show ) - { - bool belongs_to_desktop = false; - for( ClientList::ConstIterator it = group()->members().begin(); - it != group()->members().end(); - ++it ) - if( (*it)->isDesktop()) - { - belongs_to_desktop = true; - break; - } - if( !belongs_to_desktop && workspace()->showingDesktop()) - workspace()->resetShowingDesktop( true ); - if( isShade()) - setMappingState( IconicState ); - else - setMappingState( NormalState ); - rawShow(); - } - } - -void Client::setShadowed(bool shadowed) -{ - bool wasShadowed; - - wasShadowed = isShadowed(); - shadowMe = options->shadowEnabled(isActive()) ? shadowed : false; - - if (shadowMe) { - if (!wasShadowed) - drawShadow(); - } - else { - if (wasShadowed) { - removeShadow(); - - if (!activeOpacityCache.isNull()) - activeOpacityCache.resize(0); - if (!inactiveOpacityCache.isNull()) - inactiveOpacityCache.resize(0); - } - } -} - -void Client::updateOpacityCache() -{ - if (!activeOpacityCache.isNull()) - activeOpacityCache.resize(0); - if (!inactiveOpacityCache.isNull()) - inactiveOpacityCache.resize(0); - - if (!moveResizeMode) { - // If the user is manually resizing, let Client::finishMoveResize() - // decide when to redraw the shadow - removeShadow(); - drawIntersectingShadows(); - if (options->shadowEnabled(isActive())) - drawDelayedShadow(); - } -} - -/*! - Redraw shadows that were previously occluding or occluded by this window, - to avoid visual glitches. - */ -void Client::drawIntersectingShadows() { - //Client *reshadowClient; - TQRegion region; - //TQPtrList<Client> reshadowClients; - TQValueList<Client *> reshadowClients; - TQValueListIterator<ShadowRegion> it; - TQValueListIterator<Client *> it2; - - if (!options->shadowEnabled(false)) - // No point in redrawing overlapping/overlapped shadows if only the - // active window has a shadow. - return; - - region = tqshapeBoundingRegion; - - // Generate list of Clients whose shadows need to be redrawn. That is, - // those that are currently intersecting or intersected by other windows or - // shadows. - for (it = shadowRegions.begin(); it != shadowRegions.end(); ++it) - if ((isOnAllDesktops() || (*it).client->isOnCurrentDesktop()) && - !(*it).region.intersect(region).isEmpty()) - reshadowClients.append((*it).client); - - // Redraw shadows for each of the Clients in the list generated above - for (it2 = reshadowClients.begin(); it2 != reshadowClients.end(); - ++it2) { - (*it2)->removeShadow(); - (*it2)->drawDelayedShadow(); - } -} - -/*! - Redraw shadows that are above the current window in the stacking order. - Furthermore, redraw them in the same order as they come in the stacking order - from bottom to top. - */ -void Client::drawOverlappingShadows(bool waitForMe) -{ - Client *aClient; - TQRegion region; - TQValueList<Client *> reshadowClients; - ClientList stacking_order; - ClientList::ConstIterator it; - TQValueListIterator<ShadowRegion> it2; - TQValueListIterator<Client *> it3; - - if (!options->shadowEnabled(false)) - // No point in redrawing overlapping/overlapped shadows if only the - // active window has a shadow. - return; - - region = tqshapeBoundingRegion; - - stacking_order = workspace()->stackingOrder(); - for (it = stacking_order.fromLast(); it != stacking_order.end(); --it) { - // Find the position of this window in the stacking order. - if ((*it) == this) - break; - } - ++it; - while (it != stacking_order.end()) { - if ((*it)->windowType() == NET::Dock) { - // This function is only interested in windows whose shadows don't - // have weird stacking rules. - ++it; - continue; - } - - // Generate list of Clients whose shadows need to be redrawn. That is, - // those that are currently overlapping or overlapped by other windows - // or shadows. The list should be in order from bottom to top in the - // stacking order. - for (it2 = shadowRegions.begin(); it2 != shadowRegions.end(); ++it2) { - if ((*it2).client == (*it)) { - if ((isOnAllDesktops() || (*it2).client->isOnCurrentDesktop()) - && !(*it2).region.intersect(region).isEmpty()) - reshadowClients.append((*it2).client); - } - } - ++it; - } - - // Redraw shadows for each of the Clients in the list generated above - for (it3 = reshadowClients.begin(); it3 != reshadowClients.end(); ++it3) { - (*it3)->removeShadow(); - if (it3 == reshadowClients.begin()) { - if (waitForMe) - (*it3)->drawShadowAfter(this); - else - (*it3)->drawDelayedShadow(); - } - else { - --it3; - aClient = (*it3); - ++it3; - (*it3)->drawShadowAfter(aClient); - } - } -} - -/*! - Draw shadow after some time has elapsed, to give recently exposed windows a - chance to tqrepaint before a shadow gradient is drawn over them. - */ -void Client::drawDelayedShadow() -{ - shadowDelayTimer->stop(); - shadowDelayTimer->start(SHADOW_DELAY, true); -} - -/*! - Draw shadow immediately after the specified Client's shadow finishes drawing. - */ -void Client::drawShadowAfter(Client *after) -{ - shadowAfterClient = after; - connect(after, TQT_SIGNAL(shadowDrawn()), TQT_SLOT(drawShadow())); -} - -/*! - Draw a shadow under this window and XShape the shadow accordingly. - */ -void Client::drawShadow() -{ - Window shadows[2]; - XRectangle *tqshapes; - int i, count, ordering; - - // If we are waiting for another Client's shadow to be drawn, stop waiting now - if (shadowAfterClient != NULL) { - disconnect(shadowAfterClient, TQT_SIGNAL(shadowDrawn()), this, TQT_SLOT(drawShadow())); - shadowAfterClient = NULL; - } - - if (!isOnCurrentDesktop()) - return; - - /* Store this window's ShapeBoundingRegion even if shadows aren't drawn for - * this type of window. Otherwise, drawIntersectingShadows() won't update - * properly when this window is moved/resized/hidden/closed. - */ - tqshapes = XShapeGetRectangles(qt_xdisplay(), frameId(), ShapeBounding, - &count, &ordering); - if (!tqshapes) - // XShape extension not supported - tqshapeBoundingRegion = TQRegion(x(), y(), width(), height()); - else { - tqshapeBoundingRegion = TQRegion(); - for (i = 0; i < count; i++) { - // Translate XShaped window into a TQRegion - TQRegion tqshapeRectangle(tqshapes[i].x, tqshapes[i].y, tqshapes[i].width, - tqshapes[i].height); - tqshapeBoundingRegion += tqshapeRectangle; - } - if (isShade()) - // Since XResize() doesn't change a window's XShape regions, ensure that - // tqshapeBoundingRegion is not taller than the window's shaded height, - // or the bottom shadow will appear to be missing - tqshapeBoundingRegion &= TQRegion(0, 0, width(), height()); - tqshapeBoundingRegion.translate(x(), y()); - } - - if (!isShadowed() || hidden || isMinimized() || - maximizeMode() == MaximizeFull || - !options->shadowWindowType(windowType())) { - XFree(tqshapes); - - // Tell whatever Clients are listening that this Client's shadow has been drawn. - // It hasn't, but there's no sense waiting for something that won't happen. - emit shadowDrawn(); - - return; - } - - removeShadow(); - - TQMemArray<QRgb> pixelData; - TQPixmap shadowPixmap; - TQRect shadow; - TQRegion exposedRegion; - ShadowRegion shadowRegion; - int thickness, xOffset, yOffset; - - thickness = options->shadowThickness(isActive()); - xOffset = options->shadowXOffset(isActive()); - yOffset = options->shadowYOffset(isActive()); - opacityCache = active? &activeOpacityCache : &inactiveOpacityCache; - - shadow.setRect(x() - thickness + xOffset, y() - thickness + yOffset, - width() + thickness * 2, height() + thickness * 2); - shadowPixmap.resize(shadow.size()); - - // Create a fake drop-down shadow effect via blended Xwindows - shadowWidget = new TQWidget(0, 0, (WFlags)(WStyle_Customize | WX11BypassWM)); - shadowWidget->setGeometry(shadow); - XSelectInput(qt_xdisplay(), shadowWidget->winId(), - ButtonPressMask | ButtonReleaseMask | StructureNotifyMask); - shadowWidget->installEventFilter(this); - - if (!tqshapes) { - // XShape extension not supported - exposedRegion = getExposedRegion(tqshapeBoundingRegion, shadow.x(), - shadow.y(), shadow.width(), shadow.height(), thickness, - xOffset, yOffset); - shadowRegion.region = exposedRegion; - shadowRegion.client = this; - shadowRegions.append(shadowRegion); - - if (opacityCache->isNull()) - imposeRegionShadow(shadowPixmap, tqshapeBoundingRegion, - exposedRegion, thickness, - options->shadowOpacity(isActive())); - else - imposeCachedShadow(shadowPixmap, exposedRegion); - } - else { - TQMemArray<TQRect> exposedRects; - TQMemArray<TQRect>::Iterator it, itEnd; - XRectangle *shadowShapes; - - exposedRegion = getExposedRegion(tqshapeBoundingRegion, shadow.x(), - shadow.y(), shadow.width(), shadow.height(), thickness, - xOffset, yOffset); - shadowRegion.region = exposedRegion; - shadowRegion.client = this; - shadowRegions.append(shadowRegion); - - // XShape the shadow - exposedRects = exposedRegion.tqrects(); - i = 0; - itEnd = exposedRects.end(); - shadowShapes = new XRectangle[exposedRects.count()]; - for (it = exposedRects.begin(); it != itEnd; ++it) { - shadowShapes[i].x = (*it).x(); - shadowShapes[i].y = (*it).y(); - shadowShapes[i].width = (*it).width(); - shadowShapes[i].height = (*it).height(); - i++; - } - XShapeCombineRectangles(qt_xdisplay(), shadowWidget->winId(), - ShapeBounding, -x() + thickness - xOffset, - -y() + thickness - yOffset, shadowShapes, i, ShapeSet, - Unsorted); - delete [] shadowShapes; - - if (opacityCache->isNull()) - imposeRegionShadow(shadowPixmap, tqshapeBoundingRegion, - exposedRegion, thickness, - options->shadowOpacity(isActive())); - else - imposeCachedShadow(shadowPixmap, exposedRegion); - } - - XFree(tqshapes); - - // Set the background pixmap - //shadowPixmap.convertFromImage(shadowImage); - shadowWidget->setErasePixmap(shadowPixmap); - - // Restack shadows under this window so that shadows drawn for a newly - // focused (but not raised) window don't overlap any windows above it. - if (isDock()) { - ClientList stacking_order = workspace()->stackingOrder(); - for (ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) - if ((*it)->isDesktop()) - { - ++it; - shadows[0] = (*it)->frameId(); - shadows[1] = shadowWidget->winId(); - } - } - else { - shadows[0] = frameId(); - if (shadowWidget != NULL) - shadows[1] = shadowWidget->winId(); - } - - XRestackWindows(qt_xdisplay(), shadows, 2); - - // Don't use TQWidget::show() so we don't confuse QEffects, thus causing - // broken focus. - XMapWindow(qt_xdisplay(), shadowWidget->winId()); - - // Tell whatever Clients are listening that this Client's shadow has been drawn. - emit shadowDrawn(); -} - -/*! - Remove shadow under this window. - */ -void Client::removeShadow() -{ - TQValueList<ShadowRegion>::Iterator it; - - shadowDelayTimer->stop(); - - if (shadowWidget != NULL) { - for (it = shadowRegions.begin(); it != shadowRegions.end(); ++it) - if ((*it).client == this) { - shadowRegions.remove(it); - break; - } - delete shadowWidget; - shadowWidget = NULL; - } -} - -/*! - Calculate regions in which the shadow will be visible given the window's - origin, height and width and the shadow's thickness, and X- and Y-offsets. - */ -TQRegion Client::getExposedRegion(TQRegion occludedRegion, int x, int y, int w, - int h, int thickness, int xOffset, int yOffset) -{ - TQRegion exposedRegion; - - exposedRegion = TQRegion(x, y, w, h); - exposedRegion -= occludedRegion; - - if (thickness > 0) { - // Limit exposedRegion to include only where a shadow of the specified - // thickness will be drawn - TQMemArray<TQRect> occludedRects; - TQMemArray<TQRect>::Iterator it, itEnd; - TQRegion shadowRegion; - - occludedRects = occludedRegion.tqrects(); - itEnd = occludedRects.end(); - for (it = occludedRects.begin(); it != itEnd; ++it) { - // Expand each of the occluded region's tqshape rectangles to contain - // where a shadow of the specified thickness will be drawn. Create - // a new TQRegion that contains the expanded occluded region - it->setTop(it->top() - thickness + yOffset); - it->setLeft(it->left() - thickness + xOffset); - it->setRight(it->right() + thickness + xOffset); - it->setBottom(it->bottom() + thickness + yOffset); - shadowRegion += TQRegion(*it); - } - exposedRegion -= exposedRegion - shadowRegion; - } - - return exposedRegion; -} - -/*! - Draw shadow gradient around this window using cached opacity values. - */ -void Client::imposeCachedShadow(TQPixmap &pixmap, TQRegion exposed) -{ - QRgb pixel; - double opacity; - int red, green, blue, pixelRed, pixelGreen, pixelBlue; - int subW, subH, w, h, x, y, zeroX, zeroY; - TQImage image; - TQMemArray<TQRect>::Iterator it, itEnd; - TQMemArray<TQRect> rectangles; - TQPixmap subPixmap; - Window rootWindow; - int thickness, windowX, windowY, xOffset, yOffset; - - rectangles = exposed.tqrects(); - rootWindow = qt_xrootwin(); - thickness = options->shadowThickness(isActive()); - windowX = this->x(); - windowY = this->y(); - xOffset = options->shadowXOffset(isActive()); - yOffset = options->shadowYOffset(isActive()); - options->shadowColour(isActive()).rgb(&red, &green, &blue); - w = pixmap.width(); - h = pixmap.height(); - - itEnd = rectangles.end(); - for (it = rectangles.begin(); it != itEnd; ++it) { - subW = (*it).width(); - subH = (*it).height(); - subPixmap = TQPixmap::grabWindow(rootWindow, (*it).x(), (*it).y(), - subW, subH); - zeroX = (*it).x() - windowX + thickness - xOffset; - zeroY = (*it).y() - windowY + thickness - yOffset; - image = subPixmap.convertToImage(); - - for (x = 0; x < subW; x++) { - for (y = 0; y < subH; y++) { - opacity = (*(opacityCache))[(zeroY + y) * w + zeroX + x]; - pixel = image.pixel(x, y); - pixelRed = tqRed(pixel); - pixelGreen = tqGreen(pixel); - pixelBlue = tqBlue(pixel); - image.setPixel(x, y, - tqRgb((int)(pixelRed + (red - pixelRed) * opacity), - (int)(pixelGreen + (green - pixelGreen) * opacity), - (int)(pixelBlue + (blue - pixelBlue) * opacity))); - } - } - - subPixmap.convertFromImage(image); - bitBlt(&pixmap, zeroX, zeroY, &subPixmap); - } -} - -/*! - Draw shadow around this window using calculated opacity values. - */ -void Client::imposeRegionShadow(TQPixmap &pixmap, TQRegion occluded, - TQRegion exposed, int thickness, double maxOpacity) -{ - register int distance, intersectCount, i, j, x, y; - QRgb pixel; - double decay, factor, opacity; - int red, green, blue, pixelRed, pixelGreen, pixelBlue; - int halfMaxIntersects, lineIntersects, maxIntersects, maxY; - int irBottom, irLeft, irRight, irTop, yIncrement; - int subW, subH, w, h, zeroX, zeroY; - TQImage image; - TQMemArray<TQRect>::Iterator it, itEnd; - TQMemArray<TQRect> rectangles; - TQPixmap subPixmap; - Window rootWindow; - int windowX, windowY, xOffset, yOffset; - - rectangles = exposed.tqrects(); - rootWindow = qt_xrootwin(); - windowX = this->x(); - windowY = this->y(); - xOffset = options->shadowXOffset(isActive()); - yOffset = options->shadowYOffset(isActive()); - options->shadowColour(isActive()).rgb(&red, &green, &blue); - maxIntersects = thickness * thickness * 4 + (thickness * 4) + 1; - halfMaxIntersects = maxIntersects / 2; - lineIntersects = thickness * 2 + 1; - factor = maxIntersects / maxOpacity; - decay = (lineIntersects / 0.0125 - factor) / pow((double)maxIntersects, 3.0); - w = pixmap.width(); - h = pixmap.height(); - xOffset = options->shadowXOffset(isActive()); - yOffset = options->shadowYOffset(isActive()); - - opacityCache->resize(0); - opacityCache->resize(w * h); - occluded.translate(-windowX + thickness, -windowY + thickness); - - itEnd = rectangles.end(); - for (it = rectangles.begin(); it != itEnd; ++it) { - subW = (*it).width(); - subH = (*it).height(); - subPixmap = TQPixmap::grabWindow(rootWindow, (*it).x(), (*it).y(), - subW, subH); - maxY = subH; - zeroX = (*it).x() - windowX + thickness - xOffset; - zeroY = (*it).y() - windowY + thickness - yOffset; - image = subPixmap.convertToImage(); - - intersectCount = 0; - opacity = -1; - y = 0; - yIncrement = 1; - for (x = 0; x < subW; x++) { - irLeft = zeroX + x - thickness; - irRight = zeroX + x + thickness; - - while (y != maxY) { - // horizontal row about to leave the intersect region, not - // necessarily the top row - irTop = zeroY + y - thickness * yIncrement; - // horizontal row that just came into the intersect region, - // not necessarily the bottom row - irBottom = zeroY + y + thickness * yIncrement; - - if (opacity == -1) { - // If occluded pixels caused an intersect count to be - // skipped, recount it - intersectCount = 0; - - for (j = irTop; j != irBottom; j += yIncrement) { - // irTop is not necessarily larger than irBottom and - // yIncrement isn't necessarily positive - for (i = irLeft; i <= irRight; i++) { - if (occluded.contains(TQPoint(i, j))) - intersectCount++; - } - } - } - else { - if (intersectCount < 0) - intersectCount = 0; - - for (i = irLeft; i <= irRight; i++) { - if (occluded.contains(TQPoint(i, irBottom))) - intersectCount++; - } - } - - distance = maxIntersects - intersectCount; - opacity = intersectCount / (factor + pow((double)distance, 3.0) * decay); - - (*(opacityCache))[(zeroY + y) * w + zeroX + x] = opacity; - pixel = image.pixel(x, y); - pixelRed = tqRed(pixel); - pixelGreen = tqGreen(pixel); - pixelBlue = tqBlue(pixel); - image.setPixel(x, y, - tqRgb((int)(pixelRed + (red - pixelRed) * opacity), - (int)(pixelGreen + (green - pixelGreen) * opacity), - (int)(pixelBlue + (blue - pixelBlue) * opacity))); - - for (i = irLeft; i <= irRight; i++) { - if (occluded.contains(TQPoint(i, irTop))) - intersectCount--; - } - - y += yIncrement; - } - y -= yIncrement; - - irTop += yIncrement; - for (j = irTop; j != irBottom; j += yIncrement) { - if (occluded.contains(TQPoint(irLeft, j))) - intersectCount--; - } - irRight++; - for (j = irTop; j != irBottom; j += yIncrement) { - if (occluded.contains(TQPoint(irRight, j))) - intersectCount++; - } - - yIncrement *= -1; - if (yIncrement < 0) - // Scan Y-axis bottom-up for next X-coordinate iteration - maxY = -1; - else - // Scan Y-axis top-down for next X-coordinate iteration - maxY = subH; - } - - subPixmap.convertFromImage(image); - bitBlt(&pixmap, zeroX, zeroY, &subPixmap); - } -} - -/*! - Sets the client window's mapping state. Possible values are - WithdrawnState, IconicState, NormalState. - */ -void Client::setMappingState(int s) - { - assert( client != None ); - assert( !deleting || s == WithdrawnState ); - if( mapping_state == s ) - return; - bool was_unmanaged = ( mapping_state == WithdrawnState ); - mapping_state = s; - if( mapping_state == WithdrawnState ) - { - XDeleteProperty( qt_xdisplay(), window(), qt_wm_state ); - return; - } - assert( s == NormalState || s == IconicState ); - - unsigned long data[2]; - data[0] = (unsigned long) s; - data[1] = (unsigned long) None; - XChangeProperty(qt_xdisplay(), window(), qt_wm_state, qt_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 ); - } - -/*! - Reimplemented to map the managed window in the window wrapper. - Proper mapping state should be set before showing the client. - */ -void Client::rawShow() - { - if( decoration != NULL ) - decoration->widget()->show(); // not really necessary, but let it know the state - XMapWindow( qt_xdisplay(), frame ); - if( !isShade()) - { - XMapWindow( qt_xdisplay(), wrapper ); - XMapWindow( qt_xdisplay(), client ); - } - if (options->shadowEnabled(isActive())) - drawDelayedShadow(); - } - -/*! - Reimplemented to unmap the managed window in the window wrapper. - Also informs the workspace. - Proper mapping state should be set before hiding the client. -*/ -void Client::rawHide() - { -// 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, -// which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify -// will be missed is also very minimal, so I don't think it's needed to grab the server -// here. - removeShadow(); - drawIntersectingShadows(); - XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify - XUnmapWindow( qt_xdisplay(), frame ); - XUnmapWindow( qt_xdisplay(), wrapper ); - XUnmapWindow( qt_xdisplay(), client ); - XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask ); - if( decoration != NULL ) - decoration->widget()->hide(); // not really necessary, but let it know the state - workspace()->clientHidden( this ); - } - -void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3) - { - XEvent ev; - long mask; - - memset(&ev, 0, sizeof(ev)); - ev.xclient.type = ClientMessage; - ev.xclient.window = w; - ev.xclient.message_type = a; - ev.xclient.format = 32; - ev.xclient.data.l[0] = protocol; - ev.xclient.data.l[1] = GET_QT_X_TIME(); - ev.xclient.data.l[2] = data1; - ev.xclient.data.l[3] = data2; - ev.xclient.data.l[4] = data3; - mask = 0L; - if (w == qt_xrootwin()) - mask = SubstructureRedirectMask; /* magic! */ - XSendEvent(qt_xdisplay(), w, False, mask, &ev); - } - -/* - Returns whether the window may be closed (have a close button) - */ -bool Client::isCloseable() const - { - if( isModalSystemNotification()) - return false; - return rules()->checkCloseable( motif_may_close && !isSpecialWindow()); - } - -/*! - Closes the window by either sending a delete_window message or - using XKill. - */ -void Client::closeWindow() - { - if( !isCloseable()) - return; - // Update user time, because the window may create a confirming dialog. - updateUserTime(); - if ( Pdeletewindow ) - { - Notify::raise( Notify::Close ); - sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window); - pingWindow(); - } - else - { - // client will not react on wm_delete_window. We have not choice - // but destroy his connection to the XServer. - killWindow(); - } - } - - -/*! - Kills the window via XKill - */ -void Client::killWindow() - { - kdDebug( 1212 ) << "Client::killWindow():" << caption() << endl; - // not sure if we need an Notify::Kill or not.. until then, use - // Notify::Close - Notify::raise( Notify::Close ); - - if( isDialog()) - Notify::raise( Notify::TransDelete ); - if( isNormalWindow()) - Notify::raise( Notify::Delete ); - killProcess( false ); - // always kill this client at the server - XKillClient(qt_xdisplay(), window() ); - destroyClient(); - } - -// send a ping to the window using _NET_WM_PING if possible -// if it doesn't respond within a reasonable time, it will be -// killed -void Client::pingWindow() - { - if( !Pping ) - return; // can't ping :( - if( options->killPingTimeout == 0 ) - return; // turned off - if( ping_timer != NULL ) - return; // pinging already - ping_timer = new TQTimer( this ); - connect( ping_timer, TQT_SIGNAL( timeout()), TQT_SLOT( pingTimeout())); - ping_timer->start( options->killPingTimeout, true ); - ping_timestamp = GET_QT_X_TIME(); - workspace()->sendPingToWindow( window(), ping_timestamp ); - } - -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 ) - return; - delete ping_timer; - ping_timer = NULL; - if( process_killer != NULL ) - { - process_killer->kill(); - delete process_killer; - process_killer = NULL; - } - } - -void Client::pingTimeout() - { - kdDebug( 1212 ) << "Ping timeout:" << caption() << endl; - delete ping_timer; - ping_timer = NULL; - killProcess( true, ping_timestamp ); - } - -void Client::killProcess( bool ask, Time timestamp ) - { - if( process_killer != NULL ) - return; - Q_ASSERT( !ask || timestamp != CurrentTime ); - TQCString machine = wmClientMachine( true ); - pid_t pid = info->pid(); - if( pid <= 0 || machine.isEmpty()) // needed properties missing - return; - kdDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")" << endl; - if( !ask ) - { - if( machine != "localhost" ) - { - KProcess proc; - proc << "xon" << machine << "kill" << pid; - proc.start( KProcess::DontCare ); - } - else - ::kill( pid, SIGTERM ); - } - else - { // SELI TODO handle the window created by handler specially (on top,urgent?) - process_killer = new KProcess( this ); - *process_killer << KStandardDirs::findExe( "twin_killer_helper" ) - << "--pid" << TQCString().setNum( pid ) << "--hostname" << machine - << "--windowname" << caption().utf8() - << "--applicationname" << resourceClass() - << "--wid" << TQCString().setNum( window()) - << "--timestamp" << TQCString().setNum( timestamp ); - connect( process_killer, TQT_SIGNAL( processExited( KProcess* )), - TQT_SLOT( processKillerExited())); - if( !process_killer->start( KProcess::NotifyOnExit )) - { - delete process_killer; - process_killer = NULL; - return; - } - } - } - -void Client::processKillerExited() - { - kdDebug( 1212 ) << "Killer exited" << endl; - delete process_killer; - process_killer = NULL; - } - -void Client::setSkipTaskbar( bool b, bool from_outside ) - { - int was_wants_tab_focus = wantsTabFocus(); - if( from_outside ) - { - b = rules()->checkSkipTaskbar( b ); - original_skip_taskbar = b; - } - if ( b == skipTaskbar() ) - return; - skip_taskbar = b; - info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar ); - updateWindowRules(); - if( was_wants_tab_focus != wantsTabFocus()) - workspace()->updateFocusChains( this, - isActive() ? Workspace::FocusChainMakeFirst : Workspace::FocusChainUpdate ); - } - -void Client::setSkipPager( bool b ) - { - b = rules()->checkSkipPager( b ); - if ( b == skipPager() ) - return; - skip_pager = b; - info->setState( b?NET::SkipPager:0, NET::SkipPager ); - updateWindowRules(); - } - -void Client::setModal( bool m ) - { // Qt-3.2 can have even modal normal windows :( - if( modal == m ) - return; - modal = m; - if( !modal ) - return; - // changing modality for a mapped window is weird (?) - // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG - } - -void Client::setDesktop( int desktop ) - { - if( desktop != NET::OnAllDesktops ) // do range check - desktop = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desktop )); - desktop = rules()->checkDesktop( desktop ); - if( desk == desktop ) - return; - int was_desk = desk; - desk = desktop; - info->setDesktop( desktop ); - if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops )) - { // onAllDesktops changed - if ( isShown( true )) - Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops ); - workspace()->updateOnAllDesktopsOfTransients( this ); - } - if( decoration != NULL ) - decoration->desktopChange(); - workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst ); - updateVisibility(); - updateWindowRules(); - } - -void Client::setOnAllDesktops( bool b ) - { - if(( b && isOnAllDesktops()) - || ( !b && !isOnAllDesktops())) - return; - if( b ) - setDesktop( NET::OnAllDesktops ); - else - setDesktop( workspace()->currentDesktop()); - } - -bool Client::isOnCurrentDesktop() const - { - return isOnDesktop( workspace()->currentDesktop()); - } - -int Client::screen() const - { - if( !options->xineramaEnabled ) - return 0; - return workspace()->screenNumber( geometry().center()); - } - -bool Client::isOnScreen( int screen ) const - { - if( !options->xineramaEnabled ) - return screen == 0; - return workspace()->screenGeometry( screen ).intersects( geometry()); - } - -// performs activation and/or raising of the window -void Client::takeActivity( int flags, bool handled, allowed_t ) - { - if( !handled || !Ptakeactivity ) - { - if( flags & ActivityFocus ) - takeFocus( Allowed ); - if( flags & ActivityRaise ) - workspace()->raiseClient( this ); - return; - } - -#ifndef NDEBUG - static Time previous_activity_timestamp; - static Client* previous_client; - if( previous_activity_timestamp == GET_QT_X_TIME() && previous_client != this ) - { - kdDebug( 1212 ) << "Repeated use of the same X timestamp for activity" << endl; - kdDebug( 1212 ) << kdBacktrace() << endl; - } - previous_activity_timestamp = GET_QT_X_TIME(); - previous_client = this; -#endif - workspace()->sendTakeActivity( this, GET_QT_X_TIME(), flags ); - } - -// performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS -void Client::takeFocus( allowed_t ) - { -#ifndef NDEBUG - static Time previous_focus_timestamp; - static Client* previous_client; - if( previous_focus_timestamp == GET_QT_X_TIME() && previous_client != this ) - { - kdDebug( 1212 ) << "Repeated use of the same X timestamp for focus" << endl; - kdDebug( 1212 ) << kdBacktrace() << endl; - } - previous_focus_timestamp = GET_QT_X_TIME(); - previous_client = this; -#endif - if ( rules()->checkAcceptFocus( input )) - { - XSetInputFocus( qt_xdisplay(), window(), RevertToPointerRoot, GET_QT_X_TIME() ); - } - if ( Ptakefocus ) - sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus); - workspace()->setShouldGetFocus( this ); - } - -/*! - Returns whether the window provides context help or not. If it does, - you should show a help menu item or a help button like '?' and call - contextHelp() if this is invoked. - - \sa contextHelp() - */ -bool Client::providesContextHelp() const - { - if (isModalSystemNotification()) - return false; - return Pcontexthelp; - } - - -/*! - Invokes context help on the window. Only works if the window - actually provides context help. - - \sa providesContextHelp() - */ -void Client::showContextHelp() - { - if ( Pcontexthelp ) - { - sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help); - TQWhatsThis::enterWhatsThisMode(); // SELI? - } - } - - -/*! - Fetches the window's caption (WM_NAME property). It will be - stored in the client's caption(). - */ -void Client::fetchName() - { - setCaption( readName()); - } - -TQString Client::readName() const - { - if ( info->name() && info->name()[ 0 ] != '\0' ) - return TQString::fromUtf8( info->name() ); - else - return KWin::readNameProperty( window(), XA_WM_NAME ); - } - -KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); - -void Client::setCaption( const TQString& s, bool force ) - { - if ( s != cap_normal || force ) - { - bool reset_name = force; - for( unsigned int i = 0; - i < s.length(); - ++i ) - if( !s[ i ].isPrint()) - s[ i ] = ' '; - cap_normal = s; - bool was_suffix = ( !cap_suffix.isEmpty()); - TQString machine_suffix; - if( wmClientMachine( false ) != "localhost" && !isLocalMachine( wmClientMachine( false ))) - machine_suffix = " <@" + wmClientMachine( true ) + ">"; - TQString shortcut_suffix = !shortcut().isNull() ? ( " {" + shortcut().toString() + "}" ) : ""; - cap_suffix = machine_suffix + shortcut_suffix; - if ( ( !isSpecialWindow() || isToolbar()) && workspace()->findClient( FetchNameInternalPredicate( this ))) - { - int i = 2; - do - { - cap_suffix = machine_suffix + " <" + TQString::number(i) + ">" + shortcut_suffix; - i++; - } while ( workspace()->findClient( FetchNameInternalPredicate( this ))); - info->setVisibleName( caption().utf8() ); - reset_name = false; - } - if(( was_suffix && cap_suffix.isEmpty() - || reset_name )) // if it was new window, it may have old value still set, if the window is reused - { - info->setVisibleName( "" ); // remove - info->setVisibleIconName( "" ); // remove - } - else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set - info->setVisibleIconName( ( cap_iconic + cap_suffix ).utf8() ); - - if( isManaged() && decoration != NULL ) - decoration->captionChange(); - } - } - -void Client::updateCaption() - { - setCaption( cap_normal, true ); - } - -void Client::fetchIconicName() - { - TQString s; - if ( info->iconName() && info->iconName()[ 0 ] != '\0' ) - s = TQString::fromUtf8( info->iconName() ); - else - s = KWin::readNameProperty( window(), XA_WM_ICON_NAME ); - if ( s != cap_iconic ) - { - bool was_set = !cap_iconic.isEmpty(); - cap_iconic = s; - if( !cap_suffix.isEmpty()) - { - if( !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set - info->setVisibleIconName( ( s + cap_suffix ).utf8() ); - else if( was_set ) - info->setVisibleIconName( "" ); //remove - } - } - } - -/*!\reimp - */ -TQString Client::caption( bool full ) const - { - return full ? cap_normal + cap_suffix : cap_normal; - } - -void Client::getWMHints() - { - XWMHints *hints = XGetWMHints(qt_xdisplay(), window() ); - input = true; - window_group = None; - urgency = false; - if ( hints ) - { - if( hints->flags & InputHint ) - input = hints->input; - if( hints->flags & WindowGroupHint ) - window_group = hints->window_group; - urgency = ( hints->flags & UrgencyHint ) ? true : false; // true/false needed, it's uint bitfield - XFree( (char*)hints ); - } - checkGroup(); - updateUrgency(); - updateAllowedActions(); // group affects isMinimizable() - } - -void Client::getMotifHints() - { - bool mnoborder, mresize, mmove, mminimize, mmaximize, mclose; - Motif::readFlags( client, mnoborder, mresize, mmove, mminimize, mmaximize, mclose ); - motif_noborder = mnoborder; - if( !hasNETSupport()) // NETWM apps should set type and size constraints - { - motif_may_resize = mresize; // this should be set using minsize==maxsize, but oh well - motif_may_move = mmove; - } - else - motif_may_resize = motif_may_move = true; - // mminimize; - ignore, bogus - e.g. shading or sending to another desktop is "minimizing" too - // mmaximize; - ignore, bogus - maximizing is basically just resizing - motif_may_close = mclose; // motif apps like to crash when they set this hint and WM closes them anyway - if( isManaged()) - updateDecoration( true ); // check if noborder state has changed - } - -void Client::readIcons( Window win, TQPixmap* icon, TQPixmap* miniicon ) - { - // get the icons, allow scaling - if( icon != NULL ) - *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints ); - if( miniicon != NULL ) - if( icon == NULL || !icon->isNull()) - *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints ); - else - *miniicon = TQPixmap(); - } - -void Client::getIcons() - { - // first read icons from the window itself - readIcons( window(), &icon_pix, &miniicon_pix ); - if( icon_pix.isNull()) - { // then try window group - icon_pix = group()->icon(); - miniicon_pix = group()->miniIcon(); - } - if( icon_pix.isNull() && isTransient()) - { // then mainclients - ClientList mainclients = mainClients(); - for( ClientList::ConstIterator it = mainclients.begin(); - it != mainclients.end() && icon_pix.isNull(); - ++it ) - { - icon_pix = (*it)->icon(); - miniicon_pix = (*it)->miniIcon(); - } - } - if( icon_pix.isNull()) - { // and if nothing else, load icon from classhint or xapp icon - icon_pix = KWin::icon( window(), 32, 32, TRUE, KWin::ClassHint | KWin::XApp ); - miniicon_pix = KWin::icon( window(), 16, 16, TRUE, KWin::ClassHint | KWin::XApp ); - } - if( isManaged() && decoration != NULL ) - decoration->iconChange(); - } - -void Client::getWindowProtocols() - { - Atom *p; - int i,n; - - Pdeletewindow = 0; - Ptakefocus = 0; - Ptakeactivity = 0; - Pcontexthelp = 0; - Pping = 0; - - if (XGetWMProtocols(qt_xdisplay(), window(), &p, &n)) - { - for (i = 0; i < n; i++) - if (p[i] == atoms->wm_delete_window) - Pdeletewindow = 1; - else if (p[i] == atoms->wm_take_focus) - Ptakefocus = 1; - else if (p[i] == atoms->net_wm_take_activity) - Ptakeactivity = 1; - else if (p[i] == atoms->net_wm_context_help) - Pcontexthelp = 1; - else if (p[i] == atoms->net_wm_ping) - Pping = 1; - if (n>0) - XFree(p); - } - } - -static int nullErrorHandler(Display *, XErrorEvent *) - { - return 0; - } - -/*! - Returns WM_WINDOW_ROLE property for a given window. - */ -TQCString Client::staticWindowRole(WId w) - { - return getStringProperty(w, qt_window_role).lower(); - } - -/*! - Returns SM_CLIENT_ID property for a given window. - */ -TQCString Client::staticSessionId(WId w) - { - return getStringProperty(w, qt_sm_client_id); - } - -/*! - Returns WM_COMMAND property for a given window. - */ -TQCString 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( qt_xdisplay(), 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. - */ -TQCString Client::sessionId() - { - TQCString 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. - */ -TQCString Client::wmCommand() - { - TQCString 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. -*/ -TQCString Client::wmClientMachine( bool use_localhost ) const - { - TQCString 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() && !skip_taskbar; - } - - -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"; // TQCString has no startsWith() - // oo_prefix is lowercase, because resourceClass() is forced to be lowercase - if( tqstrncmp( 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 tqshape for the logical mouse position \a m - - */ -void Client::setCursor( Position m ) - { - if( !isResizable() || isShade()) - { - m = PositionCenter; - } - switch ( m ) - { - case PositionTopLeft: - case PositionBottomRight: - setCursor( tqsizeFDiagCursor ); - break; - case PositionBottomLeft: - case PositionTopRight: - setCursor( tqsizeBDiagCursor ); - break; - case PositionTop: - case PositionBottom: - setCursor( tqsizeVerCursor ); - break; - case PositionLeft: - case PositionRight: - setCursor( tqsizeHorCursor ); - break; - default: - if( buttonDown && isMovable()) - setCursor( tqsizeAllCursor ); - else - setCursor( tqarrowCursor ); - break; - } - } - -// TODO mit nejake checkCursor(), ktere se zavola v manage() a pri vecech, kdy by se kurzor mohl zmenit? -void Client::setCursor( const TQCursor& c ) - { - if( c.handle() == cursor.handle()) - return; - cursor = c; - if( decoration != NULL ) - decoration->widget()->setCursor( cursor ); - XDefineCursor( qt_xdisplay(), frameId(), cursor.handle()); - } - -Client::Position Client::mousePosition( const TQPoint& p ) const - { - if( decoration != NULL ) - return decoration->mousePosition( p ); - return PositionCenter; - } - -void Client::updateAllowedActions( bool force ) - { - if( !isManaged() && !force ) - return; - unsigned long old_allowed_actions = allowed_actions; - allowed_actions = 0; - if( isMovable()) - allowed_actions |= NET::ActionMove; - if( isResizable()) - allowed_actions |= NET::ActionResize; - if( isMinimizable()) - allowed_actions |= NET::ActionMinimize; - if( isShadeable()) - allowed_actions |= NET::ActionShade; - // sticky state not supported - if( isMaximizable()) - allowed_actions |= NET::ActionMax; - if( userCanSetFullScreen()) - allowed_actions |= NET::ActionFullScreen; - allowed_actions |= NET::ActionChangeDesktop; // always (pagers shouldn't show Docks etc.) - if( isCloseable()) - allowed_actions |= NET::ActionClose; - if( old_allowed_actions == allowed_actions ) - return; - // TODO this could be delayed and compressed - it's only for pagers etc. anyway - info->setAllowedActions( allowed_actions ); - // TODO this should also tell the decoration, so that it can update the buttons - } - -void Client::autoRaise() - { - workspace()->raiseClient( this ); - cancelAutoRaise(); - } - -void Client::cancelAutoRaise() - { - delete autoRaiseTimer; - autoRaiseTimer = 0; - } - -void Client::setOpacity(bool translucent, uint opacity) - { - if (isDesktop()) - return; // xcompmgr does not like non solid desktops and the user could set it accidently by mouse scrolling -// qWarning("setting opacity for %d",qt_xdisplay()); - //rule out activated translulcency with 100% opacity - if (!translucent || opacity == 0xFFFFFFFF) - { - opacity_ = 0xFFFFFFFF; - XDeleteProperty (qt_xdisplay(), frameId(), atoms->net_wm_window_opacity); - XDeleteProperty (qt_xdisplay(), window(), atoms->net_wm_window_opacity); // ??? frameId() is necessary for visible changes, window() is the winId() that would be set by apps - we set both to be sure the app knows what's currently displayd - } - else{ - if(opacity == opacity_) - return; - opacity_ = opacity; - long data = opacity; // 32bit XChangeProperty needs long - XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); - XChangeProperty(qt_xdisplay(), window(), atoms->net_wm_window_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); - } - } - -void Client::setShadowSize(uint shadowSize) - { - // ignoring all individual settings - if we control a window, we control it's shadow - // TODO somehow handle individual settings for docks (besides custom sizes) - long data = shadowSize; - XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shadow, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); - } - -void Client::updateOpacity() -// extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows) - { - if (!(isNormalWindow() || isDialog() || isUtility() )|| custom_opacity) - return; - if (isActive()) - { - if( ruleOpacityActive() ) - setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active); - else - setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); - if (isBMP()) - // beep-media-player, only undecorated windows (gtk2 xmms, xmms doesn't work with compmgr at all - s.e.p. :P ) - { - ClientList tmpGroupMembers = group()->members(); - ClientList activeGroupMembers; - activeGroupMembers.append(this); - tmpGroupMembers.remove(this); - ClientList::Iterator it = tmpGroupMembers.begin(); - while (it != tmpGroupMembers.end()) - // search for next attached and not activated client and repeat if found - { - if ((*it) != this && (*it)->isBMP()) - // potential "to activate" client found - { -// qWarning("client found"); - if ((*it)->touches(this)) // first test, if the new client touches the just activated one - { -// qWarning("found client touches me"); - if( ruleOpacityActive() ) - (*it)->setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active); - else - (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); -// qWarning("activated, search restarted (1)"); - (*it)->setShadowSize(options->activeWindowShadowSize); - activeGroupMembers.append(*it); - tmpGroupMembers.remove(it); - it = tmpGroupMembers.begin(); // restart, search next client - continue; - } - else - { // pot. client does not touch c, so we have to search if it touches some other activated client - bool found = false; - for( ClientList::ConstIterator it2 = activeGroupMembers.begin(); it2 != activeGroupMembers.end(); it2++ ) - { - if ((*it2) != this && (*it2) != (*it) && (*it)->touches(*it2)) - { -// qWarning("found client touches other active client"); - if( ruleOpacityActive() ) - (*it)->setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active); - else - (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); - (*it)->setShadowSize(options->activeWindowShadowSize); - activeGroupMembers.append(*it); - tmpGroupMembers.remove(it); - it = tmpGroupMembers.begin(); // reset potential client search - found = true; -// qWarning("activated, search restarted (2)"); - break; // skip this loop - } - } - if (found) continue; - } - } - it++; - } - } - else if (isNormalWindow()) - // activate dependend minor windows as well - { - for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ ) - if ((*it)->isDialog() || (*it)->isUtility()) - if( (*it)->ruleOpacityActive() ) - (*it)->setOpacity((*it)->ruleOpacityActive() < 0xFFFFFFFF, (*it)->ruleOpacityActive()); - else - (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity); - } - } - else - { - if( ruleOpacityInactive() ) - setOpacity(rule_opacity_inactive < 0xFFFFFFFF, rule_opacity_inactive); - else - setOpacity(options->translucentInactiveWindows && !(keepAbove() && options->keepAboveAsActive), - options->inactiveWindowOpacity); - // deactivate dependend minor windows as well - if (isBMP()) - // beep-media-player, only undecorated windows (gtk2 xmms, xmms doesn't work with compmgr at all - s.e.p. :P ) - { - ClientList tmpGroupMembers = group()->members(); - ClientList inactiveGroupMembers; - inactiveGroupMembers.append(this); - tmpGroupMembers.remove(this); - ClientList::Iterator it = tmpGroupMembers.begin(); - while ( it != tmpGroupMembers.end() ) - // search for next attached and not activated client and repeat if found - { - if ((*it) != this && (*it)->isBMP()) - // potential "to activate" client found - { -// qWarning("client found"); - if ((*it)->touches(this)) // first test, if the new client touches the just activated one - { -// qWarning("found client touches me"); - if( (*it)->ruleOpacityInactive() ) - (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive()); - else - (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity); - (*it)->setShadowSize(options->inactiveWindowShadowSize); -// qWarning("deactivated, search restarted (1)"); - inactiveGroupMembers.append(*it); - tmpGroupMembers.remove(it); - it = tmpGroupMembers.begin(); // restart, search next client - continue; - } - else // pot. client does not touch c, so we have to search if it touches some other activated client - { - bool found = false; - for( ClientList::ConstIterator it2 = inactiveGroupMembers.begin(); it2 != inactiveGroupMembers.end(); it2++ ) - { - if ((*it2) != this && (*it2) != (*it) && (*it)->touches(*it2)) - { -// qWarning("found client touches other inactive client"); - if( (*it)->ruleOpacityInactive() ) - (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive()); - else - (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity); - (*it)->setShadowSize(options->inactiveWindowShadowSize); -// qWarning("deactivated, search restarted (2)"); - inactiveGroupMembers.append(*it); - tmpGroupMembers.remove(it); - it = tmpGroupMembers.begin(); // reset potential client search - found = true; - break; // skip this loop - } - } - if (found) continue; - } - } - it++; - } - } - else if (isNormalWindow()) - { - for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ ) - if ((*it)->isUtility()) //don't deactivate dialogs... - if( (*it)->ruleOpacityInactive() ) - (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive()); - else - (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity); - } - } - } - -void Client::updateShadowSize() -// extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows) - { - if (!(isNormalWindow() || isDialog() || isUtility() )) - return; - if (isActive()) - setShadowSize(options->activeWindowShadowSize); - else - setShadowSize(options->inactiveWindowShadowSize); - } - -uint Client::ruleOpacityInactive() - { - return rule_opacity_inactive;// != 0 ; - } - -uint Client::ruleOpacityActive() - { - return rule_opacity_active;// != 0; - } - -bool Client::getWindowOpacity() //query translucency settings from X, returns true if window opacity is set - { - unsigned char *data = 0; - Atom actual; - int format, result; - unsigned long n, left; - result = XGetWindowProperty(qt_xdisplay(), window(), atoms->net_wm_window_opacity, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, /*(unsigned char **)*/ &data); - if (result == Success && data != None && format == 32 ) - { - opacity_ = *reinterpret_cast< long* >( data ); - custom_opacity = true; -// setOpacity(opacity_ < 0xFFFFFFFF, opacity_); - XFree ((char*)data); - return TRUE; - } - return FALSE; - } - -void Client::setCustomOpacityFlag(bool custom) - { - custom_opacity = custom; - } - -uint Client::opacity() - { - return opacity_; - } - -int Client::opacityPercentage() - { - return int(100*((double)opacity_/0xffffffff)); - } - -bool Client::touches(const Client* c) -// checks if this client borders c, needed to test beep media player window state - { - if (y() == c->y() + c->height()) // this bottom to c - return TRUE; - if (y() + height() == c->y()) // this top to c - return TRUE; - if (x() == c->x() + c->width()) // this right to c - return TRUE; - if (x() + width() == c->x()) // this left to c - return TRUE; - return FALSE; - } - -void Client::setDecoHashProperty(uint topHeight, uint rightWidth, uint bottomHeight, uint leftWidth) -{ - long data = (topHeight < 255 ? topHeight : 255) << 24 | - (rightWidth < 255 ? rightWidth : 255) << 16 | - (bottomHeight < 255 ? bottomHeight : 255) << 8 | - (leftWidth < 255 ? leftWidth : 255); - XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_decohash, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L); -} - -void Client::unsetDecoHashProperty() -{ - XDeleteProperty( qt_xdisplay(), frameId(), atoms->net_wm_window_decohash); -} - -#ifndef NDEBUG -kdbgstream& operator<<( kdbgstream& stream, const Client* cl ) - { - if( cl == NULL ) - return stream << "\'NULL_CLIENT\'"; - return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->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 - -TQPixmap * twin_get_menu_pix_hack() - { - static TQPixmap p; - if ( p.isNull() ) - p = SmallIcon( "bx2" ); - return &p; - } - -} // namespace - -#include "client.moc" |