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