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 }