diff --git a/windec/kdecoration2/CMakeLists.txt b/windec/kdecoration2/CMakeLists.txt index 6fffcf66..1921b368 100644 --- a/windec/kdecoration2/CMakeLists.txt +++ b/windec/kdecoration2/CMakeLists.txt @@ -1,12 +1,39 @@ find_package(KDecoration2 REQUIRED) find_package(KF5 REQUIRED COMPONENTS CoreAddons ConfigWidgets) -find_package(Qt5 CONFIG REQUIRED COMPONENTS DBus ) +find_package(Qt5 CONFIG REQUIRED COMPONENTS DBus) +### XCB +find_package(XCB COMPONENTS XCB) +set_package_properties(XCB PROPERTIES + DESCRIPTION "X protocol C-language Binding" + URL "http://xcb.freedesktop.org" + TYPE OPTIONAL + PURPOSE "Required to pass style properties to native Windows on X11 Platform" +) + +if(UNIX AND NOT APPLE) + + set(BREEZE_HAVE_X11 ${XCB_XCB_FOUND}) + if (XCB_XCB_FOUND) + find_package(Qt5 REQUIRED CONFIG COMPONENTS X11Extras) + endif() + +else() + + set(BREEZE_HAVE_X11 FALSE) + +endif() +################# configuration ################# +configure_file(config-breeze.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-breeze.h ) + +################# newt target ################# set(breezedecoration_SRCS breezecolorsettings.cpp breezebutton.cpp breezedecoration.cpp + breezehelper.cpp + breezesizegrip.cpp config/breezeconfig.cpp config/breezeconfigwidget.cpp) @@ -33,7 +60,14 @@ target_link_libraries(breezedecoration KF5::CoreAddons KF5::ConfigWidgets KF5::GuiAddons - KF5::I18n -) + KF5::I18n) + +if(BREEZE_HAVE_X11) + target_link_libraries(breezedecoration + PUBLIC + Qt5::X11Extras + XCB::XCB) +endif() + install(TARGETS breezedecoration DESTINATION ${PLUGIN_INSTALL_DIR}/org.kde.kdecoration2) diff --git a/windec/kdecoration2/breezedecoration.cpp b/windec/kdecoration2/breezedecoration.cpp index 345e9d9e..b5817260 100644 --- a/windec/kdecoration2/breezedecoration.cpp +++ b/windec/kdecoration2/breezedecoration.cpp @@ -1,5 +1,6 @@ /* * Copyright 2014 Martin Gräßlin +* Copyright 2014 Hugo Pereira Da Costa * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -21,9 +22,13 @@ #include "breezedecoration.h" #include "breeze.h" +#include "breezehelper.h" +#include "config-breeze.h" #include "config/breezeconfig.h" #include "breezebutton.h" +#include "breezesizegrip.h" + #include #include #include @@ -58,6 +63,7 @@ namespace Breeze , m_colorSettings(client().data()->palette()) , m_leftButtons(nullptr) , m_rightButtons(nullptr) + , m_sizeGrip(nullptr) , m_animation( new QPropertyAnimation( this ) ) { g_sDecoCount++; @@ -119,8 +125,7 @@ namespace Breeze m_animation->setPropertyName( "opacity" ); m_animation->setEasingCurve( QEasingCurve::InOutQuad ); - - recalculateBorders(); + reconfigure(); updateTitleBar(); auto s = settings(); connect(s.data(), &KDecoration2::DecorationSettings::borderSizeChanged, this, &Decoration::recalculateBorders); @@ -219,8 +224,17 @@ namespace Breeze void Decoration::reconfigure() { m_internalSettings.read(); + + // animation m_animation->setDuration( m_internalSettings.animationsDuration() ); + + // borders recalculateBorders(); + + // size grip + if( settings()->borderSize() == KDecoration2::BorderSize::None ) createSizeGrip(); + else deleteSizeGrip(); + } //________________________________________________________________ @@ -485,6 +499,39 @@ namespace Breeze setShadow(decorationShadow); } + //_________________________________________________________________ + void Decoration::createSizeGrip( void ) + { + + // do nothing if size grip already exist + if( m_sizeGrip ) return; + + #if BREEZE_HAVE_X11 + if( !Helper::isX11() ) return; + + // access client + KDecoration2::DecoratedClient *c( client().data() ); + if( !c ) return; + + if( ( c->isResizeable() && c->windowId() != 0 ) ) + { + m_sizeGrip = new SizeGrip( this ); + m_sizeGrip->setVisible( !( c->isMaximized() || c->isShaded() ) ); + } + #endif + + } + + //_________________________________________________________________ + void Decoration::deleteSizeGrip( void ) + { + if( m_sizeGrip ) + { + m_sizeGrip->deleteLater(); + m_sizeGrip = nullptr; + } + } + } // namespace diff --git a/windec/kdecoration2/breezedecoration.h b/windec/kdecoration2/breezedecoration.h index 6a7e9ee9..91a49303 100644 --- a/windec/kdecoration2/breezedecoration.h +++ b/windec/kdecoration2/breezedecoration.h @@ -39,6 +39,7 @@ namespace KDecoration2 namespace Breeze { + class SizeGrip; class Decoration : public KDecoration2::Decoration { Q_OBJECT @@ -105,10 +106,21 @@ namespace Breeze private: QRect captionRect() const; + void createButtons(); void paintTitleBar(QPainter *painter, const QRect &repaintRegion); void createShadow(); + //*@name size grip + //@{ + + void createSizeGrip( void ); + void deleteSizeGrip( void ); + SizeGrip* sizeGrip( void ) const + { return m_sizeGrip; } + + //@} + ColorSettings m_colorSettings; InternalSettings m_internalSettings; @@ -116,6 +128,9 @@ namespace Breeze KDecoration2::DecorationButtonGroup *m_leftButtons; KDecoration2::DecorationButtonGroup *m_rightButtons; + //* size grip widget + SizeGrip *m_sizeGrip; + //* active state change animation QPropertyAnimation *m_animation; diff --git a/windec/kdecoration2/breezehelper.cpp b/windec/kdecoration2/breezehelper.cpp new file mode 100644 index 00000000..07f1f96e --- /dev/null +++ b/windec/kdecoration2/breezehelper.cpp @@ -0,0 +1,41 @@ + +/* + * Copyright 2014 Hugo Pereira Da Costa + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "breezehelper.h" +#include "config-breeze.h" + +#include + +namespace Breeze +{ + + //______________________________________________________________________________ + bool Helper::isX11( void ) + { + #if BREEZE_HAVE_X11 + static bool isX11 = QApplication::platformName() == QStringLiteral("xcb"); + return isX11; + #else + return false; + #endif + } + +} diff --git a/windec/kdecoration2/breezehelper.h b/windec/kdecoration2/breezehelper.h new file mode 100644 index 00000000..13ee8403 --- /dev/null +++ b/windec/kdecoration2/breezehelper.h @@ -0,0 +1,38 @@ +#ifndef breezehelper_h +#define breezehelper_h + +/* + * Copyright 2014 Hugo Pereira Da Costa + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace Breeze +{ + class Helper + { + + public: + + //* true if style was compiled for and is running on X11 + static bool isX11( void ); + + }; + +} + +#endif diff --git a/windec/kdecoration2/breezesizegrip.cpp b/windec/kdecoration2/breezesizegrip.cpp new file mode 100644 index 00000000..3dfdfb37 --- /dev/null +++ b/windec/kdecoration2/breezesizegrip.cpp @@ -0,0 +1,317 @@ +/************************************************************************* + * Copyright (C) 2014 by Hugo Pereira Da Costa * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + *************************************************************************/ + + +#include "breezesizegrip.h" +#include "breezesizegrip.moc" + +#include "breezehelper.h" + +#include + +#include +#include +#include + +#if BREEZE_HAVE_X11 +#include +#endif + +namespace Breeze +{ + + //* scoped pointer convenience typedef + template using ScopedPointer = QScopedPointer; + + //_____________________________________________ + SizeGrip::SizeGrip( Decoration* decoration ):QWidget(nullptr) + ,m_decoration( decoration ) + + #if BREEZE_HAVE_X11 + ,m_moveResizeAtom( 0 ) + #endif + + { + + setAttribute(Qt::WA_NoSystemBackground ); + setAutoFillBackground( false ); + + // cursor + setCursor( Qt::SizeFDiagCursor ); + + // size + setFixedSize( QSize( GripSize, GripSize ) ); + + // mask + QPolygon p; + p << QPoint( 0, GripSize ) + << QPoint( GripSize, 0 ) + << QPoint( GripSize, GripSize ) + << QPoint( 0, GripSize ); + + setMask( QRegion( p ) ); + + // embed + embed(); + updatePosition(); + + // connections + KDecoration2::DecoratedClient *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 ) + {} + + //_____________________________________________ + void SizeGrip::updateActiveState( void ) + { + #if BREEZE_HAVE_X11 + if( Helper::isX11() ) + { + 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( void ) + { + + #if BREEZE_HAVE_X11 + + if( !Helper::isX11() ) return; + KDecoration2::DecoratedClient *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; + xcb_connection_t* connection = QX11Info::connection(); + xcb_query_tree_cookie_t cookie = xcb_query_tree_unchecked( connection, current ); + ScopedPointer 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; + KDecoration2::DecoratedClient *c = m_decoration.data()->client().data(); + + // get relevant colors + const QColor backgroundColor( m_decoration.data()->colorSettings().titleBar( c->isActive() ) ); + + // create and configure painter + QPainter painter(this); + painter.setRenderHints(QPainter::Antialiasing ); + + painter.setPen( Qt::NoPen ); + painter.setBrush( backgroundColor ); + + // polygon + QPolygon p; + p << QPoint( 0, GripSize ) + << QPoint( GripSize, 0 ) + << QPoint( GripSize, GripSize ) + << QPoint( 0, GripSize ); + painter.drawPolygon( p ); + + } + + //_____________________________________________ + void SizeGrip::mousePressEvent( QMouseEvent* event ) + { + + switch (event->button()) + { + + case Qt::RightButton: + { + hide(); + QTimer::singleShot(5000, this, SLOT(show())); + break; + } + + case Qt::MidButton: + { + hide(); + break; + } + + case Qt::LeftButton: + if( rect().contains( event->pos() ) ) + { sendMoveResizeEvent( event->pos() ); } + break; + + default: break; + + } + + return; + + } + + //_______________________________________________________________________________ + void SizeGrip::updatePosition( void ) + { + + #if BREEZE_HAVE_X11 + if( !Helper::isX11() ) return; + + KDecoration2::DecoratedClient *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( !Helper::isX11() ) return; + + // pointer to connection + xcb_connection_t* connection( QX11Info::connection() ); + + // client + KDecoration2::DecoratedClient *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 reply( xcb_get_geometry_reply( connection, cookie, 0x0 ) ); + 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, 0x0 ) ); + + 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 reply( xcb_intern_atom_reply( connection, cookie, 0x0 ) ); + 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(&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(&clientMessageEvent) ); + + xcb_flush( connection ); + #endif + + } + +} diff --git a/windec/kdecoration2/breezesizegrip.h b/windec/kdecoration2/breezesizegrip.h new file mode 100644 index 00000000..eb65d6ea --- /dev/null +++ b/windec/kdecoration2/breezesizegrip.h @@ -0,0 +1,100 @@ +#ifndef breezesizegrip_h +#define breezesizegrip_h + +/************************************************************************* + * Copyright (C) 2014 by Hugo Pereira Da Costa * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + *************************************************************************/ + +#include "breezedecoration.h" +#include "config-breeze.h" + +#include +#include +#include +#include + +#if BREEZE_HAVE_X11 +#include +#endif + +namespace Breeze +{ + + //* implements size grip for all widgets + class SizeGrip: public QWidget + { + + Q_OBJECT + + public: + + //* constructor + explicit SizeGrip( Decoration* ); + + //* constructor + virtual ~SizeGrip( void ); + + protected Q_SLOTS: + + //* update background color + void updateActiveState( void ); + + //* update position + void updatePosition( void ); + + //* embed into parent widget + void embed( void ); + + protected: + + //*@name event handlers + //@{ + + //* paint + virtual void paintEvent( QPaintEvent* ) override; + + //* mouse press + virtual void mousePressEvent( QMouseEvent* ) override; + + //@} + + private: + + //* send resize event + void sendMoveResizeEvent( QPoint ); + + //* grip size + enum { + Offset = 0, + GripSize = 14 + }; + + //* decoration + QPointer m_decoration; + + //* move/resize atom + #if BREEZE_HAVE_X11 + xcb_atom_t m_moveResizeAtom; + #endif + + }; + + +} + +#endif diff --git a/windec/kdecoration2/config-breeze.h.cmake b/windec/kdecoration2/config-breeze.h.cmake new file mode 100644 index 00000000..bee0f794 --- /dev/null +++ b/windec/kdecoration2/config-breeze.h.cmake @@ -0,0 +1,28 @@ +/* config-breeze.h. Generated by cmake from config-breeze.h.cmake */ + +/************************************************************************* + * Copyright (C) 2014 by Hugo Pereira Da Costa * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + *************************************************************************/ + +#ifndef config_breeze_h +#define config_breeze_h + +/* Define to 1 if XCB libraries are found */ +#cmakedefine01 BREEZE_HAVE_X11 + +#endif