Warning, file /plasma/oxygen/kdecoration/oxygensizegrip.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "oxygensizegrip.h"
0008 
0009 #include <KDecoration2/DecoratedClient>
0010 
0011 #include <QPainter>
0012 #include <QPalette>
0013 #include <QPolygon>
0014 #include <QTimer>
0015 
0016 #if OXYGEN_HAVE_X11
0017 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0018 #include <QX11Info>
0019 #else
0020 #include <private/qtx11extras_p.h>
0021 #endif
0022 #endif
0023 
0024 namespace Oxygen
0025 {
0026 
0027 //* scoped pointer convenience typedef
0028 template<typename T>
0029 using ScopedPointer = QScopedPointer<T, QScopedPointerPodDeleter>;
0030 
0031 //_____________________________________________
0032 SizeGrip::SizeGrip(Decoration *decoration)
0033     : QWidget(nullptr)
0034     , m_decoration(decoration)
0035 {
0036     setAttribute(Qt::WA_NoSystemBackground);
0037     setAutoFillBackground(false);
0038 
0039     // cursor
0040     setCursor(Qt::SizeFDiagCursor);
0041 
0042     // size
0043     setFixedSize(QSize(GripSize, GripSize));
0044 
0045     // mask
0046     QPolygon p;
0047     p << QPoint(0, GripSize) << QPoint(GripSize, 0) << QPoint(GripSize, GripSize) << QPoint(0, GripSize);
0048 
0049     setMask(QRegion(p));
0050 
0051     // embed
0052     embed();
0053     updatePosition();
0054 
0055     // connections
0056     const auto *clientP = decoration->client().toStrongRef().data();
0057     connect(clientP, &KDecoration2::DecoratedClient::widthChanged, this, &SizeGrip::updatePosition);
0058     connect(clientP, &KDecoration2::DecoratedClient::heightChanged, this, &SizeGrip::updatePosition);
0059     connect(clientP, &KDecoration2::DecoratedClient::activeChanged, this, &SizeGrip::updateActiveState);
0060 
0061     // show
0062     show();
0063 }
0064 
0065 //_____________________________________________
0066 void SizeGrip::updateActiveState(void)
0067 {
0068 #if OXYGEN_HAVE_X11
0069     if (QX11Info::isPlatformX11()) {
0070         const quint32 value = XCB_STACK_MODE_ABOVE;
0071         xcb_configure_window(QX11Info::connection(), winId(), XCB_CONFIG_WINDOW_STACK_MODE, &value);
0072         xcb_map_window(QX11Info::connection(), winId());
0073     }
0074 #endif
0075 
0076     update();
0077 }
0078 
0079 //_____________________________________________
0080 void SizeGrip::embed(void)
0081 {
0082 #if OXYGEN_HAVE_X11
0083 
0084     if (!QX11Info::isPlatformX11())
0085         return;
0086 
0087     xcb_window_t windowId = m_decoration->client().toStrongRef()->windowId();
0088     if (windowId) {
0089         /*
0090         find client's parent
0091         we want the size grip to be at the same level as the client in the stack
0092         */
0093         xcb_window_t current = windowId;
0094         auto connection = QX11Info::connection();
0095         xcb_query_tree_cookie_t cookie = xcb_query_tree_unchecked(connection, current);
0096         ScopedPointer<xcb_query_tree_reply_t> tree(xcb_query_tree_reply(connection, cookie, nullptr));
0097         if (!tree.isNull() && tree->parent)
0098             current = tree->parent;
0099 
0100         // reparent
0101         xcb_reparent_window(connection, winId(), current, 0, 0);
0102         setWindowTitle(QStringLiteral("Oxygen::SizeGrip"));
0103 
0104     } else {
0105         hide();
0106     }
0107 
0108 #endif
0109 }
0110 
0111 //_____________________________________________
0112 void SizeGrip::paintEvent(QPaintEvent *)
0113 {
0114     if (!m_decoration)
0115         return;
0116 
0117     // get relevant colors
0118     const QColor backgroundColor(m_decoration->client().toStrongRef()->palette().color(QPalette::Window));
0119 
0120     // create and configure painter
0121     QPainter painter(this);
0122     painter.setRenderHints(QPainter::Antialiasing);
0123 
0124     painter.setPen(Qt::NoPen);
0125     painter.setBrush(backgroundColor);
0126 
0127     // polygon
0128     QPolygon p;
0129     p << QPoint(0, GripSize) << QPoint(GripSize, 0) << QPoint(GripSize, GripSize) << QPoint(0, GripSize);
0130     painter.drawPolygon(p);
0131 }
0132 
0133 //_____________________________________________
0134 void SizeGrip::mousePressEvent(QMouseEvent *event)
0135 {
0136     switch (event->button()) {
0137     case Qt::RightButton: {
0138         hide();
0139         QTimer::singleShot(5000, this, SLOT(show()));
0140         break;
0141     }
0142 
0143     case Qt::MiddleButton: {
0144         hide();
0145         break;
0146     }
0147 
0148     case Qt::LeftButton:
0149         if (rect().contains(event->pos())) {
0150             sendMoveResizeEvent(event->pos());
0151         }
0152         break;
0153 
0154     default:
0155         break;
0156     }
0157 
0158     return;
0159 }
0160 
0161 //_______________________________________________________________________________
0162 void SizeGrip::updatePosition(void)
0163 {
0164 #if OXYGEN_HAVE_X11
0165     if (!QX11Info::isPlatformX11())
0166         return;
0167 
0168     const auto c = m_decoration->client().toStrongRef();
0169     QPoint position(c->width() - GripSize - Offset, c->height() - GripSize - Offset);
0170 
0171     quint32 values[2] = {quint32(position.x()), quint32(position.y())};
0172     xcb_configure_window(QX11Info::connection(), winId(), XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values);
0173 #endif
0174 }
0175 
0176 //_____________________________________________
0177 void SizeGrip::sendMoveResizeEvent(QPoint position)
0178 {
0179 #if OXYGEN_HAVE_X11
0180     if (!QX11Info::isPlatformX11())
0181         return;
0182 
0183     // pointer to connection
0184     auto connection(QX11Info::connection());
0185 
0186     /*
0187     get root position matching position
0188     need to use xcb because the embedding of the widget
0189     breaks QT's mapToGlobal and other methods
0190     */
0191     QPoint rootPosition(position);
0192     xcb_get_geometry_cookie_t cookie(xcb_get_geometry(connection, winId()));
0193     ScopedPointer<xcb_get_geometry_reply_t> reply(xcb_get_geometry_reply(connection, cookie, nullptr));
0194     if (reply) {
0195         // translate coordinates
0196         xcb_translate_coordinates_cookie_t coordCookie(
0197             xcb_translate_coordinates(connection, winId(), reply.data()->root, -reply.data()->border_width, -reply.data()->border_width));
0198 
0199         ScopedPointer<xcb_translate_coordinates_reply_t> coordReply(xcb_translate_coordinates_reply(connection, coordCookie, nullptr));
0200 
0201         if (coordReply) {
0202             rootPosition.rx() += coordReply.data()->dst_x;
0203             rootPosition.ry() += coordReply.data()->dst_y;
0204         }
0205     }
0206 
0207     // move/resize atom
0208     if (!m_moveResizeAtom) {
0209         // create atom if not found
0210         const QString atomName = QStringLiteral("_NET_WM_MOVERESIZE");
0211         xcb_intern_atom_cookie_t cookie(xcb_intern_atom(connection, false, atomName.size(), qPrintable(atomName)));
0212         ScopedPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(connection, cookie, nullptr));
0213         m_moveResizeAtom = reply ? reply->atom : 0;
0214     }
0215 
0216     if (!m_moveResizeAtom)
0217         return;
0218 
0219     // button release event
0220     xcb_button_release_event_t releaseEvent;
0221     memset(&releaseEvent, 0, sizeof(releaseEvent));
0222 
0223     releaseEvent.response_type = XCB_BUTTON_RELEASE;
0224     releaseEvent.event = winId();
0225     releaseEvent.child = XCB_WINDOW_NONE;
0226     releaseEvent.root = QX11Info::appRootWindow();
0227     releaseEvent.event_x = position.x();
0228     releaseEvent.event_y = position.y();
0229     releaseEvent.root_x = rootPosition.x();
0230     releaseEvent.root_y = rootPosition.y();
0231     releaseEvent.detail = XCB_BUTTON_INDEX_1;
0232     releaseEvent.state = XCB_BUTTON_MASK_1;
0233     releaseEvent.time = XCB_CURRENT_TIME;
0234     releaseEvent.same_screen = true;
0235     xcb_send_event(connection, false, winId(), XCB_EVENT_MASK_BUTTON_RELEASE, reinterpret_cast<const char *>(&releaseEvent));
0236 
0237     xcb_ungrab_pointer(connection, XCB_TIME_CURRENT_TIME);
0238 
0239     // move resize event
0240     xcb_client_message_event_t clientMessageEvent;
0241     memset(&clientMessageEvent, 0, sizeof(clientMessageEvent));
0242 
0243     clientMessageEvent.response_type = XCB_CLIENT_MESSAGE;
0244     clientMessageEvent.type = m_moveResizeAtom;
0245     clientMessageEvent.format = 32;
0246     clientMessageEvent.window = m_decoration->client().toStrongRef()->windowId();
0247     clientMessageEvent.data.data32[0] = rootPosition.x();
0248     clientMessageEvent.data.data32[1] = rootPosition.y();
0249     clientMessageEvent.data.data32[2] = 4; // bottom right
0250     clientMessageEvent.data.data32[3] = Qt::LeftButton;
0251     clientMessageEvent.data.data32[4] = 0;
0252 
0253     xcb_send_event(connection,
0254                    false,
0255                    QX11Info::appRootWindow(),
0256                    XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
0257                    reinterpret_cast<const char *>(&clientMessageEvent));
0258 
0259     xcb_flush(connection);
0260 #endif
0261 }
0262 }