File indexing completed on 2024-05-19 05:32:28

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