File indexing completed on 2024-05-19 16:34:57

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
0006     SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 /*
0012 
0013  This file is for (very) small utility functions/classes.
0014 
0015 */
0016 
0017 #include "utils/common.h"
0018 #include "utils/c_ptr.h"
0019 
0020 #include <QPainter>
0021 #include <QWidget>
0022 #include <kkeyserver.h>
0023 
0024 #ifndef KCMRULES
0025 #include <QApplication>
0026 #include <QDebug>
0027 #endif
0028 
0029 Q_LOGGING_CATEGORY(KWIN_CORE, "kwin_core", QtWarningMsg)
0030 Q_LOGGING_CATEGORY(KWIN_OPENGL, "kwin_scene_opengl", QtWarningMsg)
0031 Q_LOGGING_CATEGORY(KWIN_QPAINTER, "kwin_scene_qpainter", QtWarningMsg)
0032 Q_LOGGING_CATEGORY(KWIN_VIRTUALKEYBOARD, "kwin_virtualkeyboard", QtWarningMsg)
0033 namespace KWin
0034 {
0035 
0036 #ifndef KCMRULES
0037 
0038 //************************************
0039 // StrutRect
0040 //************************************
0041 
0042 StrutRect::StrutRect(QRect rect, StrutArea area)
0043     : QRect(rect)
0044     , m_area(area)
0045 {
0046 }
0047 
0048 StrutRect::StrutRect(int x, int y, int width, int height, StrutArea area)
0049     : QRect(x, y, width, height)
0050     , m_area(area)
0051 {
0052 }
0053 
0054 StrutRect::StrutRect(const StrutRect &other)
0055     : QRect(other)
0056     , m_area(other.area())
0057 {
0058 }
0059 
0060 StrutRect &StrutRect::operator=(const StrutRect &other)
0061 {
0062     if (this != &other) {
0063         QRect::operator=(other);
0064         m_area = other.area();
0065     }
0066     return *this;
0067 }
0068 
0069 static int server_grab_count = 0;
0070 
0071 void grabXServer()
0072 {
0073     if (++server_grab_count == 1) {
0074         xcb_grab_server(connection());
0075     }
0076 }
0077 
0078 void ungrabXServer()
0079 {
0080     Q_ASSERT(server_grab_count > 0);
0081     if (--server_grab_count == 0) {
0082         xcb_ungrab_server(connection());
0083         xcb_flush(connection());
0084     }
0085 }
0086 
0087 static bool keyboard_grabbed = false;
0088 
0089 bool grabXKeyboard(xcb_window_t w)
0090 {
0091     if (QWidget::keyboardGrabber() != nullptr) {
0092         return false;
0093     }
0094     if (keyboard_grabbed) {
0095         qCDebug(KWIN_CORE) << "Failed to grab X Keyboard: already grabbed by us";
0096         return false;
0097     }
0098     if (qApp->activePopupWidget() != nullptr) {
0099         qCDebug(KWIN_CORE) << "Failed to grab X Keyboard: no popup widget";
0100         return false;
0101     }
0102     if (w == XCB_WINDOW_NONE) {
0103         w = rootWindow();
0104     }
0105     const xcb_grab_keyboard_cookie_t c = xcb_grab_keyboard_unchecked(connection(), false, w, xTime(),
0106                                                                      XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
0107     UniqueCPtr<xcb_grab_keyboard_reply_t> grab(xcb_grab_keyboard_reply(connection(), c, nullptr));
0108     if (!grab) {
0109         qCDebug(KWIN_CORE) << "Failed to grab X Keyboard: grab null";
0110         return false;
0111     }
0112     if (grab->status != XCB_GRAB_STATUS_SUCCESS) {
0113         qCDebug(KWIN_CORE) << "Failed to grab X Keyboard: grab failed with status" << grab->status;
0114         return false;
0115     }
0116     keyboard_grabbed = true;
0117     return true;
0118 }
0119 
0120 void ungrabXKeyboard()
0121 {
0122     if (!keyboard_grabbed) {
0123         // grabXKeyboard() may fail sometimes, so don't fail, but at least warn anyway
0124         qCDebug(KWIN_CORE) << "ungrabXKeyboard() called but keyboard not grabbed!";
0125     }
0126     keyboard_grabbed = false;
0127     xcb_ungrab_keyboard(connection(), XCB_TIME_CURRENT_TIME);
0128 }
0129 
0130 #endif
0131 
0132 // converting between X11 mouse/keyboard state mask and Qt button/keyboard states
0133 
0134 Qt::MouseButton x11ToQtMouseButton(int button)
0135 {
0136     if (button == XCB_BUTTON_INDEX_1) {
0137         return Qt::LeftButton;
0138     }
0139     if (button == XCB_BUTTON_INDEX_2) {
0140         return Qt::MiddleButton;
0141     }
0142     if (button == XCB_BUTTON_INDEX_3) {
0143         return Qt::RightButton;
0144     }
0145     if (button == XCB_BUTTON_INDEX_4) {
0146         return Qt::XButton1;
0147     }
0148     if (button == XCB_BUTTON_INDEX_5) {
0149         return Qt::XButton2;
0150     }
0151     return Qt::NoButton;
0152 }
0153 
0154 Qt::MouseButtons x11ToQtMouseButtons(int state)
0155 {
0156     Qt::MouseButtons ret = {};
0157     if (state & XCB_KEY_BUT_MASK_BUTTON_1) {
0158         ret |= Qt::LeftButton;
0159     }
0160     if (state & XCB_KEY_BUT_MASK_BUTTON_2) {
0161         ret |= Qt::MiddleButton;
0162     }
0163     if (state & XCB_KEY_BUT_MASK_BUTTON_3) {
0164         ret |= Qt::RightButton;
0165     }
0166     if (state & XCB_KEY_BUT_MASK_BUTTON_4) {
0167         ret |= Qt::XButton1;
0168     }
0169     if (state & XCB_KEY_BUT_MASK_BUTTON_5) {
0170         ret |= Qt::XButton2;
0171     }
0172     return ret;
0173 }
0174 
0175 Qt::KeyboardModifiers x11ToQtKeyboardModifiers(int state)
0176 {
0177     Qt::KeyboardModifiers ret = {};
0178     if (state & XCB_KEY_BUT_MASK_SHIFT) {
0179         ret |= Qt::ShiftModifier;
0180     }
0181     if (state & XCB_KEY_BUT_MASK_CONTROL) {
0182         ret |= Qt::ControlModifier;
0183     }
0184     if (state & KKeyServer::modXAlt()) {
0185         ret |= Qt::AltModifier;
0186     }
0187     if (state & KKeyServer::modXMeta()) {
0188         ret |= Qt::MetaModifier;
0189     }
0190     return ret;
0191 }
0192 
0193 QPointF popupOffset(const QRectF &anchorRect, const Qt::Edges anchorEdge, const Qt::Edges gravity, const QSizeF popupSize)
0194 {
0195     QPointF anchorPoint;
0196     switch (anchorEdge & (Qt::LeftEdge | Qt::RightEdge)) {
0197     case Qt::LeftEdge:
0198         anchorPoint.setX(anchorRect.x());
0199         break;
0200     case Qt::RightEdge:
0201         anchorPoint.setX(anchorRect.x() + anchorRect.width());
0202         break;
0203     default:
0204         anchorPoint.setX(qRound(anchorRect.x() + anchorRect.width() / 2.0));
0205     }
0206     switch (anchorEdge & (Qt::TopEdge | Qt::BottomEdge)) {
0207     case Qt::TopEdge:
0208         anchorPoint.setY(anchorRect.y());
0209         break;
0210     case Qt::BottomEdge:
0211         anchorPoint.setY(anchorRect.y() + anchorRect.height());
0212         break;
0213     default:
0214         anchorPoint.setY(qRound(anchorRect.y() + anchorRect.height() / 2.0));
0215     }
0216 
0217     // calculate where the top left point of the popup will end up with the applied gravity
0218     // gravity indicates direction. i.e if gravitating towards the top the popup's bottom edge
0219     // will next to the anchor point
0220     QPointF popupPosAdjust;
0221     switch (gravity & (Qt::LeftEdge | Qt::RightEdge)) {
0222     case Qt::LeftEdge:
0223         popupPosAdjust.setX(-popupSize.width());
0224         break;
0225     case Qt::RightEdge:
0226         popupPosAdjust.setX(0);
0227         break;
0228     default:
0229         popupPosAdjust.setX(qRound(-popupSize.width() / 2.0));
0230     }
0231     switch (gravity & (Qt::TopEdge | Qt::BottomEdge)) {
0232     case Qt::TopEdge:
0233         popupPosAdjust.setY(-popupSize.height());
0234         break;
0235     case Qt::BottomEdge:
0236         popupPosAdjust.setY(0);
0237         break;
0238     default:
0239         popupPosAdjust.setY(qRound(-popupSize.height() / 2.0));
0240     }
0241 
0242     return anchorPoint + popupPosAdjust;
0243 }
0244 
0245 QRectF gravitateGeometry(const QRectF &rect, const QRectF &bounds, Gravity gravity)
0246 {
0247     QRectF geometry = rect;
0248 
0249     switch (gravity) {
0250     case Gravity::TopLeft:
0251         geometry.moveRight(bounds.right());
0252         geometry.moveBottom(bounds.bottom());
0253         break;
0254     case Gravity::Top:
0255     case Gravity::TopRight:
0256         geometry.moveLeft(bounds.left());
0257         geometry.moveBottom(bounds.bottom());
0258         break;
0259     case Gravity::Right:
0260     case Gravity::BottomRight:
0261     case Gravity::Bottom:
0262     case Gravity::None:
0263         geometry.moveLeft(bounds.left());
0264         geometry.moveTop(bounds.top());
0265         break;
0266     case Gravity::BottomLeft:
0267     case Gravity::Left:
0268         geometry.moveRight(bounds.right());
0269         geometry.moveTop(bounds.top());
0270         break;
0271     }
0272 
0273     return geometry;
0274 }
0275 
0276 } // namespace
0277 
0278 #ifndef KCMRULES
0279 #endif