/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2012, 2013 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_XCB_UTILS_H #define KWIN_XCB_UTILS_H #include #include "utils.h" #include #include #include #include #include #include namespace KWin { namespace Xcb { typedef xcb_window_t WindowId; // forward declaration of methods static void defineCursor(xcb_window_t window, xcb_cursor_t cursor); static void setInputFocus(xcb_window_t window, uint8_t revertTo = XCB_INPUT_FOCUS_POINTER_ROOT, xcb_timestamp_t time = xTime()); static void moveWindow(xcb_window_t window, const QPoint &pos); static void moveWindow(xcb_window_t window, uint32_t x, uint32_t y); static void selectInput(xcb_window_t window, uint32_t events); template class Wrapper { public: Wrapper() : m_retrieved(false) , m_window(XCB_WINDOW_NONE) , m_reply(NULL) { m_cookie.sequence = 0; } explicit Wrapper(WindowId window) : m_retrieved(false) , m_cookie(requestFunc(connection(), window)) , m_window(window) , m_reply(NULL) { } explicit Wrapper(const Wrapper &other) : m_retrieved(other.m_retrieved) , m_cookie(other.m_cookie) , m_window(other.m_window) , m_reply(NULL) { takeFromOther(const_cast(other)); } virtual ~Wrapper() { cleanup(); } inline Wrapper &operator=(const Wrapper &other) { if (this != &other) { // if we had managed a reply, free it cleanup(); // copy members m_retrieved = other.m_retrieved; m_cookie = other.m_cookie; m_window = other.m_window; m_reply = other.m_reply; // take over the responsibility for the reply pointer takeFromOther(const_cast(other)); } return *this; } inline const Reply *operator->() { getReply(); return m_reply; } inline bool isNull() { getReply(); return m_reply == NULL; } inline operator bool() { return !isNull(); } inline const Reply *data() { getReply(); return m_reply; } inline WindowId window() const { return m_window; } inline bool isRetrieved() const { return m_retrieved; } /** * Returns the value of the reply pointer referenced by this object. The reply pointer of * this object will be reset to null. Calling any method which requires the reply to be valid * will crash. * * Callers of this function take ownership of the pointer. **/ inline Reply *take() { getReply(); Reply *ret = m_reply; m_reply = NULL; m_window = XCB_WINDOW_NONE; return ret; } protected: void getReply() { if (m_retrieved || !m_cookie.sequence) { return; } m_reply = replyFunc(connection(), m_cookie, NULL); m_retrieved = true; } private: inline void cleanup() { if (!m_retrieved && m_cookie.sequence) { xcb_discard_reply(connection(), m_cookie.sequence); } else if (m_reply) { free(m_reply); } } inline void takeFromOther(Wrapper &other) { if (m_retrieved) { m_reply = other.take(); } else { //ensure that other object doesn't try to get the reply or discards it in the dtor other.m_retrieved = true; other.m_window = XCB_WINDOW_NONE; } } bool m_retrieved; Cookie m_cookie; WindowId m_window; Reply *m_reply; }; class Atom { public: explicit Atom(const QByteArray &name, bool onlyIfExists = false) : m_retrieved(false) , m_cookie(xcb_intern_atom_unchecked(connection(), onlyIfExists, name.length(), name.constData())) , m_atom(XCB_ATOM_NONE) , m_name(name) { } Atom() = delete; Atom(const Atom &) = delete; ~Atom() { if (!m_retrieved && m_cookie.sequence) { xcb_discard_reply(connection(), m_cookie.sequence); } } operator xcb_atom_t() const { (const_cast(this))->getReply(); return m_atom; } inline const QByteArray &name() const { return m_name; } private: void getReply() { if (m_retrieved || !m_cookie.sequence) { return; } ScopedCPointer reply(xcb_intern_atom_reply(connection(), m_cookie, nullptr)); if (!reply.isNull()) { m_atom = reply->atom; } m_retrieved = true; } bool m_retrieved; xcb_intern_atom_cookie_t m_cookie; xcb_atom_t m_atom; QByteArray m_name; }; typedef Wrapper WindowAttributes; typedef Wrapper OverlayWindow; class WindowGeometry : public Wrapper { public: WindowGeometry() : Wrapper() {} explicit WindowGeometry(xcb_window_t window) : Wrapper(window) {} inline QRect rect() { const xcb_get_geometry_reply_t *geometry = data(); if (!geometry) { return QRect(); } return QRect(geometry->x, geometry->y, geometry->width, geometry->height); } }; class Tree : public Wrapper { public: explicit Tree(WindowId window) : Wrapper(window) {} inline WindowId *children() { return xcb_query_tree_children(data()); } inline xcb_window_t parent() { if (isNull()) return XCB_WINDOW_NONE; return (*this)->parent; } }; inline xcb_get_input_focus_cookie_t get_input_focus(xcb_connection_t *c, xcb_window_t) { return xcb_get_input_focus(c); } class CurrentInput : public Wrapper { public: CurrentInput() : Wrapper() {} inline xcb_window_t window() { if (isNull()) return XCB_WINDOW_NONE; return (*this)->focus; } }; inline xcb_get_property_cookie_t get_transient_for(xcb_connection_t *c, xcb_window_t window) { return xcb_get_property_unchecked(c, 0, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 1); } class TransientFor : public Wrapper { public: explicit TransientFor(WindowId window) : Wrapper(window) {} /** * @brief Fill given window pointer with the WM_TRANSIENT_FOR property of a window. * @param prop WM_TRANSIENT_FOR property value. * @returns @c true on success, @c false otherwise **/ inline bool getTransientFor(WindowId *prop) { if (isNull()) { return false; } const xcb_get_property_reply_t *reply = data(); if (!reply || reply->type != XCB_ATOM_WINDOW || reply->format != 32 || reply->length == 0) return false; *prop = *reinterpret_cast(xcb_get_property_value(reply)); return true; } }; namespace RandR { typedef Wrapper ScreenInfo; class ScreenResources : public Wrapper { public: explicit ScreenResources(WindowId window) : Wrapper(window) {} inline xcb_randr_crtc_t *crtcs() { if (isNull()) { return nullptr; } return xcb_randr_get_screen_resources_crtcs(data()); } }; class CrtcGamma : public Wrapper { public: explicit CrtcGamma(xcb_randr_crtc_t c) : Wrapper(c) {} inline uint16_t *red() { return xcb_randr_get_crtc_gamma_red(data()); } inline uint16_t *green() { return xcb_randr_get_crtc_gamma_green(data()); } inline uint16_t *blue() { return xcb_randr_get_crtc_gamma_blue(data()); } }; } class ExtensionData { public: ExtensionData(); int version; int eventBase; int errorBase; int majorOpcode; bool present; QByteArray name; }; class Extensions { public: bool isShapeAvailable() const { return m_shape.version > 0; } bool isShapeInputAvailable() const; int shapeNotifyEvent() const; bool hasShape(xcb_window_t w) const; bool isRandrAvailable() const { return m_randr.present; } int randrNotifyEvent() const; bool isDamageAvailable() const { return m_damage.present; } int damageNotifyEvent() const; bool isCompositeAvailable() const { return m_composite.version > 0; } bool isCompositeOverlayAvailable() const; bool isRenderAvailable() const { return m_render.version > 0; } bool isFixesAvailable() const { return m_fixes.version > 0; } int fixesCursorNotifyEvent() const; bool isFixesRegionAvailable() const; bool isSyncAvailable() const { return m_sync.present; } int syncAlarmNotifyEvent() const; QVector extensions() const; static Extensions *self(); static void destroy(); private: Extensions(); ~Extensions(); void init(); template void initVersion(T cookie, F f, ExtensionData *dataToFill); void extensionQueryReply(const xcb_query_extension_reply_t *extension, ExtensionData *dataToFill); ExtensionData m_shape; ExtensionData m_randr; ExtensionData m_damage; ExtensionData m_composite; ExtensionData m_render; ExtensionData m_fixes; ExtensionData m_sync; static Extensions *s_self; }; /** * This class is an RAII wrapper for an xcb_window_t. An xcb_window_t hold by an instance of this class * will be freed when the instance gets destroyed. * * Furthermore the class provides wrappers around some xcb methods operating on an xcb_window_t. * * For the cases that one is more interested in wrapping the xcb methods the constructor which takes * an existing window and the @link reset method allow to disable the RAII functionality. **/ class Window { public: /** * Takes over responsibility of @p window. If @p window is not provided an invalid Window is * created. Use @link create to set an xcb_window_t later on. * * If @p destroy is @c true the window will be destroyed together with this object, if @c false * the window will be kept around. This is useful if you are not interested in the RAII capabilities * but still want to use a window like an object. * * @param window The window to manage. * @param destroy Whether the window should be destroyed together with the object. * @see reset **/ Window(xcb_window_t window = XCB_WINDOW_NONE, bool destroy = true); /** * Creates an xcb_window_t and manages it. It's a convenient method to create a window with * depth, class and visual being copied from parent and border being @c 0. * @param geometry The geometry for the window to be created * @param mask The mask for the values * @param values The values to be passed to xcb_create_window * @param parent The parent window **/ Window(const QRect &geometry, uint32_t mask = 0, const uint32_t *values = NULL, xcb_window_t parent = rootWindow()); /** * Creates an xcb_window_t and manages it. It's a convenient method to create a window with * depth and visual being copied from parent and border being @c 0. * @param geometry The geometry for the window to be created * @param class The window class * @param mask The mask for the values * @param values The values to be passed to xcb_create_window * @param parent The parent window **/ Window(const QRect &geometry, uint16_t windowClass, uint32_t mask = 0, const uint32_t *values = NULL, xcb_window_t parent = rootWindow()); Window(const Window &other) = delete; ~Window(); /** * Creates a new window for which the responsibility is taken over. If a window had been managed * before it is freed. * * Depth, class and visual are being copied from parent and border is @c 0. * @param geometry The geometry for the window to be created * @param mask The mask for the values * @param values The values to be passed to xcb_create_window * @param parent The parent window **/ void create(const QRect &geometry, uint32_t mask = 0, const uint32_t *values = NULL, xcb_window_t parent = rootWindow()); /** * Creates a new window for which the responsibility is taken over. If a window had been managed * before it is freed. * * Depth and visual are being copied from parent and border is @c 0. * @param geometry The geometry for the window to be created * @param class The window class * @param mask The mask for the values * @param values The values to be passed to xcb_create_window * @param parent The parent window **/ void create(const QRect &geometry, uint16_t windowClass, uint32_t mask = 0, const uint32_t *values = NULL, xcb_window_t parent = rootWindow()); /** * Frees the existing window and starts to manage the new @p window. * If @p destroy is @c true the new managed window will be destroyed together with this * object or when reset is called again. If @p destroy is @c false the window will not * be destroyed. It is then the responsibility of the caller to destroy the window. **/ void reset(xcb_window_t window = XCB_WINDOW_NONE, bool destroy = true); /** * @returns @c true if a window is managed, @c false otherwise. **/ bool isValid() const; /** * Configures the window with a new geometry. * @param geometry The new window geometry to be used **/ void setGeometry(const QRect &geometry); void setGeometry(uint32_t x, uint32_t y, uint32_t width, uint32_t height); void move(const QPoint &pos); void move(uint32_t x, uint32_t y); void resize(const QSize &size); void resize(uint32_t width, uint32_t height); void raise(); void lower(); void map(); void unmap(); void reparent(xcb_window_t parent, int x = 0, int y = 0); void changeProperty(xcb_atom_t property, xcb_atom_t type, uint8_t format, uint32_t lenght, const void *data, uint8_t mode = XCB_PROP_MODE_REPLACE); void deleteProperty(xcb_atom_t property); void setBorderWidth(uint32_t width); void grabButton(uint8_t pointerMode, uint8_t keyboardmode, uint16_t modifiers = XCB_MOD_MASK_ANY, uint8_t button = XCB_BUTTON_INDEX_ANY, uint16_t eventMask = XCB_EVENT_MASK_BUTTON_PRESS, xcb_window_t confineTo = XCB_WINDOW_NONE, xcb_cursor_t cursor = XCB_CURSOR_NONE, bool ownerEvents = false); void ungrabButton(uint16_t modifiers = XCB_MOD_MASK_ANY, uint8_t button = XCB_BUTTON_INDEX_ANY); /** * Clears the window area. Same as xcb_clear_area with x, y, width, height being @c 0. **/ void clear(); void setBackgroundPixmap(xcb_pixmap_t pixmap); void defineCursor(xcb_cursor_t cursor); void focus(uint8_t revertTo = XCB_INPUT_FOCUS_POINTER_ROOT, xcb_timestamp_t time = xTime()); void selectInput(uint32_t events); void kill(); operator xcb_window_t() const; private: xcb_window_t doCreate(const QRect &geometry, uint16_t windowClass, uint32_t mask = 0, const uint32_t *values = NULL, xcb_window_t parent = rootWindow()); void destroy(); xcb_window_t m_window; bool m_destroy; }; inline Window::Window(xcb_window_t window, bool destroy) : m_window(window) , m_destroy(destroy) { } inline Window::Window(const QRect &geometry, uint32_t mask, const uint32_t *values, xcb_window_t parent) : m_window(doCreate(geometry, XCB_COPY_FROM_PARENT, mask, values, parent)) , m_destroy(true) { } inline Window::Window(const QRect &geometry, uint16_t windowClass, uint32_t mask, const uint32_t *values, xcb_window_t parent) : m_window(doCreate(geometry, windowClass, mask, values, parent)) , m_destroy(true) { } inline Window::~Window() { destroy(); } inline void Window::destroy() { if (!isValid() || !m_destroy) { return; } xcb_destroy_window(connection(), m_window); m_window = XCB_WINDOW_NONE; } inline bool Window::isValid() const { return m_window != XCB_WINDOW_NONE; } inline Window::operator xcb_window_t() const { return m_window; } inline void Window::create(const QRect &geometry, uint16_t windowClass, uint32_t mask, const uint32_t *values, xcb_window_t parent) { destroy(); m_window = doCreate(geometry, windowClass, mask, values, parent); } inline void Window::create(const QRect &geometry, uint32_t mask, const uint32_t *values, xcb_window_t parent) { create(geometry, XCB_COPY_FROM_PARENT, mask, values, parent); } inline xcb_window_t Window::doCreate(const QRect &geometry, uint16_t windowClass, uint32_t mask, const uint32_t *values, xcb_window_t parent) { xcb_window_t w = xcb_generate_id(connection()); xcb_create_window(connection(), XCB_COPY_FROM_PARENT, w, parent, geometry.x(), geometry.y(), geometry.width(), geometry.height(), 0, windowClass, XCB_COPY_FROM_PARENT, mask, values); return w; } inline void Window::reset(xcb_window_t window, bool shouldDestroy) { destroy(); m_window = window; m_destroy = shouldDestroy; } inline void Window::setGeometry(const QRect &geometry) { setGeometry(geometry.x(), geometry.y(), geometry.width(), geometry.height()); } inline void Window::setGeometry(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { if (!isValid()) { return; } const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; const uint32_t values[] = { x, y, width, height }; xcb_configure_window(connection(), m_window, mask, values); } inline void Window::move(const QPoint &pos) { move(pos.x(), pos.y()); } inline void Window::move(uint32_t x, uint32_t y) { if (!isValid()) { return; } moveWindow(m_window, x, y); } inline void Window::resize(const QSize &size) { resize(size.width(), size.height()); } inline void Window::resize(uint32_t width, uint32_t height) { if (!isValid()) { return; } const uint16_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; const uint32_t values[] = { width, height }; xcb_configure_window(connection(), m_window, mask, values); } inline void Window::raise() { const uint32_t values[] = { XCB_STACK_MODE_ABOVE }; xcb_configure_window(connection(), m_window, XCB_CONFIG_WINDOW_STACK_MODE, values); } inline void Window::lower() { const uint32_t values[] = { XCB_STACK_MODE_BELOW }; xcb_configure_window(connection(), m_window, XCB_CONFIG_WINDOW_STACK_MODE, values); } inline void Window::map() { if (!isValid()) { return; } xcb_map_window(connection(), m_window); } inline void Window::unmap() { if (!isValid()) { return; } xcb_unmap_window(connection(), m_window); } inline void Window::reparent(xcb_window_t parent, int x, int y) { if (!isValid()) { return; } xcb_reparent_window(connection(), m_window, parent, x, y); } inline void Window::changeProperty(xcb_atom_t property, xcb_atom_t type, uint8_t format, uint32_t lenght, const void *data, uint8_t mode) { if (!isValid()) { return; } xcb_change_property(connection(), mode, m_window, property, type, format, lenght, data); } inline void Window::deleteProperty(xcb_atom_t property) { if (!isValid()) { return; } xcb_delete_property(connection(), m_window, property); } inline void Window::setBorderWidth(uint32_t width) { if (!isValid()) { return; } xcb_configure_window(connection(), m_window, XCB_CONFIG_WINDOW_BORDER_WIDTH, &width); } inline void Window::grabButton(uint8_t pointerMode, uint8_t keyboardmode, uint16_t modifiers, uint8_t button, uint16_t eventMask, xcb_window_t confineTo, xcb_cursor_t cursor, bool ownerEvents) { if (!isValid()) { return; } xcb_grab_button(connection(), ownerEvents, m_window, eventMask, pointerMode, keyboardmode, confineTo, cursor, button, modifiers); } inline void Window::ungrabButton(uint16_t modifiers, uint8_t button) { if (!isValid()) { return; } xcb_ungrab_button(connection(), button, m_window, modifiers); } inline void Window::clear() { if (!isValid()) { return; } xcb_clear_area(connection(), false, m_window, 0, 0, 0, 0); } inline void Window::setBackgroundPixmap(xcb_pixmap_t pixmap) { if (!isValid()) { return; } const uint32_t values[] = {pixmap}; xcb_change_window_attributes(connection(), m_window, XCB_CW_BACK_PIXMAP, values); } inline void Window::defineCursor(xcb_cursor_t cursor) { Xcb::defineCursor(m_window, cursor); } inline void Window::focus(uint8_t revertTo, xcb_timestamp_t time) { setInputFocus(m_window, revertTo, time); } inline void Window::selectInput(uint32_t events) { Xcb::selectInput(m_window, events); } inline void Window::kill() { xcb_kill_client(connection(), m_window); } // helper functions static inline void moveResizeWindow(WindowId window, const QRect &geometry) { const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; const uint32_t values[] = { static_cast(geometry.x()), static_cast(geometry.y()), static_cast(geometry.width()), static_cast(geometry.height()) }; xcb_configure_window(connection(), window, mask, values); } static inline void moveWindow(xcb_window_t window, const QPoint& pos) { moveWindow(window, pos.x(), pos.y()); } static inline void moveWindow(xcb_window_t window, uint32_t x, uint32_t y) { const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y; const uint32_t values[] = { x, y }; xcb_configure_window(connection(), window, mask, values); } static inline WindowId createInputWindow(const QRect &geometry, uint32_t mask, const uint32_t *values) { WindowId window = xcb_generate_id(connection()); xcb_create_window(connection(), 0, window, rootWindow(), geometry.x(), geometry.y(), geometry.width(), geometry.height(), 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, mask, values); return window; } static inline void restackWindows(const QVector &windows) { if (windows.count() < 2) { // only one window, nothing to do return; } for (int i=1; i &windows) { if (windows.isEmpty()) { return; } const uint32_t values[] = { XCB_STACK_MODE_ABOVE }; xcb_configure_window(connection(), windows.first(), XCB_CONFIG_WINDOW_STACK_MODE, values); restackWindows(windows); } static inline int defaultDepth() { static int depth = 0; if (depth != 0) { return depth; } int screen = QX11Info::appScreen(); for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(connection())); it.rem; --screen, xcb_screen_next(&it)) { if (screen == 0) { depth = it.data->root_depth; break; } } return depth; } static inline xcb_rectangle_t fromQt(const QRect &rect) { xcb_rectangle_t rectangle; rectangle.x = rect.x(); rectangle.y = rect.y(); rectangle.width = rect.width(); rectangle.height = rect.height(); return rectangle; } static inline QVector regionToRects(const QRegion ®ion) { const QVector regionRects = region.rects(); QVector rects(regionRects.count()); for (int i=0; i sync(xcb_get_input_focus_reply(c, cookie, &error)); if (error) { free(error); } } void selectInput(xcb_window_t window, uint32_t events) { xcb_change_window_attributes(connection(), window, XCB_CW_EVENT_MASK, &events); } } // namespace X11 } // namespace KWin #endif // KWIN_X11_UTILS_H