diff options
Diffstat (limited to 'src/plugins/platforms')
54 files changed, 1817 insertions, 788 deletions
diff --git a/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp b/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp index 005758d83d..5a35310111 100644 --- a/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp +++ b/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp @@ -62,6 +62,7 @@ public: EGLNativeWindowType createNativeWindow(const QSize &size, const QSurfaceFormat &format); void destroyNativeWindow(EGLNativeWindowType window); bool hasCapability(QPlatformIntegration::Capability cap) const; + QEglFSCursor *createCursor(QEglFSScreen *screen) const; }; void QEglFSAndroidHooks::platformInit() @@ -134,6 +135,12 @@ QSurfaceFormat QEglFSAndroidHooks::surfaceFormatFor(const QSurfaceFormat &inputF return ret; } +QEglFSCursor *QEglFSAndroidHooks::createCursor(QEglFSScreen *screen) const +{ + Q_UNUSED(screen); + return 0; +} + static QEglFSAndroidHooks eglFSAndroidHooks; QEglFSHooks *platformHooks = &eglFSAndroidHooks; diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index 7320fc11e6..95fe9129d3 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -100,11 +100,23 @@ static void cleanupCocoaApplicationDelegate() - (id)init { self = [super init]; - if (self) + if (self) { inLaunch = true; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(updateScreens:) + name:NSApplicationDidChangeScreenParametersNotification + object:NSApp]; + } return self; } +- (void)updateScreens:(NSNotification *)notification +{ + if (QCocoaIntegration *ci = dynamic_cast<QCocoaIntegration *>(QGuiApplicationPrivate::platformIntegration())) + ci->updateScreens(); +} + - (void)dealloc { sharedCocoaApplicationDelegate = nil; @@ -114,6 +126,8 @@ static void cleanupCocoaApplicationDelegate() [NSApp setDelegate:reflectionDelegate]; [reflectionDelegate release]; } + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; } diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index d4673baaef..3dee137038 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -133,18 +133,6 @@ void QCocoaGLContext::setActiveWindow(QWindow *window) cocoaWindow->setCurrentContext(this); [(QNSView *) cocoaWindow->contentView() setQCocoaGLContext:this]; - - // Enable high-dpi OpenGL for retina displays. Enabling has the side - // effect that Cooca will start calling glViewport(0, 0, width, height), - // overriding any glViewport calls in application code. This is usually not a - // problem, except if the applcation wants to have a "custom" viewport. - // (like the hellogl example) -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { - if (cocoaWindow->devicePixelRatio() > 1) - [cocoaWindow->contentView() setWantsBestResolutionOpenGLSurface:YES]; - } -#endif } void QCocoaGLContext::doneCurrent() diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index 6e690dd51e..7831888da1 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -125,6 +125,7 @@ public: QList<int> possibleKeys(const QKeyEvent *event) const; void updateScreens(); + QCocoaScreen *screenAtIndex(int index) const { return mScreens.at(index); } private: diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 50d68f8311..987520f307 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -352,6 +352,7 @@ bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) cons case BufferQueueingOpenGL: case WindowMasks: case MultipleWindows: + case ForeignWindows: return true; default: return QPlatformIntegration::hasCapability(cap); diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index 0a98819d16..df0ef390c9 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -98,6 +98,16 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaMenuDelegate); return self; } + +- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item +{ + Q_UNUSED(menu); + if (item && [item tag]) { + QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([item tag]); + cocoaItem->hovered(); + } +} + - (void) menuWillOpen:(NSMenu*)m { Q_UNUSED(m); diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 5d1600dba6..47341e2262 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -118,7 +118,7 @@ static bool isMouseEvent(NSEvent *ev) // Windows with a transient parent (such as combobox popup windows) // cannot become the main window: - if (m_cocoaPlatformWindow && m_cocoaPlatformWindow->window()->transientParent()) + if (!m_cocoaPlatformWindow || m_cocoaPlatformWindow->window()->transientParent()) canBecomeMain = NO; return canBecomeMain; @@ -159,7 +159,8 @@ static bool isMouseEvent(NSEvent *ev) return NO; // Only tool or dialog windows should become key: - if (m_cocoaPlatformWindow->window()->type() == Qt::Tool || m_cocoaPlatformWindow->window()->type() == Qt::Dialog) + if (m_cocoaPlatformWindow + && (m_cocoaPlatformWindow->window()->type() == Qt::Tool || m_cocoaPlatformWindow->window()->type() == Qt::Dialog)) return YES; return NO; } @@ -195,6 +196,8 @@ const int QCocoaWindow::NoAlertRequest = -1; QCocoaWindow::QCocoaWindow(QWindow *tlw) : QPlatformWindow(tlw) + , m_contentView(nil) + , m_qtView(nil) , m_nsWindow(0) , m_contentViewIsEmbedded(false) , m_contentViewIsToBeEmbedded(false) @@ -215,8 +218,23 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) #endif QCocoaAutoReleasePool pool; - m_qtView = [[QNSView alloc] initWithQWindow:tlw platformWindow:this]; - m_contentView = m_qtView; + if (tlw->type() == Qt::ForeignWindow) { + NSView *foreignView = (NSView *)WId(tlw->property("_q_foreignWinId").value<WId>()); + setContentView(foreignView); + } else { + m_qtView = [[QNSView alloc] initWithQWindow:tlw platformWindow:this]; + m_contentView = m_qtView; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + // Enable high-dpi OpenGL for retina displays. Enabling has the side + // effect that Cocoa will start calling glViewport(0, 0, width, height), + // overriding any glViewport calls in application code. This is usually not a + // problem, except if the appilcation wants to have a "custom" viewport. + // (like the hellogl example) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7 + && tlw->surfaceType() == QSurface::OpenGLSurface) + [m_contentView setWantsBestResolutionOpenGLSurface:YES]; +#endif + } setGeometry(tlw->geometry()); recreateWindow(parent()); tlw->setGeometry(geometry()); @@ -258,8 +276,7 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect) if (m_nsWindow) { NSRect bounds = qt_mac_flipRect(rect, window()); - [m_nsWindow setContentSize : bounds.size]; - [m_nsWindow setFrameOrigin : bounds.origin]; + [m_nsWindow setFrame:[m_nsWindow frameRectForContentRect:bounds] display:YES animate:NO]; } else { [m_contentView setFrame : NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())]; } @@ -308,6 +325,8 @@ void QCocoaWindow::setVisible(bool visible) exposeWindow(); if (m_nsWindow) { + QWindowSystemInterface::flushWindowSystemEvents(); + // setWindowState might have been called while the window was hidden and // will not change the NSWindow state in that case. Sync up here: syncWindowState(window()->windowState()); @@ -349,11 +368,22 @@ void QCocoaWindow::setVisible(bool visible) QCocoaEventDispatcherPrivate *cocoaEventDispatcherPrivate = static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(cocoaEventDispatcher)); cocoaEventDispatcherPrivate->endModalSession(window()); m_hasModalSession = false; + + [m_nsWindow orderOut:m_nsWindow]; + if (m_nsWindow == [NSApp keyWindow] && !cocoaEventDispatcherPrivate->currentModalSession()) { + // Probably because we call runModalSession: outside [NSApp run] in QCocoaEventDispatcher + // (e.g., when show()-ing a modal QDialog instead of exec()-ing it), it can happen that + // the current NSWindow is still key after being ordered out. Then, after checking we + // don't have any other modal session left, it's safe to make the main window key again. + NSWindow *mainWindow = [NSApp mainWindow]; + if (mainWindow && [mainWindow canBecomeKeyWindow]) + [mainWindow makeKeyWindow]; + } } else { if ([m_nsWindow isSheet]) [NSApp endSheet:m_nsWindow]; + [m_nsWindow orderOut:m_nsWindow]; } - [m_nsWindow orderOut:m_nsWindow]; } else { [m_contentView setHidden:YES]; } @@ -446,8 +476,6 @@ void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags) NSInteger level = this->windowLevel(flags); [m_nsWindow setStyleMask:styleMask]; [m_nsWindow setLevel:level]; - [m_nsWindow setIgnoresMouseEvents:((flags & Qt::ToolTip) == Qt::ToolTip) ? YES : NO]; - // TODO deal with WindowTransparentForInput; setIgnoresMouseEvents is too extreme, you can't click the titlebar setWindowShadow(flags); } @@ -784,8 +812,10 @@ NSWindow * QCocoaWindow::createNSWindow() // before the window is shown and needs a proper window.). if ((type & Qt::Popup) == Qt::Popup) [window setHasShadow:YES]; - else + else { setWindowShadow(flags); + [window setHidesOnDeactivate: NO]; + } #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { @@ -987,7 +1017,8 @@ QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint) if (QWindow *childWindow = qobject_cast<QWindow *>(child)) { if (childWindow->geometry().contains(windowPoint)) { QCocoaWindow* platformWindow = static_cast<QCocoaWindow*>(childWindow->handle()); - targetWindow = platformWindow->childWindowAt(windowPoint - childWindow->position()); + if (platformWindow->isExposed()) + targetWindow = platformWindow->childWindowAt(windowPoint - childWindow->position()); } } } diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h index 67b16b4b32..85f72a4dbb 100644 --- a/src/plugins/platforms/cocoa/qnsview.h +++ b/src/plugins/platforms/cocoa/qnsview.h @@ -69,6 +69,8 @@ QT_END_NAMESPACE bool m_sendUpAsRightButton; Qt::KeyboardModifiers currentWheelModifiers; bool m_subscribesForGlobalFrameNotifications; + QCocoaGLContext *m_glContext; + bool m_shouldSetGLContextinDrawRect; } - (id)init; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index c114ab20d0..42117a64a1 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -58,6 +58,7 @@ #include <private/qguiapplication_p.h> #include "qcocoabackingstore.h" #include "qcocoaglcontext.h" +#include "qcocoaintegration.h" #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR #include <accessibilityinspector.h> @@ -85,6 +86,8 @@ static QTouchDevice *touchDevice = 0; m_buttons = Qt::NoButton; m_sendKeyEvent = false; m_subscribesForGlobalFrameNotifications = false; + m_glContext = 0; + m_shouldSetGLContextinDrawRect = false; currentCustomDragTypes = 0; m_sendUpAsRightButton = false; @@ -150,7 +153,13 @@ static QTouchDevice *touchDevice = 0; - (void) setQCocoaGLContext:(QCocoaGLContext *)context { - [context->nsOpenGLContext() setView:self]; + m_glContext = context; + [m_glContext->nsOpenGLContext() setView:self]; + if (![m_glContext->nsOpenGLContext() view]) { + //was unable to set view + m_shouldSetGLContextinDrawRect = true; + } + if (!m_subscribesForGlobalFrameNotifications) { // NSOpenGLContext expects us to repaint (or update) the view when // it changes position on screen. Since this happens unnoticed for @@ -268,6 +277,15 @@ static QTouchDevice *touchDevice = 0; m_platformWindow->obscureWindow(); } else if ([notificationName isEqualToString: @"NSWindowDidOrderOnScreenAndFinishAnimatingNotification"]) { m_platformWindow->exposeWindow(); + } else if (notificationName == NSWindowDidChangeScreenNotification) { + if (m_window) { + QCocoaIntegration *ci = static_cast<QCocoaIntegration *>(QGuiApplicationPrivate::platformIntegration()); + NSUInteger screenIndex = [[NSScreen screens] indexOfObject:self.window.screen]; + if (screenIndex != NSNotFound) { + QCocoaScreen *cocoaScreen = ci->screenAtIndex(screenIndex); + QWindowSystemInterface::handleWindowScreenChanged(m_window, cocoaScreen->screen()); + } + } } else { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 @@ -344,6 +362,11 @@ static QTouchDevice *touchDevice = 0; - (void) drawRect:(NSRect)dirtyRect { + if (m_glContext && m_shouldSetGLContextinDrawRect) { + [m_glContext->nsOpenGLContext() setView:self]; + m_shouldSetGLContextinDrawRect = false; + } + if (!m_backingStore) return; @@ -1304,14 +1327,17 @@ static QTouchDevice *touchDevice = 0; QCocoaDropData mimeData([sender draggingPasteboard]); response = QWindowSystemInterface::handleDrop(m_window, &mimeData, qt_windowPoint, qtAllowed); } + if (response.isAccepted()) { + QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag()); + nativeDrag->setAcceptedAction(response.acceptedAction()); + } return response.isAccepted(); } - (void)draggedImage:(NSImage*) img endedAt:(NSPoint) point operation:(NSDragOperation) operation { Q_UNUSED(img); - QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag()); - nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation)); + Q_UNUSED(operation); // keep our state, and QGuiApplication state (buttons member) in-sync, // or future mouse events will be processed incorrectly diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.h b/src/plugins/platforms/cocoa/qnswindowdelegate.h index a5b46a971f..9a616ba8e8 100644 --- a/src/plugins/platforms/cocoa/qnswindowdelegate.h +++ b/src/plugins/platforms/cocoa/qnswindowdelegate.h @@ -55,7 +55,7 @@ - (void)windowDidResize:(NSNotification *)notification; - (void)windowDidMove:(NSNotification *)notification; -- (void)windowWillClose:(NSNotification *)notification; +- (void)windowWillMove:(NSNotification *)notification; - (BOOL)windowShouldClose:(NSNotification *)notification; @end diff --git a/src/plugins/platforms/eglfs/eglfs.pri b/src/plugins/platforms/eglfs/eglfs.pri index eb66e17479..44455ed3d8 100644 --- a/src/plugins/platforms/eglfs/eglfs.pri +++ b/src/plugins/platforms/eglfs/eglfs.pri @@ -7,7 +7,7 @@ DEFINES += MESA_EGL_NO_X11_HEADERS #To test the hooks on x11 (xlib), comment the above define too #EGLFS_PLATFORM_HOOKS_SOURCES += qeglfshooks_x11.cpp -#LIBS += -lX11 +#LIBS += -lX11 -lX11-xcb -lxcb SOURCES += $$PWD/qeglfsintegration.cpp \ $$PWD/qeglfswindow.cpp \ diff --git a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp index 5c264834b3..91a97ff977 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp +++ b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qeglfshooks.h" +#include "qeglfscursor.h" #include <fcntl.h> #include <unistd.h> @@ -249,8 +250,7 @@ bool QEglFSHooks::hasCapability(QPlatformIntegration::Capability cap) const QEglFSCursor *QEglFSHooks::createCursor(QEglFSScreen *screen) const { - Q_UNUSED(screen); - return 0; + return new QEglFSCursor(screen); } void QEglFSHooks::waitForVSync() const diff --git a/src/plugins/platforms/eglfs/qeglfshooks_x11.cpp b/src/plugins/platforms/eglfs/qeglfshooks_x11.cpp index f44107e830..0d8be66438 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks_x11.cpp +++ b/src/plugins/platforms/eglfs/qeglfshooks_x11.cpp @@ -41,13 +41,36 @@ #include "qeglfshooks.h" +#include <qpa/qwindowsysteminterface.h> +#include <QThread> + #include <X11/Xlib.h> +#include <X11/Xlib-xcb.h> +#include <xcb/xcb.h> QT_BEGIN_NAMESPACE +class EventReader : public QThread +{ +public: + EventReader(xcb_connection_t *connection) + : m_connection(connection) + { + } + + void run(); + + xcb_connection_t *connection() { return m_connection; } + +private: + xcb_connection_t *m_connection; +}; + class QEglFSX11Hooks : public QEglFSHooks { public: + QEglFSX11Hooks() : m_eventReader(0) {} + virtual void platformInit(); virtual void platformDestroy(); virtual EGLNativeDisplayType platformDisplay() const; @@ -55,20 +78,151 @@ public: virtual EGLNativeWindowType createNativeWindow(const QSize &size, const QSurfaceFormat &format); virtual void destroyNativeWindow(EGLNativeWindowType window); virtual bool hasCapability(QPlatformIntegration::Capability cap) const; + +private: + void sendConnectionEvent(xcb_atom_t a); + + EventReader *m_eventReader; + xcb_connection_t *m_connection; + xcb_window_t m_connectionEventListener; }; static Display *display = 0; +QAtomicInt running; + +static Qt::MouseButtons translateMouseButtons(int s) +{ + Qt::MouseButtons ret = 0; + if (s & XCB_BUTTON_MASK_1) + ret |= Qt::LeftButton; + if (s & XCB_BUTTON_MASK_2) + ret |= Qt::MidButton; + if (s & XCB_BUTTON_MASK_3) + ret |= Qt::RightButton; + return ret; +} + +static Qt::MouseButton translateMouseButton(xcb_button_t s) +{ + switch (s) { + case 1: return Qt::LeftButton; + case 2: return Qt::MidButton; + case 3: return Qt::RightButton; + // Button values 4-7 were already handled as Wheel events, and won't occur here. + case 8: return Qt::BackButton; // Also known as Qt::ExtraButton1 + case 9: return Qt::ForwardButton; // Also known as Qt::ExtraButton2 + case 10: return Qt::ExtraButton3; + case 11: return Qt::ExtraButton4; + case 12: return Qt::ExtraButton5; + case 13: return Qt::ExtraButton6; + case 14: return Qt::ExtraButton7; + case 15: return Qt::ExtraButton8; + case 16: return Qt::ExtraButton9; + case 17: return Qt::ExtraButton10; + case 18: return Qt::ExtraButton11; + case 19: return Qt::ExtraButton12; + case 20: return Qt::ExtraButton13; + case 21: return Qt::ExtraButton14; + case 22: return Qt::ExtraButton15; + case 23: return Qt::ExtraButton16; + case 24: return Qt::ExtraButton17; + case 25: return Qt::ExtraButton18; + case 26: return Qt::ExtraButton19; + case 27: return Qt::ExtraButton20; + case 28: return Qt::ExtraButton21; + case 29: return Qt::ExtraButton22; + case 30: return Qt::ExtraButton23; + case 31: return Qt::ExtraButton24; + default: return Qt::NoButton; + } +} + +void EventReader::run() +{ + Qt::MouseButtons buttons; + + xcb_generic_event_t *event; + while (running.load() && (event = xcb_wait_for_event(m_connection))) { + uint response_type = event->response_type & ~0x80; + switch (response_type) { + case XCB_BUTTON_PRESS: { + xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; + QPoint p(press->event_x, press->event_y); + buttons = (buttons & ~0x7) | translateMouseButtons(press->state); + buttons |= translateMouseButton(press->detail); + QWindowSystemInterface::handleMouseEvent(0, press->time, p, p, buttons); + break; + } + case XCB_BUTTON_RELEASE: { + xcb_button_release_event_t *release = (xcb_button_release_event_t *)event; + QPoint p(release->event_x, release->event_y); + buttons = (buttons & ~0x7) | translateMouseButtons(release->state); + buttons &= ~translateMouseButton(release->detail); + QWindowSystemInterface::handleMouseEvent(0, release->time, p, p, buttons); + break; + } + case XCB_MOTION_NOTIFY: { + xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event; + QPoint p(motion->event_x, motion->event_y); + QWindowSystemInterface::handleMouseEvent(0, motion->time, p, p, buttons); + break; + } + default: + break; + } + } +} + +void QEglFSX11Hooks::sendConnectionEvent(xcb_atom_t a) +{ + xcb_client_message_event_t event; + memset(&event, 0, sizeof(event)); + + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.sequence = 0; + event.window = m_connectionEventListener; + event.type = a; + + xcb_send_event(m_connection, false, m_connectionEventListener, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); + xcb_flush(m_connection); +} + void QEglFSX11Hooks::platformInit() { display = XOpenDisplay(NULL); if (!display) qFatal("Could not open display"); + XSetEventQueueOwner(display, XCBOwnsEventQueue); + + running.ref(); + + m_connection = XGetXCBConnection(display); + + xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection)); + + m_connectionEventListener = xcb_generate_id(m_connection); + xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, + m_connectionEventListener, it.data->root, + 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, + it.data->root_visual, 0, 0); + + m_eventReader = new EventReader(m_connection); + m_eventReader->start(); } void QEglFSX11Hooks::platformDestroy() { + running.deref(); + + sendConnectionEvent(XCB_ATOM_NONE); + XCloseDisplay(display); + + m_eventReader->wait(); + delete m_eventReader; + m_eventReader = 0; } EGLNativeDisplayType QEglFSX11Hooks::platformDisplay() const @@ -91,10 +245,12 @@ EGLNativeWindowType QEglFSX11Hooks::createNativeWindow(const QSize &size, const Window root = DefaultRootWindow(display); XSetWindowAttributes swa; memset(&swa, 0, sizeof(swa)); - Window win = XCreateWindow(display, root, 0, 0, size.width(), size.height(), 0, CopyFromParent, - InputOutput, CopyFromParent, CWEventMask, &swa); + swa.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ButtonMotionMask; + Window win = XCreateWindow(display, root, 0, 0, size.width(), size.height(), 0, CopyFromParent, + InputOutput, CopyFromParent, CWEventMask, &swa); XMapWindow(display, win); XStoreName(display, win, "EGLFS"); + return win; } diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/qeglfsintegration.cpp index 615b69f7d9..cf1503b7f9 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsintegration.cpp @@ -53,7 +53,7 @@ #include <QtPlatformSupport/private/qeglplatformcontext_p.h> #include <QtPlatformSupport/private/qeglpbuffer_p.h> -#if !defined(QT_NO_EVDEV) && !defined(Q_OS_ANDROID) +#if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) #include <QtPlatformSupport/private/qevdevmousemanager_p.h> #include <QtPlatformSupport/private/qevdevkeyboardmanager_p.h> #include <QtPlatformSupport/private/qevdevtouch_p.h> @@ -79,7 +79,7 @@ QEglFSIntegration::QEglFSIntegration() { QGuiApplicationPrivate::instance()->setEventDispatcher(mEventDispatcher); -#if !defined(QT_NO_EVDEV) && !defined(Q_OS_ANDROID) +#if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) new QEvdevKeyboardManager(QLatin1String("EvdevKeyboard"), QString() /* spec */, this); new QEvdevMouseManager(QLatin1String("EvdevMouse"), QString() /* spec */, this); new QEvdevTouchScreenHandlerThread(QString() /* spec */, this); diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.cpp b/src/plugins/platforms/eglfs/qeglfsscreen.cpp index e104bd7f2b..6cb1f88c66 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.cpp +++ b/src/plugins/platforms/eglfs/qeglfsscreen.cpp @@ -55,12 +55,8 @@ QEglFSScreen::QEglFSScreen(EGLDisplay dpy) #endif static int hideCursor = qgetenv("QT_QPA_EGLFS_HIDECURSOR").toInt(); - if (!hideCursor) { - if (QEglFSCursor *customCursor = QEglFSHooks::hooks()->createCursor(this)) - m_cursor = customCursor; - else - m_cursor = new QEglFSCursor(this); - } + if (!hideCursor) + m_cursor = QEglFSHooks::hooks()->createCursor(this); } QEglFSScreen::~QEglFSScreen() diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm index 807c75df54..735dd26b6d 100644 --- a/src/plugins/platforms/ios/qioscontext.mm +++ b/src/plugins/platforms/ios/qioscontext.mm @@ -156,6 +156,9 @@ GLuint QIOSContext::defaultFramebufferObject(QPlatformSurface *surface) const if (framebufferObject.renderbufferWidth != platformWindow->effectiveWidth() || framebufferObject.renderbufferHeight != platformWindow->effectiveHeight()) { + [EAGLContext setCurrentContext:m_eaglContext]; + glBindFramebuffer(GL_FRAMEBUFFER, framebufferObject.handle); + glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.colorRenderbuffer); UIView *view = reinterpret_cast<UIView *>(platformWindow->winId()); CAEAGLLayer *layer = static_cast<CAEAGLLayer *>(view.layer); diff --git a/src/plugins/platforms/ios/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h index b86dbf7d46..20f0aa59b6 100644 --- a/src/plugins/platforms/ios/qioswindow.h +++ b/src/plugins/platforms/ios/qioswindow.h @@ -82,15 +82,8 @@ public: WId winId() const { return WId(m_view); }; - QList<QWindowSystemInterface::TouchPoint> &touchPoints() { return m_touchPoints; } - QHash<UITouch *, int> &activeTouches() { return m_activeTouches; } - int &touchId() { return m_touchId; } - private: UIView *m_view; - QList<QWindowSystemInterface::TouchPoint> m_touchPoints; - QHash<UITouch *, int> m_activeTouches; - int m_touchId; QRect m_requestedGeometry; int m_windowLevel; diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index b173fb786f..02ac413b3b 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -68,6 +68,8 @@ UIReturnKeyType returnKeyType; BOOL secureTextEntry; QIOSWindow *m_qioswindow; + QHash<UITouch *, QWindowSystemInterface::TouchPoint> m_activeTouches; + int m_nextTouchId; } @property(nonatomic) UITextAutocapitalizationType autocapitalizationType; @@ -113,6 +115,7 @@ keyboardType = UIKeyboardTypeDefault; returnKeyType = UIReturnKeyDone; secureTextEntry = NO; + m_nextTouchId = 0; if (isQtApplication()) self.hidden = YES; @@ -145,45 +148,25 @@ [super layoutSubviews]; } -/* - Touch handling: - - UIKit generates [Began -> Moved -> Ended] event sequences for - each touch point. The iOS plugin tracks each individual - touch and assigns it an id for use by Qt. The id counter is - incremented on each began and decrement as follows: - 1) by one when the most recent touch ends. - 2) to zero when all touches ends. - - The TouchPoint list is reused between events. -*/ - (void)updateTouchList:(NSSet *)touches withState:(Qt::TouchPointState)state { - QList<QWindowSystemInterface::TouchPoint> &touchPoints = m_qioswindow->touchPoints(); - QHash<UITouch *, int> &activeTouches = m_qioswindow->activeTouches(); - - // Mark all touch points as stationary - for (QList<QWindowSystemInterface::TouchPoint>::iterator it = touchPoints.begin(); it != touchPoints.end(); ++it) - it->state = Qt::TouchPointStationary; - - // Update changed touch points with the new state - for (UITouch *touch in touches) { - const int touchId = activeTouches.value(touch); - QWindowSystemInterface::TouchPoint &touchPoint = touchPoints[touchId]; - touchPoint.state = state; - if (state == Qt::TouchPointPressed) - touchPoint.pressure = 1.0; - else if (state == Qt::TouchPointReleased) - touchPoint.pressure = 0.0; - - // Set position - QRect viewGeometry = fromCGRect(self.frame); - QPoint touchViewLocation = fromCGPoint([touch locationInView:self]); - QPoint touchScreenLocation = touchViewLocation + viewGeometry.topLeft(); - touchPoint.area = QRectF(touchScreenLocation , QSize(0, 0)); - - CGSize fullscreenSize = self.window.rootViewController.view.bounds.size; - touchPoint.normalPosition = QPointF(touchScreenLocation.x() / fullscreenSize.width, touchScreenLocation.y() / fullscreenSize.height); + QRect applicationRect = fromCGRect(self.window.screen.applicationFrame); + + foreach (UITouch *uiTouch, m_activeTouches.keys()) { + QWindowSystemInterface::TouchPoint &touchPoint = m_activeTouches[uiTouch]; + if (![touches containsObject:uiTouch]) { + touchPoint.state = Qt::TouchPointStationary; + } else { + touchPoint.state = state; + touchPoint.pressure = (state == Qt::TouchPointReleased) ? 0.0 : 1.0; + + // Find the touch position relative to the window. Then calculate the screen + // position by subtracting the position of the applicationRect (since UIWindow + // does not take that into account when reporting its own frame): + QPoint touchPos = fromCGPoint([uiTouch locationInView:nil]); + touchPoint.area = QRectF(touchPos - applicationRect.topLeft(), QSize(0, 0)); + touchPoint.normalPosition = QPointF(touchPos.x() / applicationRect.width(), touchPos.y() / applicationRect.height()); + } } } @@ -191,8 +174,7 @@ { // Send touch event synchronously QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration()); - QWindowSystemInterface::handleTouchEvent(m_qioswindow->window(), timeStamp, - iosIntegration->touchDevice(), m_qioswindow->touchPoints()); + QWindowSystemInterface::handleTouchEvent(m_qioswindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values()); QWindowSystemInterface::flushWindowSystemEvents(); } @@ -204,19 +186,13 @@ if (window != QGuiApplication::focusWindow()) m_qioswindow->requestActivateWindow(); - // Track Cocoa touch id to Qt touch id. The UITouch pointer is constant - // for the touch duration. - QHash<UITouch *, int> &activeTouches = m_qioswindow->activeTouches(); - QList<QWindowSystemInterface::TouchPoint> &touchPoints = m_qioswindow->touchPoints(); - for (UITouch *touch in touches) - activeTouches.insert(touch, m_qioswindow->touchId()++); - - // Create new touch points if needed. - int newTouchPointsNeeded = m_qioswindow->touchId() - touchPoints.count(); - for (int i = 0; i < newTouchPointsNeeded; ++i) { - QWindowSystemInterface::TouchPoint touchPoint; - touchPoint.id = touchPoints.count(); // id is the index in the touchPoints list. - touchPoints.append(touchPoint); + // UIKit generates [Began -> Moved -> Ended] event sequences for + // each touch point. Internally we keep a hashmap of active UITouch + // points to QWindowSystemInterface::TouchPoints, and assigns each TouchPoint + // an id for use by Qt. + for (UITouch *touch in touches) { + Q_ASSERT(!m_activeTouches.contains(touch)); + m_activeTouches[touch].id = m_nextTouchId++; } [self updateTouchList:touches withState:Qt::TouchPointPressed]; @@ -234,19 +210,11 @@ [self updateTouchList:touches withState:Qt::TouchPointReleased]; [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)]; - // Remove ended touch points from the active set (event processing has completed at this point) - QHash<UITouch *, int> &activeTouches = m_qioswindow->activeTouches(); - for (UITouch *touch in touches) { - int id = activeTouches.take(touch); - - // If this touch is the most recent touch we can reuse its id - if (id == m_qioswindow->touchId() - 1) - --m_qioswindow->touchId(); - } - - // Reset the touch id when there are no more active touches - if (activeTouches.isEmpty()) - m_qioswindow->touchId() = 0; + // Remove ended touch points from the active set: + for (UITouch *touch in touches) + m_activeTouches.remove(touch); + if (m_activeTouches.isEmpty()) + m_nextTouchId = 0; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event @@ -254,8 +222,8 @@ Q_UNUSED(touches) // ### can a subset of the active touches be cancelled? // Clear current touch points - m_qioswindow->activeTouches().clear(); - m_qioswindow->touchId() = 0; + m_activeTouches.clear(); + m_nextTouchId = 0; // Send cancel touch event synchronously QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration()); @@ -322,7 +290,6 @@ QT_BEGIN_NAMESPACE QIOSWindow::QIOSWindow(QWindow *window) : QPlatformWindow(window) , m_view([[EAGLView alloc] initWithQIOSWindow:this]) - , m_touchId(0) , m_requestedGeometry(QPlatformWindow::geometry()) , m_windowLevel(0) , m_devicePixelRatio(1.0) diff --git a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp index cd987d8d02..ad5206ba41 100644 --- a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp +++ b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp @@ -369,9 +369,10 @@ bool QLinuxFbScreen::initialize(const QStringList &args) mDepth = determineDepth(vinfo); mBytesPerLine = finfo.line_length; - mGeometry = determineGeometry(vinfo, userGeometry); + QRect geometry = determineGeometry(vinfo, userGeometry); + mGeometry = QRect(QPoint(0, 0), geometry.size()); mFormat = determineFormat(vinfo, mDepth); - mPhysicalSize = determinePhysicalSize(vinfo, userMmSize, mGeometry.size()); + mPhysicalSize = determinePhysicalSize(vinfo, userMmSize, geometry.size()); // mmap the framebuffer mMmap.size = finfo.smem_len; @@ -381,11 +382,11 @@ bool QLinuxFbScreen::initialize(const QStringList &args) return false; } - mMmap.offset = mGeometry.y() * mBytesPerLine + mGeometry.x() * mDepth / 8; + mMmap.offset = geometry.y() * mBytesPerLine + geometry.x() * mDepth / 8; mMmap.data = data + mMmap.offset; QFbScreen::initializeCompositor(); - mFbScreenImage = QImage(data, mGeometry.width(), mGeometry.height(), mBytesPerLine, mFormat); + mFbScreenImage = QImage(mMmap.data, geometry.width(), geometry.height(), mBytesPerLine, mFormat); mCursor = new QFbCursor(this); mTtyFd = openTtyDevice(ttyDevice); diff --git a/src/plugins/platforms/qnx/qnx.pro b/src/plugins/platforms/qnx/qnx.pro index 50884ace66..7c497b4434 100644 --- a/src/plugins/platforms/qnx/qnx.pro +++ b/src/plugins/platforms/qnx/qnx.pro @@ -1,6 +1,6 @@ TARGET = qqnx -QT += platformsupport platformsupport-private +QT += platformsupport-private core-private gui-private # Uncomment this to build with support for IMF once it becomes available in the BBNDK #CONFIG += qqnx_imf diff --git a/src/plugins/platforms/qnx/qqnxbuffer.cpp b/src/plugins/platforms/qnx/qqnxbuffer.cpp index c5e99a2001..abb8a07026 100644 --- a/src/plugins/platforms/qnx/qqnxbuffer.cpp +++ b/src/plugins/platforms/qnx/qqnxbuffer.cpp @@ -69,36 +69,31 @@ QQnxBuffer::QQnxBuffer(screen_buffer_t buffer) errno = 0; int size[2]; int result = screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_BUFFER_SIZE, size); - if (result != 0) { + if (result != 0) qFatal("QQNX: failed to query buffer size, errno=%d", errno); - } // Get stride of buffer errno = 0; int stride; result = screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_STRIDE, &stride); - if (result != 0) { + if (result != 0) qFatal("QQNX: failed to query buffer stride, errno=%d", errno); - } // Get access to buffer's data errno = 0; uchar *dataPtr = 0; result = screen_get_buffer_property_pv(buffer, SCREEN_PROPERTY_POINTER, (void **)&dataPtr); - if (result != 0) { + if (result != 0) qFatal("QQNX: failed to query buffer pointer, errno=%d", errno); - } - if (dataPtr == 0) { + if (dataPtr == 0) qFatal("QQNX: buffer pointer is NULL, errno=%d", errno); - } // Get format of buffer errno = 0; int screenFormat; result = screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_FORMAT, &screenFormat); - if (result != 0) { + if (result != 0) qFatal("QQNX: failed to query buffer format, errno=%d", errno); - } // Convert screen format to QImage format QImage::Format imageFormat = QImage::Format_Invalid; @@ -146,16 +141,14 @@ void QQnxBuffer::invalidateInCache() qBufferDebug() << Q_FUNC_INFO; // Verify native buffer exists - if (m_buffer == 0) { + if (m_buffer == 0) qFatal("QQNX: can't invalidate cache for null buffer"); - } // Evict buffer's data from cache errno = 0; int result = msync(m_image.bits(), m_image.height() * m_image.bytesPerLine(), MS_INVALIDATE | MS_CACHE_ONLY); - if (result != 0) { + if (result != 0) qFatal("QQNX: failed to invalidate cache, errno=%d", errno); - } } QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxglcontext.cpp b/src/plugins/platforms/qnx/qqnxglcontext.cpp index f77bb73614..005b6d272a 100644 --- a/src/plugins/platforms/qnx/qqnxglcontext.cpp +++ b/src/plugins/platforms/qnx/qqnxglcontext.cpp @@ -96,9 +96,8 @@ QQnxGLContext::QQnxGLContext(QOpenGLContext *glContext) // Set current rendering API EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); - if (eglResult != EGL_TRUE) { + if (eglResult != EGL_TRUE) qFatal("QQNX: failed to set EGL API, err=%d", eglGetError()); - } // Get colour channel sizes from window format int alphaSize = format.alphaBufferSize(); @@ -149,9 +148,8 @@ QQnxGLContext::QQnxGLContext(QOpenGLContext *glContext) // Select EGL config based on requested window format m_eglConfig = q_configFromGLFormat(ms_eglDisplay, format); - if (m_eglConfig == 0) { + if (m_eglConfig == 0) qFatal("QQnxGLContext: failed to find EGL config"); - } m_eglContext = eglCreateContext(ms_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttrs()); if (m_eglContext == EGL_NO_CONTEXT) { @@ -168,9 +166,8 @@ QQnxGLContext::~QQnxGLContext() qGLContextDebug() << Q_FUNC_INFO; // Cleanup EGL context if it exists - if (m_eglContext != EGL_NO_CONTEXT) { + if (m_eglContext != EGL_NO_CONTEXT) eglDestroyContext(ms_eglDisplay, m_eglContext); - } // Cleanup EGL surface if it exists destroySurface(); @@ -216,9 +213,8 @@ bool QQnxGLContext::makeCurrent(QPlatformSurface *surface) // Set current rendering API EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); - if (eglResult != EGL_TRUE) { + if (eglResult != EGL_TRUE) qFatal("QQnxGLContext: failed to set EGL API, err=%d", eglGetError()); - } if (m_newSurfaceRequested.testAndSetOrdered(true, false)) { qGLContextDebug() << "New EGL surface requested"; @@ -241,15 +237,13 @@ void QQnxGLContext::doneCurrent() // set current rendering API EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); - if (eglResult != EGL_TRUE) { + if (eglResult != EGL_TRUE) qFatal("QQNX: failed to set EGL API, err=%d", eglGetError()); - } // clear curent EGL context and unbind EGL surface eglResult = eglMakeCurrent(ms_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (eglResult != EGL_TRUE) { + if (eglResult != EGL_TRUE) qFatal("QQNX: failed to clear current EGL context, err=%d", eglGetError()); - } } void QQnxGLContext::swapBuffers(QPlatformSurface *surface) @@ -259,15 +253,13 @@ void QQnxGLContext::swapBuffers(QPlatformSurface *surface) // Set current rendering API EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); - if (eglResult != EGL_TRUE) { + if (eglResult != EGL_TRUE) qFatal("QQNX: failed to set EGL API, err=%d", eglGetError()); - } // Post EGL surface to window eglResult = eglSwapBuffers(ms_eglDisplay, m_eglSurface); - if (eglResult != EGL_TRUE) { + if (eglResult != EGL_TRUE) qFatal("QQNX: failed to swap EGL buffers, err=%d", eglGetError()); - } } QFunctionPointer QQnxGLContext::getProcAddress(const QByteArray &procName) @@ -276,9 +268,8 @@ QFunctionPointer QQnxGLContext::getProcAddress(const QByteArray &procName) // Set current rendering API EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API); - if (eglResult != EGL_TRUE) { + if (eglResult != EGL_TRUE) qFatal("QQNX: failed to set EGL API, err=%d", eglGetError()); - } // Lookup EGL extension function pointer return static_cast<QFunctionPointer>(eglGetProcAddress(procName.constData())); @@ -309,9 +300,8 @@ void QQnxGLContext::createSurface(QPlatformSurface *surface) // Get a pointer to the corresponding platform window QQnxWindow *platformWindow = dynamic_cast<QQnxWindow*>(surface); - if (!platformWindow) { + if (!platformWindow) qFatal("QQNX: unable to create EGLSurface without a QQnxWindow"); - } // Link the window and context platformWindow->setPlatformOpenGLContext(this); diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp index 97a361158e..580553f6e2 100644 --- a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp +++ b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp @@ -606,12 +606,10 @@ static bool imfAvailable() static bool s_imfDisabled = getenv("DISABLE_IMF") != 0; static bool s_imfReady = false; - if ( s_imfInitFailed || s_imfDisabled) { + if ( s_imfInitFailed || s_imfDisabled) return false; - } - else if ( s_imfReady ) { + else if ( s_imfReady ) return true; - } if ( p_imf_client_init == 0 ) { void *handle = dlopen("libinput_client.so.1", 0); @@ -623,17 +621,15 @@ static bool imfAvailable() p_ictrl_dispatch_event = (int32_t (*)(event_t *))dlsym(handle, "ictrl_dispatch_event"); p_vkb_init_selection_service = (int32_t (*)())dlsym(handle, "vkb_init_selection_service"); p_ictrl_get_num_active_sessions = (int32_t (*)())dlsym(handle, "ictrl_get_num_active_sessions"); - } - else - { + } else { qCritical() << Q_FUNC_INFO << "libinput_client.so.1 is not present - IMF services are disabled."; s_imfDisabled = true; return false; } + if ( p_imf_client_init && p_ictrl_open_session && p_ictrl_dispatch_event ) { s_imfReady = true; - } - else { + } else { p_ictrl_open_session = 0; p_ictrl_dispatch_event = 0; s_imfDisabled = true; @@ -1400,11 +1396,10 @@ spannable_string_t *QQnxInputContext::onGetTextBeforeCursor(input_session_t *ic, QString text = query.value(Qt::ImSurroundingText).toString(); m_lastCaretPos = query.value(Qt::ImCursorPosition).toInt(); - if (n < m_lastCaretPos) { + if (n < m_lastCaretPos) return toSpannableString(text.mid(m_lastCaretPos - n, n)); - } else { + else return toSpannableString(text.mid(0, m_lastCaretPos)); - } } int32_t QQnxInputContext::onPerformEditorAction(input_session_t *ic, int32_t editor_action) diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index 00706f715d..a933f7d571 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -155,9 +155,8 @@ QQnxIntegration::QQnxIntegration(const QStringList ¶mList) // Open connection to QNX composition manager errno = 0; int result = screen_create_context(&m_screenContext, SCREEN_APPLICATION_CONTEXT); - if (result != 0) { + if (result != 0) qFatal("QQnx: failed to connect to composition manager, errno=%d", errno); - } // Not on BlackBerry, it has specialized event dispatcher which also handles navigator events #if !defined(Q_OS_BLACKBERRY) && defined(QQNX_PPS) @@ -388,9 +387,8 @@ QPlatformClipboard *QQnxIntegration::clipboard() const qIntegrationDebug() << Q_FUNC_INFO; #if defined(QQNX_PPS) - if (!m_clipboard) { + if (!m_clipboard) m_clipboard = new QQnxClipboard; - } #endif return m_clipboard; } @@ -463,9 +461,8 @@ void QQnxIntegration::createDisplays() errno = 0; int displayCount; int result = screen_get_context_property_iv(m_screenContext, SCREEN_PROPERTY_DISPLAY_COUNT, &displayCount); - if (result != 0) { + if (result != 0) qFatal("QQnxIntegration: failed to query display count, errno=%d", errno); - } if (displayCount < 1) { // Never happens, even if there's no display, libscreen returns 1 @@ -476,9 +473,8 @@ void QQnxIntegration::createDisplays() errno = 0; screen_display_t *displays = (screen_display_t *)alloca(sizeof(screen_display_t) * displayCount); result = screen_get_context_property_pv(m_screenContext, SCREEN_PROPERTY_DISPLAYS, (void **)displays); - if (result != 0) { + if (result != 0) qFatal("QQnxIntegration: failed to query displays, errno=%d", errno); - } // If it's primary, we create a QScreen for it even if it's not attached // since Qt will dereference QGuiApplication::primaryScreen() diff --git a/src/plugins/platforms/qnx/qqnxnavigatorpps.cpp b/src/plugins/platforms/qnx/qqnxnavigatorpps.cpp index 1656ab029b..c5c40f5a23 100644 --- a/src/plugins/platforms/qnx/qqnxnavigatorpps.cpp +++ b/src/plugins/platforms/qnx/qqnxnavigatorpps.cpp @@ -150,9 +150,8 @@ void QQnxNavigatorPps::parsePPS(const QByteArray &ppsData, QHash<QByteArray, QBy QList<QByteArray> lines = ppsData.split('\n'); // validate pps object - if (lines.size() == 0 || lines.at(0) != "@control") { + if (lines.size() == 0 || lines.at(0) != "@control") qFatal("QQNX: unrecognized pps object, data=%s", ppsData.constData()); - } // parse pps object attributes and extract values for (int i = 1; i < lines.size(); i++) { diff --git a/src/plugins/platforms/qnx/qqnxrootwindow.cpp b/src/plugins/platforms/qnx/qqnxrootwindow.cpp index 198801a832..dddadb5ca8 100644 --- a/src/plugins/platforms/qnx/qqnxrootwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxrootwindow.cpp @@ -68,17 +68,15 @@ QQnxRootWindow::QQnxRootWindow(const QQnxScreen *screen) errno = 0; int result = screen_create_window(&m_window, m_screen->nativeContext()); int val[2]; - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to create window, errno=%d", errno); - } // Move window to proper display errno = 0; screen_display_t display = m_screen->nativeDisplay(); result = screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window display, errno=%d", errno); - } // Make sure window is above navigator but below keyboard if running as root // since navigator won't automatically set our z-order in this case @@ -86,39 +84,34 @@ QQnxRootWindow::QQnxRootWindow(const QQnxScreen *screen) errno = 0; val[0] = MAGIC_ZORDER_FOR_NO_NAV; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ZORDER, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window z-order, errno=%d", errno); - } } // Window won't be visible unless it has some buffers so make one dummy buffer that is 1x1 errno = 0; val[0] = SCREEN_USAGE_NATIVE; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_USAGE, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window buffer usage, errno=%d", errno); - } errno = 0; val[0] = m_screen->nativeFormat(); result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window pixel format, errno=%d", errno); - } errno = 0; val[0] = 1; val[1] = 1; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BUFFER_SIZE, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window buffer size, errno=%d", errno); - } errno = 0; result = screen_create_window_buffers(m_window, 1); - if (result != 0) { + if (result != 0) qFatal("QQNX: failed to create window buffer, errno=%d", errno); - } // Window is always the size of the display errno = 0; @@ -126,50 +119,44 @@ QQnxRootWindow::QQnxRootWindow(const QQnxScreen *screen) val[0] = geometry.width(); val[1] = geometry.height(); result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window size, errno=%d", errno); - } // Fill the window with solid black errno = 0; val[0] = 0; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_COLOR, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window colour, errno=%d", errno); - } // Make the window opaque errno = 0; val[0] = SCREEN_TRANSPARENCY_NONE; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_TRANSPARENCY, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window transparency, errno=%d", errno); - } // Set the swap interval to 1 errno = 0; val[0] = 1; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SWAP_INTERVAL, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window swap interval, errno=%d", errno); - } // Set viewport size equal to window size but move outside buffer so the fill colour is used exclusively errno = 0; val[0] = geometry.width(); val[1] = geometry.height(); result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window source size, errno=%d", errno); - } errno = 0; val[0] = 1; val[1] = 0; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_POSITION, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window source position, errno=%d", errno); - } createWindowGroup(); post(); @@ -187,16 +174,14 @@ void QQnxRootWindow::post() const errno = 0; screen_buffer_t buffer; int result = screen_get_window_property_pv(m_window, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&buffer); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to query window buffer, errno=%d", errno); - } errno = 0; int dirtyRect[] = {0, 0, 1, 1}; result = screen_post_window(m_window, buffer, 1, dirtyRect, 0); - if (result != 0) { + if (result != 0) qFatal("QQNX: failed to post window buffer, errno=%d", errno); - } } void QQnxRootWindow::flush() const @@ -205,9 +190,8 @@ void QQnxRootWindow::flush() const // Force immediate display update errno = 0; int result = screen_flush_context(m_screen->nativeContext(), 0); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to flush context, errno=%d", errno); - } } void QQnxRootWindow::setRotation(int rotation) @@ -215,9 +199,8 @@ void QQnxRootWindow::setRotation(int rotation) qRootWindowDebug() << Q_FUNC_INFO << "angle =" << rotation; errno = 0; int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ROTATION, &rotation); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window rotation, errno=%d", errno); - } } void QQnxRootWindow::resize(const QSize &size) @@ -225,15 +208,13 @@ void QQnxRootWindow::resize(const QSize &size) errno = 0; int val[] = {size.width(), size.height()}; int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window size, errno=%d", errno); - } errno = 0; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to set window source size, errno=%d", errno); - } // NOTE: display will update when child windows relayout and repaint } @@ -246,7 +227,6 @@ void QQnxRootWindow::createWindowGroup() // Create window group so child windows can be parented by container window errno = 0; int result = screen_create_window_group(m_window, m_windowGroupName.constData()); - if (result != 0) { + if (result != 0) qFatal("QQnxRootWindow: failed to create app window group, errno=%d", errno); - } } diff --git a/src/plugins/platforms/qnx/qqnxscreen.cpp b/src/plugins/platforms/qnx/qqnxscreen.cpp index 2b81559ab6..f8203b2329 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.cpp +++ b/src/plugins/platforms/qnx/qqnxscreen.cpp @@ -118,18 +118,17 @@ QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, // Cache initial orientation of this display errno = 0; int result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_ROTATION, &m_initialRotation); - if (result != 0) { + if (result != 0) qFatal("QQnxScreen: failed to query display rotation, errno=%d", errno); - } + m_currentRotation = m_initialRotation; // Cache size of this display in pixels errno = 0; int val[2]; result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_SIZE, val); - if (result != 0) { + if (result != 0) qFatal("QQnxScreen: failed to query display size, errno=%d", errno); - } m_currentGeometry = m_initialGeometry = QRect(0, 0, val[0], val[1]); @@ -165,9 +164,8 @@ static int defaultDepth() // check if display depth was specified in environment variable; // use default value if no valid value found defaultDepth = qgetenv("QQNX_DISPLAY_DEPTH").toInt(); - if (defaultDepth != 16 && defaultDepth != 32) { + if (defaultDepth != 16 && defaultDepth != 32) defaultDepth = 32; - } } return defaultDepth; } diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp index 57cfdc5eb6..c2d0e3e41c 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp @@ -90,9 +90,8 @@ bool QQnxScreenEventHandler::handleEvent(screen_event_t event) errno = 0; int qnxType; int result = screen_get_event_property_iv(event, SCREEN_PROPERTY_TYPE, &qnxType); - if (result) { + if (result) qFatal("QQNX: failed to query event type, errno=%d", errno); - } return handleEvent(event, qnxType); } @@ -161,9 +160,8 @@ void QQnxScreenEventHandler::injectKeyboardEvent(int flags, int sym, int modifie if ( qtMod & Qt::ControlModifier ) { keyStr = QChar((int)(key & 0x3f)); } else { - if (flags & KEY_SYM_VALID) { + if (flags & KEY_SYM_VALID) keyStr = QChar(sym); - } } } else if ((cap > 0x0ff && cap < UNICODE_PRIVATE_USE_AREA_FIRST) || cap > UNICODE_PRIVATE_USE_AREA_LAST) { key = (Qt::Key)cap; @@ -186,35 +184,30 @@ void QQnxScreenEventHandler::handleKeyboardEvent(screen_event_t event) errno = 0; int flags; int result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_FLAGS, &flags); - if (result) { + if (result) qFatal("QQNX: failed to query event flags, errno=%d", errno); - } // get key code errno = 0; int sym; result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_SYM, &sym); - if (result) { + if (result) qFatal("QQNX: failed to query event sym, errno=%d", errno); - } int modifiers; result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_MODIFIERS, &modifiers); - if (result) { + if (result) qFatal("QQNX: failed to query event modifiers, errno=%d", errno); - } int scan; result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_SCAN, &scan); - if (result) { + if (result) qFatal("QQNX: failed to query event modifiers, errno=%d", errno); - } int cap; result = screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_CAP, &cap); - if (result) { + if (result) qFatal("QQNX: failed to query event cap, errno=%d", errno); - } injectKeyboardEvent(flags, sym, modifiers, scan, cap); } @@ -227,38 +220,34 @@ void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event) screen_window_t qnxWindow; void *handle; int result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &handle); - if (result) { + if (result) qFatal("QQNX: failed to query event window, errno=%d", errno); - } + qnxWindow = static_cast<screen_window_t>(handle); // Query the button states int buttonState = 0; result = screen_get_event_property_iv(event, SCREEN_PROPERTY_BUTTONS, &buttonState); - if (result) { + if (result) qFatal("QQNX: failed to query event button state, errno=%d", errno); - } // Query the window position int windowPos[2]; result = screen_get_event_property_iv(event, SCREEN_PROPERTY_SOURCE_POSITION, windowPos); - if (result) { + if (result) qFatal("QQNX: failed to query event window position, errno=%d", errno); - } // Query the screen position int pos[2]; result = screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, pos); - if (result) { + if (result) qFatal("QQNX: failed to query event position, errno=%d", errno); - } // Query the wheel delta int wheelDelta = 0; result = screen_get_event_property_iv(event, SCREEN_PROPERTY_MOUSE_WHEEL, &wheelDelta); - if (result) { + if (result) qFatal("QQNX: failed to query event wheel delta, errno=%d", errno); - } // Map window handle to top-level QWindow QWindow *w = QQnxIntegration::window(qnxWindow); @@ -343,9 +332,8 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType) errno = 0; int pos[2]; int result = screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, pos); - if (result) { + if (result) qFatal("QQNX: failed to query event position, errno=%d", errno); - } QCursor::setPos(pos[0], pos[1]); @@ -353,25 +341,23 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType) errno = 0; int windowPos[2]; result = screen_get_event_property_iv(event, SCREEN_PROPERTY_SOURCE_POSITION, windowPos); - if (result) { + if (result) qFatal("QQNX: failed to query event window position, errno=%d", errno); - } // determine which finger touched errno = 0; int touchId; result = screen_get_event_property_iv(event, SCREEN_PROPERTY_TOUCH_ID, &touchId); - if (result) { + if (result) qFatal("QQNX: failed to query event touch id, errno=%d", errno); - } // determine which window was touched errno = 0; void *handle; result = screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, &handle); - if (result) { + if (result) qFatal("QQNX: failed to query event window, errno=%d", errno); - } + screen_window_t qnxWindow = static_cast<screen_window_t>(handle); // check if finger is valid @@ -454,9 +440,8 @@ void QQnxScreenEventHandler::handleCloseEvent(screen_event_t event) // Map window handle to top-level QWindow QWindow *w = QQnxIntegration::window(window); - if (w != 0) { + if (w != 0) QWindowSystemInterface::handleCloseEvent(w); - } } void QQnxScreenEventHandler::handleCreateEvent(screen_event_t event) diff --git a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp index e810b47c22..20fce3da70 100644 --- a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp +++ b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp @@ -209,13 +209,14 @@ void QQnxVirtualKeyboardPps::ppsDataReady() } if (pps_decoder_get_string(m_decoder, "msg", &value) == PPS_DECODER_OK) { - if (strcmp(value, "show") == 0) { + if (strcmp(value, "show") == 0) setVisible(true); - } else if (strcmp(value, "hide") == 0) { + else if (strcmp(value, "hide") == 0) setVisible(false); - } else if (strcmp(value, "info") == 0) + else if (strcmp(value, "info") == 0) handleKeyboardInfoMessage(); - else if (strcmp(value, "connect") == 0) { } + else if (strcmp(value, "connect") == 0) + qVirtualKeyboardDebug() << Q_FUNC_INFO << "Unhandled command 'connect'"; else qCritical("QQnxVirtualKeyboard: Unexpected keyboard PPS msg value: %s", value ? value : "[null]"); } else if (pps_decoder_get_string(m_decoder, "res", &value) == PPS_DECODER_OK) { @@ -223,8 +224,9 @@ void QQnxVirtualKeyboardPps::ppsDataReady() handleKeyboardInfoMessage(); else qCritical("QQnxVirtualKeyboard: Unexpected keyboard PPS res value: %s", value ? value : "[null]"); - } else + } else { qCritical("QQnxVirtualKeyboard: Unexpected keyboard PPS message type"); + } } void QQnxVirtualKeyboardPps::handleKeyboardInfoMessage() @@ -368,9 +370,8 @@ void QQnxVirtualKeyboardPps::applyKeyboardModeOptions(KeyboardMode mode) pps_encoder_end_object(m_encoder); - if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) { + if (::write(m_fd, pps_encoder_buffer(m_encoder), pps_encoder_length(m_encoder)) == -1) close(); - } pps_encoder_reset(m_encoder); } diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp index 2bcc11f0b0..6992d70fb1 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxwindow.cpp @@ -87,9 +87,8 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context) // Create child QNX window errno = 0; result = screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to create window, errno=%d", errno); - } // Set window buffer usage based on rendering API int val; @@ -108,32 +107,36 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context) errno = 0; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_USAGE, &val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window buffer usage, errno=%d", errno); - } // Alpha channel is always pre-multiplied if present errno = 0; val = SCREEN_PRE_MULTIPLIED_ALPHA; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ALPHA_MODE, &val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window alpha mode, errno=%d", errno); - } // Make the window opaque errno = 0; val = SCREEN_TRANSPARENCY_NONE; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_TRANSPARENCY, &val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window transparency, errno=%d", errno); - } // Set the window swap interval errno = 0; val = 1; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SWAP_INTERVAL, &val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window swap interval, errno=%d", errno); + + if (window->flags() && Qt::WindowDoesNotAcceptFocus) { + errno = 0; + val = SCREEN_SENSITIVITY_NO_FOCUS; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SENSITIVITY, &val); + if (result != 0) + qFatal("QQnxWindow: failed to set window sensitivity, errno=%d", errno); } setScreen(static_cast<QQnxScreen *>(window->screen()->handle())); @@ -219,24 +222,21 @@ QRect QQnxWindow::setGeometryHelper(const QRect &rect) val[0] = rect.x(); val[1] = rect.y(); int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window position, errno=%d", errno); - } errno = 0; val[0] = rect.width(); val[1] = rect.height(); result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window size, errno=%d", errno); - } // Set viewport size equal to window size errno = 0; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window source size, errno=%d", errno); - } return oldGeometry; } @@ -257,9 +257,8 @@ void QQnxWindow::setOffset(const QPoint &offset) val[0] = newGeometry.x(); val[1] = newGeometry.y(); int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window position, errno=%d", errno); - } Q_FOREACH (QQnxWindow *childWindow, m_childWindows) childWindow->setOffset(offset); @@ -296,9 +295,8 @@ void QQnxWindow::updateVisibility(bool parentVisible) errno = 0; int val = (m_visible && parentVisible) ? 1 : 0; int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window visibility, errno=%d", errno); - } Q_FOREACH (QQnxWindow *childWindow, m_childWindows) childWindow->updateVisibility(m_visible && parentVisible); @@ -311,9 +309,8 @@ void QQnxWindow::setOpacity(qreal level) errno = 0; int val = (int)(level * 255); int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_GLOBAL_ALPHA, &val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window global alpha, errno=%d", errno); - } // TODO: How to handle children of this window? If we change all the visibilities, then // the transparency will look wrong... @@ -352,9 +349,8 @@ void QQnxWindow::setBufferSize(const QSize &size) int val[2] = { nonEmptySize.width(), nonEmptySize.height() }; int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BUFFER_SIZE, val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window buffer size, errno=%d", errno); - } // Create window buffers if they do not exist if (m_bufferSize.isEmpty()) { @@ -362,31 +358,27 @@ void QQnxWindow::setBufferSize(const QSize &size) #if !defined(QT_NO_OPENGL) // Get pixel format from EGL config if using OpenGL; // otherwise inherit pixel format of window's screen - if (m_platformOpenGLContext != 0) { + if (m_platformOpenGLContext != 0) val[0] = platformWindowFormatToNativeFormat(m_platformOpenGLContext->format()); - } #endif errno = 0; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, val); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window pixel format, errno=%d", errno); - } errno = 0; result = screen_create_window_buffers(m_window, MAX_BUFFER_COUNT); - if (result != 0) { + if (result != 0) qWarning() << "QQnxWindow: Buffer size was" << size; qFatal("QQnxWindow: failed to create window buffers, errno=%d", errno); - } // check if there are any buffers available int bufferCount = 0; result = screen_get_window_property_iv(m_window, SCREEN_PROPERTY_RENDER_BUFFER_COUNT, &bufferCount); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to query window buffer count, errno=%d", errno); - } if (bufferCount != MAX_BUFFER_COUNT) { qFatal("QQnxWindow: invalid buffer count. Expected = %d, got = %d. You might experience problems.", @@ -416,9 +408,8 @@ QQnxBuffer &QQnxWindow::renderBuffer() errno = 0; screen_buffer_t buffers[MAX_BUFFER_COUNT]; const int result = screen_get_window_property_pv(m_window, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)buffers); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to query window buffers, errno=%d", errno); - } // Wrap each buffer for (int i = 0; i < MAX_BUFFER_COUNT; ++i) { @@ -482,24 +473,21 @@ void QQnxWindow::post(const QRegion &dirty) // Update the display with contents of render buffer errno = 0; int result = screen_post_window(m_window, currentBuffer.nativeBuffer(), 1, dirtyRect, 0); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to post window buffer, errno=%d", errno); - } // Advance to next nender buffer m_previousBufferIndex = m_currentBufferIndex++; - if (m_currentBufferIndex >= MAX_BUFFER_COUNT) { + if (m_currentBufferIndex >= MAX_BUFFER_COUNT) m_currentBufferIndex = 0; - } // Save modified region and clear scrolled region m_previousDirty = dirty; m_scrolled = QRegion(); // Notify screen that window posted - if (m_screen != 0) { + if (m_screen != 0) m_screen->onWindowPost(this); - } } } @@ -524,16 +512,14 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen) errno = 0; screen_display_t display = platformScreen->nativeDisplay(); int result = screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to set window display, errno=%d", errno); - } // Add window to display's window group errno = 0; result = screen_join_window_group(m_window, platformScreen->windowGroupName()); - if (result != 0) { + if (result != 0) qFatal("QQnxWindow: failed to join window group, errno=%d", errno); - } Q_FOREACH (QQnxWindow *childWindow, m_childWindows) { // Only subwindows and tooltips need necessarily be moved to another display with the window. @@ -813,19 +799,16 @@ int QQnxWindow::platformWindowFormatToNativeFormat(const QSurfaceFormat &format) qWindowDebug() << Q_FUNC_INFO; // Extract size of colour channels from window format int redSize = format.redBufferSize(); - if (redSize == -1) { + if (redSize == -1) qFatal("QQnxWindow: red size not defined"); - } int greenSize = format.greenBufferSize(); - if (greenSize == -1) { + if (greenSize == -1) qFatal("QQnxWindow: green size not defined"); - } int blueSize = format.blueBufferSize(); - if (blueSize == -1) { + if (blueSize == -1) qFatal("QQnxWindow: blue size not defined"); - } // select matching native format if (redSize == 5 && greenSize == 6 && blueSize == 5) { diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index 5757bcad4e..792e79df19 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -1095,7 +1095,7 @@ QWindowsNativeImage *QWindowsFontEngine::drawGDIGlyph(HFONT font, glyph_t glyph, xform.eDx = margin; xform.eDy = margin; - HDC hdc = CreateCompatibleDC(QWindowsContext::instance()->displayContext()); + const HDC hdc = m_fontEngineData->hdc; SetGraphicsMode(hdc, GM_ADVANCED); SetWorldTransform(hdc, &xform); @@ -1107,8 +1107,16 @@ QWindowsNativeImage *QWindowsFontEngine::drawGDIGlyph(HFONT font, glyph_t glyph, memset(&mat, 0, sizeof(mat)); mat.eM11.value = mat.eM22.value = 1; - if (GetGlyphOutline(hdc, glyph, ggo_options, &tgm, 0, 0, &mat) == GDI_ERROR) { - qWarning("QWinFontEngine: unable to query transformed glyph metrics..."); + const DWORD result = GetGlyphOutline(hdc, glyph, ggo_options, &tgm, 0, 0, &mat); + + XFORM identity = {1, 0, 0, 1, 0, 0}; + SetWorldTransform(hdc, &identity); + SetGraphicsMode(hdc, GM_COMPATIBLE); + SelectObject(hdc, old_font); + + if (result == GDI_ERROR) { + const int errorCode = GetLastError(); + qErrnoWarning(errorCode, "QWinFontEngine: unable to query transformed glyph metrics (GetGlyphOutline() failed, error %d)...", errorCode); return 0; } @@ -1117,10 +1125,6 @@ QWindowsNativeImage *QWindowsFontEngine::drawGDIGlyph(HFONT font, glyph_t glyph, xform.eDx -= tgm.gmptGlyphOrigin.x; xform.eDy += tgm.gmptGlyphOrigin.y; - - SetGraphicsMode(hdc, GM_COMPATIBLE); - SelectObject(hdc, old_font); - DeleteDC(hdc); } #else // else wince unsigned int options = 0; @@ -1412,5 +1416,11 @@ void QWindowsMultiFontEngine::loadEngine(int at) // TODO: increase cost in QFontCache for the font engine loaded here } +bool QWindowsFontEngine::supportsTransformation(const QTransform &transform) const +{ + // Support all transformations for ttf files, and translations for raster fonts + return ttf || transform.type() <= QTransform::TxTranslate; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsfontengine.h b/src/plugins/platforms/windows/qwindowsfontengine.h index 2bf6ead503..9e92a8fbff 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.h +++ b/src/plugins/platforms/windows/qwindowsfontengine.h @@ -123,6 +123,7 @@ public: virtual QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, const QTransform &xform); virtual QFontEngine *cloneWithSize(qreal pixelSize) const; + virtual bool supportsTransformation(const QTransform &transform) const; #ifndef Q_CC_MINGW virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0); diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp index ae66ef8a3d..f6dda04c13 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -613,8 +613,12 @@ static HGLRC createContext(const QOpenGLStaticContext &staticContext, const HGLRC result = staticContext.wglCreateContextAttribsARB(hdc, shared, attributes); - if (!result) - qErrnoWarning("%s: wglCreateContextAttribsARB() failed.", __FUNCTION__); + if (!result) { + QString message; + QDebug(&message).nospace() << __FUNCTION__ << ": wglCreateContextAttribsARB() failed (GL error code: 0x" + << hex << glGetError() << dec << ") for format: " << format << ", shared context: " << shared; + qErrnoWarning("%s", qPrintable(message)); + } return result; } diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index dd16ea1c6f..fd00a07d6c 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -237,8 +237,8 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, platformWindow->setFlag(QWindowsWindow::AutoMouseCapture); if (QWindowsContext::verboseEvents) qDebug() << "Automatic mouse capture " << window; - // Implement "Click to focus" for native child windows. - if (!window->isTopLevel() && QGuiApplication::focusWindow() != window) + // Implement "Click to focus" for native child windows (unless it is a native widget window). + if (!window->isTopLevel() && !window->inherits("QWidgetWindow") && QGuiApplication::focusWindow() != window) window->requestActivate(); } else if (platformWindow->hasMouseCapture() && platformWindow->testFlag(QWindowsWindow::AutoMouseCapture) diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 010197d6d8..73c78f0090 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -203,10 +203,9 @@ static inline QSize clientSize(HWND hwnd) return qSizeOfRect(rect); } -// from qwidget_win.cpp -static bool shouldShowMaximizeButton(const QWindow *w) +// from qwidget_win.cpp, pass flags separately in case they have been "autofixed". +static bool shouldShowMaximizeButton(const QWindow *w, Qt::WindowFlags flags) { - const Qt::WindowFlags flags = w->flags(); if ((flags & Qt::MSWindowsFixedSizeDialogHint) || !(flags & Qt::WindowMaximizeButtonHint)) return false; // if the user explicitly asked for the maximize button, we try to add @@ -333,6 +332,25 @@ QDebug operator<<(QDebug debug, const WindowCreationData &d) return debug; } +// Fix top level window flags in case only the type flags are passed. +static inline void fixTopLevelWindowFlags(Qt::WindowFlags &flags) +{ + switch (flags) { + case Qt::Window: + flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint + |Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint; + break; + case Qt::Dialog: + flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowContextHelpButtonHint | Qt::WindowCloseButtonHint; + break; + case Qt::Tool: + flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; + break; + default: + break; + } +} + void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flagsIn, unsigned creationFlags) { @@ -359,10 +377,8 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag topLevel = (creationFlags & ForceTopLevel) ? true : w->isTopLevel(); } - if (topLevel && flags == 1) { - flags |= Qt::WindowTitleHint|Qt::WindowSystemMenuHint|Qt::WindowMinimizeButtonHint - |Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint; - } + if (topLevel) + fixTopLevelWindowFlags(flags); type = static_cast<Qt::WindowType>(int(flags) & Qt::WindowType_Mask); switch (type) { @@ -433,7 +449,7 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag style |= WS_SYSMENU; if (flags & Qt::WindowMinimizeButtonHint) style |= WS_MINIMIZEBOX; - if (shouldShowMaximizeButton(w)) + if (shouldShowMaximizeButton(w, flags)) style |= WS_MAXIMIZEBOX; if (tool) exStyle |= WS_EX_TOOLWINDOW; @@ -775,7 +791,7 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : QPlatformWindow(aWindow), m_data(data), - m_flags(0), + m_flags(WithinCreate), m_hdc(0), m_windowState(Qt::WindowNoState), m_opacity(1.0), @@ -826,6 +842,7 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : const qreal opacity = qt_window_private(aWindow)->opacity; if (!qFuzzyCompare(opacity, qreal(1.0))) setOpacity(opacity); + clearFlag(WithinCreate); } QWindowsWindow::~QWindowsWindow() @@ -1481,8 +1498,10 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState) if (visible) newStyle |= WS_VISIBLE; setStyle(newStyle); - - const QRect r = effectiveScreen(window())->geometry(); + // Use geometry of QWindow::screen() within creation or the virtual screen the + // window is in (QTBUG-31166, QTBUG-30724). + const QScreen *screen = testFlag(WithinCreate) ? window()->screen() : effectiveScreen(window()); + const QRect r = screen->geometry(); const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE; const bool wasSync = testFlag(SynchronousGeometryChangeEvent); setFlag(SynchronousGeometryChangeEvent); diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 6c735ede7d..07f3976d87 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -134,7 +134,8 @@ public: WithinDestroy = 0x1000, TouchRegistered = 0x2000, AlertState = 0x4000, - Exposed = 0x08000 + Exposed = 0x08000, + WithinCreate = 0x10000 }; struct WindowData diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 10a8f26614..209c7bb187 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -253,10 +253,13 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra #endif , xfixes_first_event(0) , xrandr_first_event(0) + , xkb_first_event(0) , has_glx_extension(false) , has_shape_extension(false) , has_randr_extension(false) , has_input_shape(false) + , has_touch_without_mouse_emulation(false) + , has_xkb(false) , m_buttons(0) , m_focusWindow(0) { @@ -297,6 +300,9 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra xcb_extension_t *extensions[] = { &xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id, +#ifndef QT_NO_XKB + &xcb_xkb_id, +#endif #ifdef XCB_USE_RENDER &xcb_render_id, #endif @@ -335,6 +341,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra initializeXInput2(); #endif initializeXShape(); + initializeXKB(); m_wmSupport.reset(new QXcbWMSupport(this)); m_keyboard = new QXcbKeyboard(this); @@ -478,7 +485,6 @@ void printXcbEvent(const char *message, xcb_generic_event_t *event) PRINT_XCB_EVENT(XCB_SELECTION_NOTIFY); PRINT_XCB_EVENT(XCB_COLORMAP_NOTIFY); PRINT_XCB_EVENT(XCB_CLIENT_MESSAGE); - PRINT_XCB_EVENT(XCB_MAPPING_NOTIFY); default: qDebug("QXcbConnection: %s: unknown event - response_type: %d - sequence: %d", message, int(event->response_type & ~0x80), int(event->sequence)); } @@ -744,6 +750,23 @@ void QXcbConnection::handleButtonRelease(xcb_generic_event_t *ev) m_buttons &= ~translateMouseButton(event->detail); } +#ifndef QT_NO_XKB +namespace { + typedef union { + /* All XKB events share these fields. */ + struct { + uint8_t response_type; + uint8_t xkbType; + uint16_t sequence; + xcb_timestamp_t time; + uint8_t deviceID; + } any; + xcb_xkb_map_notify_event_t map_notify; + xcb_xkb_state_notify_event_t state_notify; + } _xkb_event; +} +#endif + void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) { #ifdef Q_XCB_DEBUG @@ -768,12 +791,21 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) case XCB_EXPOSE: HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent); case XCB_BUTTON_PRESS: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_button_press_event_t *)event)->state); +#endif handleButtonPress(event); HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent); case XCB_BUTTON_RELEASE: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_button_release_event_t *)event)->state); +#endif handleButtonRelease(event); HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent); case XCB_MOTION_NOTIFY: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_motion_notify_event_t *)event)->state); +#endif HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent); case XCB_CONFIGURE_NOTIFY: HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent); @@ -787,18 +819,29 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) case XCB_ENTER_NOTIFY: HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent); case XCB_LEAVE_NOTIFY: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_leave_notify_event_t *)event)->state); +#endif HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent); case XCB_FOCUS_IN: HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent); case XCB_FOCUS_OUT: HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent); case XCB_KEY_PRESS: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_key_press_event_t *)event)->state); +#endif HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent); case XCB_KEY_RELEASE: +#ifdef QT_NO_XKB + m_keyboard->updateXKBStateFromCore(((xcb_key_release_event_t *)event)->state); +#endif HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent); +#ifdef QT_NO_XKB case XCB_MAPPING_NOTIFY: m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event); break; +#endif case XCB_SELECTION_REQUEST: { xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)event; @@ -861,6 +904,24 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) } } handled = true; +#ifndef QT_NO_XKB + } else if (response_type == xkb_first_event) { // https://bugs.freedesktop.org/show_bug.cgi?id=51295 + _xkb_event *xkb_event = reinterpret_cast<_xkb_event *>(event); + if (xkb_event->any.deviceID == m_keyboard->coreDeviceId()) { + switch (xkb_event->any.xkbType) { + case XCB_XKB_STATE_NOTIFY: + m_keyboard->updateXKBState(&xkb_event->state_notify); + handled = true; + break; + case XCB_XKB_MAP_NOTIFY: + m_keyboard->handleMappingNotifyEvent(&xkb_event->map_notify); + handled = true; + break; + default: + break; + } + } +#endif } } @@ -868,7 +929,6 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) if (!handled) { // Check if a custom XEvent constructor was registered in xlib for this event type, and call it discarding the constructed XEvent if any. // XESetWireToEvent might be used by libraries to intercept messages from the X server e.g. the OpenGL lib waiting for DRI2 events. - Display *xdisplay = (Display *)m_xlib_display; XLockDisplay(xdisplay); Bool (*proc)(Display*, XEvent*, xEvent*) = XESetWireToEvent(xdisplay, response_type, 0); @@ -1016,32 +1076,36 @@ namespace xcb_timestamp_t QXcbConnection::getTimestamp() { // send a dummy event to myself to get the timestamp from X server. - xcb_window_t rootWindow = screens().at(primaryScreen())->root(); - xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, rootWindow, atom(QXcbAtom::CLIP_TEMPORARY), + xcb_window_t root_win = rootWindow(); + xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, root_win, atom(QXcbAtom::CLIP_TEMPORARY), XCB_ATOM_INTEGER, 32, 0, NULL); connection()->flush(); - PropertyNotifyEvent checker(rootWindow, atom(QXcbAtom::CLIP_TEMPORARY)); + PropertyNotifyEvent checker(root_win, atom(QXcbAtom::CLIP_TEMPORARY)); xcb_generic_event_t *event = 0; // lets keep this inside a loop to avoid a possible race condition, where // reader thread has not yet had the time to acquire the mutex in order // to add the new set of events to its event queue - while (true) { + while (!event) { connection()->sync(); - if ((event = checkEvent(checker))) - break; + event = checkEvent(checker); } xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event; xcb_timestamp_t timestamp = pn->time; free(event); - xcb_delete_property(xcb_connection(), rootWindow, atom(QXcbAtom::CLIP_TEMPORARY)); + xcb_delete_property(xcb_connection(), root_win, atom(QXcbAtom::CLIP_TEMPORARY)); return timestamp; } +xcb_window_t QXcbConnection::rootWindow() +{ + return screens().at(primaryScreen())->root(); +} + void QXcbConnection::processXcbEvents() { int connection_error = xcb_connection_has_error(xcb_connection()); @@ -1333,6 +1397,7 @@ static const char * xcb_atomnames = { #if XCB_USE_MAEMO_WINDOW_PROPERTIES "_MEEGOTOUCH_ORIENTATION_ANGLE\0" #endif + "_XSETTINGS_SETTINGS" }; xcb_atom_t QXcbConnection::atom(QXcbAtom::Atom atom) @@ -1538,6 +1603,67 @@ void QXcbConnection::initializeXShape() free(shape_query); } +void QXcbConnection::initializeXKB() +{ +#ifndef QT_NO_XKB + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xkb_id); + if (!reply || !reply->present) { + xkb_first_event = 0; + return; + } + xkb_first_event = reply->first_event; + + xcb_connection_t *c = connection()->xcb_connection(); + xcb_xkb_use_extension_cookie_t xkb_query_cookie; + xcb_xkb_use_extension_reply_t *xkb_query; + + xkb_query_cookie = xcb_xkb_use_extension(c, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION); + xkb_query = xcb_xkb_use_extension_reply(c, xkb_query_cookie, 0); + + if (!xkb_query) { + qWarning("Qt: Failed to initialize XKB extension"); + return; + } else if (!xkb_query->supported) { + qWarning("Qt: Unsupported XKB version (want %d %d, has %d %d)", + XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION, + xkb_query->serverMajor, xkb_query->serverMinor); + free(xkb_query); + return; + } + + has_xkb = true; + free(xkb_query); + + uint affectMap, map; + affectMap = map = XCB_XKB_MAP_PART_KEY_TYPES | + XCB_XKB_MAP_PART_KEY_SYMS | + XCB_XKB_MAP_PART_MODIFIER_MAP | + XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | + XCB_XKB_MAP_PART_KEY_ACTIONS | + XCB_XKB_MAP_PART_KEY_BEHAVIORS | + XCB_XKB_MAP_PART_VIRTUAL_MODS | + XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP; + + // Xkb events are reported to all interested clients without regard + // to the current keyboard input focus or grab state + xcb_void_cookie_t select = xcb_xkb_select_events_checked(c, + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY, + 0, + XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY, + affectMap, + map, + 0); + + xcb_generic_error_t *error = xcb_request_check(c, select); + if (error) { + free(error); + qWarning() << "Qt: failed to select notify events from xcb-xkb"; + return; + } +#endif +} + #if defined(XCB_USE_EGL) bool QXcbConnection::hasEgl() const { @@ -1594,4 +1720,24 @@ bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int o } #endif // defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO) +QXcbConnectionGrabber::QXcbConnectionGrabber(QXcbConnection *connection) + :m_connection(connection) +{ + connection->grabServer(); +} + +QXcbConnectionGrabber::~QXcbConnectionGrabber() +{ + if (m_connection) + m_connection->ungrabServer(); +} + +void QXcbConnectionGrabber::release() +{ + if (m_connection) { + m_connection->ungrabServer(); + m_connection = 0; + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 44c0e28dd5..883ee95e22 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -53,6 +53,14 @@ #include <QVarLengthArray> #include <qpa/qwindowsysteminterface.h> +// This is needed to make Qt compile together with XKB. xkb.h is using a variable +// which is called 'explicit', this is a reserved keyword in c++ */ +#ifndef QT_NO_XKB +#define explicit dont_use_cxx_explicit +#include <xcb/xkb.h> +#undef explicit +#endif + #ifndef QT_NO_TABLETEVENT #include <QTabletEvent> #endif @@ -261,6 +269,7 @@ namespace QXcbAtom { #if XCB_USE_MAEMO_WINDOW_PROPERTIES MeegoTouchOrientationAngle, #endif + _XSETTINGS_SETTINGS, NPredefinedAtoms, @@ -355,7 +364,7 @@ public: #endif QXcbWMSupport *wmSupport() const { return m_wmSupport.data(); } - + xcb_window_t rootWindow(); #ifdef XCB_USE_XLIB void *xlib_display() const { return m_xlib_display; } #endif @@ -401,6 +410,8 @@ public: bool hasXShape() const { return has_shape_extension; } bool hasXRandr() const { return has_randr_extension; } bool hasInputShape() const { return has_input_shape; } + bool hasTouchWithoutMouseEmulation() const { return has_touch_without_mouse_emulation; } + bool hasXKB() const { return has_xkb; } bool supportsThreadedRendering() const { return m_reader->isRunning(); } @@ -429,6 +440,7 @@ private: void initializeXRender(); void initializeXRandr(); void initializeXShape(); + void initializeXKB(); #ifdef XCB_USE_XINPUT2_MAEMO void initializeXInput2Maemo(); void finalizeXInput2Maemo(); @@ -538,11 +550,14 @@ private: uint32_t xfixes_first_event; uint32_t xrandr_first_event; + uint32_t xkb_first_event; bool has_glx_extension; bool has_shape_extension; bool has_randr_extension; bool has_input_shape; + bool has_touch_without_mouse_emulation; + bool has_xkb; Qt::MouseButtons m_buttons; @@ -570,6 +585,15 @@ xcb_generic_event_t *QXcbConnection::checkEvent(T &checker) return 0; } +class QXcbConnectionGrabber +{ +public: + QXcbConnectionGrabber(QXcbConnection *connection); + ~QXcbConnectionGrabber(); + void release(); +private: + QXcbConnection *m_connection; +}; #ifdef Q_XCB_DEBUG template <typename cookie_t> diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index dfd4feb254..991c82eaaa 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -92,6 +92,9 @@ void QXcbConnection::initializeXInput2() // Tablet support: Find the stylus-related devices. xi2SetupTabletDevices(); #endif // QT_NO_TABLETEVENT +#ifdef XI2_TOUCH_DEBUG + qDebug("XInput version %d.%d is supported", xiMajor, m_xi2Minor); +#endif } } } @@ -118,7 +121,16 @@ void QXcbConnection::xi2Select(xcb_window_t window) mask.deviceid = XIAllMasterDevices; mask.mask_len = sizeof(bitMask); mask.mask = xiBitMask; - XISelectEvents(xDisplay, window, &mask, 1); + Status result = XISelectEvents(xDisplay, window, &mask, 1); + // If we have XInput 2.2 and successfully enable touch on the master + // devices, then evdev touchscreens will provide touch only. In most other + // cases, there will be emulated mouse events, because true X11 touch + // support is so new that for the older drivers, mouse emulation was the + // only way; and it's still the fallback even with the modern evdev driver. + // But if neither Qt nor X11 does mouse emulation, it will not be possible + // to interact with mouse-oriented QWidgets; so we have to let Qt do it. + if (m_xi2Minor >= 2 && result == Success) + has_touch_without_mouse_emulation = true; #endif #ifndef QT_NO_TABLETEVENT diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index 3fd2ca70e3..756c3c22dd 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -43,6 +43,8 @@ #include "qxcbconnection.h" #include "qxcbwindow.h" #include "qxcbimage.h" +#include "qxcbxsettings.h" + #include <QtCore/QLibrary> #include <QtGui/QWindow> #include <QtGui/QBitmap> @@ -54,9 +56,17 @@ QT_BEGIN_NAMESPACE typedef int (*PtrXcursorLibraryLoadCursor)(void *, const char *); +typedef char *(*PtrXcursorLibraryGetTheme)(void *); +typedef int (*PtrXcursorLibrarySetTheme)(void *, const char *); +typedef int (*PtrXcursorLibraryGetDefaultSize)(void *); + #ifdef XCB_USE_XLIB static PtrXcursorLibraryLoadCursor ptrXcursorLibraryLoadCursor = 0; +static PtrXcursorLibraryGetTheme ptrXcursorLibraryGetTheme = 0; +static PtrXcursorLibrarySetTheme ptrXcursorLibrarySetTheme = 0; +static PtrXcursorLibraryGetDefaultSize ptrXcursorLibraryGetDefaultSize = 0; #endif + static xcb_font_t cursorFont = 0; static int cursorCount = 0; @@ -263,7 +273,7 @@ static const char * const cursorNames[] = { }; QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) - : QXcbObject(conn), m_screen(screen) + : QXcbObject(conn), m_screen(screen), m_gtkCursorThemeInitialized(false) { if (cursorCount++) return; @@ -273,21 +283,38 @@ QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) xcb_open_font(xcb_connection(), cursorFont, strlen(cursorStr), cursorStr); #ifdef XCB_USE_XLIB - QLibrary xcursorLib(QLatin1String("Xcursor"), 1); - bool xcursorFound = xcursorLib.load(); - if (!xcursorFound) { // try without the version number - xcursorLib.setFileName(QLatin1String("Xcursor")); - xcursorFound = xcursorLib.load(); + static bool function_ptrs_not_initialized = true; + if (function_ptrs_not_initialized) { + QLibrary xcursorLib(QLatin1String("Xcursor"), 1); + bool xcursorFound = xcursorLib.load(); + if (!xcursorFound) { // try without the version number + xcursorLib.setFileName(QLatin1String("Xcursor")); + xcursorFound = xcursorLib.load(); + } + if (xcursorFound) { + ptrXcursorLibraryLoadCursor = + (PtrXcursorLibraryLoadCursor) xcursorLib.resolve("XcursorLibraryLoadCursor"); + ptrXcursorLibraryGetTheme = + (PtrXcursorLibraryGetTheme) xcursorLib.resolve("XcursorGetTheme"); + ptrXcursorLibrarySetTheme = + (PtrXcursorLibrarySetTheme) xcursorLib.resolve("XcursorSetTheme"); + ptrXcursorLibraryGetDefaultSize = + (PtrXcursorLibraryGetDefaultSize) xcursorLib.resolve("XcursorGetDefaultSize"); + } + function_ptrs_not_initialized = false; } - if (xcursorFound) - ptrXcursorLibraryLoadCursor = - (PtrXcursorLibraryLoadCursor) xcursorLib.resolve("XcursorLibraryLoadCursor"); + #endif } QXcbCursor::~QXcbCursor() { xcb_connection_t *conn = xcb_connection(); + + if (m_gtkCursorThemeInitialized) { + m_screen->xSettings()->removeCallbackForHandle(this); + } + if (!--cursorCount) xcb_close_font(conn, cursorFont); @@ -448,6 +475,52 @@ xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape) return cursor; } +#ifdef XCB_USE_XLIB +bool updateCursorTheme(void *dpy, const QByteArray theme) { + if (!ptrXcursorLibraryGetTheme + || !ptrXcursorLibrarySetTheme) + return false; + QByteArray oldTheme = ptrXcursorLibraryGetTheme(dpy); + if (oldTheme == theme) + return false; + + int setTheme = ptrXcursorLibrarySetTheme(dpy,theme.constData()); + return setTheme; +} + + void QXcbCursor::cursorThemePropertyChanged(QXcbScreen *screen, const QByteArray &name, const QVariant &property, void *handle) +{ + Q_UNUSED(screen); + Q_UNUSED(name); + QXcbCursor *self = static_cast<QXcbCursor *>(handle); + updateCursorTheme(self->connection()->xlib_display(),property.toByteArray()); +} + +static xcb_cursor_t loadCursor(void *dpy, int cshape) +{ + xcb_cursor_t cursor = XCB_NONE; + if (!ptrXcursorLibraryLoadCursor || !dpy) + return cursor; + switch (cshape) { + case Qt::DragCopyCursor: + cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-copy"); + break; + case Qt::DragMoveCursor: + cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-move"); + break; + case Qt::DragLinkCursor: + cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-link"); + break; + default: + break; + } + if (!cursor) { + cursor = ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]); + } + return cursor; +} +#endif //XCB_USE_XLIB + xcb_cursor_t QXcbCursor::createFontCursor(int cshape) { xcb_connection_t *conn = xcb_connection(); @@ -456,24 +529,18 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) // Try Xcursor first #ifdef XCB_USE_XLIB - if (ptrXcursorLibraryLoadCursor && cshape >= 0 && cshape < Qt::LastCursor) { + if (cshape >= 0 && cshape < Qt::LastCursor) { void *dpy = connection()->xlib_display(); // special case for non-standard dnd-* cursors - switch (cshape) { - case Qt::DragCopyCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-copy"); - break; - case Qt::DragMoveCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-move"); - break; - case Qt::DragLinkCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-link"); - break; - default: - break; + cursor = loadCursor(dpy, cshape); + if (!cursor && !m_gtkCursorThemeInitialized) { + QByteArray gtkCursorTheme = m_screen->xSettings()->setting("Gtk/CursorThemeName").toByteArray(); + m_screen->xSettings()->registerCallbackForProperty("Gtk/CursorThemeName",cursorThemePropertyChanged,this); + if (updateCursorTheme(dpy,gtkCursorTheme)) { + cursor = loadCursor(dpy, cshape); + } + m_gtkCursorThemeInitialized = true; } - if (!cursor) - cursor = ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]); } if (cursor) return cursor; diff --git a/src/plugins/platforms/xcb/qxcbcursor.h b/src/plugins/platforms/xcb/qxcbcursor.h index 4bbb99e802..081300868c 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.h +++ b/src/plugins/platforms/xcb/qxcbcursor.h @@ -72,6 +72,13 @@ private: QMap<int, xcb_cursor_t> m_shapeCursorMap; QMap<qint64, xcb_cursor_t> m_bitmapCursorMap; #endif +#ifdef XCB_USE_XLIB + static void cursorThemePropertyChanged(QXcbScreen *screen, + const QByteArray &name, + const QVariant &property, + void *handle); +#endif + bool m_gtkCursorThemeInitialized; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index dceac09be5..db736cef4e 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -140,7 +140,6 @@ QXcbDrag::QXcbDrag(QXcbConnection *c) : QXcbObject(c) dropData = new QXcbDropData(this); init(); - heartbeat = -1; cleanup_timer = -1; } @@ -179,9 +178,6 @@ void QXcbDrag::startDrag() init(); - heartbeat = startTimer(200); - - xcb_set_selection_owner(xcb_connection(), connection()->clipboard()->owner(), atom(QXcbAtom::XdndSelection), connection()->time()); @@ -202,10 +198,6 @@ void QXcbDrag::startDrag() void QXcbDrag::endDrag() { - if (heartbeat != -1) { - killTimer(heartbeat); - heartbeat = -1; - } QBasicDrag::endDrag(); } @@ -485,11 +477,6 @@ void QXcbDrag::drop(const QMouseEvent *event) { QBasicDrag::drop(event); - if (heartbeat != -1) { - killTimer(heartbeat); - heartbeat = -1; - } - if (!current_target) return; @@ -536,7 +523,6 @@ void QXcbDrag::drop(const QMouseEvent *event) current_proxy_target = 0; source_time = 0; // current_embedding_widget = 0; - // #fixme resetDndState(false); } Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const @@ -1041,12 +1027,7 @@ void QXcbDrag::handleFinished(const xcb_client_message_event_t *event) void QXcbDrag::timerEvent(QTimerEvent* e) { - if (e->timerId() == heartbeat && source_sameanswer.isNull()) { - QPointF pos = QCursor::pos(); - QMouseEvent me(QEvent::MouseMove, pos, pos, pos, Qt::LeftButton, - QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); - move(&me); - } else if (e->timerId() == cleanup_timer) { + if (e->timerId() == cleanup_timer) { bool stopTimer = true; for (int i = 0; i < transactions.count(); ++i) { const Transaction &t = transactions.at(i); @@ -1160,7 +1141,7 @@ bool QXcbDrag::dndEnable(QXcbWindow *w, bool on) if (desktop_proxy) // *WE* already have one. return false; - connection()->grabServer(); + QXcbConnectionGrabber grabber(connection()); // As per Xdnd4, use XdndProxy xcb_window_t proxy_id = xdndProxy(connection(), w->xcb_window()); @@ -1176,7 +1157,6 @@ bool QXcbDrag::dndEnable(QXcbWindow *w, bool on) XCB_ATOM_WINDOW, 32, 1, &proxy_id); } - connection()->ungrabServer(); } else { xdnd_widget = w; } diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index cc74d85b51..5678c2d303 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -143,8 +143,6 @@ private: xcb_window_t current_proxy_target; QXcbScreen *current_screen; - // timer used when target wants "continuous" move messages (eg. scroll) - int heartbeat; // 10 minute timer used to discard old XdndDrop transactions enum { XdndDropTransactionTimeout = 600000 }; diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index dd1466d23c..77c265fd09 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -327,6 +327,11 @@ Qt::KeyboardModifiers QXcbIntegration::queryKeyboardModifiers() const return conn->keyboard()->translateModifiers(keybMask); } +QList<int> QXcbIntegration::possibleKeys(const QKeyEvent *e) const +{ + return m_connections.at(0)->keyboard()->possibleKeys(e); +} + QStringList QXcbIntegration::themeNames() const { return QGenericUnixTheme::themeNames(); @@ -337,4 +342,31 @@ QPlatformTheme *QXcbIntegration::createPlatformTheme(const QString &name) const return QGenericUnixTheme::createUnixTheme(name); } +QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint) const +{ + switch (hint) { + case QPlatformIntegration::CursorFlashTime: + case QPlatformIntegration::KeyboardInputInterval: + case QPlatformIntegration::MouseDoubleClickInterval: + case QPlatformIntegration::StartDragDistance: + case QPlatformIntegration::StartDragTime: + case QPlatformIntegration::KeyboardAutoRepeatRate: + case QPlatformIntegration::PasswordMaskDelay: + case QPlatformIntegration::FontSmoothingGamma: + case QPlatformIntegration::StartDragVelocity: + case QPlatformIntegration::UseRtlExtensions: + case QPlatformIntegration::PasswordMaskCharacter: + // TODO using various xcb, gnome or KDE settings + break; // Not implemented, use defaults + case QPlatformIntegration::ShowIsFullScreen: + // X11 always has support for windows, but the + // window manager could prevent it (e.g. matchbox) + return false; + case QPlatformIntegration::SynthesizeMouseFromTouchEvents: + // We do not want Qt to synthesize mouse events if X11 already does it. + return m_connections.at(0)->hasTouchWithoutMouseEmulation(); + } + return QPlatformIntegration::styleHint(hint); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h index 451dc43475..7042628203 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -91,9 +91,11 @@ public: QPlatformServices *services() const; Qt::KeyboardModifiers queryKeyboardModifiers() const; + QList<int> possibleKeys(const QKeyEvent *e) const; QStringList themeNames() const; QPlatformTheme *createPlatformTheme(const QString &name) const; + QVariant styleHint(StyleHint hint) const; QXcbConnection *defaultConnection() const { return m_connections.first(); } diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index c66ed53152..155b327315 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -42,8 +42,6 @@ #include "qxcbkeyboard.h" #include "qxcbwindow.h" #include "qxcbscreen.h" -#include "qxlibconvenience.h" -#include <xcb/xcb_keysyms.h> #include <X11/keysym.h> #include <qpa/qwindowsysteminterface.h> #include <QtCore/QTextCodec> @@ -55,6 +53,10 @@ #include <qpa/qplatformintegration.h> #include <qpa/qplatformcursor.h> +#ifdef XKBCOMMON_0_2_0 +#include <xkbcommon_workaround.h> +#endif + #ifndef XK_ISO_Left_Tab #define XK_ISO_Left_Tab 0xFE20 #endif @@ -561,288 +563,333 @@ static const unsigned int KeyTbl[] = { 0, 0 }; -static const unsigned short katakanaKeysymsToUnicode[] = { - 0x0000, 0x3002, 0x300C, 0x300D, 0x3001, 0x30FB, 0x30F2, 0x30A1, - 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30E3, 0x30E5, 0x30E7, 0x30C3, - 0x30FC, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, - 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, - 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, - 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, - 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, - 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F3, 0x309B, 0x309C -}; - -static const unsigned short cyrillicKeysymsToUnicode[] = { - 0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457, - 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0000, 0x045e, 0x045f, - 0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407, - 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0000, 0x040e, 0x040f, - 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, - 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, - 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, - 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, - 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, - 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, - 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, - 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a -}; - -static const unsigned short greekKeysymsToUnicode[] = { - 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c, - 0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015, - 0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc, - 0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, - 0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7, - 0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, - 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, - 0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7, - 0x03c8, 0x03c9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 -}; - -static const unsigned short technicalKeysymsToUnicode[] = { - 0x0000, 0x23B7, 0x250C, 0x2500, 0x2320, 0x2321, 0x2502, 0x23A1, - 0x23A3, 0x23A4, 0x23A6, 0x239B, 0x239D, 0x239E, 0x23A0, 0x23A8, - 0x23AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222B, - 0x2234, 0x221D, 0x221E, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000, - 0x223C, 0x2243, 0x0000, 0x0000, 0x0000, 0x21D4, 0x21D2, 0x2261, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221A, 0x0000, - 0x0000, 0x0000, 0x2282, 0x2283, 0x2229, 0x222A, 0x2227, 0x2228, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2202, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000, - 0x0000, 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193, 0x0000 -}; - -static const unsigned short specialKeysymsToUnicode[] = { - 0x25C6, 0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x0000, 0x0000, - 0x2424, 0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA, - 0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C, - 0x2502, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +// Possible modifier states. +static const Qt::KeyboardModifiers ModsTbl[] = { + Qt::NoModifier, // 0 + Qt::ShiftModifier, // 1 + Qt::ControlModifier, // 2 + Qt::ControlModifier | Qt::ShiftModifier, // 3 + Qt::AltModifier, // 4 + Qt::AltModifier | Qt::ShiftModifier, // 5 + Qt::AltModifier | Qt::ControlModifier, // 6 + Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7 + Qt::NoModifier // Fall-back to raw Key_* }; -static const unsigned short publishingKeysymsToUnicode[] = { - 0x0000, 0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009, - 0x200a, 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025, - 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, - 0x2105, 0x0000, 0x0000, 0x2012, 0x2329, 0x0000, 0x232a, 0x0000, - 0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000, - 0x0000, 0x2122, 0x2613, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25af, - 0x2018, 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033, - 0x0000, 0x271d, 0x0000, 0x25ac, 0x25c0, 0x25b6, 0x25cf, 0x25ae, - 0x25e6, 0x25ab, 0x25ad, 0x25b3, 0x25bd, 0x2606, 0x2022, 0x25aa, - 0x25b2, 0x25bc, 0x261c, 0x261e, 0x2663, 0x2666, 0x2665, 0x0000, - 0x2720, 0x2020, 0x2021, 0x2713, 0x2717, 0x266f, 0x266d, 0x2642, - 0x2640, 0x260e, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e, 0x0000 -}; - -static const unsigned short aplKeysymsToUnicode[] = { - 0x0000, 0x0000, 0x0000, 0x003c, 0x0000, 0x0000, 0x003e, 0x0000, - 0x2228, 0x2227, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00af, 0x0000, 0x22a5, 0x2229, 0x230a, 0x0000, 0x005f, 0x0000, - 0x0000, 0x0000, 0x2218, 0x0000, 0x2395, 0x0000, 0x22a4, 0x25cb, - 0x0000, 0x0000, 0x0000, 0x2308, 0x0000, 0x0000, 0x222a, 0x0000, - 0x2283, 0x0000, 0x2282, 0x0000, 0x22a2, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x22a3, 0x0000, 0x0000, 0x0000 -}; - -static const unsigned short koreanKeysymsToUnicode[] = { - 0x0000, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, - 0x3138, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f, - 0x3140, 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147, - 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x314f, - 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, - 0x3158, 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, - 0x3160, 0x3161, 0x3162, 0x3163, 0x11a8, 0x11a9, 0x11aa, 0x11ab, - 0x11ac, 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3, - 0x11b4, 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb, - 0x11bc, 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x316d, - 0x3171, 0x3178, 0x317f, 0x3181, 0x3184, 0x3186, 0x318d, 0x318e, - 0x11eb, 0x11f0, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9 -}; - -static QChar keysymToUnicode(unsigned char byte3, unsigned char byte4) -{ - switch (byte3) { - case 0x04: - // katakana - if (byte4 > 0xa0 && byte4 < 0xe0) - return QChar(katakanaKeysymsToUnicode[byte4 - 0xa0]); - else if (byte4 == 0x7e) - return QChar(0x203e); // Overline - break; - case 0x06: - // russian, use lookup table - if (byte4 > 0xa0) - return QChar(cyrillicKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x07: - // greek - if (byte4 > 0xa0) - return QChar(greekKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x08: - // technical - if (byte4 > 0xa0) - return QChar(technicalKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x09: - // special - if (byte4 >= 0xe0) - return QChar(specialKeysymsToUnicode[byte4 - 0xe0]); - break; - case 0x0a: - // publishing - if (byte4 > 0xa0) - return QChar(publishingKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x0b: - // APL - if (byte4 > 0xa0) - return QChar(aplKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x0e: - // Korean - if (byte4 > 0xa0) - return QChar(koreanKeysymsToUnicode[byte4 - 0xa0]); - break; - default: - break; - } - return QChar(0x0); -} - -Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) +Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const { Qt::KeyboardModifiers ret = 0; if (s & XCB_MOD_MASK_SHIFT) ret |= Qt::ShiftModifier; if (s & XCB_MOD_MASK_CONTROL) ret |= Qt::ControlModifier; - if (s & m_alt_mask) + if (s & rmod_masks.alt) ret |= Qt::AltModifier; - if (s & m_meta_mask) + if (s & rmod_masks.meta) ret |= Qt::MetaModifier; + if (s & rmod_masks.altgr) + ret |= Qt::GroupSwitchModifier; return ret; } -int QXcbKeyboard::translateKeySym(uint key) const +void QXcbKeyboard::readXKBConfig(struct xkb_rule_names *xkb_names) { - int code = Qt::Key_unknown; - int i = 0; // any other keys - while (KeyTbl[i]) { - if (key == KeyTbl[i]) { - code = (int)KeyTbl[i+1]; - break; + xcb_generic_error_t *error; + xcb_get_property_cookie_t cookie; + xcb_get_property_reply_t *config_reply; + + xcb_connection_t *c = xcb_connection(); + xcb_window_t rootWindow = connection()->rootWindow(); + + cookie = xcb_get_property(c, 0, rootWindow, + atom(QXcbAtom::_XKB_RULES_NAMES), XCB_ATOM_STRING, 0, 1024); + + config_reply = xcb_get_property_reply(c, cookie, &error); + if (!config_reply) { + qWarning("Qt: Couldn't interpret the _XKB_RULES_NAMES property"); + return; + } + char *xkb_config = (char *)xcb_get_property_value(config_reply); + int length = xcb_get_property_value_length(config_reply); + + char *names[5] = { 0, 0, 0, 0, 0 }; + char *p = xkb_config, *end = p + length; + int i = 0; + // The result from xcb_get_property_value() is not necessarily \0-terminated, + // we need to make sure that too many or missing '\0' symbols are handled safely. + do { + uint len = qstrnlen(p, length); + names[i++] = p; + p += len + 1; + length -= len + 1; + } while (p < end || i < 5); + + xkb_names->rules = qstrdup(names[0]); + xkb_names->model = qstrdup(names[1]); + xkb_names->layout = qstrdup(names[2]); + xkb_names->variant = qstrdup(names[3]); + xkb_names->options = qstrdup(names[4]); + + free(config_reply); +} + +void QXcbKeyboard::updateKeymap() +{ + m_config = true; + if (!xkb_context) { + xkb_context = xkb_context_new((xkb_context_flags)0); + if (!xkb_context) { + qWarning("Qt: Failed to create XKB context"); + m_config = false; + return; } - i += 2; } - if (m_meta_mask) { - // translate Super/Hyper keys to Meta if we're using them as the MetaModifier - if (m_meta_mask == m_super_mask && (code == Qt::Key_Super_L || code == Qt::Key_Super_R)) { - code = Qt::Key_Meta; - } else if (m_meta_mask == m_hyper_mask && (code == Qt::Key_Hyper_L || code == Qt::Key_Hyper_R)) { - code = Qt::Key_Meta; + + struct xkb_rule_names xkb_names = {0, 0, 0, 0, 0}; + + readXKBConfig(&xkb_names); + // Compile a keymap from RMLVO (rules, models, layouts, variants and options) names + if (xkb_keymap) + xkb_keymap_unref(xkb_keymap); + + xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, (xkb_keymap_compile_flags)0); + + delete[] xkb_names.rules; + delete[] xkb_names.model; + delete[] xkb_names.layout; + delete[] xkb_names.variant; + delete[] xkb_names.options; + + if (!xkb_keymap) { + qWarning("Qt: Failed to compile a keymap"); + m_config = false; + return; + } + // Create a new keyboard state object for a keymap + struct xkb_state *new_state = xkb_state_new(xkb_keymap); + if (!new_state) { + qWarning("Qt: Failed to create a new keyboard state"); + m_config = false; + return; + } + + if (xkb_state) { + xkb_state_unref(xkb_state); + xkb_state = new_state; + } else { + xkb_state = new_state; +#ifndef QT_NO_XKB + // get initial state from the X server (and keep it up-to-date at all times) + xcb_xkb_get_state_cookie_t state; + xcb_xkb_get_state_reply_t *init_state; + + xcb_connection_t *c = xcb_connection(); + state = xcb_xkb_get_state(c, XCB_XKB_ID_USE_CORE_KBD); + init_state = xcb_xkb_get_state_reply(c, state, 0); + if (!init_state) { + qWarning("Qt: couldn't retrieve an initial keyboard state"); + return; } + /* The xkb keyboard state is comprised of the state of all keyboard modifiers, + the keyboard group, and the state of the pointer buttons */ + xkb_state_update_mask(xkb_state, + init_state->baseMods, + init_state->latchedMods, + init_state->lockedMods, + init_state->baseGroup, + init_state->latchedGroup, + init_state->lockedGroup); + free(init_state); +#else + updateXKBMods(); +#endif } - return code; } -QString QXcbKeyboard::translateKeySym(xcb_keysym_t keysym, uint xmodifiers, - int &code, Qt::KeyboardModifiers &modifiers, - QByteArray &chars, int &count) +#ifndef QT_NO_XKB +void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state) { - // all keysyms smaller than 0xff00 are actally keys that can be mapped to unicode chars - - QTextCodec *mapper = QTextCodec::codecForLocale(); - QChar converted; - - if (/*count == 0 &&*/ keysym < 0xff00) { - unsigned char byte3 = (unsigned char)(keysym >> 8); - int mib = -1; - switch(byte3) { - case 0: // Latin 1 - case 1: // Latin 2 - case 2: //latin 3 - case 3: // latin4 - mib = byte3 + 4; break; - case 5: // arabic - mib = 82; break; - case 12: // Hebrew - mib = 85; break; - case 13: // Thai - mib = 2259; break; - case 4: // kana - case 6: // cyrillic - case 7: // greek - case 8: // technical, no mapping here at the moment - case 9: // Special - case 10: // Publishing - case 11: // APL - case 14: // Korean, no mapping - mib = -1; // manual conversion - mapper= 0; -#if !defined(QT_NO_XIM) - converted = keysymToUnicode(byte3, keysym & 0xff); -#endif - case 0x20: - // currency symbols - if (keysym >= 0x20a0 && keysym <= 0x20ac) { - mib = -1; // manual conversion - mapper = 0; - converted = (uint)keysym; - } - break; - default: - break; + if (!m_config) + return; + + if (connection()->hasXKB()) { + + xkb_state_component newState; + newState = xkb_state_update_mask(xkb_state, + state->baseMods, + state->latchedMods, + state->lockedMods, + state->baseGroup, + state->latchedGroup, + state->lockedGroup); + + if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) { + //qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)"); } - if (mib != -1) { - mapper = QTextCodec::codecForMib(mib); - if (chars.isEmpty()) - chars.resize(1); - chars[0] = (unsigned char) (keysym & 0xff); // get only the fourth bit for conversion later - count = 1; + } +} + +#else +void QXcbKeyboard::updateXKBStateFromCore(quint16 state) +{ + if (!m_config) + return; + + quint32 modsDepressed, modsLatched, modsLocked; + modsDepressed = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_DEPRESSED); + modsLatched = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED); + modsLocked = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED); + + quint32 xkbMask = xkbModMask(state); + xkb_state_component newState; + newState = xkb_state_update_mask(xkb_state, + modsDepressed & xkbMask, + modsLatched & xkbMask, + modsLocked & xkbMask, + 0, + 0, + (state >> 13) & 3); // bits 13 and 14 report the state keyboard group + + if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) { + //qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)"); + } +} + +quint32 QXcbKeyboard::xkbModMask(quint16 state) +{ + quint32 xkb_mask = 0; + + if ((state & XCB_MOD_MASK_SHIFT) && xkb_mods.shift != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.shift); + if ((state & XCB_MOD_MASK_LOCK) && xkb_mods.lock != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.lock); + if ((state & XCB_MOD_MASK_CONTROL) && xkb_mods.control != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.control); + if ((state & XCB_MOD_MASK_1) && xkb_mods.mod1 != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.mod1); + if ((state & XCB_MOD_MASK_2) && xkb_mods.mod2 != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.mod2); + if ((state & XCB_MOD_MASK_3) && xkb_mods.mod3 != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.mod3); + if ((state & XCB_MOD_MASK_4) && xkb_mods.mod4 != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.mod4); + if ((state & XCB_MOD_MASK_5) && xkb_mods.mod5 != XKB_MOD_INVALID) + xkb_mask |= (1 << xkb_mods.mod5); + + return xkb_mask; +} + +void QXcbKeyboard::updateXKBMods() +{ + xkb_mods.shift = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_SHIFT); + xkb_mods.lock = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_CAPS); + xkb_mods.control = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_CTRL); + xkb_mods.mod1 = xkb_map_mod_get_index(xkb_keymap, "Mod1"); + xkb_mods.mod2 = xkb_map_mod_get_index(xkb_keymap, "Mod2"); + xkb_mods.mod3 = xkb_map_mod_get_index(xkb_keymap, "Mod3"); + xkb_mods.mod4 = xkb_map_mod_get_index(xkb_keymap, "Mod4"); + xkb_mods.mod5 = xkb_map_mod_get_index(xkb_keymap, "Mod5"); +} +#endif + +QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const +{ + // turn off the modifier bits which doesn't participate in shortcuts + Qt::KeyboardModifiers notNeeded = Qt::MetaModifier | Qt::KeypadModifier | Qt::GroupSwitchModifier; + Qt::KeyboardModifiers modifiers = event->modifiers() &= ~notNeeded; + // create a fresh kb state and test against the relevant modifier combinations + // NOTE: it should be possible to query the keymap directly, once it gets + // supported by libxkbcommon + struct xkb_state * kb_state = xkb_state_new(xkb_keymap); + if (!kb_state) { + qWarning("QXcbKeyboard: failed to compile xkb keymap"); + return QList<int>(); + } + // get kb state from the master xkb_state and update the temporary kb_state + xkb_layout_index_t baseLayout = xkb_state_serialize_layout(xkb_state, XKB_STATE_LAYOUT_DEPRESSED); + xkb_layout_index_t latchedLayout = xkb_state_serialize_layout(xkb_state, XKB_STATE_LAYOUT_LATCHED); + xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(xkb_state, XKB_STATE_LAYOUT_LOCKED); + xkb_mod_index_t latchedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED); + xkb_mod_index_t lockedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED); + + xkb_state_update_mask(kb_state, 0, latchedMods, lockedMods, + baseLayout, latchedLayout, lockedLayout); + + xkb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, event->nativeScanCode()); + if (sym == XKB_KEY_NoSymbol) + return QList<int>(); + + QList<int> result; + int baseQtKey = keysymToQtKey(sym, modifiers, keysymToUnicode(sym)); + result += (baseQtKey + modifiers); // The base key is _always_ valid, of course + + xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(xkb_keymap, "Shift"); + xkb_mod_index_t altMod = xkb_keymap_mod_get_index(xkb_keymap, "Alt"); + xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(xkb_keymap, "Control"); + + xkb_mod_mask_t depressed; + + int qtKey = 0; + //obtain a list of possible shortcuts for the given key event + for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl) ; ++i) { + Qt::KeyboardModifiers neededMods = ModsTbl[i]; + if ((modifiers & neededMods) == neededMods) { + + depressed = 0; + if (neededMods & Qt::AltModifier) + depressed |= (1 << altMod); + if (neededMods & Qt::ShiftModifier) + depressed |= (1 << shiftMod); + if (neededMods & Qt::ControlModifier) + depressed |= (1 << controlMod); + + // update a keyboard state from a set of explicit masks + xkb_state_update_mask(kb_state, depressed, latchedMods, lockedMods, + baseLayout, latchedLayout, lockedLayout); + sym = xkb_state_key_get_one_sym(kb_state, event->nativeScanCode()); + + if (sym == XKB_KEY_NoSymbol) + continue; + + Qt::KeyboardModifiers mods = modifiers & ~neededMods; + qtKey = keysymToQtKey(sym, mods, keysymToUnicode(sym)); + + if (qtKey == baseQtKey) + continue; + + result += (qtKey + mods); } - } else if (keysym >= 0x1000000 && keysym <= 0x100ffff) { - converted = (ushort) (keysym - 0x1000000); - mapper = 0; } - if (count < (int)chars.size()-1) - chars[count] = '\0'; - - QString text; - if (!mapper && converted.unicode() != 0x0) { - text = converted; - } else if (!chars.isEmpty()) { - // convert chars (8bit) to text (unicode). - if (mapper) - text = mapper->toUnicode(chars.data(), count, 0); - if (text.isEmpty()) { - // no mapper, or codec couldn't convert to unicode (this - // can happen when running in the C locale or with no LANG - // set). try converting from latin-1 - text = QString::fromLatin1(chars); + + xkb_state_unref(kb_state); + return result; + } + +int QXcbKeyboard::keysymToQtKey(xcb_keysym_t key) const +{ + int code = 0; + int i = 0; + while (KeyTbl[i]) { + if (key == KeyTbl[i]) { + code = (int)KeyTbl[i+1]; + break; } + i += 2; } - modifiers = translateModifiers(xmodifiers); + return code; +} +int QXcbKeyboard::keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers &modifiers, QString text) const +{ + int code = 0; + QTextCodec *systemCodec = QTextCodec::codecForLocale(); // Commentary in X11/keysymdef says that X codes match ASCII, so it // is safe to use the locale functions to process X codes in ISO8859-1. - // // This is mainly for compatibility - applications should not use the - // Qt keycodes between 128 and 255, but should rather use the - // QKeyEvent::text(). - // - if (keysym < 128 || (keysym < 256 && (!mapper || mapper->mibEnum()==4))) { + // Qt keycodes between 128 and 255 (extended ACSII codes), but should + // rather use the QKeyEvent::text(). + if (keysym < 128 || (keysym < 256 && systemCodec->mibEnum() == 4)) { // upper-case key, if known code = isprint((int)keysym) ? toupper((int)keysym) : 0; } else if (keysym >= XK_F1 && keysym <= XK_F35) { @@ -853,48 +900,207 @@ QString QXcbKeyboard::translateKeySym(xcb_keysym_t keysym, uint xmodifiers, // numeric keypad keys code = Qt::Key_0 + ((int)keysym - XK_KP_0); } else { - code = translateKeySym(keysym); + code = keysymToQtKey(keysym); } modifiers |= Qt::KeypadModifier; - } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f && text.unicode()->unicode() != 0x7f && !(keysym >= XK_dead_grave && keysym <= XK_dead_horn)) { + } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f + && text.unicode()->unicode() != 0x7f + && !(keysym >= XK_dead_grave && keysym <= XK_dead_currency)) { code = text.unicode()->toUpper().unicode(); } else { // any other keys - code = translateKeySym(keysym); - - if (code == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) { - // map shift+tab to shift+backtab, QShortcutMap knows about it - // and will handle it. - code = Qt::Key_Backtab; - text = QString(); - } + code = keysymToQtKey(keysym); } - return text; + return code; } QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection) : QXcbObject(connection) , m_autorepeat_code(0) + , xkb_context(0) + , xkb_keymap(0) + , xkb_state(0) +#ifndef QT_NO_XKB + , core_device_id(0) +#endif { + updateKeymap(); +#ifndef QT_NO_XKB + if (connection->hasXKB()) { + + updateVModMapping(); + updateVModToRModMapping(); + + // get the core keyboard id + xcb_xkb_get_device_info_cookie_t device_id_cookie; + xcb_xkb_get_device_info_reply_t *device_id; + + device_id_cookie = xcb_xkb_get_device_info(xcb_connection(), + XCB_XKB_ID_USE_CORE_KBD, + 0, 0, 0, 0, 0, 0); + + device_id = xcb_xkb_get_device_info_reply(xcb_connection(), device_id_cookie, 0); + if (!device_id) { + qWarning("Qt: couldn't get core keyboard device info"); + return; + } + + core_device_id = device_id->deviceID; + free(device_id); + } +#else m_key_symbols = xcb_key_symbols_alloc(xcb_connection()); - setupModifiers(); + updateModifiers(); +#endif } QXcbKeyboard::~QXcbKeyboard() { + if (xkb_state) + xkb_state_unref(xkb_state); + if (xkb_keymap) + xkb_keymap_unref(xkb_keymap); + if (xkb_context) + xkb_context_unref(xkb_context); +#ifdef QT_NO_XKB xcb_key_symbols_free(m_key_symbols); +#endif +} + +#ifndef QT_NO_XKB +void QXcbKeyboard::updateVModMapping() +{ + xcb_xkb_get_names_cookie_t names_cookie; + xcb_xkb_get_names_reply_t *name_reply; + xcb_xkb_get_names_value_list_t names_list; + + memset(&vmod_masks, 0, sizeof(vmod_masks)); + + names_cookie = xcb_xkb_get_names(xcb_connection(), + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES); + + name_reply = xcb_xkb_get_names_reply(xcb_connection(), names_cookie, 0); + if (!name_reply) { + qWarning("Qt: failed to retrieve the virtual modifier names from XKB"); + return; + } + + const void *buffer = xcb_xkb_get_names_value_list(name_reply); + xcb_xkb_get_names_value_list_unpack(buffer, + name_reply->nTypes, + name_reply->indicators, + name_reply->virtualMods, + name_reply->groupNames, + name_reply->nKeys, + name_reply->nKeyAliases, + name_reply->nRadioGroups, + name_reply->which, + &names_list); + + int count = 0; + uint vmod_mask, bit; + char *vmod_name; + vmod_mask = name_reply->virtualMods; + // find the virtual modifiers for which names are defined. + for (bit = 1; vmod_mask; bit <<= 1) { + vmod_name = 0; + + if (!(vmod_mask & bit)) + continue; + + vmod_mask &= ~bit; + // virtualModNames - the list of virtual modifier atoms beginning with the lowest-numbered + // virtual modifier for which a name is defined and proceeding to the highest. + QByteArray atomName = connection()->atomName(names_list.virtualModNames[count]); + vmod_name = atomName.data(); + count++; + + if (!vmod_name) + continue; + + // similarly we could retrieve NumLock, Super, Hyper modifiers if needed. + if (qstrcmp(vmod_name, "Alt") == 0) + vmod_masks.alt = bit; + else if (qstrcmp(vmod_name, "Meta") == 0) + vmod_masks.meta = bit; + else if (qstrcmp(vmod_name, "AltGr") == 0) + vmod_masks.altgr = bit; + } + + free(name_reply); } -void QXcbKeyboard::setupModifiers() +void QXcbKeyboard::updateVModToRModMapping() +{ + xcb_xkb_get_map_cookie_t map_cookie; + xcb_xkb_get_map_reply_t *map_reply; + xcb_xkb_get_map_map_t map; + + memset(&rmod_masks, 0, sizeof(rmod_masks)); + + map_cookie = xcb_xkb_get_map(xcb_connection(), + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_MAP_PART_VIRTUAL_MODS, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + map_reply = xcb_xkb_get_map_reply(xcb_connection(), map_cookie, 0); + if (!map_reply) { + qWarning("Qt: failed to retrieve the virtual modifier map from XKB"); + return; + } + + const void *buffer = xcb_xkb_get_map_map(map_reply); + xcb_xkb_get_map_map_unpack(buffer, + map_reply->nTypes, + map_reply->nKeySyms, + map_reply->nKeyActions, + map_reply->totalActions, + map_reply->totalKeyBehaviors, + map_reply->nVModMapKeys, + map_reply->totalKeyExplicit, + map_reply->totalModMapKeys, + map_reply->totalVModMapKeys, + map_reply->present, + &map); + + uint vmod_mask, bit; + // the virtual modifiers mask for which a set of corresponding + // real modifiers is to be returned + vmod_mask = map_reply->virtualMods; + int count = 0; + + for (bit = 1; vmod_mask; bit <<= 1) { + uint modmap; + + if (!(vmod_mask & bit)) + continue; + + vmod_mask &= ~bit; + // real modifier bindings for the specified virtual modifiers + modmap = map.vmods_rtrn[count]; + count++; + + if (vmod_masks.alt == bit) + rmod_masks.alt = modmap; + else if (vmod_masks.meta == bit) + rmod_masks.meta = modmap; + else if (vmod_masks.altgr == bit) + rmod_masks.altgr = modmap; + } + + free(map_reply); +} +#else +void QXcbKeyboard::updateModifiers() { - m_alt_mask = 0; - m_super_mask = 0; - m_hyper_mask = 0; - m_meta_mask = 0; - m_mode_switch_mask = 0; - m_num_lock_mask = 0; - m_caps_lock_mask = 0; + // The core protocol does not provide a convenient way to determine the mapping + // of modifier bits. Clients must retrieve and search the modifier map to determine + // the keycodes bound to each modifier, and then retrieve and search the keyboard + // mapping to determine the keysyms bound to the keycodes. They must repeat this + // process for all modifiers whenever any part of the modifier mapping is changed. + memset(&rmod_masks, 0, sizeof(rmod_masks)); xcb_generic_error_t *error = 0; xcb_connection_t *conn = xcb_connection(); @@ -902,15 +1108,14 @@ void QXcbKeyboard::setupModifiers() xcb_get_modifier_mapping_reply_t *modMapReply = xcb_get_modifier_mapping_reply(conn, modMapCookie, &error); if (error) { - qWarning("QXcbKeyboard: failed to get modifier mapping"); + qWarning("Qt: failed to get modifier mapping"); free(error); return; } // for Alt and Meta L and R are the same static const xcb_keysym_t symbols[] = { - XK_Alt_L, XK_Meta_L, XK_Super_L, XK_Super_R, - XK_Hyper_L, XK_Hyper_R, XK_Num_Lock, XK_Mode_switch, XK_Caps_Lock, + XK_Alt_L, XK_Meta_L, XK_Mode_switch }; static const size_t numSymbols = sizeof symbols / sizeof *symbols; @@ -928,8 +1133,15 @@ void QXcbKeyboard::setupModifiers() xcb_keycode_t keyCode = modMap[x + bit * w]; xcb_keycode_t *itk = modKeyCodes[i]; while (itk && *itk != XCB_NO_SYMBOL) - if (*itk++ == keyCode) - setMask(symbols[i], mask); + if (*itk++ == keyCode) { + uint sym = symbols[i]; + if ((sym == XK_Alt_L || sym == XK_Alt_R)) + rmod_masks.alt = mask; + if ((sym == XK_Meta_L || sym == XK_Meta_R)) + rmod_masks.meta = mask; + if (sym == XK_Mode_switch) + rmod_masks.altgr = mask; + } } } } @@ -938,53 +1150,7 @@ void QXcbKeyboard::setupModifiers() free(modKeyCodes[i]); free(modMapReply); } - -void QXcbKeyboard::setMask(uint sym, uint mask) -{ - if (m_alt_mask == 0 - && m_meta_mask != mask - && m_super_mask != mask - && m_hyper_mask != mask - && (sym == XK_Alt_L || sym == XK_Alt_R)) - m_alt_mask = mask; - - if (m_meta_mask == 0 - && m_alt_mask != mask - && m_super_mask != mask - && m_hyper_mask != mask - && (sym == XK_Meta_L || sym == XK_Meta_R)) - m_meta_mask = mask; - - if (m_super_mask == 0 - && m_alt_mask != mask - && m_meta_mask != mask - && m_hyper_mask != mask - && (sym == XK_Super_L || sym == XK_Super_R)) - m_super_mask = mask; - - if (m_hyper_mask == 0 - && m_alt_mask != mask - && m_meta_mask != mask - && m_super_mask != mask - && (sym == XK_Hyper_L || sym == XK_Hyper_R)) - m_hyper_mask = mask; - - if (m_mode_switch_mask == 0 - && m_alt_mask != mask - && m_meta_mask != mask - && m_super_mask != mask - && m_hyper_mask != mask - && sym == XK_Mode_switch) - m_mode_switch_mask = mask; - - if (m_num_lock_mask == 0 && sym == XK_Num_Lock) - m_num_lock_mask = mask; - - if (m_caps_lock_mask == 0 && sym == XK_Caps_Lock) - m_caps_lock_mask = mask; -} - -// #define XCB_KEYBOARD_DEBUG +#endif class KeyChecker { @@ -1046,16 +1212,21 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod quint16 state, xcb_timestamp_t time) { Q_XCB_NOOP(connection()); -#ifdef XCB_KEYBOARD_DEBUG - printf("key code: %d, state: %d, syms: ", code, state); - for (int i = 0; i <= 5; ++i) { - printf("%d ", xcb_key_symbols_get_keysym(m_key_symbols, code, i)); - } - printf("\n"); + + if (!m_config) + return; + // It is crucial the order of xkb_state_key_get_one_sym & + // xkb_state_update_key operations is not reversed! + xcb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, code); +#ifdef QT_NO_XKB + enum xkb_key_direction direction; + if (type == QEvent::KeyPress) + direction = XKB_KEY_DOWN; + else + direction = XKB_KEY_UP; + xkb_state_update_key(xkb_state, code, direction); #endif - QByteArray chars; - xcb_keysym_t sym = lookupString(window, state, code, type, &chars); QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext(); QMetaMethod method; @@ -1077,12 +1248,12 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod return; } - Qt::KeyboardModifiers modifiers; - int qtcode = 0; - int count = chars.count(); - QString string = translateKeySym(sym, state, qtcode, modifiers, chars, count); + Qt::KeyboardModifiers modifiers = translateModifiers(state); + QString string = keysymToUnicode(sym); + int count = string.size(); string.truncate(count); + int qtcode = keysymToQtKey(sym, modifiers, string); bool isAutoRepeat = false; if (type == QEvent::KeyPress) { @@ -1141,35 +1312,33 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod } } -xcb_keysym_t QXcbKeyboard::lookupString(QWindow *window, uint state, xcb_keycode_t code, - QEvent::Type type, QByteArray *chars) +QString QXcbKeyboard::keysymToUnicode(xcb_keysym_t sym) const { -#ifdef XCB_USE_XLIB - xcb_window_t xWindow = static_cast<QXcbWindow *>(window->handle())->xcb_window(); - xcb_window_t root = connection()->screens().at(0)->root(); - void *xDisplay = connection()->xlib_display(); - int xType = (type == QEvent::KeyRelease ? 3 : 2); - return q_XLookupString(xDisplay, xWindow, root, state, code, xType, chars); -#else - - // No XLookupString available. The following is really incomplete... - - int col = state & XCB_MOD_MASK_SHIFT ? 1 : 0; - const int altGrOffset = 4; - if (state & 128) - col += altGrOffset; - xcb_keysym_t sym = xcb_key_symbols_get_keysym(m_key_symbols, code, col); - if (sym == XCB_NO_SYMBOL) - sym = xcb_key_symbols_get_keysym(m_key_symbols, code, col ^ 0x1); - if (state & XCB_MOD_MASK_LOCK && sym <= 0x7f && isprint(sym)) { - if (isupper(sym)) - sym = tolower(sym); + QByteArray chars; + int bytes; + chars.resize(7); + +#ifdef XKBCOMMON_0_2_0 + if (needWorkaround(sym)) { + quint32 codepoint; + if (sym == XKB_KEY_KP_Space) + codepoint = XKB_KEY_space & 0x7f; else - sym = toupper(sym); - } - return sym; + codepoint = sym & 0x7f; + bytes = utf32_to_utf8(codepoint, chars.data()); + } else { + bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size()); + } +#else + bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size()); #endif + + if (bytes == -1) + qWarning("QXcbKeyboard::handleKeyEvent - buffer too small"); + chars.resize(bytes-1); + + return QString::fromUtf8(chars); } void QXcbKeyboard::handleKeyPressEvent(QXcbWindowEventListener *eventListener, const xcb_key_press_event_t *event) @@ -1189,10 +1358,20 @@ void QXcbKeyboard::handleKeyReleaseEvent(QXcbWindowEventListener *eventListener, handleKeyEvent(window->window(), QEvent::KeyRelease, event->detail, event->state, event->time); } -void QXcbKeyboard::handleMappingNotifyEvent(const xcb_mapping_notify_event_t *event) +void QXcbKeyboard::handleMappingNotifyEvent(const void *event) { - xcb_refresh_keyboard_mapping(m_key_symbols, const_cast<xcb_mapping_notify_event_t *>(event)); - setupModifiers(); + updateKeymap(); +#ifdef QT_NO_XKB + void *ev = const_cast<void *>(event); + xcb_refresh_keyboard_mapping(m_key_symbols, static_cast<xcb_mapping_notify_event_t *>(ev)); + updateModifiers(); +#else + Q_UNUSED(event) + if (connection()->hasXKB()) { + updateVModMapping(); + updateVModToRModMapping(); + } +#endif } QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h index 3c71daa57f..af6677c20f 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.h +++ b/src/plugins/platforms/xcb/qxcbkeyboard.h @@ -44,7 +44,11 @@ #include "qxcbobject.h" -#include "xcb/xcb_keysyms.h" +#ifdef QT_NO_XKB +#include <xcb/xcb_keysyms.h> +#endif + +#include <xkbcommon/xkbcommon.h> #include <QEvent> @@ -56,37 +60,80 @@ class QXcbKeyboard : public QXcbObject { public: QXcbKeyboard(QXcbConnection *connection); + ~QXcbKeyboard(); void handleKeyPressEvent(QXcbWindowEventListener *eventListener, const xcb_key_press_event_t *event); void handleKeyReleaseEvent(QXcbWindowEventListener *eventListener, const xcb_key_release_event_t *event); - void handleMappingNotifyEvent(const xcb_mapping_notify_event_t *event); + void handleMappingNotifyEvent(const void *event); - Qt::KeyboardModifiers translateModifiers(int s); + Qt::KeyboardModifiers translateModifiers(int s) const; -private: + void updateKeymap(); + QList<int> possibleKeys(const QKeyEvent *e) const; + +#ifdef QT_NO_XKB + void updateXKBStateFromCore(quint16 state); + void updateXKBMods(); + quint32 xkbModMask(quint16 state); +#else + int coreDeviceId() { return core_device_id; } + void updateXKBState(xcb_xkb_state_notify_event_t *state); +#endif + +protected: void handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycode_t code, quint16 state, xcb_timestamp_t time); - int translateKeySym(uint key) const; - QString translateKeySym(xcb_keysym_t keysym, uint xmodifiers, - int &code, Qt::KeyboardModifiers &modifiers, - QByteArray &chars, int &count); - void setupModifiers(); - void setMask(uint sym, uint mask); - xcb_keysym_t lookupString(QWindow *window, uint state, xcb_keycode_t code, - QEvent::Type type, QByteArray *chars); - - uint m_alt_mask; - uint m_super_mask; - uint m_hyper_mask; - uint m_meta_mask; - uint m_mode_switch_mask; - uint m_num_lock_mask; - uint m_caps_lock_mask; + QString keysymToUnicode(xcb_keysym_t sym) const; - xcb_key_symbols_t *m_key_symbols; + int keysymToQtKey(xcb_keysym_t keysym) const; + int keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers &modifiers, QString text) const; + + void readXKBConfig(struct xkb_rule_names *names); + +#ifdef QT_NO_XKB + void updateModifiers(); +#else + void updateVModMapping(); + void updateVModToRModMapping(); +#endif + +private: + bool m_config; xcb_keycode_t m_autorepeat_code; + + struct xkb_context *xkb_context; + struct xkb_keymap *xkb_keymap; + struct xkb_state *xkb_state; + + struct _mod_masks { + uint alt; + uint altgr; + uint meta; + }; + + _mod_masks rmod_masks; + +#ifdef QT_NO_XKB + xcb_key_symbols_t *m_key_symbols; + + struct _xkb_mods { + xkb_mod_index_t shift; + xkb_mod_index_t lock; + xkb_mod_index_t control; + xkb_mod_index_t mod1; + xkb_mod_index_t mod2; + xkb_mod_index_t mod3; + xkb_mod_index_t mod4; + xkb_mod_index_t mod5; + }; + + _xkb_mods xkb_mods; +#else + _mod_masks vmod_masks; + int core_device_id; +#endif }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index da60cfd2bd..7d832a1c08 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -58,7 +58,9 @@ #include "qglxintegration.h" #endif -#ifndef XCB_USE_XLIB +#ifdef XCB_USE_XLIB +# include <X11/Xlib.h> +#else # include <stdio.h> #endif diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index a6ead49a27..37c6c97bc4 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -44,6 +44,7 @@ #include "qxcbcursor.h" #include "qxcbimage.h" #include "qnamespace.h" +#include "qxcbxsettings.h" #include <stdio.h> @@ -68,6 +69,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr, , m_refreshRate(60) , m_forcedDpi(-1) , m_hintStyle(QFontEngine::HintStyle(-1)) + , m_xSettings(0) { if (connection->hasXRandr()) xcb_randr_select_input(xcb_connection(), screen()->root, true); @@ -580,4 +582,12 @@ void QXcbScreen::readXResources() } } +QXcbXSettings *QXcbScreen::xSettings() const +{ + if (!m_xSettings) { + QXcbScreen *self = const_cast<QXcbScreen *>(this); + self->m_xSettings = new QXcbXSettings(self); + } + return m_xSettings; +} QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 0382be8a29..c36492db64 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE class QXcbConnection; class QXcbCursor; +class QXcbXSettings; class QXcbScreen : public QXcbObject, public QPlatformScreen { @@ -102,6 +103,9 @@ public: void readXResources(); QFontEngine::HintStyle hintStyle() const { return m_hintStyle; } + + QXcbXSettings *xSettings() const; + private: static bool xResource(const QByteArray &identifier, const QByteArray &expectedIdentifier, @@ -127,6 +131,7 @@ private: int m_refreshRate; int m_forcedDpi; QFontEngine::HintStyle m_hintStyle; + QXcbXSettings *m_xSettings; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbxsettings.cpp b/src/plugins/platforms/xcb/qxcbxsettings.cpp new file mode 100644 index 0000000000..7ffd3e105f --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbxsettings.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxcbxsettings.h" + +#include <QtCore/QByteArray> + +#include <X11/extensions/XIproto.h> + +QT_BEGIN_NAMESPACE +/* Implementation of http://standards.freedesktop.org/xsettings-spec/xsettings-0.5.html */ + +enum XSettingsType { + XSettingsTypeInteger = 0, + XSettingsTypeString = 1, + XSettingsTypeColor = 2 +}; + +class QXcbXSettingsCallback +{ +public: + QXcbXSettings::PropertyChangeFunc func; + void *handle; +}; + +class QXcbXSettingsPropertyValue +{ +public: + QXcbXSettingsPropertyValue() + : last_change_serial(-1) + {} + + void updateValue(QXcbScreen *screen, const QByteArray &name, const QVariant &value, int last_change_serial) + { + if (last_change_serial <= this->last_change_serial) + return; + this->value = value; + this->last_change_serial = last_change_serial; + QLinkedList<QXcbXSettingsCallback>::const_iterator it = callback_links.begin(); + for (;it != callback_links.end();++it) { + it->func(screen,name,value,it->handle); + } + } + + void addCallback(QXcbXSettings::PropertyChangeFunc func, void *handle) + { + QXcbXSettingsCallback callback; + callback.func = func; + callback.handle = handle; + callback_links.append(callback); + } + + QVariant value; + int last_change_serial; + QLinkedList<QXcbXSettingsCallback> callback_links; + +}; + +class QXcbXSettingsPrivate +{ +public: + QXcbXSettingsPrivate(QXcbScreen *screen) + : screen(screen) + { + } + + QByteArray getSettings() + { + QXcbConnectionGrabber connectionGrabber(screen->connection()); + + int offset = 0; + QByteArray settings; + xcb_atom_t _xsettings_atom = screen->connection()->atom(QXcbAtom::_XSETTINGS_SETTINGS); + while (1) { + xcb_get_property_cookie_t get_prop_cookie = + xcb_get_property_unchecked(screen->xcb_connection(), + false, + x_settings_window, + _xsettings_atom, + _xsettings_atom, + offset/4, + 8192); + xcb_get_property_reply_t *reply = xcb_get_property_reply(screen->xcb_connection(), get_prop_cookie, NULL); + bool more = false; + if (!reply) + return settings; + + settings += QByteArray((const char *)xcb_get_property_value(reply), xcb_get_property_value_length(reply)); + offset += xcb_get_property_value_length(reply); + more = reply->bytes_after != 0; + + free(reply); + + if (!more) + break; + } + + return settings; + } + + static int round_to_nearest_multiple_of_4(int value) + { + int remainder = value % 4; + if (!remainder) + return value; + return value + 4 - remainder; + } + + void populateSettings(const QByteArray &xSettings) + { + if (xSettings.length() < 12) + return; + // we ignore byteorder for now + char byteOrder = xSettings.at(1); + Q_UNUSED(byteOrder); + uint serial = *reinterpret_cast<const uint *>(xSettings.mid(4,4).constData()); + serial = serial; + uint number_of_settings = *reinterpret_cast<const uint *>(xSettings.mid(8,4).constData()); + + const char *data = xSettings.constData() + 12; + size_t offset = 0; + for (uint i = 0; i < number_of_settings; i++) { + int local_offset = 0; + XSettingsType type = static_cast<XSettingsType>(*reinterpret_cast<const quint8 *>(data + offset)); + local_offset += 2; + + quint16 name_len = *reinterpret_cast<const quint16 *>(data + offset + local_offset); + local_offset += 2; + + QByteArray name(data + offset + local_offset, name_len); + local_offset += round_to_nearest_multiple_of_4(name_len); + + int last_change_serial = *reinterpret_cast<const int *>(data + offset + local_offset); + Q_UNUSED(last_change_serial); + local_offset += 4; + + QVariant value; + if (type == XSettingsTypeString) { + int value_length = *reinterpret_cast<const int *>(data + offset + local_offset); + local_offset+=4; + QByteArray value_string(data + offset + local_offset, value_length); + value.setValue(value_string); + local_offset += round_to_nearest_multiple_of_4(value_length); + } else if (type == XSettingsTypeInteger) { + int value_length = *reinterpret_cast<const int *>(data + offset + local_offset); + local_offset += 4; + value.setValue(value_length); + } else if (type == XSettingsTypeColor) { + quint16 red = *reinterpret_cast<const quint16 *>(data + offset + local_offset); + local_offset += 2; + quint16 green = *reinterpret_cast<const quint16 *>(data + offset + local_offset); + local_offset += 2; + quint16 blue = *reinterpret_cast<const quint16 *>(data + offset + local_offset); + local_offset += 2; + quint16 alpha= *reinterpret_cast<const quint16 *>(data + offset + local_offset); + local_offset += 2; + QColor color_value(red,green,blue,alpha); + value.setValue(color_value); + } + offset += local_offset; + settings[name].updateValue(screen,name,value,last_change_serial); + } + + } + + QXcbScreen *screen; + xcb_window_t x_settings_window; + int serial; + QMap<QByteArray, QXcbXSettingsPropertyValue> settings; +}; + + +QXcbXSettings::QXcbXSettings(QXcbScreen *screen) + : d_ptr(new QXcbXSettingsPrivate(screen)) +{ + QByteArray settings_atom_for_screen("_XSETTINGS_S"); + settings_atom_for_screen.append(QByteArray::number(screen->screenNumber())); + xcb_intern_atom_cookie_t atom_cookie = xcb_intern_atom(screen->xcb_connection(), + false, + settings_atom_for_screen.length(), + settings_atom_for_screen.constData()); + xcb_intern_atom_reply_t *atom_reply = xcb_intern_atom_reply(screen->xcb_connection(),atom_cookie,NULL); + xcb_atom_t selection_owner_atom = atom_reply->atom; + free(atom_reply); + + xcb_get_selection_owner_cookie_t selection_cookie = + xcb_get_selection_owner(screen->xcb_connection(), selection_owner_atom); + xcb_get_selection_owner_reply_t *selection_result = + xcb_get_selection_owner_reply(screen->xcb_connection(), selection_cookie, NULL); + + d_ptr->x_settings_window = selection_result->owner; + free(selection_result); + + const uint32_t event_mask[] = { XCB_EVENT_MASK_STRUCTURE_NOTIFY|XCB_EVENT_MASK_PROPERTY_CHANGE }; + xcb_change_window_attributes(screen->xcb_connection(),d_ptr->x_settings_window,XCB_CW_EVENT_MASK,event_mask); + + d_ptr->populateSettings(d_ptr->getSettings()); +} + +void QXcbXSettings::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) +{ + Q_D(QXcbXSettings); + if (event->window != d->x_settings_window) + return; + d->populateSettings(d->getSettings()); +} + +void QXcbXSettings::registerCallbackForProperty(const QByteArray &property, QXcbXSettings::PropertyChangeFunc func, void *handle) +{ + Q_D(QXcbXSettings); + d->settings[property].addCallback(func,handle); +} + +void QXcbXSettings::removeCallbackForHandle(const QByteArray &property, void *handle) +{ + Q_D(QXcbXSettings); + QXcbXSettingsPropertyValue &value = d->settings[property]; + QLinkedList<QXcbXSettingsCallback>::iterator it = value.callback_links.begin(); + while (it != value.callback_links.end()) { + if (it->handle == handle) + it = value.callback_links.erase(it); + else + ++it; + } +} + +void QXcbXSettings::removeCallbackForHandle(void *handle) +{ + Q_D(QXcbXSettings); + for (QMap<QByteArray, QXcbXSettingsPropertyValue>::const_iterator it = d->settings.cbegin(); + it != d->settings.cend(); ++it) { + removeCallbackForHandle(it.key(),handle); + } +} + +QVariant QXcbXSettings::setting(const QByteArray &property) const +{ + Q_D(const QXcbXSettings); + return d->settings.value(property).value; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbxsettings.h b/src/plugins/platforms/xcb/qxcbxsettings.h new file mode 100644 index 0000000000..16fed862bc --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbxsettings.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXCBXSETTINGS_H +#define QXCBXSETTINGS_H + +#include "qxcbscreen.h" + +QT_BEGIN_NAMESPACE + +class QXcbXSettingsPrivate; + +class QXcbXSettings : public QXcbWindowEventListener +{ + Q_DECLARE_PRIVATE(QXcbXSettings) +public: + QXcbXSettings(QXcbScreen *screen); + + QVariant setting(const QByteArray &property) const; + + typedef void (*PropertyChangeFunc)(QXcbScreen *screen, const QByteArray &name, const QVariant &property, void *handle); + void registerCallbackForProperty(const QByteArray &property, PropertyChangeFunc func, void *handle); + void removeCallbackForHandle(const QByteArray &property, void *handle); + void removeCallbackForHandle(void *handle); + + void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) Q_DECL_OVERRIDE; +private: + QXcbXSettingsPrivate *d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QXCBXSETTINGS_H diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro index 5823e97f36..82995286c4 100644 --- a/src/plugins/platforms/xcb/xcb-plugin.pro +++ b/src/plugins/platforms/xcb/xcb-plugin.pro @@ -6,7 +6,6 @@ load(qt_plugin) QT += core-private gui-private platformsupport-private - SOURCES = \ qxcbclipboard.cpp \ qxcbconnection.cpp \ @@ -22,7 +21,7 @@ SOURCES = \ qxcbnativeinterface.cpp \ qxcbcursor.cpp \ qxcbimage.cpp \ - qxlibconvenience.cpp + qxcbxsettings.cpp HEADERS = \ qxcbclipboard.h \ @@ -39,11 +38,11 @@ HEADERS = \ qxcbnativeinterface.h \ qxcbcursor.h \ qxcbimage.h \ - qxlibconvenience.h + qxcbxsettings.h LIBS += -ldl -# needed by GLX, Xcursor, XLookupString, ... +# needed by GLX, Xcursor ... contains(QT_CONFIG, xcb-xlib) { DEFINES += XCB_USE_XLIB LIBS += -lX11 -lX11-xcb @@ -112,7 +111,23 @@ contains(QT_CONFIG, xcb-qt) { INCLUDEPATH += $$XCB_DIR/include $$XCB_DIR/sysinclude LIBS += -lxcb -L$$OUT_PWD/xcb-static -lxcb-static } else { - LIBS += -lxcb -lxcb-image -lxcb-keysyms -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr + LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr !contains(DEFINES, QT_NO_SHAPE):LIBS += -lxcb-shape + contains(DEFINES, QT_NO_XKB) { + LIBS += -lxcb-keysyms + } else { + LIBS += -lxcb-xkb + } } +# libxkbcommon +contains(QT_CONFIG, xkbcommon-qt): { + include(../../../3rdparty/xkbcommon.pri) +} else { + LIBS += $$QMAKE_LIBS_XKBCOMMON + QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XKBCOMMON + equals(QMAKE_VERSION_XKBCOMMON, "0.2.0") { + DEFINES += XKBCOMMON_0_2_0 + INCLUDEPATH += ../../../3rdparty/xkbcommon/xkbcommon/ + } +} diff --git a/src/plugins/platforms/xcb/xcb-static/xcb-static.pro b/src/plugins/platforms/xcb/xcb-static/xcb-static.pro index 01667d41db..2fd5519053 100644 --- a/src/plugins/platforms/xcb/xcb-static/xcb-static.pro +++ b/src/plugins/platforms/xcb/xcb-static/xcb-static.pro @@ -24,7 +24,8 @@ SOURCES += \ $$LIBXCB_DIR/shm.c \ $$LIBXCB_DIR/sync.c \ $$LIBXCB_DIR/render.c \ - $$LIBXCB_DIR/shape.c + $$LIBXCB_DIR/shape.c \ + $$LIBXCB_DIR/xkb.c # # xcb-util |