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 }