291 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			291 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
#include "breezesizegrip.h"
 | 
						|
 | 
						|
#include <KDecoration2/DecoratedClient>
 | 
						|
 | 
						|
#include <QPainter>
 | 
						|
#include <QPolygon>
 | 
						|
#include <QTimer>
 | 
						|
 | 
						|
#if BREEZE_HAVE_X11
 | 
						|
#include <QX11Info>
 | 
						|
#endif
 | 
						|
 | 
						|
namespace Breeze
 | 
						|
{
 | 
						|
 | 
						|
    //* scoped pointer convenience typedef
 | 
						|
    template <typename T> using ScopedPointer = QScopedPointer<T, QScopedPointerPodDeleter>;
 | 
						|
 | 
						|
    //_____________________________________________
 | 
						|
    SizeGrip::SizeGrip( Decoration* decoration ):QWidget(nullptr)
 | 
						|
        ,m_decoration( decoration )
 | 
						|
    {
 | 
						|
 | 
						|
        setAttribute(Qt::WA_NoSystemBackground );
 | 
						|
        setAutoFillBackground( false );
 | 
						|
 | 
						|
        // cursor
 | 
						|
        setCursor( Qt::SizeFDiagCursor );
 | 
						|
 | 
						|
        // size
 | 
						|
        setFixedSize( QSize( GripSize, GripSize ) );
 | 
						|
 | 
						|
        // mask
 | 
						|
        setMask( QRegion( QVector<QPoint>{
 | 
						|
            QPoint( 0, GripSize ),
 | 
						|
            QPoint( GripSize, 0 ),
 | 
						|
            QPoint( GripSize, GripSize ),
 | 
						|
            QPoint( 0, GripSize )} ) );
 | 
						|
 | 
						|
        // embed
 | 
						|
        embed();
 | 
						|
        updatePosition();
 | 
						|
 | 
						|
        // connections
 | 
						|
        auto c = decoration->client().data();
 | 
						|
        connect( c, &KDecoration2::DecoratedClient::widthChanged, this, &SizeGrip::updatePosition );
 | 
						|
        connect( c, &KDecoration2::DecoratedClient::heightChanged, this, &SizeGrip::updatePosition );
 | 
						|
        connect( c, &KDecoration2::DecoratedClient::activeChanged, this, &SizeGrip::updateActiveState );
 | 
						|
 | 
						|
        // show
 | 
						|
        show();
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //_____________________________________________
 | 
						|
    SizeGrip::~SizeGrip()
 | 
						|
    {}
 | 
						|
 | 
						|
    //_____________________________________________
 | 
						|
    void SizeGrip::updateActiveState()
 | 
						|
    {
 | 
						|
        #if BREEZE_HAVE_X11
 | 
						|
        if( QX11Info::isPlatformX11() )
 | 
						|
        {
 | 
						|
            const quint32 value = XCB_STACK_MODE_ABOVE;
 | 
						|
            xcb_configure_window( QX11Info::connection(), winId(), XCB_CONFIG_WINDOW_STACK_MODE, &value );
 | 
						|
            xcb_map_window( QX11Info::connection(), winId() );
 | 
						|
        }
 | 
						|
        #endif
 | 
						|
 | 
						|
        update();
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //_____________________________________________
 | 
						|
    void SizeGrip::embed()
 | 
						|
    {
 | 
						|
 | 
						|
        #if BREEZE_HAVE_X11
 | 
						|
 | 
						|
        if( !QX11Info::isPlatformX11() ) return;
 | 
						|
        auto c = m_decoration.data()->client().data();
 | 
						|
 | 
						|
        xcb_window_t windowId = c->windowId();
 | 
						|
        if( windowId )
 | 
						|
        {
 | 
						|
 | 
						|
            /*
 | 
						|
            find client's parent
 | 
						|
            we want the size grip to be at the same level as the client in the stack
 | 
						|
            */
 | 
						|
            xcb_window_t current = windowId;
 | 
						|
            auto connection = QX11Info::connection();
 | 
						|
            xcb_query_tree_cookie_t cookie = xcb_query_tree_unchecked( connection, current );
 | 
						|
            ScopedPointer<xcb_query_tree_reply_t> tree(xcb_query_tree_reply( connection, cookie, nullptr ) );
 | 
						|
            if( !tree.isNull() && tree->parent ) current = tree->parent;
 | 
						|
 | 
						|
            // reparent
 | 
						|
            xcb_reparent_window( connection, winId(), current, 0, 0 );
 | 
						|
            setWindowTitle( "Breeze::SizeGrip" );
 | 
						|
 | 
						|
        } else {
 | 
						|
 | 
						|
            hide();
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        #endif
 | 
						|
    }
 | 
						|
 | 
						|
    //_____________________________________________
 | 
						|
    void SizeGrip::paintEvent( QPaintEvent* )
 | 
						|
    {
 | 
						|
 | 
						|
        if( !m_decoration ) return;
 | 
						|
 | 
						|
        // get relevant colors
 | 
						|
        const QColor backgroundColor( m_decoration.data()->titleBarColor() );
 | 
						|
 | 
						|
        // create and configure painter
 | 
						|
        QPainter painter(this);
 | 
						|
        painter.setRenderHints(QPainter::Antialiasing );
 | 
						|
 | 
						|
        painter.setPen( Qt::NoPen );
 | 
						|
        painter.setBrush( backgroundColor );
 | 
						|
 | 
						|
        // polygon
 | 
						|
        painter.drawPolygon( QVector<QPoint> {
 | 
						|
            QPoint( 0, GripSize ),
 | 
						|
            QPoint( GripSize, 0 ),
 | 
						|
            QPoint( GripSize, GripSize ),
 | 
						|
            QPoint( 0, GripSize )} );
 | 
						|
    }
 | 
						|
 | 
						|
    //_____________________________________________
 | 
						|
    void SizeGrip::mousePressEvent( QMouseEvent* event )
 | 
						|
    {
 | 
						|
 | 
						|
        switch (event->button())
 | 
						|
        {
 | 
						|
 | 
						|
            case Qt::RightButton:
 | 
						|
            {
 | 
						|
                hide();
 | 
						|
                QTimer::singleShot(5000, this, &QWidget::show);
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            case Qt::MiddleButton:
 | 
						|
            {
 | 
						|
                hide();
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            case Qt::LeftButton:
 | 
						|
            if( rect().contains( event->pos() ) )
 | 
						|
            { sendMoveResizeEvent( event->pos() ); }
 | 
						|
            break;
 | 
						|
 | 
						|
            default: break;
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        
 | 
						|
    }
 | 
						|
 | 
						|
    //_______________________________________________________________________________
 | 
						|
    void SizeGrip::updatePosition()
 | 
						|
    {
 | 
						|
 | 
						|
        #if BREEZE_HAVE_X11
 | 
						|
        if( !QX11Info::isPlatformX11() ) return;
 | 
						|
 | 
						|
        auto c = m_decoration.data()->client().data();
 | 
						|
        QPoint position(
 | 
						|
            c->width() - GripSize - Offset,
 | 
						|
            c->height() - GripSize - Offset );
 | 
						|
 | 
						|
        quint32 values[2] = { quint32(position.x()), quint32(position.y()) };
 | 
						|
        xcb_configure_window( QX11Info::connection(), winId(), XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values );
 | 
						|
        #endif
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //_____________________________________________
 | 
						|
    void SizeGrip::sendMoveResizeEvent( QPoint position )
 | 
						|
    {
 | 
						|
 | 
						|
        #if BREEZE_HAVE_X11
 | 
						|
        if( !QX11Info::isPlatformX11() ) return;
 | 
						|
 | 
						|
        // pointer to connection
 | 
						|
        auto connection( QX11Info::connection() );
 | 
						|
 | 
						|
        // client
 | 
						|
        auto c = m_decoration.data()->client().data();
 | 
						|
 | 
						|
        /*
 | 
						|
        get root position matching position
 | 
						|
        need to use xcb because the embedding of the widget
 | 
						|
        breaks QT's mapToGlobal and other methods
 | 
						|
        */
 | 
						|
        QPoint rootPosition( position );
 | 
						|
        xcb_get_geometry_cookie_t cookie( xcb_get_geometry( connection, winId() ) );
 | 
						|
        ScopedPointer<xcb_get_geometry_reply_t> reply( xcb_get_geometry_reply( connection, cookie, nullptr ) );
 | 
						|
        if( reply )
 | 
						|
        {
 | 
						|
 | 
						|
            // translate coordinates
 | 
						|
            xcb_translate_coordinates_cookie_t coordCookie( xcb_translate_coordinates(
 | 
						|
                connection, winId(), reply.data()->root,
 | 
						|
                -reply.data()->border_width,
 | 
						|
                -reply.data()->border_width ) );
 | 
						|
 | 
						|
            ScopedPointer< xcb_translate_coordinates_reply_t> coordReply( xcb_translate_coordinates_reply( connection, coordCookie, nullptr ) );
 | 
						|
 | 
						|
            if( coordReply )
 | 
						|
            {
 | 
						|
                rootPosition.rx() += coordReply.data()->dst_x;
 | 
						|
                rootPosition.ry() += coordReply.data()->dst_y;
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        // move/resize atom
 | 
						|
        if( !m_moveResizeAtom )
 | 
						|
        {
 | 
						|
 | 
						|
            // create atom if not found
 | 
						|
            const QString atomName( "_NET_WM_MOVERESIZE" );
 | 
						|
            xcb_intern_atom_cookie_t cookie( xcb_intern_atom( connection, false, atomName.size(), qPrintable( atomName ) ) );
 | 
						|
            ScopedPointer<xcb_intern_atom_reply_t> reply( xcb_intern_atom_reply( connection, cookie, nullptr ) );
 | 
						|
            m_moveResizeAtom = reply ? reply->atom:0;
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        if( !m_moveResizeAtom ) return;
 | 
						|
 | 
						|
        // button release event
 | 
						|
        xcb_button_release_event_t releaseEvent;
 | 
						|
        memset(&releaseEvent, 0, sizeof(releaseEvent));
 | 
						|
 | 
						|
        releaseEvent.response_type = XCB_BUTTON_RELEASE;
 | 
						|
        releaseEvent.event =  winId();
 | 
						|
        releaseEvent.child = XCB_WINDOW_NONE;
 | 
						|
        releaseEvent.root = QX11Info::appRootWindow();
 | 
						|
        releaseEvent.event_x = position.x();
 | 
						|
        releaseEvent.event_y = position.y();
 | 
						|
        releaseEvent.root_x = rootPosition.x();
 | 
						|
        releaseEvent.root_y = rootPosition.y();
 | 
						|
        releaseEvent.detail = XCB_BUTTON_INDEX_1;
 | 
						|
        releaseEvent.state = XCB_BUTTON_MASK_1;
 | 
						|
        releaseEvent.time = XCB_CURRENT_TIME;
 | 
						|
        releaseEvent.same_screen = true;
 | 
						|
        xcb_send_event( connection, false, winId(), XCB_EVENT_MASK_BUTTON_RELEASE, reinterpret_cast<const char*>(&releaseEvent));
 | 
						|
 | 
						|
        xcb_ungrab_pointer( connection, XCB_TIME_CURRENT_TIME );
 | 
						|
 | 
						|
        // move resize event
 | 
						|
        xcb_client_message_event_t clientMessageEvent;
 | 
						|
        memset(&clientMessageEvent, 0, sizeof(clientMessageEvent));
 | 
						|
 | 
						|
        clientMessageEvent.response_type = XCB_CLIENT_MESSAGE;
 | 
						|
        clientMessageEvent.type = m_moveResizeAtom;
 | 
						|
        clientMessageEvent.format = 32;
 | 
						|
        clientMessageEvent.window = c->windowId();
 | 
						|
        clientMessageEvent.data.data32[0] = rootPosition.x();
 | 
						|
        clientMessageEvent.data.data32[1] = rootPosition.y();
 | 
						|
        clientMessageEvent.data.data32[2] = 4; // bottom right
 | 
						|
        clientMessageEvent.data.data32[3] = Qt::LeftButton;
 | 
						|
        clientMessageEvent.data.data32[4] = 0;
 | 
						|
 | 
						|
        xcb_send_event( connection, false, QX11Info::appRootWindow(),
 | 
						|
            XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
 | 
						|
            XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
 | 
						|
            reinterpret_cast<const char*>(&clientMessageEvent) );
 | 
						|
 | 
						|
        xcb_flush( connection );
 | 
						|
        #endif
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
}
 |