1 /* 2 KWin - the KDE window manager 3 This file is part of the KDE project. 4 5 SPDX-FileCopyrightText: 2012, 2013 Martin Gräßlin <mgraesslin@kde.org> 6 7 SPDX-License-Identifier: GPL-2.0-or-later 8 */ 9 #ifndef KWIN_XCB_UTILS_H 10 #define KWIN_XCB_UTILS_H 11 12 #include <kwinglobals.h> 13 #include "main.h" 14 15 #include <QRect> 16 #include <QRegion> 17 #include <QScopedPointer> 18 #include <QVector> 19 20 #include <xcb/xcb.h> 21 #include <xcb/composite.h> 22 #include <xcb/randr.h> 23 24 #include <xcb/shm.h> 25 26 class TestXcbSizeHints; 27 28 namespace KWin { 29 30 template <typename T> using ScopedCPointer = QScopedPointer<T, QScopedPointerPodDeleter>; 31 32 namespace Xcb { 33 34 typedef xcb_window_t WindowId; 35 36 // forward declaration of methods 37 static void defineCursor(xcb_window_t window, xcb_cursor_t cursor); 38 static void setInputFocus(xcb_window_t window, uint8_t revertTo = XCB_INPUT_FOCUS_POINTER_ROOT, xcb_timestamp_t time = xTime()); 39 static void moveWindow(xcb_window_t window, const QPoint &pos); 40 static void moveWindow(xcb_window_t window, uint32_t x, uint32_t y); 41 static void lowerWindow(xcb_window_t window); 42 static void selectInput(xcb_window_t window, uint32_t events); 43 44 /** 45 * @brief Variadic template to wrap an xcb request. 46 * 47 * This struct is part of the generic implementation to wrap xcb requests 48 * and fetching their reply. Each request is represented by two templated 49 * elements: WrapperData and Wrapper. 50 * 51 * The WrapperData defines the following types: 52 * @li reply_type of the xcb request 53 * @li cookie_type of the xcb request 54 * @li function pointer type for the xcb request 55 * @li function pointer type for the reply 56 * This uses variadic template arguments thus it can be used to specify any 57 * xcb request. 58 * 59 * As the WrapperData does not specify the actual function pointers one needs 60 * to derive another struct which specifies the function pointer requestFunc and 61 * the function pointer replyFunc as static constexpr of type reply_func and 62 * reply_type respectively. E.g. for the command xcb_get_geometry: 63 * @code 64 * struct GeometryData : public WrapperData< xcb_get_geometry_reply_t, xcb_get_geometry_cookie_t, xcb_drawable_t > 65 * { 66 * static constexpr request_func requestFunc = &xcb_get_geometry_unchecked; 67 * static constexpr reply_func replyFunc = &xcb_get_geometry_reply; 68 * }; 69 * @endcode 70 * 71 * To simplify this definition the macro XCB_WRAPPER_DATA is provided. 72 * For the same xcb command this looks like this: 73 * @code 74 * XCB_WRAPPER_DATA(GeometryData, xcb_get_geometry, xcb_drawable_t) 75 * @endcode 76 * 77 * The derived WrapperData has to be passed as first template argument to Wrapper. The other 78 * template arguments of Wrapper are the same variadic template arguments as passed into 79 * WrapperData. This is ensured at compile time and will cause a compile error in case there 80 * is a mismatch of the variadic template arguments passed to WrapperData and Wrapper. 81 * Passing another type than a struct derived from WrapperData to Wrapper will result in a 82 * compile error. The following code snippets won't compile: 83 * @code 84 * XCB_WRAPPER_DATA(GeometryData, xcb_get_geometry, xcb_drawable_t) 85 * // fails with "static assertion failed: Argument miss-match between Wrapper and WrapperData" 86 * class IncorrectArguments : public Wrapper<GeometryData, uint8_t> 87 * { 88 * public: 89 * IncorrectArguments() = default; 90 * IncorrectArguments(xcb_window_t window) : Wrapper<GeometryData, uint8_t>(window) {} 91 * }; 92 * 93 * // fails with "static assertion failed: Data template argument must be derived from WrapperData" 94 * class WrapperDataDirectly : public Wrapper<WrapperData<xcb_get_geometry_reply_t, xcb_get_geometry_request_t, xcb_drawable_t>, xcb_drawable_t> 95 * { 96 * public: 97 * WrapperDataDirectly() = default; 98 * WrapperDataDirectly(xcb_window_t window) : Wrapper<WrapperData<xcb_get_geometry_reply_t, xcb_get_geometry_request_t, xcb_drawable_t>, xcb_drawable_t>(window) {} 99 * }; 100 * 101 * // fails with "static assertion failed: Data template argument must be derived from WrapperData" 102 * struct FakeWrapperData 103 * { 104 * typedef xcb_get_geometry_reply_t reply_type; 105 * typedef xcb_get_geometry_cookie_t cookie_type; 106 * typedef std::tuple<xcb_drawable_t> argument_types; 107 * typedef cookie_type (*request_func)(xcb_connection_t*, xcb_drawable_t); 108 * typedef reply_type *(*reply_func)(xcb_connection_t*, cookie_type, xcb_generic_error_t**); 109 * static constexpr std::size_t argumentCount = 1; 110 * static constexpr request_func requestFunc = &xcb_get_geometry_unchecked; 111 * static constexpr reply_func replyFunc = &xcb_get_geometry_reply; 112 * }; 113 * class NotDerivedFromWrapperData : public Wrapper<FakeWrapperData, xcb_drawable_t> 114 * { 115 * public: 116 * NotDerivedFromWrapperData() = default; 117 * NotDerivedFromWrapperData(xcb_window_t window) : Wrapper<FakeWrapperData, xcb_drawable_t>(window) {} 118 * }; 119 * @endcode 120 * 121 * The Wrapper provides an easy to use RAII API which calls the WrapperData's requestFunc in 122 * the ctor and fetches the reply the first time it is used. In addition the dtor takes care 123 * of freeing the reply if it got fetched, otherwise it discards the reply. The Wrapper can 124 * be used as if it were the reply_type directly. 125 * 126 * There are several command wrappers defined which either subclass Wrapper to add methods to 127 * simplify the usage of the result_type or use a typedef. To add a new typedef one can use the 128 * macro XCB_WRAPPER which creates the WrapperData struct as XCB_WRAPPER_DATA does and the 129 * typedef. E.g: 130 * @code 131 * XCB_WRAPPER(Geometry, xcb_get_geometry, xcb_drawable_t) 132 * @endcode 133 * 134 * creates a typedef Geometry and the struct GeometryData. 135 * 136 * Overall this allows to simplify the Xcb usage. For example consider the 137 * following xcb code snippet: 138 * @code 139 * xcb_window_t w; // some window 140 * xcb_connection_t *c = connection(); 141 * const xcb_get_geometry_cookie_t cookie = xcb_get_geometry_unchecked(c, w); 142 * // do other stuff 143 * xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(c, cookie, nullptr); 144 * if (reply) { 145 * reply->x; // do something with the geometry 146 * } 147 * free(reply); 148 * @endcode 149 * 150 * With the help of the Wrapper class this can be simplified to: 151 * @code 152 * xcb_window_t w; // some window 153 * Xcb::Geometry geo(w); 154 * if (!geo.isNull()) { 155 * geo->x; // do something with the geometry 156 * } 157 * @endcode 158 * 159 * @see XCB_WRAPPER_DATA 160 * @see XCB_WRAPPER 161 * @see Wrapper 162 * @see WindowAttributes 163 * @see OverlayWindow 164 * @see WindowGeometry 165 * @see Tree 166 * @see CurrentInput 167 * @see TransientFor 168 */ 169 template <typename Reply, 170 typename Cookie, 171 typename... Args> 172 struct WrapperData 173 { 174 /** 175 * @brief The type returned by the xcb reply function. 176 */ 177 typedef Reply reply_type; 178 /** 179 * @brief The type returned by the xcb request function. 180 */ 181 typedef Cookie cookie_type; 182 /** 183 * @brief Variadic arguments combined as a std::tuple. 184 * @internal Used for verifying the arguments. 185 */ 186 typedef std::tuple<Args...> argument_types; 187 /** 188 * @brief The function pointer definition for the xcb request function. 189 */ 190 typedef Cookie (*request_func)(xcb_connection_t*, Args...); 191 /** 192 * @brief The function pointer definition for the xcb reply function. 193 */ 194 typedef Reply *(*reply_func)(xcb_connection_t*, Cookie, xcb_generic_error_t**); 195 /** 196 * @brief Number of variadic arguments. 197 * @internal Used for verifying the arguments. 198 */ 199 static constexpr std::size_t argumentCount = sizeof...(Args); 200 }; 201 202 /** 203 * @brief Partial template specialization for WrapperData with no further arguments. 204 * 205 * This will be used for xcb requests just taking the xcb_connection_t* argument. 206 */ 207 template <typename Reply, 208 typename Cookie> 209 struct WrapperData<Reply, Cookie> 210 { 211 typedef Reply reply_type; 212 typedef Cookie cookie_type; 213 typedef std::tuple<> argument_types; 214 typedef Cookie (*request_func)(xcb_connection_t*); 215 typedef Reply *(*reply_func)(xcb_connection_t*, Cookie, xcb_generic_error_t**); 216 static constexpr std::size_t argumentCount = 0; 217 }; 218 219 /** 220 * @brief Abstract base class for the wrapper. 221 * 222 * This class contains the complete functionality of the Wrapper. It's only an abstract 223 * base class to provide partial template specialization for more specific constructors. 224 */ 225 template<typename Data> 226 class AbstractWrapper 227 { 228 public: 229 typedef typename Data::cookie_type Cookie; 230 typedef typename Data::reply_type Reply; 231 virtual ~AbstractWrapper() { 232 cleanup(); 233 } 234 inline AbstractWrapper &operator=(const AbstractWrapper &other) { 235 if (this != &other) { 236 // if we had managed a reply, free it 237 cleanup(); 238 // copy members 239 m_retrieved = other.m_retrieved; 240 m_cookie = other.m_cookie; 241 m_window = other.m_window; 242 m_reply = other.m_reply; 243 // take over the responsibility for the reply pointer 244 takeFromOther(const_cast<AbstractWrapper&>(other)); 245 } 246 return *this; 247 } 248 249 inline const Reply *operator->() { 250 getReply(); 251 return m_reply; 252 } 253 inline bool isNull() { 254 getReply(); 255 return m_reply == nullptr; 256 } 257 inline bool isNull() const { 258 const_cast<AbstractWrapper*>(this)->getReply(); 259 return m_reply == NULL; 260 } 261 inline operator bool() { 262 return !isNull(); 263 } 264 inline operator bool() const { 265 return !isNull(); 266 } 267 inline const Reply *data() { 268 getReply(); 269 return m_reply; 270 } 271 inline const Reply *data() const { 272 const_cast<AbstractWrapper*>(this)->getReply(); 273 return m_reply; 274 } 275 inline WindowId window() const { 276 return m_window; 277 } 278 inline bool isRetrieved() const { 279 return m_retrieved; 280 } 281 /** 282 * Returns the value of the reply pointer referenced by this object. The reply pointer of 283 * this object will be reset to null. Calling any method which requires the reply to be valid 284 * will crash. 285 * 286 * Callers of this function take ownership of the pointer. 287 */ 288 inline Reply *take() { 289 getReply(); 290 Reply *ret = m_reply; 291 m_reply = nullptr; 292 m_window = XCB_WINDOW_NONE; 293 return ret; 294 } 295 296 protected: 297 AbstractWrapper() 298 : m_retrieved(false) 299 , m_window(XCB_WINDOW_NONE) 300 , m_reply(nullptr) 301 { 302 m_cookie.sequence = 0; 303 } 304 explicit AbstractWrapper(WindowId window, Cookie cookie) 305 : m_retrieved(false) 306 , m_cookie(cookie) 307 , m_window(window) 308 , m_reply(nullptr) 309 { 310 } 311 explicit AbstractWrapper(const AbstractWrapper &other) 312 : m_retrieved(other.m_retrieved) 313 , m_cookie(other.m_cookie) 314 , m_window(other.m_window) 315 , m_reply(nullptr) 316 { 317 takeFromOther(const_cast<AbstractWrapper&>(other)); 318 } 319 void getReply() { 320 if (m_retrieved || !m_cookie.sequence) { 321 return; 322 } 323 m_reply = Data::replyFunc(connection(), m_cookie, nullptr); 324 m_retrieved = true; 325 } 326 327 private: 328 inline void cleanup() { 329 if (!m_retrieved && m_cookie.sequence) { 330 xcb_discard_reply(connection(), m_cookie.sequence); 331 } else if (m_reply) { 332 free(m_reply); 333 } 334 } 335 inline void takeFromOther(AbstractWrapper &other) { 336 if (m_retrieved) { 337 m_reply = other.take(); 338 } else { 339 //ensure that other object doesn't try to get the reply or discards it in the dtor 340 other.m_retrieved = true; 341 other.m_window = XCB_WINDOW_NONE; 342 } 343 } 344 bool m_retrieved; 345 Cookie m_cookie; 346 WindowId m_window; 347 Reply *m_reply; 348 }; 349 350 /** 351 * @brief Template to compare the arguments of two std::tuple. 352 * 353 * @internal Used by static_assert in Wrapper 354 */ 355 template <typename T1, typename T2, std::size_t I> 356 struct tupleCompare 357 { 358 typedef typename std::tuple_element<I, T1>::type tuple1Type; 359 typedef typename std::tuple_element<I, T2>::type tuple2Type; 360 /** 361 * @c true if both tuple have the same arguments, @c false otherwise. 362 */ 363 static constexpr bool value = std::is_same< tuple1Type, tuple2Type >::value && tupleCompare<T1, T2, I-1>::value; 364 }; 365 366 /** 367 * @brief Recursive template case for first tuple element. 368 */ 369 template <typename T1, typename T2> 370 struct tupleCompare<T1, T2, 0> 371 { 372 typedef typename std::tuple_element<0, T1>::type tuple1Type; 373 typedef typename std::tuple_element<0, T2>::type tuple2Type; 374 static constexpr bool value = std::is_same< tuple1Type, tuple2Type >::value; 375 }; 376 377 /** 378 * @brief Wrapper taking a WrapperData as first template argument and xcb request args as variadic args. 379 */ 380 template<typename Data, typename... Args> 381 class Wrapper : public AbstractWrapper<Data> 382 { 383 public: 384 static_assert(!std::is_same<Data, Xcb::WrapperData<typename Data::reply_type, typename Data::cookie_type, Args...> >::value, 385 "Data template argument must be derived from WrapperData"); 386 static_assert(std::is_base_of<Xcb::WrapperData<typename Data::reply_type, typename Data::cookie_type, Args...>, Data>::value, 387 "Data template argument must be derived from WrapperData"); 388 static_assert(sizeof...(Args) == Data::argumentCount, 389 "Wrapper and WrapperData need to have same template argument count"); 390 static_assert(tupleCompare<std::tuple<Args...>, typename Data::argument_types, sizeof...(Args) - 1>::value, 391 "Argument miss-match between Wrapper and WrapperData"); 392 Wrapper() = default; 393 explicit Wrapper(Args... args) 394 : AbstractWrapper<Data>(XCB_WINDOW_NONE, Data::requestFunc(connection(), args...)) 395 { 396 } 397 explicit Wrapper(xcb_window_t w, Args... args) 398 : AbstractWrapper<Data>(w, Data::requestFunc(connection(), args...)) 399 { 400 } 401 }; 402 403 /** 404 * @brief Template specialization for xcb_window_t being first variadic argument. 405 */ 406 template<typename Data, typename... Args> 407 class Wrapper<Data, xcb_window_t, Args...> : public AbstractWrapper<Data> 408 { 409 public: 410 static_assert(!std::is_same<Data, Xcb::WrapperData<typename Data::reply_type, typename Data::cookie_type, xcb_window_t, Args...> >::value, 411 "Data template argument must be derived from WrapperData"); 412 static_assert(std::is_base_of<Xcb::WrapperData<typename Data::reply_type, typename Data::cookie_type, xcb_window_t, Args...>, Data>::value, 413 "Data template argument must be derived from WrapperData"); 414 static_assert(sizeof...(Args) + 1 == Data::argumentCount, 415 "Wrapper and WrapperData need to have same template argument count"); 416 static_assert(tupleCompare<std::tuple<xcb_window_t, Args...>, typename Data::argument_types, sizeof...(Args)>::value, 417 "Argument miss-match between Wrapper and WrapperData"); 418 Wrapper() = default; 419 explicit Wrapper(xcb_window_t w, Args... args) 420 : AbstractWrapper<Data>(w, Data::requestFunc(connection(), w, args...)) 421 { 422 } 423 }; 424 425 /** 426 * @brief Template specialization for no variadic arguments. 427 * 428 * It's needed to prevent ambiguous constructors being generated. 429 */ 430 template<typename Data> 431 class Wrapper<Data> : public AbstractWrapper<Data> 432 { 433 public: 434 static_assert(!std::is_same<Data, Xcb::WrapperData<typename Data::reply_type, typename Data::cookie_type> >::value, 435 "Data template argument must be derived from WrapperData"); 436 static_assert(std::is_base_of<Xcb::WrapperData<typename Data::reply_type, typename Data::cookie_type>, Data>::value, 437 "Data template argument must be derived from WrapperData"); 438 static_assert(Data::argumentCount == 0, "Wrapper for no arguments constructed with WrapperData with arguments"); 439 explicit Wrapper() 440 : AbstractWrapper<Data>(XCB_WINDOW_NONE, Data::requestFunc(connection())) 441 { 442 } 443 }; 444 445 class Atom 446 { 447 public: 448 explicit Atom(const QByteArray &name, bool onlyIfExists = false, xcb_connection_t *c = connection()) 449 : m_connection(c) 450 , m_retrieved(false) 451 , m_cookie(xcb_intern_atom_unchecked(m_connection, onlyIfExists, name.length(), name.constData())) 452 , m_atom(XCB_ATOM_NONE) 453 , m_name(name) 454 { 455 } 456 Atom() = delete; 457 Atom(const Atom &) = delete; 458 459 ~Atom() { 460 if (!m_retrieved && m_cookie.sequence) { 461 xcb_discard_reply(m_connection, m_cookie.sequence); 462 } 463 } 464 465 operator xcb_atom_t() const { 466 (const_cast<Atom*>(this))->getReply(); 467 return m_atom; 468 } 469 bool isValid() { 470 getReply(); 471 return m_atom != XCB_ATOM_NONE; 472 } 473 bool isValid() const { 474 (const_cast<Atom*>(this))->getReply(); 475 return m_atom != XCB_ATOM_NONE; 476 } 477 478 inline const QByteArray &name() const { 479 return m_name; 480 } 481 482 private: 483 void getReply() { 484 if (m_retrieved || !m_cookie.sequence) { 485 return; 486 } 487 ScopedCPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(m_connection, m_cookie, nullptr)); 488 if (!reply.isNull()) { 489 m_atom = reply->atom; 490 } 491 m_retrieved = true; 492 } 493 xcb_connection_t *m_connection; 494 bool m_retrieved; 495 xcb_intern_atom_cookie_t m_cookie; 496 xcb_atom_t m_atom; 497 QByteArray m_name; 498 }; 499 500 /** 501 * @brief Macro to create the WrapperData subclass. 502 * 503 * Creates a struct with name @p __NAME__ for the xcb request identified by @p __REQUEST__. 504 * The variadic arguments are used to pass as template arguments to the WrapperData. 505 * 506 * The @p __REQUEST__ is the common prefix of the cookie type, reply type, request function and 507 * reply function. E.g. "xcb_get_geometry" is used to create: 508 * @li cookie type xcb_get_geometry_cookie_t 509 * @li reply type xcb_get_geometry_reply_t 510 * @li request function pointer xcb_get_geometry_unchecked 511 * @li reply function pointer xcb_get_geometry_reply 512 * 513 * @param __NAME__ The name of the WrapperData subclass 514 * @param __REQUEST__ The name of the xcb request, e.g. xcb_get_geometry 515 * @param __VA_ARGS__ The variadic template arguments, e.g. xcb_drawable_t 516 * @see XCB_WRAPPER 517 */ 518 #define XCB_WRAPPER_DATA( __NAME__, __REQUEST__, ... ) \ 519 struct __NAME__ : public WrapperData< __REQUEST__##_reply_t, __REQUEST__##_cookie_t, __VA_ARGS__ > \ 520 { \ 521 static constexpr request_func requestFunc = &__REQUEST__##_unchecked; \ 522 static constexpr reply_func replyFunc = &__REQUEST__##_reply; \ 523 }; 524 525 /** 526 * @brief Macro to create Wrapper typedef and WrapperData. 527 * 528 * This macro expands the XCB_WRAPPER_DATA macro and creates an additional 529 * typedef for Wrapper with name @p __NAME__. The created WrapperData is also derived 530 * from @p __NAME__ with "Data" as suffix. 531 * 532 * @param __NAME__ The name for the Wrapper typedef 533 * @param __REQUEST__ The name of the xcb request, passed to XCB_WRAPPER_DATA 534 * @param __VA_ARGS__ The variadic template arguments for Wrapper and WrapperData 535 * @see XCB_WRAPPER_DATA 536 */ 537 #define XCB_WRAPPER( __NAME__, __REQUEST__, ... ) \ 538 XCB_WRAPPER_DATA( __NAME__##Data, __REQUEST__, __VA_ARGS__ ) \ 539 typedef Wrapper< __NAME__##Data, __VA_ARGS__ > __NAME__; 540 541 XCB_WRAPPER(WindowAttributes, xcb_get_window_attributes, xcb_window_t) 542 XCB_WRAPPER(OverlayWindow, xcb_composite_get_overlay_window, xcb_window_t) 543 544 XCB_WRAPPER_DATA(GeometryData, xcb_get_geometry, xcb_drawable_t) 545 class WindowGeometry : public Wrapper<GeometryData, xcb_window_t> 546 { 547 public: 548 WindowGeometry() : Wrapper<GeometryData, xcb_window_t>() {} 549 explicit WindowGeometry(xcb_window_t window) : Wrapper<GeometryData, xcb_window_t>(window) {} 550 551 inline QRect rect() { 552 const xcb_get_geometry_reply_t *geometry = data(); 553 if (!geometry) { 554 return QRect(); 555 } 556 return QRect(geometry->x, geometry->y, geometry->width, geometry->height); 557 } 558 559 inline QSize size() { 560 const xcb_get_geometry_reply_t *geometry = data(); 561 if (!geometry) { 562 return QSize(); 563 } 564 return QSize(geometry->width, geometry->height); 565 } 566 }; 567 568 XCB_WRAPPER_DATA(TreeData, xcb_query_tree, xcb_window_t) 569 class Tree : public Wrapper<TreeData, xcb_window_t> 570 { 571 public: 572 explicit Tree(WindowId window) : Wrapper<TreeData, xcb_window_t>(window) {} 573 574 inline WindowId *children() { 575 if (isNull() || data()->children_len == 0) { 576 return nullptr; 577 } 578 return xcb_query_tree_children(data()); 579 } 580 inline xcb_window_t parent() { 581 if (isNull()) 582 return XCB_WINDOW_NONE; 583 return (*this)->parent; 584 } 585 }; 586 587 XCB_WRAPPER(Pointer, xcb_query_pointer, xcb_window_t) 588 589 struct CurrentInputData : public WrapperData< xcb_get_input_focus_reply_t, xcb_get_input_focus_cookie_t > 590 { 591 static constexpr request_func requestFunc = &xcb_get_input_focus_unchecked; 592 static constexpr reply_func replyFunc = &xcb_get_input_focus_reply; 593 }; 594 595 class CurrentInput : public Wrapper<CurrentInputData> 596 { 597 public: 598 CurrentInput() : Wrapper<CurrentInputData>() {} 599 600 inline xcb_window_t window() { 601 if (isNull()) 602 return XCB_WINDOW_NONE; 603 return (*this)->focus; 604 } 605 }; 606 607 struct QueryKeymapData : public WrapperData< xcb_query_keymap_reply_t, xcb_query_keymap_cookie_t > 608 { 609 static constexpr request_func requestFunc = &xcb_query_keymap_unchecked; 610 static constexpr reply_func replyFunc = &xcb_query_keymap_reply; 611 }; 612 613 class QueryKeymap : public Wrapper<QueryKeymapData> 614 { 615 public: 616 QueryKeymap() : Wrapper<QueryKeymapData>() {} 617 }; 618 619 struct ModifierMappingData : public WrapperData< xcb_get_modifier_mapping_reply_t, xcb_get_modifier_mapping_cookie_t > 620 { 621 static constexpr request_func requestFunc = &xcb_get_modifier_mapping_unchecked; 622 static constexpr reply_func replyFunc = &xcb_get_modifier_mapping_reply; 623 }; 624 625 class ModifierMapping : public Wrapper<ModifierMappingData> 626 { 627 public: 628 ModifierMapping() : Wrapper<ModifierMappingData>() {} 629 630 inline xcb_keycode_t *keycodes() { 631 if (isNull()) { 632 return nullptr; 633 } 634 return xcb_get_modifier_mapping_keycodes(data()); 635 } 636 inline int size() { 637 if (isNull()) { 638 return 0; 639 } 640 return xcb_get_modifier_mapping_keycodes_length(data()); 641 } 642 }; 643 644 XCB_WRAPPER_DATA(PropertyData, xcb_get_property, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t) 645 class Property : public Wrapper<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t> 646 { 647 public: 648 Property() 649 : Wrapper<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t>() 650 , m_type(XCB_ATOM_NONE) 651 { 652 } 653 Property(const Property &other) 654 : Wrapper<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t>(other) 655 , m_type(other.m_type) 656 { 657 } 658 explicit Property(uint8_t _delete, xcb_window_t window, xcb_atom_t property, xcb_atom_t type, uint32_t long_offset, uint32_t long_length) 659 : Wrapper<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t>(window, _delete, window, property, type, long_offset, long_length) 660 , m_type(type) 661 { 662 } 663 Property &operator=(const Property &other) { 664 Wrapper<PropertyData, uint8_t, xcb_window_t, xcb_atom_t, xcb_atom_t, uint32_t, uint32_t>::operator=(other); 665 m_type = other.m_type; 666 return *this; 667 } 668 669 /** 670 * @brief Overloaded method for convenience. 671 * 672 * Uses the type which got passed into the ctor and derives the format from the sizeof(T). 673 * Note: for the automatic format detection the size of the type T may not vary between 674 * architectures. Thus one needs to use e.g. uint32_t instead of long. In general all xcb 675 * data types can be used, all Xlib data types can not be used. 676 * 677 * @param defaultValue The default value to return in case of error 678 * @param ok Set to @c false in case of error, @c true in case of success 679 * @return The read value or @p defaultValue in error case 680 */ 681 template <typename T> 682 inline typename std::enable_if<!std::is_pointer<T>::value, T>::type value(T defaultValue = T(), bool *ok = nullptr) { 683 return value<T>(sizeof(T) * 8, m_type, defaultValue, ok); 684 } 685 /** 686 * @brief Reads the property as a POD type. 687 * 688 * Returns the first value of the property data. In case of @p format or @p type mismatch 689 * the @p defaultValue is returned. The optional argument @p ok is set 690 * to @c false in case of error and to @c true in case of successful reading of 691 * the property. 692 * 693 * @param format The expected format of the property value, e.g. 32 for XCB_ATOM_CARDINAL 694 * @param type The expected type of the property value, e.g. XCB_ATOM_CARDINAL 695 * @param defaultValue The default value to return in case of error 696 * @param ok Set to @c false in case of error, @c true in case of success 697 * @return The read value or @p defaultValue in error case 698 */ 699 template <typename T> 700 inline typename std::enable_if<!std::is_pointer<T>::value, T>::type value(uint8_t format, xcb_atom_t type, T defaultValue = T(), bool *ok = nullptr) { 701 T *reply = value<T*>(format, type, nullptr, ok); 702 if (!reply) { 703 return defaultValue; 704 } 705 return reply[0]; 706 } 707 /** 708 * @brief Overloaded method for convenience. 709 * 710 * Uses the type which got passed into the ctor and derives the format from the sizeof(T). 711 * Note: for the automatic format detection the size of the type T may not vary between 712 * architectures. Thus one needs to use e.g. uint32_t instead of long. In general all xcb 713 * data types can be used, all Xlib data types can not be used. 714 * 715 * @param defaultValue The default value to return in case of error 716 * @param ok Set to @c false in case of error, @c true in case of success 717 * @return The read value or @p defaultValue in error case 718 */ 719 template <typename T> 720 inline typename std::enable_if<std::is_pointer<T>::value, T>::type value(T defaultValue = nullptr, bool *ok = nullptr) { 721 return value<T>(sizeof(typename std::remove_pointer<T>::type) * 8, m_type, defaultValue, ok); 722 } 723 /** 724 * @brief Reads the property as an array of T. 725 * 726 * This method is an overload for the case that T is a pointer type. 727 * 728 * Return the property value casted to the pointer type T. In case of @p format 729 * or @p type mismatch the @p defaultValue is returned. Also if the value length 730 * is @c 0 the @p defaultValue is returned. The optional argument @p ok is set 731 * to @c false in case of error and to @c true in case of successful reading of 732 * the property. Ok will always be true if the property exists and has been 733 * successfully read, even in the case the property is empty and its length is 0 734 * 735 * @param format The expected format of the property value, e.g. 32 for XCB_ATOM_CARDINAL 736 * @param type The expected type of the property value, e.g. XCB_ATOM_CARDINAL 737 * @param defaultValue The default value to return in case of error 738 * @param ok Set to @c false in case of error, @c true in case of success 739 * @return The read value or @p defaultValue in error case 740 */ 741 template <typename T> 742 inline typename std::enable_if<std::is_pointer<T>::value, T>::type value(uint8_t format, xcb_atom_t type, T defaultValue = nullptr, bool *ok = nullptr) { 743 if (ok) { 744 *ok = false; 745 } 746 const PropertyData::reply_type *reply = data(); 747 if (!reply) { 748 return defaultValue; 749 } 750 if (reply->type != type) { 751 return defaultValue; 752 } 753 if (reply->format != format) { 754 return defaultValue; 755 } 756 757 if (ok) { 758 *ok = true; 759 } 760 if (xcb_get_property_value_length(reply) == 0) { 761 return defaultValue; 762 } 763 764 return reinterpret_cast<T>(xcb_get_property_value(reply)); 765 } 766 /** 767 * @brief Reads the property as string and returns a QByteArray. 768 * 769 * In case of error this method returns a null QByteArray. 770 */ 771 inline QByteArray toByteArray(uint8_t format = 8, xcb_atom_t type = XCB_ATOM_STRING, bool *ok = nullptr) { 772 bool valueOk = false; 773 const char *reply = value<const char*>(format, type, nullptr, &valueOk); 774 if (ok) { 775 *ok = valueOk; 776 } 777 778 if (valueOk && !reply) { 779 return QByteArray("", 0); // valid, not null, but empty data 780 } else if (!valueOk) { 781 return QByteArray(); // Property not found, data empty and null 782 } 783 return QByteArray(reply, xcb_get_property_value_length(data())); 784 } 785 /** 786 * @brief Overloaded method for convenience. 787 */ 788 inline QByteArray toByteArray(bool *ok) { 789 return toByteArray(8, m_type, ok); 790 } 791 /** 792 * @brief Reads the property as a boolean value. 793 * 794 * If the property reply length is @c 1 the first element is interpreted as a boolean 795 * value returning @c true for any value unequal to @c 0 and @c false otherwise. 796 * 797 * In case of error this method returns @c false. Thus it is not possible to distinguish 798 * between error case and a read @c false value. Use the optional argument @p ok to 799 * distinguish the error case. 800 * 801 * @param format Expected format. Defaults to 32. 802 * @param type Expected type Defaults to XCB_ATOM_CARDINAL. 803 * @param ok Set to @c false in case of error, @c true in case of success 804 * @return bool The first element interpreted as a boolean value or @c false in error case 805 * @see value 806 */ 807 inline bool toBool(uint8_t format = 32, xcb_atom_t type = XCB_ATOM_CARDINAL, bool *ok = nullptr) { 808 bool *reply = value<bool*>(format, type, nullptr, ok); 809 if (!reply) { 810 return false; 811 } 812 if (data()->value_len != 1) { 813 if (ok) { 814 *ok = false; 815 } 816 return false; 817 } 818 return reply[0] != 0; 819 } 820 /** 821 * @brief Overloaded method for convenience. 822 */ 823 inline bool toBool(bool *ok) { 824 return toBool(32, m_type, ok); 825 } 826 private: 827 xcb_atom_t m_type; 828 }; 829 830 class StringProperty : public Property 831 { 832 public: 833 StringProperty() = default; 834 explicit StringProperty(xcb_window_t w, xcb_atom_t p) 835 : Property(false, w, p, XCB_ATOM_STRING, 0, 10000) 836 { 837 } 838 operator QByteArray() { 839 return toByteArray(); 840 } 841 }; 842 843 class TransientFor : public Property 844 { 845 public: 846 explicit TransientFor(WindowId window) 847 : Property(0, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 1) 848 { 849 } 850 851 /** 852 * @brief Fill given window pointer with the WM_TRANSIENT_FOR property of a window. 853 * @param prop WM_TRANSIENT_FOR property value. 854 * @returns @c true on success, @c false otherwise 855 */ 856 inline bool getTransientFor(WindowId *prop) { 857 WindowId *windows = value<WindowId*>(); 858 if (!windows) { 859 return false; 860 } 861 862 *prop = *windows; 863 return true; 864 } 865 }; 866 867 class GeometryHints 868 { 869 public: 870 GeometryHints() = default; 871 void init(xcb_window_t window) { 872 Q_ASSERT(window); 873 if (m_window) { 874 // already initialized 875 return; 876 } 877 m_window = window; 878 fetch(); 879 } 880 void fetch() { 881 if (!m_window) { 882 return; 883 } 884 m_sizeHints = nullptr; 885 m_hints = NormalHints(m_window); 886 } 887 void read() { 888 m_sizeHints = m_hints.sizeHints(); 889 } 890 891 bool hasPosition() const { 892 return testFlag(NormalHints::SizeHints::UserPosition) || testFlag(NormalHints::SizeHints::ProgramPosition); 893 } 894 bool hasSize() const { 895 return testFlag(NormalHints::SizeHints::UserSize) || testFlag(NormalHints::SizeHints::ProgramSize); 896 } 897 bool hasMinSize() const { 898 return testFlag(NormalHints::SizeHints::MinSize); 899 } 900 bool hasMaxSize() const { 901 return testFlag(NormalHints::SizeHints::MaxSize); 902 } 903 bool hasResizeIncrements() const { 904 return testFlag(NormalHints::SizeHints::ResizeIncrements); 905 } 906 bool hasAspect() const { 907 return testFlag(NormalHints::SizeHints::Aspect); 908 } 909 bool hasBaseSize() const { 910 return testFlag(NormalHints::SizeHints::BaseSize); 911 } 912 bool hasWindowGravity() const { 913 return testFlag(NormalHints::SizeHints::WindowGravity); 914 } 915 QSize maxSize() const { 916 if (!hasMaxSize()) { 917 return QSize(INT_MAX, INT_MAX); 918 } 919 return QSize(qMax(m_sizeHints->maxWidth, 1), qMax(m_sizeHints->maxHeight, 1)); 920 } 921 QSize minSize() const { 922 if (!hasMinSize()) { 923 // according to ICCCM 4.1.23 base size should be used as a fallback 924 return baseSize(); 925 } 926 return QSize(m_sizeHints->minWidth, m_sizeHints->minHeight); 927 } 928 QSize baseSize() const { 929 // Note: not using minSize as fallback 930 if (!hasBaseSize()) { 931 return QSize(0, 0); 932 } 933 return QSize(m_sizeHints->baseWidth, m_sizeHints->baseHeight); 934 } 935 QSize resizeIncrements() const { 936 if (!hasResizeIncrements()) { 937 return QSize(1, 1); 938 } 939 return QSize(qMax(m_sizeHints->widthInc, 1), qMax(m_sizeHints->heightInc, 1)); 940 } 941 xcb_gravity_t windowGravity() const { 942 if (!hasWindowGravity()) { 943 return XCB_GRAVITY_NORTH_WEST; 944 } 945 return xcb_gravity_t(m_sizeHints->winGravity); 946 } 947 QSize minAspect() const { 948 if (!hasAspect()) { 949 return QSize(1, INT_MAX); 950 } 951 // prevent division by zero 952 return QSize(m_sizeHints->minAspect[0], qMax(m_sizeHints->minAspect[1], 1)); 953 } 954 QSize maxAspect() const { 955 if (!hasAspect()) { 956 return QSize(INT_MAX, 1); 957 } 958 // prevent division by zero 959 return QSize(m_sizeHints->maxAspect[0], qMax(m_sizeHints->maxAspect[1], 1)); 960 } 961 962 private: 963 /** 964 * NormalHints as specified in ICCCM 4.1.2.3. 965 */ 966 class NormalHints : public Property 967 { 968 public: 969 struct SizeHints { 970 enum Flags { 971 UserPosition = 1, 972 UserSize = 2, 973 ProgramPosition = 4, 974 ProgramSize = 8, 975 MinSize = 16, 976 MaxSize = 32, 977 ResizeIncrements = 64, 978 Aspect = 128, 979 BaseSize = 256, 980 WindowGravity = 512 981 }; 982 qint32 flags = 0; 983 qint32 pad[4] = {0, 0, 0, 0}; 984 qint32 minWidth = 0; 985 qint32 minHeight = 0; 986 qint32 maxWidth = 0; 987 qint32 maxHeight = 0; 988 qint32 widthInc = 0; 989 qint32 heightInc = 0; 990 qint32 minAspect[2] = {0, 0}; 991 qint32 maxAspect[2] = {0, 0}; 992 qint32 baseWidth = 0; 993 qint32 baseHeight = 0; 994 qint32 winGravity = 0; 995 }; 996 explicit NormalHints() : Property() {}; 997 explicit NormalHints(WindowId window) 998 : Property(0, window, XCB_ATOM_WM_NORMAL_HINTS, XCB_ATOM_WM_SIZE_HINTS, 0, 18) 999 { 1000 } 1001 inline SizeHints *sizeHints() { 1002 return value<SizeHints*>(32, XCB_ATOM_WM_SIZE_HINTS, nullptr); 1003 } 1004 }; 1005 friend TestXcbSizeHints; 1006 bool testFlag(NormalHints::SizeHints::Flags flag) const { 1007 if (!m_window || !m_sizeHints) { 1008 return false; 1009 } 1010 return m_sizeHints->flags & flag; 1011 } 1012 xcb_window_t m_window = XCB_WINDOW_NONE; 1013 NormalHints m_hints; 1014 NormalHints::SizeHints *m_sizeHints = nullptr; 1015 }; 1016 1017 class MotifHints 1018 { 1019 public: 1020 MotifHints(xcb_atom_t atom) : m_atom(atom) {} 1021 void init(xcb_window_t window) { 1022 Q_ASSERT(window); 1023 if (m_window) { 1024 // already initialized 1025 return; 1026 } 1027 m_window = window; 1028 fetch(); 1029 } 1030 void fetch() { 1031 if (!m_window) { 1032 return; 1033 } 1034 m_hints = nullptr; 1035 m_prop = Property(0, m_window, m_atom, m_atom, 0, 5); 1036 } 1037 void read() { 1038 m_hints = m_prop.value<MwmHints*>(32, m_atom, nullptr); 1039 } 1040 bool hasDecoration() const { 1041 if (!m_window || !m_hints) { 1042 return false; 1043 } 1044 return m_hints->flags & uint32_t(Hints::Decorations); 1045 } 1046 bool noBorder() const { 1047 if (!hasDecoration()) { 1048 return false; 1049 } 1050 return !m_hints->decorations; 1051 } 1052 bool resize() const { 1053 return testFunction(Functions::Resize); 1054 } 1055 bool move() const { 1056 return testFunction(Functions::Move); 1057 } 1058 bool minimize() const { 1059 return testFunction(Functions::Minimize); 1060 } 1061 bool maximize() const { 1062 return testFunction(Functions::Maximize); 1063 } 1064 bool close() const { 1065 return testFunction(Functions::Close); 1066 } 1067 1068 private: 1069 struct MwmHints { 1070 uint32_t flags; 1071 uint32_t functions; 1072 uint32_t decorations; 1073 int32_t input_mode; 1074 uint32_t status; 1075 }; 1076 enum class Hints { 1077 Functions = (1L << 0), 1078 Decorations = (1L << 1) 1079 }; 1080 enum class Functions { 1081 All = (1L << 0), 1082 Resize = (1L << 1), 1083 Move = (1L << 2), 1084 Minimize = (1L << 3), 1085 Maximize = (1L << 4), 1086 Close = (1L << 5) 1087 }; 1088 bool testFunction(Functions flag) const { 1089 if (!m_window || !m_hints) { 1090 return true; 1091 } 1092 if (!(m_hints->flags & uint32_t(Hints::Functions))) { 1093 return true; 1094 } 1095 // if MWM_FUNC_ALL is set, other flags say what to turn _off_ 1096 const bool set_value = ((m_hints->functions & uint32_t(Functions::All)) == 0); 1097 if (m_hints->functions & uint32_t(flag)) { 1098 return set_value; 1099 } 1100 return !set_value; 1101 } 1102 xcb_window_t m_window = XCB_WINDOW_NONE; 1103 Property m_prop; 1104 xcb_atom_t m_atom; 1105 MwmHints *m_hints = nullptr; 1106 }; 1107 1108 namespace RandR 1109 { 1110 XCB_WRAPPER(ScreenInfo, xcb_randr_get_screen_info, xcb_window_t) 1111 1112 XCB_WRAPPER_DATA(ScreenResourcesData, xcb_randr_get_screen_resources, xcb_window_t) 1113 class ScreenResources : public Wrapper<ScreenResourcesData, xcb_window_t> 1114 { 1115 public: 1116 explicit ScreenResources(WindowId window) : Wrapper<ScreenResourcesData, xcb_window_t>(window) {} 1117 1118 inline xcb_randr_crtc_t *crtcs() { 1119 if (isNull()) { 1120 return nullptr; 1121 } 1122 return xcb_randr_get_screen_resources_crtcs(data()); 1123 } 1124 inline xcb_randr_mode_info_t *modes() { 1125 if (isNull()) { 1126 return nullptr; 1127 } 1128 return xcb_randr_get_screen_resources_modes(data()); 1129 } 1130 inline uint8_t *names() { 1131 if (isNull()) { 1132 return nullptr; 1133 } 1134 return xcb_randr_get_screen_resources_names(data()); 1135 } 1136 }; 1137 1138 XCB_WRAPPER_DATA(CrtcGammaData, xcb_randr_get_crtc_gamma, xcb_randr_crtc_t) 1139 class CrtcGamma : public Wrapper<CrtcGammaData, xcb_randr_crtc_t> 1140 { 1141 public: 1142 explicit CrtcGamma(xcb_randr_crtc_t c) : Wrapper<CrtcGammaData, xcb_randr_crtc_t>(c) {} 1143 1144 inline uint16_t *red() { 1145 return xcb_randr_get_crtc_gamma_red(data()); 1146 } 1147 inline uint16_t *green() { 1148 return xcb_randr_get_crtc_gamma_green(data()); 1149 } 1150 inline uint16_t *blue() { 1151 return xcb_randr_get_crtc_gamma_blue(data()); 1152 } 1153 }; 1154 1155 XCB_WRAPPER_DATA(CrtcInfoData, xcb_randr_get_crtc_info, xcb_randr_crtc_t, xcb_timestamp_t) 1156 class CrtcInfo : public Wrapper<CrtcInfoData, xcb_randr_crtc_t, xcb_timestamp_t> 1157 { 1158 public: 1159 CrtcInfo() = default; 1160 CrtcInfo(const CrtcInfo&) = default; 1161 explicit CrtcInfo(xcb_randr_crtc_t c, xcb_timestamp_t t) : Wrapper<CrtcInfoData, xcb_randr_crtc_t, xcb_timestamp_t>(c, t) {} 1162 1163 inline QRect rect() { 1164 const CrtcInfoData::reply_type *info = data(); 1165 if (!info || info->num_outputs == 0 || info->mode == XCB_NONE || info->status != XCB_RANDR_SET_CONFIG_SUCCESS) { 1166 return QRect(); 1167 } 1168 return QRect(info->x, info->y, info->width, info->height); 1169 } 1170 inline xcb_randr_output_t *outputs() { 1171 const CrtcInfoData::reply_type *info = data(); 1172 if (!info || info->num_outputs == 0 || info->mode == XCB_NONE || info->status != XCB_RANDR_SET_CONFIG_SUCCESS) { 1173 return nullptr; 1174 } 1175 return xcb_randr_get_crtc_info_outputs(info); 1176 } 1177 }; 1178 1179 XCB_WRAPPER_DATA(OutputInfoData, xcb_randr_get_output_info, xcb_randr_output_t, xcb_timestamp_t) 1180 class OutputInfo : public Wrapper<OutputInfoData, xcb_randr_output_t, xcb_timestamp_t> 1181 { 1182 public: 1183 OutputInfo() = default; 1184 OutputInfo(const OutputInfo&) = default; 1185 explicit OutputInfo(xcb_randr_output_t c, xcb_timestamp_t t) : Wrapper<OutputInfoData, xcb_randr_output_t, xcb_timestamp_t>(c, t) {} 1186 1187 inline QString name() { 1188 const OutputInfoData::reply_type *info = data(); 1189 if (!info || info->num_crtcs == 0 || info->num_modes == 0 || info->status != XCB_RANDR_SET_CONFIG_SUCCESS) { 1190 return QString(); 1191 } 1192 return QString::fromUtf8(reinterpret_cast<char*>(xcb_randr_get_output_info_name(info)), info->name_len); 1193 } 1194 }; 1195 1196 XCB_WRAPPER_DATA(CurrentResourcesData, xcb_randr_get_screen_resources_current, xcb_window_t) 1197 class CurrentResources : public Wrapper<CurrentResourcesData, xcb_window_t> 1198 { 1199 public: 1200 explicit CurrentResources(WindowId window) : Wrapper<CurrentResourcesData, xcb_window_t>(window) {} 1201 1202 inline xcb_randr_crtc_t *crtcs() { 1203 if (isNull()) { 1204 return nullptr; 1205 } 1206 return xcb_randr_get_screen_resources_current_crtcs(data()); 1207 } 1208 inline xcb_randr_mode_info_t *modes() { 1209 if (isNull()) { 1210 return nullptr; 1211 } 1212 return xcb_randr_get_screen_resources_current_modes(data()); 1213 } 1214 }; 1215 1216 XCB_WRAPPER(SetCrtcConfig, xcb_randr_set_crtc_config, xcb_randr_crtc_t, xcb_timestamp_t, xcb_timestamp_t, int16_t, int16_t, xcb_randr_mode_t, uint16_t, uint32_t, const xcb_randr_output_t*) 1217 } 1218 1219 class ExtensionData 1220 { 1221 public: 1222 ExtensionData(); 1223 int version; 1224 int eventBase; 1225 int errorBase; 1226 int majorOpcode; 1227 bool present; 1228 QByteArray name; 1229 QVector<QByteArray> opCodes; 1230 QVector<QByteArray> errorCodes; 1231 }; 1232 1233 class KWIN_EXPORT Extensions 1234 { 1235 public: 1236 bool isShapeAvailable() const { 1237 return m_shape.version > 0; 1238 } 1239 bool isShapeInputAvailable() const; 1240 int shapeNotifyEvent() const; 1241 bool hasShape(xcb_window_t w) const; 1242 bool isRandrAvailable() const { 1243 return m_randr.present; 1244 } 1245 int randrNotifyEvent() const; 1246 bool isDamageAvailable() const { 1247 return m_damage.present; 1248 } 1249 int damageNotifyEvent() const; 1250 bool isCompositeAvailable() const { 1251 return m_composite.version > 0; 1252 } 1253 bool isCompositeOverlayAvailable() const; 1254 bool isRenderAvailable() const { 1255 return m_render.version > 0; 1256 } 1257 bool isFixesAvailable() const { 1258 return m_fixes.version > 0; 1259 } 1260 int fixesCursorNotifyEvent() const; 1261 int fixesSelectionNotifyEvent() const; 1262 bool isFixesRegionAvailable() const; 1263 bool isSyncAvailable() const { 1264 return m_sync.present; 1265 } 1266 int syncAlarmNotifyEvent() const; 1267 QVector<ExtensionData> extensions() const; 1268 bool hasGlx() const { 1269 return m_glx.present; 1270 } 1271 int glxEventBase() const { 1272 return m_glx.eventBase; 1273 } 1274 int glxMajorOpcode() const { 1275 return m_glx.majorOpcode; 1276 } 1277 1278 static Extensions *self(); 1279 static void destroy(); 1280 private: 1281 Extensions(); 1282 ~Extensions(); 1283 void init(); 1284 template <typename reply, typename T, typename F> 1285 void initVersion(T cookie, F f, ExtensionData *dataToFill); 1286 void extensionQueryReply(const xcb_query_extension_reply_t *extension, ExtensionData *dataToFill); 1287 1288 ExtensionData m_shape; 1289 ExtensionData m_randr; 1290 ExtensionData m_damage; 1291 ExtensionData m_composite; 1292 ExtensionData m_render; 1293 ExtensionData m_fixes; 1294 ExtensionData m_sync; 1295 ExtensionData m_glx; 1296 1297 static Extensions *s_self; 1298 }; 1299 1300 /** 1301 * This class is an RAII wrapper for an xcb_window_t. An xcb_window_t hold by an instance of this class 1302 * will be freed when the instance gets destroyed. 1303 * 1304 * Furthermore the class provides wrappers around some xcb methods operating on an xcb_window_t. 1305 * 1306 * For the cases that one is more interested in wrapping the xcb methods the constructor which takes 1307 * an existing window and the @ref reset method allow to disable the RAII functionality. 1308 */ 1309 class Window 1310 { 1311 public: 1312 /** 1313 * Takes over responsibility of @p window. If @p window is not provided an invalid Window is 1314 * created. Use @ref create to set an xcb_window_t later on. 1315 * 1316 * If @p destroy is @c true the window will be destroyed together with this object, if @c false 1317 * the window will be kept around. This is useful if you are not interested in the RAII capabilities 1318 * but still want to use a window like an object. 1319 * 1320 * @param window The window to manage. 1321 * @param destroy Whether the window should be destroyed together with the object. 1322 * @see reset 1323 */ 1324 Window(xcb_window_t window = XCB_WINDOW_NONE, bool destroy = true); 1325 /** 1326 * Creates an xcb_window_t and manages it. It's a convenient method to create a window with 1327 * depth, class and visual being copied from parent and border being @c 0. 1328 * @param geometry The geometry for the window to be created 1329 * @param mask The mask for the values 1330 * @param values The values to be passed to xcb_create_window 1331 * @param parent The parent window 1332 */ 1333 Window(const QRect &geometry, uint32_t mask = 0, const uint32_t *values = nullptr, xcb_window_t parent = rootWindow()); 1334 /** 1335 * Creates an xcb_window_t and manages it. It's a convenient method to create a window with 1336 * depth and visual being copied from parent and border being @c 0. 1337 * @param geometry The geometry for the window to be created 1338 * @param windowClass The window class 1339 * @param mask The mask for the values 1340 * @param values The values to be passed to xcb_create_window 1341 * @param parent The parent window 1342 */ 1343 Window(const QRect &geometry, uint16_t windowClass, uint32_t mask = 0, const uint32_t *values = nullptr, xcb_window_t parent = rootWindow()); 1344 Window(const Window &other) = delete; 1345 ~Window(); 1346 1347 /** 1348 * Creates a new window for which the responsibility is taken over. If a window had been managed 1349 * before it is freed. 1350 * 1351 * Depth, class and visual are being copied from parent and border is @c 0. 1352 * @param geometry The geometry for the window to be created 1353 * @param mask The mask for the values 1354 * @param values The values to be passed to xcb_create_window 1355 * @param parent The parent window 1356 */ 1357 void create(const QRect &geometry, uint32_t mask = 0, const uint32_t *values = nullptr, xcb_window_t parent = rootWindow()); 1358 /** 1359 * Creates a new window for which the responsibility is taken over. If a window had been managed 1360 * before it is freed. 1361 * 1362 * Depth and visual are being copied from parent and border is @c 0. 1363 * @param geometry The geometry for the window to be created 1364 * @param windowClass The window class 1365 * @param mask The mask for the values 1366 * @param values The values to be passed to xcb_create_window 1367 * @param parent The parent window 1368 */ 1369 void create(const QRect &geometry, uint16_t windowClass, uint32_t mask = 0, const uint32_t *values = nullptr, xcb_window_t parent = rootWindow()); 1370 /** 1371 * Frees the existing window and starts to manage the new @p window. 1372 * If @p destroy is @c true the new managed window will be destroyed together with this 1373 * object or when reset is called again. If @p destroy is @c false the window will not 1374 * be destroyed. It is then the responsibility of the caller to destroy the window. 1375 */ 1376 void reset(xcb_window_t window = XCB_WINDOW_NONE, bool destroy = true); 1377 /** 1378 * @returns @c true if a window is managed, @c false otherwise. 1379 */ 1380 bool isValid() const; 1381 inline const QRect &geometry() const { return m_logicGeometry; } 1382 /** 1383 * Configures the window with a new geometry. 1384 * @param geometry The new window geometry to be used 1385 */ 1386 void setGeometry(const QRect &geometry); 1387 void setGeometry(uint32_t x, uint32_t y, uint32_t width, uint32_t height); 1388 void move(const QPoint &pos); 1389 void move(uint32_t x, uint32_t y); 1390 void resize(const QSize &size); 1391 void resize(uint32_t width, uint32_t height); 1392 void raise(); 1393 void lower(); 1394 void map(); 1395 void unmap(); 1396 void reparent(xcb_window_t parent, int x = 0, int y = 0); 1397 void changeProperty(xcb_atom_t property, xcb_atom_t type, uint8_t format, uint32_t length, 1398 const void *data, uint8_t mode = XCB_PROP_MODE_REPLACE); 1399 void deleteProperty(xcb_atom_t property); 1400 void setBorderWidth(uint32_t width); 1401 void grabButton(uint8_t pointerMode, uint8_t keyboardmode, 1402 uint16_t modifiers = XCB_MOD_MASK_ANY, 1403 uint8_t button = XCB_BUTTON_INDEX_ANY, 1404 uint16_t eventMask = XCB_EVENT_MASK_BUTTON_PRESS, 1405 xcb_window_t confineTo = XCB_WINDOW_NONE, 1406 xcb_cursor_t cursor = XCB_CURSOR_NONE, 1407 bool ownerEvents = false); 1408 void ungrabButton(uint16_t modifiers = XCB_MOD_MASK_ANY, uint8_t button = XCB_BUTTON_INDEX_ANY); 1409 /** 1410 * Clears the window area. Same as xcb_clear_area with x, y, width, height being @c 0. 1411 */ 1412 void clear(); 1413 void setBackgroundPixmap(xcb_pixmap_t pixmap); 1414 void defineCursor(xcb_cursor_t cursor); 1415 void focus(uint8_t revertTo = XCB_INPUT_FOCUS_POINTER_ROOT, xcb_timestamp_t time = XCB_TIME_CURRENT_TIME); 1416 void selectInput(uint32_t events); 1417 void kill(); 1418 operator xcb_window_t() const; 1419 private: 1420 xcb_window_t doCreate(const QRect &geometry, uint16_t windowClass, uint32_t mask = 0, const uint32_t *values = nullptr, xcb_window_t parent = rootWindow()); 1421 void destroy(); 1422 xcb_window_t m_window; 1423 bool m_destroy; 1424 QRect m_logicGeometry; 1425 }; 1426 1427 inline 1428 Window::Window(xcb_window_t window, bool destroy) 1429 : m_window(window) 1430 , m_destroy(destroy) 1431 { 1432 } 1433 1434 inline 1435 Window::Window(const QRect &geometry, uint32_t mask, const uint32_t *values, xcb_window_t parent) 1436 : m_window(doCreate(geometry, XCB_COPY_FROM_PARENT, mask, values, parent)) 1437 , m_destroy(true) 1438 { 1439 } 1440 1441 inline 1442 Window::Window(const QRect &geometry, uint16_t windowClass, uint32_t mask, const uint32_t *values, xcb_window_t parent) 1443 : m_window(doCreate(geometry, windowClass, mask, values, parent)) 1444 , m_destroy(true) 1445 { 1446 } 1447 1448 inline 1449 Window::~Window() 1450 { 1451 destroy(); 1452 } 1453 1454 inline 1455 void Window::destroy() 1456 { 1457 if (!isValid() || !m_destroy) { 1458 return; 1459 } 1460 xcb_destroy_window(connection(), m_window); 1461 m_window = XCB_WINDOW_NONE; 1462 } 1463 1464 inline 1465 bool Window::isValid() const 1466 { 1467 return m_window != XCB_WINDOW_NONE; 1468 } 1469 1470 inline 1471 Window::operator xcb_window_t() const 1472 { 1473 return m_window; 1474 } 1475 1476 inline 1477 void Window::create(const QRect &geometry, uint16_t windowClass, uint32_t mask, const uint32_t *values, xcb_window_t parent) 1478 { 1479 destroy(); 1480 m_window = doCreate(geometry, windowClass, mask, values, parent); 1481 } 1482 1483 inline 1484 void Window::create(const QRect &geometry, uint32_t mask, const uint32_t *values, xcb_window_t parent) 1485 { 1486 create(geometry, XCB_COPY_FROM_PARENT, mask, values, parent); 1487 } 1488 1489 inline 1490 xcb_window_t Window::doCreate(const QRect &geometry, uint16_t windowClass, uint32_t mask, const uint32_t *values, xcb_window_t parent) 1491 { 1492 m_logicGeometry = geometry; 1493 xcb_window_t w = xcb_generate_id(connection()); 1494 xcb_create_window(connection(), XCB_COPY_FROM_PARENT, w, parent, 1495 geometry.x(), geometry.y(), geometry.width(), geometry.height(), 1496 0, windowClass, XCB_COPY_FROM_PARENT, mask, values); 1497 return w; 1498 } 1499 1500 inline 1501 void Window::reset(xcb_window_t window, bool shouldDestroy) 1502 { 1503 destroy(); 1504 m_window = window; 1505 m_destroy = shouldDestroy; 1506 } 1507 1508 inline 1509 void Window::setGeometry(const QRect &geometry) 1510 { 1511 setGeometry(geometry.x(), geometry.y(), geometry.width(), geometry.height()); 1512 } 1513 1514 inline 1515 void Window::setGeometry(uint32_t x, uint32_t y, uint32_t width, uint32_t height) 1516 { 1517 m_logicGeometry.setRect(x, y, width, height); 1518 if (!isValid()) { 1519 return; 1520 } 1521 const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; 1522 const uint32_t values[] = { x, y, width, height }; 1523 xcb_configure_window(connection(), m_window, mask, values); 1524 } 1525 1526 inline 1527 void Window::move(const QPoint &pos) 1528 { 1529 move(pos.x(), pos.y()); 1530 } 1531 1532 inline 1533 void Window::move(uint32_t x, uint32_t y) 1534 { 1535 m_logicGeometry.moveTo(x, y); 1536 if (!isValid()) { 1537 return; 1538 } 1539 moveWindow(m_window, x, y); 1540 } 1541 1542 inline 1543 void Window::resize(const QSize &size) 1544 { 1545 resize(size.width(), size.height()); 1546 } 1547 1548 inline 1549 void Window::resize(uint32_t width, uint32_t height) 1550 { 1551 m_logicGeometry.setSize(QSize(width, height)); 1552 if (!isValid()) { 1553 return; 1554 } 1555 const uint16_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; 1556 const uint32_t values[] = { width, height }; 1557 xcb_configure_window(connection(), m_window, mask, values); 1558 } 1559 1560 inline 1561 void Window::raise() 1562 { 1563 const uint32_t values[] = { XCB_STACK_MODE_ABOVE }; 1564 xcb_configure_window(connection(), m_window, XCB_CONFIG_WINDOW_STACK_MODE, values); 1565 } 1566 1567 inline 1568 void Window::lower() 1569 { 1570 lowerWindow(m_window); 1571 } 1572 1573 inline 1574 void Window::map() 1575 { 1576 if (!isValid()) { 1577 return; 1578 } 1579 xcb_map_window(connection(), m_window); 1580 } 1581 1582 inline 1583 void Window::unmap() 1584 { 1585 if (!isValid()) { 1586 return; 1587 } 1588 xcb_unmap_window(connection(), m_window); 1589 } 1590 1591 inline 1592 void Window::reparent(xcb_window_t parent, int x, int y) 1593 { 1594 if (!isValid()) { 1595 return; 1596 } 1597 xcb_reparent_window(connection(), m_window, parent, x, y); 1598 } 1599 1600 inline 1601 void Window::changeProperty(xcb_atom_t property, xcb_atom_t type, uint8_t format, uint32_t length, const void *data, uint8_t mode) 1602 { 1603 if (!isValid()) { 1604 return; 1605 } 1606 xcb_change_property(connection(), mode, m_window, property, type, format, length, data); 1607 } 1608 1609 inline 1610 void Window::deleteProperty(xcb_atom_t property) 1611 { 1612 if (!isValid()) { 1613 return; 1614 } 1615 xcb_delete_property(connection(), m_window, property); 1616 } 1617 1618 inline 1619 void Window::setBorderWidth(uint32_t width) 1620 { 1621 if (!isValid()) { 1622 return; 1623 } 1624 xcb_configure_window(connection(), m_window, XCB_CONFIG_WINDOW_BORDER_WIDTH, &width); 1625 } 1626 1627 inline 1628 void Window::grabButton(uint8_t pointerMode, uint8_t keyboardmode, uint16_t modifiers, 1629 uint8_t button, uint16_t eventMask, xcb_window_t confineTo, 1630 xcb_cursor_t cursor, bool ownerEvents) 1631 { 1632 if (!isValid()) { 1633 return; 1634 } 1635 xcb_grab_button(connection(), ownerEvents, m_window, eventMask, 1636 pointerMode, keyboardmode, confineTo, cursor, button, modifiers); 1637 } 1638 1639 inline 1640 void Window::ungrabButton(uint16_t modifiers, uint8_t button) 1641 { 1642 if (!isValid()) { 1643 return; 1644 } 1645 xcb_ungrab_button(connection(), button, m_window, modifiers); 1646 } 1647 1648 inline 1649 void Window::clear() 1650 { 1651 if (!isValid()) { 1652 return; 1653 } 1654 xcb_clear_area(connection(), false, m_window, 0, 0, 0, 0); 1655 } 1656 1657 inline 1658 void Window::setBackgroundPixmap(xcb_pixmap_t pixmap) 1659 { 1660 if (!isValid()) { 1661 return; 1662 } 1663 const uint32_t values[] = {pixmap}; 1664 xcb_change_window_attributes(connection(), m_window, XCB_CW_BACK_PIXMAP, values); 1665 } 1666 1667 inline 1668 void Window::defineCursor(xcb_cursor_t cursor) 1669 { 1670 Xcb::defineCursor(m_window, cursor); 1671 } 1672 1673 inline 1674 void Window::focus(uint8_t revertTo, xcb_timestamp_t time) 1675 { 1676 setInputFocus(m_window, revertTo, time); 1677 } 1678 1679 inline 1680 void Window::selectInput(uint32_t events) 1681 { 1682 Xcb::selectInput(m_window, events); 1683 } 1684 1685 inline 1686 void Window::kill() 1687 { 1688 xcb_kill_client(connection(), m_window); 1689 } 1690 1691 // helper functions 1692 static inline void moveResizeWindow(WindowId window, const QRect &geometry) 1693 { 1694 const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; 1695 const uint32_t values[] = { 1696 static_cast<uint32_t>(geometry.x()), 1697 static_cast<uint32_t>(geometry.y()), 1698 static_cast<uint32_t>(geometry.width()), 1699 static_cast<uint32_t>(geometry.height()) 1700 }; 1701 xcb_configure_window(connection(), window, mask, values); 1702 } 1703 1704 static inline void moveWindow(xcb_window_t window, const QPoint& pos) 1705 { 1706 moveWindow(window, pos.x(), pos.y()); 1707 } 1708 1709 static inline void moveWindow(xcb_window_t window, uint32_t x, uint32_t y) 1710 { 1711 const uint16_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y; 1712 const uint32_t values[] = { x, y }; 1713 xcb_configure_window(connection(), window, mask, values); 1714 } 1715 1716 static inline void lowerWindow(xcb_window_t window) 1717 { 1718 const uint32_t values[] = { XCB_STACK_MODE_BELOW }; 1719 xcb_configure_window(connection(), window, XCB_CONFIG_WINDOW_STACK_MODE, values); 1720 } 1721 1722 static inline WindowId createInputWindow(const QRect &geometry, uint32_t mask, const uint32_t *values) 1723 { 1724 WindowId window = xcb_generate_id(connection()); 1725 xcb_create_window(connection(), 0, window, rootWindow(), 1726 geometry.x(), geometry.y(), geometry.width(), geometry.height(), 1727 0, XCB_WINDOW_CLASS_INPUT_ONLY, 1728 XCB_COPY_FROM_PARENT, mask, values); 1729 return window; 1730 } 1731 1732 static inline void restackWindows(const QVector<xcb_window_t> &windows) 1733 { 1734 if (windows.count() < 2) { 1735 // only one window, nothing to do 1736 return; 1737 } 1738 for (int i=1; i<windows.count(); ++i) { 1739 const uint16_t mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE; 1740 const uint32_t stackingValues[] = { 1741 windows.at(i-1), 1742 XCB_STACK_MODE_BELOW 1743 }; 1744 xcb_configure_window(connection(), windows.at(i), mask, stackingValues); 1745 } 1746 } 1747 1748 static inline void restackWindowsWithRaise(const QVector<xcb_window_t> &windows) 1749 { 1750 if (windows.isEmpty()) { 1751 return; 1752 } 1753 const uint32_t values[] = { XCB_STACK_MODE_ABOVE }; 1754 xcb_configure_window(connection(), windows.first(), XCB_CONFIG_WINDOW_STACK_MODE, values); 1755 restackWindows(windows); 1756 } 1757 1758 static inline int defaultDepth() 1759 { 1760 static int depth = 0; 1761 if (depth != 0) { 1762 return depth; 1763 } 1764 int screen = Application::x11ScreenNumber(); 1765 for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(connection())); 1766 it.rem; 1767 --screen, xcb_screen_next(&it)) { 1768 if (screen == 0) { 1769 depth = it.data->root_depth; 1770 break; 1771 } 1772 } 1773 return depth; 1774 } 1775 1776 static inline xcb_rectangle_t fromQt(const QRect &rect) 1777 { 1778 xcb_rectangle_t rectangle; 1779 rectangle.x = rect.x(); 1780 rectangle.y = rect.y(); 1781 rectangle.width = rect.width(); 1782 rectangle.height = rect.height(); 1783 return rectangle; 1784 } 1785 1786 static inline QVector<xcb_rectangle_t> regionToRects(const QRegion ®ion) 1787 { 1788 QVector<xcb_rectangle_t> rects; 1789 rects.reserve(region.rectCount()); 1790 for (const QRect &rect : region) { 1791 rects.append(Xcb::fromQt(rect)); 1792 } 1793 return rects; 1794 } 1795 1796 static inline void defineCursor(xcb_window_t window, xcb_cursor_t cursor) 1797 { 1798 xcb_change_window_attributes(connection(), window, XCB_CW_CURSOR, &cursor); 1799 } 1800 1801 static inline void setInputFocus(xcb_window_t window, uint8_t revertTo, xcb_timestamp_t time) 1802 { 1803 xcb_set_input_focus(connection(), revertTo, window, time); 1804 } 1805 1806 static inline void setTransientFor(xcb_window_t window, xcb_window_t transient_for_window) 1807 { 1808 xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_TRANSIENT_FOR, 1809 XCB_ATOM_WINDOW, 32, 1, &transient_for_window); 1810 } 1811 1812 static inline void sync() 1813 { 1814 auto *c = connection(); 1815 const auto cookie = xcb_get_input_focus(c); 1816 xcb_generic_error_t *error = nullptr; 1817 ScopedCPointer<xcb_get_input_focus_reply_t> sync(xcb_get_input_focus_reply(c, cookie, &error)); 1818 if (error) { 1819 free(error); 1820 } 1821 } 1822 1823 void selectInput(xcb_window_t window, uint32_t events) 1824 { 1825 xcb_change_window_attributes(connection(), window, XCB_CW_EVENT_MASK, &events); 1826 } 1827 1828 /** 1829 * @brief Small helper class to encapsulate SHM related functionality. 1830 */ 1831 class Shm 1832 { 1833 public: 1834 Shm(); 1835 ~Shm(); 1836 int shmId() const; 1837 void *buffer() const; 1838 xcb_shm_seg_t segment() const; 1839 bool isValid() const; 1840 uint8_t pixmapFormat() const; 1841 private: 1842 bool init(); 1843 int m_shmId; 1844 void *m_buffer; 1845 xcb_shm_seg_t m_segment; 1846 bool m_valid; 1847 uint8_t m_pixmapFormat; 1848 }; 1849 1850 inline 1851 void *Shm::buffer() const 1852 { 1853 return m_buffer; 1854 } 1855 1856 inline 1857 bool Shm::isValid() const 1858 { 1859 return m_valid; 1860 } 1861 1862 inline 1863 xcb_shm_seg_t Shm::segment() const 1864 { 1865 return m_segment; 1866 } 1867 1868 inline 1869 int Shm::shmId() const 1870 { 1871 return m_shmId; 1872 } 1873 1874 inline 1875 uint8_t Shm::pixmapFormat() const 1876 { 1877 return m_pixmapFormat; 1878 } 1879 1880 } // namespace X11 1881 1882 } // namespace KWin 1883 #endif // KWIN_X11_UTILS_H 1884