1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the plugins of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qxcbconnection.h"
43 #include "qxcbkeyboard.h"
44 #include "qxcbscreen.h"
45 #include "qxcbwindow.h"
47 #include <QtAlgorithms>
48 #include <QSocketNotifier>
49 #include <QtGui/private/qapplication_p.h>
50 #include <QAbstractEventDispatcher>
52 #include <QtCore/QDebug>
59 #include <X11/Xlib-xcb.h>
62 #ifdef XCB_USE_EGL //dont pull in eglext prototypes
68 #include <xcb/xfixes.h>
72 #define MESA_EGL_NO_X11_HEADERS
73 #define EGL_EGLEXT_PROTOTYPES
75 #include <EGL/eglext.h>
78 QXcbConnection::QXcbConnection(const char *displayName)
79 : m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY"))
83 , m_dri2_support_probed(false)
84 , m_has_support_for_dri2(false)
87 int primaryScreen = 0;
90 Display *dpy = XOpenDisplay(m_displayName.constData());
91 primaryScreen = DefaultScreen(dpy);
92 m_connection = XGetXCBConnection(dpy);
93 XSetEventQueueOwner(dpy, XCBOwnsEventQueue);
96 EGLDisplay eglDisplay = eglGetDisplay(dpy);
97 m_egl_display = eglDisplay;
99 eglBindAPI(EGL_OPENGL_ES_API);
100 m_has_egl = eglInitialize(eglDisplay,&major,&minor);
103 m_connection = xcb_connect(m_displayName.constData(), &primaryScreen);
105 #endif //XCB_USE_XLIB
106 m_setup = xcb_get_setup(xcb_connection());
108 initializeAllAtoms();
110 xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup);
112 int screenNumber = 0;
114 m_screens << new QXcbScreen(this, it.data, screenNumber++);
115 xcb_screen_next(&it);
118 m_keyboard = new QXcbKeyboard(this);
124 QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(xcb_connection()), QSocketNotifier::Read, this);
125 connect(notifier, SIGNAL(activated(int)), this, SLOT(processXcbEvents()));
127 QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance(qApp->thread());
128 connect(dispatcher, SIGNAL(aboutToBlock()), this, SLOT(processXcbEvents()));
133 QXcbConnection::~QXcbConnection()
135 qDeleteAll(m_screens);
138 XCloseDisplay((Display *)m_xlib_display);
140 xcb_disconnect(xcb_connection());
146 QXcbWindow *platformWindowFromId(xcb_window_t id)
148 QWidget *widget = QWidget::find(id);
150 return static_cast<QXcbWindow *>(widget->platformWindow());
154 #define HANDLE_PLATFORM_WINDOW_EVENT(event_t, window, handler) \
156 event_t *e = (event_t *)event; \
157 if (QXcbWindow *platformWindow = platformWindowFromId(e->window)) { \
158 QObjectPrivate *d = QObjectPrivate::get(platformWindow->widget()); \
159 if (!d->wasDeleted) \
160 platformWindow->handler(e); \
165 #define HANDLE_KEYBOARD_EVENT(event_t, handler) \
167 event_t *e = (event_t *)event; \
168 if (QXcbWindow *platformWindow = platformWindowFromId(e->event)) \
169 m_keyboard->handler(platformWindow->widget(), e); \
173 //#define XCB_EVENT_DEBUG
175 void printXcbEvent(const char *message, xcb_generic_event_t *event)
177 #ifdef XCB_EVENT_DEBUG
178 #define PRINT_XCB_EVENT(ev) \
180 printf("%s: %d - %s - sequence: %d\n", message, int(ev), #ev, event->sequence); \
183 switch (event->response_type & ~0x80) {
184 PRINT_XCB_EVENT(XCB_KEY_PRESS);
185 PRINT_XCB_EVENT(XCB_KEY_RELEASE);
186 PRINT_XCB_EVENT(XCB_BUTTON_PRESS);
187 PRINT_XCB_EVENT(XCB_BUTTON_RELEASE);
188 PRINT_XCB_EVENT(XCB_MOTION_NOTIFY);
189 PRINT_XCB_EVENT(XCB_ENTER_NOTIFY);
190 PRINT_XCB_EVENT(XCB_LEAVE_NOTIFY);
191 PRINT_XCB_EVENT(XCB_FOCUS_IN);
192 PRINT_XCB_EVENT(XCB_FOCUS_OUT);
193 PRINT_XCB_EVENT(XCB_KEYMAP_NOTIFY);
194 PRINT_XCB_EVENT(XCB_EXPOSE);
195 PRINT_XCB_EVENT(XCB_GRAPHICS_EXPOSURE);
196 PRINT_XCB_EVENT(XCB_VISIBILITY_NOTIFY);
197 PRINT_XCB_EVENT(XCB_CREATE_NOTIFY);
198 PRINT_XCB_EVENT(XCB_DESTROY_NOTIFY);
199 PRINT_XCB_EVENT(XCB_UNMAP_NOTIFY);
200 PRINT_XCB_EVENT(XCB_MAP_NOTIFY);
201 PRINT_XCB_EVENT(XCB_MAP_REQUEST);
202 PRINT_XCB_EVENT(XCB_REPARENT_NOTIFY);
203 PRINT_XCB_EVENT(XCB_CONFIGURE_NOTIFY);
204 PRINT_XCB_EVENT(XCB_CONFIGURE_REQUEST);
205 PRINT_XCB_EVENT(XCB_GRAVITY_NOTIFY);
206 PRINT_XCB_EVENT(XCB_RESIZE_REQUEST);
207 PRINT_XCB_EVENT(XCB_CIRCULATE_NOTIFY);
208 PRINT_XCB_EVENT(XCB_CIRCULATE_REQUEST);
209 PRINT_XCB_EVENT(XCB_PROPERTY_NOTIFY);
210 PRINT_XCB_EVENT(XCB_SELECTION_CLEAR);
211 PRINT_XCB_EVENT(XCB_SELECTION_REQUEST);
212 PRINT_XCB_EVENT(XCB_SELECTION_NOTIFY);
213 PRINT_XCB_EVENT(XCB_COLORMAP_NOTIFY);
214 PRINT_XCB_EVENT(XCB_CLIENT_MESSAGE);
215 PRINT_XCB_EVENT(XCB_MAPPING_NOTIFY);
217 printf("%s: unknown event - response_type: %d - sequence: %d\n", message, int(event->response_type & ~0x80), int(event->sequence));
225 const char *xcb_errors[] =
248 const char *xcb_protocol_request_codes[] =
252 "ChangeWindowAttributes",
253 "GetWindowAttributes",
280 "ChangeActivePointerGrab",
330 "CopyColormapAndFree",
333 "ListInstalledColormaps",
350 "ChangeKeyboardMapping",
351 "GetKeyboardMapping",
352 "ChangeKeyboardControl",
353 "GetKeyboardControl",
355 "ChangePointerControl",
368 "SetModifierMapping",
369 "GetModifierMapping",
374 void QXcbConnection::log(const char *file, int line, int sequence)
377 info.sequence = sequence;
384 void QXcbConnection::handleXcbError(xcb_generic_error_t *error)
386 uint clamped_error_code = qMin<uint>(error->error_code, (sizeof(xcb_errors) / sizeof(xcb_errors[0])) - 1);
387 uint clamped_major_code = qMin<uint>(error->major_code, (sizeof(xcb_protocol_request_codes) / sizeof(xcb_protocol_request_codes[0])) - 1);
389 printf("XCB error: %d (%s), sequence: %d, resource id: %d, major code: %d (%s), minor code: %d\n",
390 int(error->error_code), xcb_errors[clamped_error_code],
391 int(error->sequence), int(error->resource_id),
392 int(error->major_code), xcb_protocol_request_codes[clamped_major_code],
393 int(error->minor_code));
396 for (; i < m_callLog.size(); ++i) {
397 if (m_callLog.at(i).sequence == error->sequence) {
398 printf("Caused by: %s:%d\n", qPrintable(m_callLog.at(i).file), m_callLog.at(i).line);
400 } else if (m_callLog.at(i).sequence > error->sequence) {
401 printf("Caused some time before: %s:%d\n", qPrintable(m_callLog.at(i).file), m_callLog.at(i).line);
403 printf("and after: %s:%d\n", qPrintable(m_callLog.at(i-1).file), m_callLog.at(i-1).line);
407 if (i == m_callLog.size() && !m_callLog.isEmpty())
408 printf("Caused some time after: %s:%d\n", qPrintable(m_callLog.first().file), m_callLog.first().line);
412 void QXcbConnection::processXcbEvents()
414 while (xcb_generic_event_t *event = xcb_poll_for_event(xcb_connection())) {
417 uint response_type = event->response_type & ~0x80;
419 if (!response_type) {
420 handleXcbError((xcb_generic_error_t *)event);
427 for (; i < m_callLog.size(); ++i)
428 if (m_callLog.at(i).sequence >= event->sequence)
430 m_callLog.remove(0, i);
434 switch (response_type) {
436 HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
437 case XCB_BUTTON_PRESS:
438 HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
439 case XCB_BUTTON_RELEASE:
440 HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);
441 case XCB_MOTION_NOTIFY:
442 HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent);
443 case XCB_CONFIGURE_NOTIFY:
444 HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent);
445 case XCB_CLIENT_MESSAGE:
446 HANDLE_PLATFORM_WINDOW_EVENT(xcb_client_message_event_t, window, handleClientMessageEvent);
447 case XCB_ENTER_NOTIFY:
448 HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
449 case XCB_LEAVE_NOTIFY:
450 HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent);
452 HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent);
454 HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent);
456 HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent);
457 case XCB_KEY_RELEASE:
458 HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent);
459 case XCB_MAPPING_NOTIFY:
460 m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event);
467 printXcbEvent("Handled XCB event", event);
469 printXcbEvent("Unhandled XCB event", event);
472 xcb_flush(xcb_connection());
475 static const char * xcb_atomnames = {
476 // window-manager <-> client protocols
481 "_NET_WM_CONTEXT_HELP\0"
482 "_NET_WM_SYNC_REQUEST\0"
483 "_NET_WM_SYNC_REQUEST_COUNTER\0"
485 // ICCCM window state
489 // Session management
503 "_QT_CLIPBOARD_SENTINEL\0"
504 "_QT_SELECTION_SENTINEL\0"
505 "CLIPBOARD_MANAGER\0"
512 "_QT_INPUT_ENCODING\0"
517 "ENLIGHTENMENT_DESKTOP\0"
519 "_SGI_DESKS_MANAGER\0"
523 "_NET_VIRTUAL_ROOTS\0"
526 "_NET_MOVERESIZE_WINDOW\0"
527 "_NET_WM_MOVERESIZE\0"
530 "_NET_WM_ICON_NAME\0"
535 "_NET_WM_WINDOW_OPACITY\0"
538 "_NET_WM_STATE_ABOVE\0"
539 "_NET_WM_STATE_BELOW\0"
540 "_NET_WM_STATE_FULLSCREEN\0"
541 "_NET_WM_STATE_MAXIMIZED_HORZ\0"
542 "_NET_WM_STATE_MAXIMIZED_VERT\0"
543 "_NET_WM_STATE_MODAL\0"
544 "_NET_WM_STATE_STAYS_ON_TOP\0"
545 "_NET_WM_STATE_DEMANDS_ATTENTION\0"
547 "_NET_WM_USER_TIME\0"
548 "_NET_WM_USER_TIME_WINDOW\0"
549 "_NET_WM_FULL_PLACEMENT\0"
551 "_NET_WM_WINDOW_TYPE\0"
552 "_NET_WM_WINDOW_TYPE_DESKTOP\0"
553 "_NET_WM_WINDOW_TYPE_DOCK\0"
554 "_NET_WM_WINDOW_TYPE_TOOLBAR\0"
555 "_NET_WM_WINDOW_TYPE_MENU\0"
556 "_NET_WM_WINDOW_TYPE_UTILITY\0"
557 "_NET_WM_WINDOW_TYPE_SPLASH\0"
558 "_NET_WM_WINDOW_TYPE_DIALOG\0"
559 "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0"
560 "_NET_WM_WINDOW_TYPE_POPUP_MENU\0"
561 "_NET_WM_WINDOW_TYPE_TOOLTIP\0"
562 "_NET_WM_WINDOW_TYPE_NOTIFICATION\0"
563 "_NET_WM_WINDOW_TYPE_COMBO\0"
564 "_NET_WM_WINDOW_TYPE_DND\0"
565 "_NET_WM_WINDOW_TYPE_NORMAL\0"
566 "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0"
568 "_KDE_NET_WM_FRAME_STRUT\0"
570 "_NET_STARTUP_INFO\0"
571 "_NET_STARTUP_INFO_BEGIN\0"
573 "_NET_SUPPORTING_WM_CHECK\0"
577 "_NET_SYSTEM_TRAY_VISUAL\0"
579 "_NET_ACTIVE_WINDOW\0"
604 "XdndActionPrivate\0"
607 "_MOTIF_DRAG_AND_DROP_MESSAGE\0"
608 "_MOTIF_DRAG_INITIATOR_INFO\0"
609 "_MOTIF_DRAG_RECEIVER_INFO\0"
610 "_MOTIF_DRAG_WINDOW\0"
611 "_MOTIF_DRAG_TARGETS\0"
613 "XmTRANSFER_SUCCESS\0"
614 "XmTRANSFER_FAILURE\0"
623 // Wacom old. (before version 0.10)
633 xcb_atom_t QXcbConnection::atom(QXcbAtom::Atom atom)
635 return m_allAtoms[atom];
638 void QXcbConnection::initializeAllAtoms() {
639 const char *names[QXcbAtom::NAtoms];
640 const char *ptr = xcb_atomnames;
650 Q_ASSERT(i == QXcbAtom::NPredefinedAtoms);
652 QByteArray settings_atom_name("_QT_SETTINGS_TIMESTAMP_");
653 settings_atom_name += m_displayName;
654 names[i++] = settings_atom_name;
656 xcb_intern_atom_cookie_t cookies[QXcbAtom::NAtoms];
658 Q_ASSERT(i == QXcbAtom::NAtoms);
659 for (i = 0; i < QXcbAtom::NAtoms; ++i)
660 cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]);
662 for (i = 0; i < QXcbAtom::NAtoms; ++i)
663 m_allAtoms[i] = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0)->atom;
666 void QXcbConnection::sync()
669 xcb_get_input_focus_cookie_t cookie = Q_XCB_CALL(xcb_get_input_focus(xcb_connection()));
670 free(xcb_get_input_focus_reply(xcb_connection(), cookie, 0));
673 #if defined(XCB_USE_EGL)
674 bool QXcbConnection::hasEgl() const
678 #endif // defined(XCB_USE_EGL)
681 void QXcbConnection::initializeDri2()
683 xcb_dri2_connect_cookie_t connect_cookie = xcb_dri2_connect_unchecked (m_connection,
684 m_screens[0]->root(),
685 XCB_DRI2_DRIVER_TYPE_DRI);
687 xcb_dri2_connect_reply_t *connect = xcb_dri2_connect_reply (m_connection,
688 connect_cookie, NULL);
690 if (! connect || connect->driver_name_length + connect->device_name_length == 0) {
691 qDebug() << "Failed to connect to dri2";
695 m_dri2_device_name = QByteArray(xcb_dri2_connect_device_name (connect),
696 xcb_dri2_connect_device_name_length (connect));
699 int fd = open(m_dri2_device_name.constData(), O_RDWR);
701 qDebug() << "InitializeDri2: Could'nt open device << dri2DeviceName";
702 m_dri2_device_name = QByteArray();
707 if (drmGetMagic(fd, &magic)) {
708 qDebug() << "Failed to get drmMagic";
712 xcb_dri2_authenticate_cookie_t authenticate_cookie = xcb_dri2_authenticate_unchecked(m_connection,
713 m_screens[0]->root(), magic);
714 xcb_dri2_authenticate_reply_t *authenticate = xcb_dri2_authenticate_reply(m_connection,
715 authenticate_cookie, NULL);
716 if (authenticate == NULL || !authenticate->authenticated) {
717 fprintf(stderr, "DRI2: failed to authenticate\n");
724 EGLDisplay display = eglGetDRMDisplayMESA(fd);
726 fprintf(stderr, "failed to create display\n");
730 m_egl_display = display;
732 if (!eglInitialize(display, &major, &minor)) {
733 fprintf(stderr, "failed to initialize display\n");
738 bool QXcbConnection::hasSupportForDri2() const
740 if (!m_dri2_support_probed) {
741 xcb_generic_error_t *error = 0;
743 xcb_prefetch_extension_data (m_connection, &xcb_xfixes_id);
744 xcb_prefetch_extension_data (m_connection, &xcb_dri2_id);
746 xcb_xfixes_query_version_cookie_t xfixes_query_cookie = xcb_xfixes_query_version(m_connection,
747 XCB_XFIXES_MAJOR_VERSION,
748 XCB_XFIXES_MINOR_VERSION);
750 xcb_dri2_query_version_cookie_t dri2_query_cookie = xcb_dri2_query_version (m_connection,
751 XCB_DRI2_MAJOR_VERSION,
752 XCB_DRI2_MINOR_VERSION);
754 xcb_xfixes_query_version_reply_t *xfixes_query = xcb_xfixes_query_version_reply (m_connection,
755 xfixes_query_cookie, &error);
756 if (!xfixes_query || error || xfixes_query->major_version < 2) {
763 xcb_dri2_query_version_reply_t *dri2_query = xcb_dri2_query_version_reply (m_connection,
764 dri2_query_cookie, &error);
765 if (!dri2_query || error) {
771 QXcbConnection *that = const_cast<QXcbConnection *>(this);
772 that->m_dri2_major = dri2_query->major_version;
773 that->m_dri2_minor = dri2_query->minor_version;
775 that->m_has_support_for_dri2 = true;
776 that->m_dri2_support_probed = true;
778 return m_has_support_for_dri2;
780 #endif //XCB_USE_DRI2