diff options
Diffstat (limited to 'src/plugins/platforms')
200 files changed, 12956 insertions, 1321 deletions
diff --git a/src/plugins/platforms/android/android.pro b/src/plugins/platforms/android/android.pro new file mode 100644 index 0000000000..aa5ab4ddbd --- /dev/null +++ b/src/plugins/platforms/android/android.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += raster opengl diff --git a/src/plugins/platforms/android/opengl/opengl.pro b/src/plugins/platforms/android/opengl/opengl.pro new file mode 100644 index 0000000000..301c8e6e4c --- /dev/null +++ b/src/plugins/platforms/android/opengl/opengl.pro @@ -0,0 +1,30 @@ +TARGET = qtforandroidGL + +PLUGIN_TYPE = platforms +load(qt_plugin) + +# STATICPLUGIN needed because there's a Q_IMPORT_PLUGIN in androidjnimain.cpp +# Yes, the plugin imports itself statically +DEFINES += QT_STATICPLUGIN ANDROID_PLUGIN_OPENGL + +!equals(ANDROID_PLATFORM, android-9) { + INCLUDEPATH += $$NDK_ROOT/platforms/android-9/arch-$$ANDROID_ARCHITECTURE/usr/include + LIBS += -L$$NDK_ROOT/platforms/android-9/arch-$$ANDROID_ARCHITECTURE/usr/lib -ljnigraphics -landroid +} else { + LIBS += -ljnigraphics -landroid +} + +EGLFS_PLATFORM_HOOKS_SOURCES = $$PWD/../src/opengl/qeglfshooks_android.cpp + +INCLUDEPATH += $$PWD/../src/opengl/ + +HEADERS += \ + $$PWD/../src/opengl/qandroidopenglcontext.h \ + $$PWD/../src/opengl/qandroidopenglplatformwindow.h + +SOURCES += \ + $$PWD/../src/opengl/qandroidopenglcontext.cpp \ + $$PWD/../src/opengl/qandroidopenglplatformwindow.cpp + +include($$PWD/../../eglfs/eglfs.pri) +include($$PWD/../src/src.pri) diff --git a/src/plugins/platforms/android/raster/raster.pro b/src/plugins/platforms/android/raster/raster.pro new file mode 100644 index 0000000000..53d8ee7a2b --- /dev/null +++ b/src/plugins/platforms/android/raster/raster.pro @@ -0,0 +1,19 @@ +TARGET = qtforandroid + +PLUGIN_TYPE = platforms + +# STATICPLUGIN needed because there's a Q_IMPORT_PLUGIN in androidjnimain.cpp +# Yes, the plugin imports itself statically +DEFINES += QT_STATICPLUGIN + +load(qt_plugin) + +!contains(ANDROID_PLATFORM, android-9) { + INCLUDEPATH += $$NDK_ROOT/platforms/android-9/arch-$$ANDROID_ARCHITECTURE/usr/include + LIBS += -L$$NDK_ROOT/platforms/android-9/arch-$$ANDROID_ARCHITECTURE/usr/lib -ljnigraphics -landroid +} else { + LIBS += -ljnigraphics -landroid +} + +include($$PWD/../src/src.pri) +include($$PWD/../src/raster/raster.pri) diff --git a/src/plugins/platforms/android/src/android.json b/src/plugins/platforms/android/src/android.json new file mode 100644 index 0000000000..6843bd3301 --- /dev/null +++ b/src/plugins/platforms/android/src/android.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "android" ] +} diff --git a/src/plugins/platforms/android/src/androidjniclipboard.cpp b/src/plugins/platforms/android/src/androidjniclipboard.cpp new file mode 100644 index 0000000000..05270ac374 --- /dev/null +++ b/src/plugins/platforms/android/src/androidjniclipboard.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "androidjniclipboard.h" +#include "androidjnimain.h" + +using namespace QtAndroid; +namespace QtAndroidClipboard +{ + // Clipboard support + static jmethodID m_registerClipboardManagerMethodID = 0; + static jmethodID m_setClipboardTextMethodID = 0; + static jmethodID m_hasClipboardTextMethodID = 0; + static jmethodID m_getClipboardTextMethodID = 0; + // Clipboard support + + void setClipboardListener(QAndroidPlatformClipboard *listener) + { + Q_UNUSED(listener); + + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + env.jniEnv->CallStaticVoidMethod(applicationClass(), m_registerClipboardManagerMethodID); + } + + void setClipboardText(const QString &text) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + jstring jtext = env.jniEnv->NewString(reinterpret_cast<const jchar *>(text.data()), + text.length()); + env.jniEnv->CallStaticVoidMethod(applicationClass(), m_setClipboardTextMethodID, jtext); + env.jniEnv->DeleteLocalRef(jtext); + } + + bool hasClipboardText() + { + AttachedJNIEnv env; + if (!env.jniEnv) + return false; + + return env.jniEnv->CallStaticBooleanMethod(applicationClass(), m_hasClipboardTextMethodID); + } + + QString clipboardText() + { + AttachedJNIEnv env; + if (!env.jniEnv) + return QString(); + + jstring text = reinterpret_cast<jstring>(env.jniEnv->CallStaticObjectMethod(applicationClass(), + m_getClipboardTextMethodID)); + const jchar *jstr = env.jniEnv->GetStringChars(text, 0); + QString str(reinterpret_cast<const QChar *>(jstr), env.jniEnv->GetStringLength(text)); + env.jniEnv->ReleaseStringChars(text, jstr); + return str; + } + + +#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ + VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ + if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), methodErrorMsgFmt(), METHOD_NAME, METHOD_SIGNATURE); \ + return false; \ + } + + bool registerNatives(JNIEnv *env) + { + jclass appClass = QtAndroid::applicationClass(); + + GET_AND_CHECK_STATIC_METHOD(m_registerClipboardManagerMethodID, appClass, "registerClipboardManager", "()V"); + GET_AND_CHECK_STATIC_METHOD(m_setClipboardTextMethodID, appClass, "setClipboardText", "(Ljava/lang/String;)V"); + GET_AND_CHECK_STATIC_METHOD(m_hasClipboardTextMethodID, appClass, "hasClipboardText", "()Z"); + GET_AND_CHECK_STATIC_METHOD(m_getClipboardTextMethodID, appClass, "getClipboardText", "()Ljava/lang/String;"); + + return true; + } +} diff --git a/src/plugins/platforms/android/src/androidjniclipboard.h b/src/plugins/platforms/android/src/androidjniclipboard.h new file mode 100644 index 0000000000..15cd93202e --- /dev/null +++ b/src/plugins/platforms/android/src/androidjniclipboard.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 ANDROIDJNICLIPBOARD_H +#define ANDROIDJNICLIPBOARD_H + +#include <jni.h> +#include <QString> + +class QAndroidPlatformClipboard; +namespace QtAndroidClipboard +{ + // Clipboard support + void setClipboardListener(QAndroidPlatformClipboard *listener); + void setClipboardText(const QString &text); + bool hasClipboardText(); + QString clipboardText(); + // Clipboard support + + bool registerNatives(JNIEnv *env); +} + +#endif // ANDROIDJNICLIPBOARD_H diff --git a/src/plugins/platforms/android/src/androidjniinput.cpp b/src/plugins/platforms/android/src/androidjniinput.cpp new file mode 100644 index 0000000000..da6156a330 --- /dev/null +++ b/src/plugins/platforms/android/src/androidjniinput.cpp @@ -0,0 +1,497 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "androidjniinput.h" +#include "androidjnimain.h" +#include "qandroidplatformintegration.h" + +#include <qpa/qwindowsysteminterface.h> +#include <QTouchEvent> +#include <QPointer> + +using namespace QtAndroid; + +namespace QtAndroidInput +{ + static jmethodID m_showSoftwareKeyboardMethodID = 0; + static jmethodID m_resetSoftwareKeyboardMethodID = 0; + static jmethodID m_hideSoftwareKeyboardMethodID = 0; + static jmethodID m_isSoftwareKeyboardVisibleMethodID = 0; + static jmethodID m_updateSelectionMethodID = 0; + + static bool m_ignoreMouseEvents = false; + + static QList<QWindowSystemInterface::TouchPoint> m_touchPoints; + + static QPointer<QWindow> m_mouseGrabber; + + void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + env.jniEnv->CallStaticVoidMethod(applicationClass(), m_updateSelectionMethodID, + selStart, selEnd, candidatesStart, candidatesEnd); + } + + void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + env.jniEnv->CallStaticVoidMethod(applicationClass(), + m_showSoftwareKeyboardMethodID, + left, + top, + width, + height, + inputHints); + } + + void resetSoftwareKeyboard() + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + env.jniEnv->CallStaticVoidMethod(applicationClass(), m_resetSoftwareKeyboardMethodID); + } + + void hideSoftwareKeyboard() + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + env.jniEnv->CallStaticVoidMethod(applicationClass(), m_hideSoftwareKeyboardMethodID); + } + + bool isSoftwareKeyboardVisible() + { + AttachedJNIEnv env; + if (!env.jniEnv) + return false; + + return env.jniEnv->CallStaticBooleanMethod(applicationClass(), m_isSoftwareKeyboardVisibleMethodID); + } + + + static void mouseDown(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y) + { + if (m_ignoreMouseEvents) + return; + + QPoint globalPos(x,y); + QWindow *tlw = topLevelWindowAt(globalPos); + m_mouseGrabber = tlw; + QPoint localPos = tlw ? (globalPos - tlw->position()) : globalPos; + QWindowSystemInterface::handleMouseEvent(tlw, + localPos, + globalPos, + Qt::MouseButtons(Qt::LeftButton)); + } + + static void mouseUp(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y) + { + QPoint globalPos(x,y); + QWindow *tlw = m_mouseGrabber.data(); + if (!tlw) + tlw = topLevelWindowAt(globalPos); + QPoint localPos = tlw ? (globalPos -tlw->position()) : globalPos; + QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos + , Qt::MouseButtons(Qt::NoButton)); + m_ignoreMouseEvents = false; + m_mouseGrabber = 0; + } + + static void mouseMove(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y) + { + + if (m_ignoreMouseEvents) + return; + + QPoint globalPos(x,y); + QWindow *tlw = m_mouseGrabber.data(); + if (!tlw) + tlw = topLevelWindowAt(globalPos); + QPoint localPos = tlw ? (globalPos-tlw->position()) : globalPos; + QWindowSystemInterface::handleMouseEvent(tlw, + localPos, + globalPos, + Qt::MouseButtons(Qt::LeftButton)); + } + + static void longPress(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y) + { + m_ignoreMouseEvents = true; + QPoint globalPos(x,y); + QWindow *tlw = topLevelWindowAt(globalPos); + QPoint localPos = tlw ? (globalPos-tlw->position()) : globalPos; + + // Release left button + QWindowSystemInterface::handleMouseEvent(tlw, + localPos, + globalPos, + Qt::MouseButtons(Qt::NoButton)); + + // Press right button + QWindowSystemInterface::handleMouseEvent(tlw, + localPos, + globalPos, + Qt::MouseButtons(Qt::RightButton)); + } + + static void touchBegin(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/) + { + m_touchPoints.clear(); + } + + static void touchAdd(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint id, jint action, jboolean /*primary*/, jint x, jint y, jfloat size, jfloat pressure) + { + Qt::TouchPointState state = Qt::TouchPointStationary; + switch (action) { + case 0: + state = Qt::TouchPointPressed; + break; + case 1: + state = Qt::TouchPointMoved; + break; + case 2: + state = Qt::TouchPointStationary; + break; + case 3: + state = Qt::TouchPointReleased; + break; + } + + const int dw = desktopWidthPixels(); + const int dh = desktopHeightPixels(); + QWindowSystemInterface::TouchPoint touchPoint; + touchPoint.id = id; + touchPoint.pressure = pressure; + touchPoint.normalPosition = QPointF(double(x / dw), double(y / dh)); + touchPoint.state = state; + touchPoint.area = QRectF(x - double(dw*size) / 2.0, + y - double(dh*size) / 2.0, + double(dw*size), + double(dh*size)); + m_touchPoints.push_back(touchPoint); + } + + static void touchEnd(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint action) + { + if (m_touchPoints.isEmpty()) + return; + + QEvent::Type eventType = QEvent::None; + switch (action) { + case 0: + eventType = QEvent::TouchBegin; + break; + case 1: + eventType = QEvent::TouchUpdate; + break; + case 2: + eventType = QEvent::TouchEnd; + break; + } + + QAndroidPlatformIntegration *platformIntegration = QtAndroid::androidPlatformIntegration(); + QTouchDevice *touchDevice = platformIntegration->touchDevice(); + if (touchDevice == 0) { + touchDevice = new QTouchDevice; + touchDevice->setType(QTouchDevice::TouchScreen); + touchDevice->setCapabilities(QTouchDevice::Position + | QTouchDevice::Area + | QTouchDevice::Pressure + | QTouchDevice::NormalizedPosition); + QWindowSystemInterface::registerTouchDevice(touchDevice); + platformIntegration->setTouchDevice(touchDevice); + } + + QWindow *window = QtAndroid::topLevelWindowAt(m_touchPoints.at(0).area.center().toPoint()); + QWindowSystemInterface::handleTouchEvent(window, touchDevice, m_touchPoints); + } + + static int mapAndroidKey(int key) + { + // 0--9 0x00000007 -- 0x00000010 + if (key >= 0x00000007 && key <= 0x00000010) + return Qt::Key_0 + key - 0x00000007; + + // A--Z 0x0000001d -- 0x00000036 + if (key >= 0x0000001d && key <= 0x00000036) + return Qt::Key_A + key - 0x0000001d; + + switch (key) { + case 0x00000039: + case 0x0000003a: + return Qt::Key_Alt; + + case 0x0000004b: + return Qt::Key_Apostrophe; + + case 0x00000004: // KEYCODE_BACK + return Qt::Key_Back; + + case 0x00000049: + return Qt::Key_Backslash; + + case 0x00000005: + return Qt::Key_Call; + + case 0x0000001b: + return Qt::Key_WebCam; + + case 0x0000001c: + return Qt::Key_Clear; + + case 0x00000037: + return Qt::Key_Comma; + + case 0x00000043: + return Qt::Key_Backspace; + + case 0x00000017: // KEYCODE_DPAD_CENTER + return Qt::Key_Enter; + + case 0x00000014: // KEYCODE_DPAD_DOWN + return Qt::Key_Down; + + case 0x00000015: //KEYCODE_DPAD_LEFT + return Qt::Key_Left; + + case 0x00000016: //KEYCODE_DPAD_RIGHT + return Qt::Key_Right; + + case 0x00000013: //KEYCODE_DPAD_UP + return Qt::Key_Up; + + case 0x00000006: //KEYCODE_ENDCALL + return Qt::Key_Hangup; + + case 0x00000042: + return Qt::Key_Return; + + case 0x00000041: //KEYCODE_ENVELOPE + return Qt::Key_LaunchMail; + + case 0x00000046: + return Qt::Key_Equal; + + case 0x00000040: + return Qt::Key_Explorer; + + case 0x00000003: + return Qt::Key_Home; + + case 0x00000047: + return Qt::Key_BracketLeft; + + case 0x0000005a: // KEYCODE_MEDIA_FAST_FORWARD + return Qt::Key_Forward; + + case 0x00000057: + return Qt::Key_MediaNext; + + case 0x00000055: + return Qt::Key_MediaPlay; + + case 0x00000058: + return Qt::Key_MediaPrevious; + + case 0x00000059: + return Qt::Key_AudioRewind; + + case 0x00000056: + return Qt::Key_MediaStop; + + case 0x00000052: //KEYCODE_MENU + return Qt::Key_Menu; + + case 0x00000045: + return Qt::Key_Minus; + + case 0x0000005b: + return Qt::Key_VolumeMute; + + case 0x0000004e: + return Qt::Key_NumLock; + + case 0x00000038: + return Qt::Key_Period; + + case 0x00000051: + return Qt::Key_Plus; + + case 0x0000001a: + return Qt::Key_PowerOff; + + case 0x00000048: + return Qt::Key_BracketRight; + + case 0x00000054: + return Qt::Key_Search; + + case 0x0000004a: + return Qt::Key_Semicolon; + + case 0x0000003b: + case 0x0000003c: + return Qt::Key_Shift; + + case 0x0000004c: + return Qt::Key_Slash; + + case 0x00000001: + return Qt::Key_Left; + + case 0x00000002: + return Qt::Key_Right; + + case 0x0000003e: + return Qt::Key_Space; + + case 0x0000003f: // KEYCODE_SYM + return Qt::Key_Meta; + + case 0x0000003d: + return Qt::Key_Tab; + + case 0x00000019: + return Qt::Key_VolumeDown; + + case 0x00000018: + return Qt::Key_VolumeUp; + + case 0x00000000: // KEYCODE_UNKNOWN + case 0x00000011: // KEYCODE_STAR ?!?!? + case 0x00000012: // KEYCODE_POUND ?!?!? + case 0x00000053: // KEYCODE_NOTIFICATION ?!?!? + case 0x0000004f: // KEYCODE_HEADSETHOOK ?!?!? + case 0x00000044: // KEYCODE_GRAVE ?!?!? + case 0x00000050: // KEYCODE_FOCUS ?!?!? + return Qt::Key_Any; + + default: + return 0; + } + } + + static void keyDown(JNIEnv */*env*/, jobject /*thiz*/, jint key, jint unicode, jint modifier) + { + Qt::KeyboardModifiers modifiers; + if (modifier & 1) + modifiers |= Qt::ShiftModifier; + + if (modifier & 2) + modifiers |= Qt::AltModifier; + + if (modifier & 4) + modifiers |= Qt::MetaModifier; + + QWindowSystemInterface::handleKeyEvent(0, + QEvent::KeyPress, + mapAndroidKey(key), + modifiers, + QChar(unicode), + true); + } + + static void keyUp(JNIEnv */*env*/, jobject /*thiz*/, jint key, jint unicode, jint modifier) + { + Qt::KeyboardModifiers modifiers; + if (modifier & 1) + modifiers |= Qt::ShiftModifier; + + if (modifier & 2) + modifiers |= Qt::AltModifier; + + if (modifier & 4) + modifiers |= Qt::MetaModifier; + + QWindowSystemInterface::handleKeyEvent(0, + QEvent::KeyRelease, + mapAndroidKey(key), + modifiers, + QChar(unicode), + true); + } + + + static JNINativeMethod methods[] = { + {"touchBegin","(I)V",(void*)touchBegin}, + {"touchAdd","(IIIZIIFF)V",(void*)touchAdd}, + {"touchEnd","(II)V",(void*)touchEnd}, + {"mouseDown", "(III)V", (void *)mouseDown}, + {"mouseUp", "(III)V", (void *)mouseUp}, + {"mouseMove", "(III)V", (void *)mouseMove}, + {"longPress", "(III)V", (void *)longPress}, + {"keyDown", "(III)V", (void *)keyDown}, + {"keyUp", "(III)V", (void *)keyUp} + }; + +#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ + VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ + if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), methodErrorMsgFmt(), METHOD_NAME, METHOD_SIGNATURE); \ + return false; \ + } + + bool registerNatives(JNIEnv *env) + { + jclass appClass = QtAndroid::applicationClass(); + + if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed"); + return false; + } + + GET_AND_CHECK_STATIC_METHOD(m_showSoftwareKeyboardMethodID, appClass, "showSoftwareKeyboard", "(IIIII)V"); + GET_AND_CHECK_STATIC_METHOD(m_resetSoftwareKeyboardMethodID, appClass, "resetSoftwareKeyboard", "()V"); + GET_AND_CHECK_STATIC_METHOD(m_hideSoftwareKeyboardMethodID, appClass, "hideSoftwareKeyboard", "()V"); + GET_AND_CHECK_STATIC_METHOD(m_isSoftwareKeyboardVisibleMethodID, appClass, "isSoftwareKeyboardVisible", "()Z"); + GET_AND_CHECK_STATIC_METHOD(m_updateSelectionMethodID, appClass, "updateSelection", "(IIII)V"); + return true; + } +} diff --git a/src/plugins/platforms/android/src/androidjniinput.h b/src/plugins/platforms/android/src/androidjniinput.h new file mode 100644 index 0000000000..a78c7519db --- /dev/null +++ b/src/plugins/platforms/android/src/androidjniinput.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 ANDROIDJNIINPUT_H +#define ANDROIDJNIINPUT_H +#include <jni.h> + +namespace QtAndroidInput +{ + // Software keyboard support + void showSoftwareKeyboard(int top, int left, int width, int height, int inputHints); + void resetSoftwareKeyboard(); + void hideSoftwareKeyboard(); + bool isSoftwareKeyboardVisible(); + void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd); + // Software keyboard support + + bool registerNatives(JNIEnv *env); +} + +#endif // ANDROIDJNIINPUT_H diff --git a/src/plugins/platforms/android/src/androidjnimain.cpp b/src/plugins/platforms/android/src/androidjnimain.cpp new file mode 100644 index 0000000000..f8f077908c --- /dev/null +++ b/src/plugins/platforms/android/src/androidjnimain.cpp @@ -0,0 +1,845 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 <dlfcn.h> +#include <pthread.h> +#include <qcoreapplication.h> +#include <qimage.h> +#include <qpoint.h> +#include <qplugin.h> +#include <qsemaphore.h> +#include <qmutex.h> +#include <qdebug.h> +#include <qglobal.h> +#include <qobjectdefs.h> +#include <stdlib.h> + +#include "androidjnimain.h" +#include "androidjniinput.h" +#include "androidjniclipboard.h" +#include "androidjnimenu.h" +#include "qandroidplatformintegration.h" +#include <QtWidgets/QApplication> + +#include <qabstracteventdispatcher.h> + +#include <android/bitmap.h> +#include <android/asset_manager_jni.h> +#include "qandroidassetsfileenginehandler.h" +#include <android/api-level.h> + +#include <qpa/qwindowsysteminterface.h> + +#ifdef ANDROID_PLUGIN_OPENGL +# include "qandroidopenglplatformwindow.h" +#endif + +#if __ANDROID_API__ > 8 +# include <android/native_window_jni.h> +#endif + +static jmethodID m_redrawSurfaceMethodID = 0; + +Q_IMPORT_PLUGIN(QAndroidPlatformIntegrationPlugin) + +static JavaVM *m_javaVM = NULL; +static jclass m_applicationClass = NULL; +static jobject m_classLoaderObject = NULL; +static jmethodID m_loadClassMethodID = NULL; +static AAssetManager *m_assetManager = NULL; +static jobject m_resourcesObj; +static jobject m_activityObject = NULL; + +static jclass m_bitmapClass = 0; +static jmethodID m_createBitmapMethodID = 0; +static jobject m_ARGB_8888_BitmapConfigValue = 0; +static jobject m_RGB_565_BitmapConfigValue = 0; + +static jclass m_bitmapDrawableClass = 0; +static jmethodID m_bitmapDrawableConstructorMethodID = 0; + +extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application +static Main m_main = NULL; +static void *m_mainLibraryHnd = NULL; +static QList<QByteArray> m_applicationParams; + +#ifndef ANDROID_PLUGIN_OPENGL +static jobject m_surface = NULL; +#else +static EGLNativeWindowType m_nativeWindow = 0; +static QSemaphore m_waitForWindowSemaphore; +static bool m_waitForWindow = false; + +static jfieldID m_surfaceFieldID = 0; +#endif + + +static QSemaphore m_quitAppSemaphore; +static QMutex m_surfaceMutex(QMutex::Recursive); +static QSemaphore m_pauseApplicationSemaphore; +static QMutex m_pauseApplicationMutex; + +static QAndroidPlatformIntegration *m_androidPlatformIntegration = 0; + +static int m_desktopWidthPixels = 0; +static int m_desktopHeightPixels = 0; + +static volatile bool m_pauseApplication; + +static jmethodID m_setFullScreenMethodID = 0; + +static AndroidAssetsFileEngineHandler *m_androidAssetsFileEngineHandler = 0; + + + +static const char m_qtTag[] = "Qt"; +static const char m_classErrorMsg[] = "Can't find class \"%s\""; +static const char m_methodErrorMsg[] = "Can't find method \"%s%s\""; + +static inline void checkPauseApplication() +{ + m_pauseApplicationMutex.lock(); + if (m_pauseApplication) { + m_pauseApplicationMutex.unlock(); + m_pauseApplicationSemaphore.acquire(); // wait until surface is created + + m_pauseApplicationMutex.lock(); + m_pauseApplication = false; + m_pauseApplicationMutex.unlock(); + + //FIXME +// QWindowSystemInterface::handleScreenAvailableGeometryChange(0); +// QWindowSystemInterface::handleScreenGeometryChange(0); + } else { + m_pauseApplicationMutex.unlock(); + } +} + +namespace QtAndroid +{ +#ifndef ANDROID_PLUGIN_OPENGL + void flushImage(const QPoint &pos, const QImage &image, const QRect &destinationRect) + { + checkPauseApplication(); + QMutexLocker locker(&m_surfaceMutex); + if (!m_surface) + return; + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + int bpp = 2; + AndroidBitmapInfo info; + int ret; + + if ((ret = AndroidBitmap_getInfo(env.jniEnv, m_surface, &info)) < 0) { + qWarning() << "AndroidBitmap_getInfo() failed ! error=" << ret; + m_javaVM->DetachCurrentThread(); + return; + } + + if (info.format != ANDROID_BITMAP_FORMAT_RGB_565) { + qWarning() << "Bitmap format is not RGB_565!"; + m_javaVM->DetachCurrentThread(); + return; + } + + void *pixels; + unsigned char *screenBits; + if ((ret = AndroidBitmap_lockPixels(env.jniEnv, m_surface, &pixels)) < 0) { + qWarning() << "AndroidBitmap_lockPixels() failed! error=" << ret; + m_javaVM->DetachCurrentThread(); + return; + } + + screenBits = static_cast<unsigned char *>(pixels); + int sbpl = info.stride; + int swidth = info.width; + int sheight = info.height; + + unsigned sposx = pos.x() + destinationRect.x(); + unsigned sposy = pos.y() + destinationRect.y(); + + screenBits += sposy * sbpl; + + unsigned ibpl = image.bytesPerLine(); + unsigned iposx = destinationRect.x(); + unsigned iposy = destinationRect.y(); + + const unsigned char *imageBits = static_cast<const unsigned char *>(image.bits()); + imageBits += iposy * ibpl; + + unsigned width = swidth - sposx < unsigned(destinationRect.width()) + ? (swidth-sposx) + : destinationRect.width(); + unsigned height = sheight - sposy < unsigned(destinationRect.height()) + ? (sheight - sposy) + : destinationRect.height(); + + for (unsigned y = 0; y < height; y++) { + memcpy(screenBits + y*sbpl + sposx*bpp, + imageBits + y*ibpl + iposx*bpp, + width*bpp); + } + AndroidBitmap_unlockPixels(env.jniEnv, m_surface); + + env.jniEnv->CallStaticVoidMethod(m_applicationClass, + m_redrawSurfaceMethodID, + jint(destinationRect.left()), + jint(destinationRect.top()), + jint(destinationRect.right() + 1), + jint(destinationRect.bottom() + 1)); +#warning FIXME dirty hack, figure out why it needs to add 1 to right and bottom !!!! + } + +#else // for #ifndef ANDROID_PLUGIN_OPENGL + EGLNativeWindowType nativeWindow(bool waitForWindow) + { + m_surfaceMutex.lock(); + if (!m_nativeWindow && waitForWindow) { + m_waitForWindow = true; + m_surfaceMutex.unlock(); + m_waitForWindowSemaphore.acquire(); + m_waitForWindow = false; + return m_nativeWindow; + } + m_surfaceMutex.unlock(); + return m_nativeWindow; + } + + QSize nativeWindowSize() + { + if (m_nativeWindow == 0) + return QAndroidPlatformIntegration::defaultDesktopSize(); + + int width = ANativeWindow_getWidth(m_nativeWindow); + int height = ANativeWindow_getHeight(m_nativeWindow); + + return QSize(width, height); + } +#endif + + void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration) + { + m_surfaceMutex.lock(); + m_androidPlatformIntegration = androidPlatformIntegration; + m_surfaceMutex.unlock(); + } + + QAndroidPlatformIntegration *androidPlatformIntegration() + { + QMutexLocker locker(&m_surfaceMutex); + return m_androidPlatformIntegration; + } + + void setFullScreen(QWidget *widget) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + bool fullScreen = widget->isFullScreen(); + if (!fullScreen) { + foreach (QWidget *w, qApp->topLevelWidgets()) { + fullScreen |= w->isFullScreen(); + if (fullScreen) + break; + } + } + + env.jniEnv->CallStaticVoidMethod(m_applicationClass, m_setFullScreenMethodID, fullScreen); + } + + QWindow *topLevelWindowAt(const QPoint &globalPos) + { + return m_androidPlatformIntegration + ? m_androidPlatformIntegration->screen()->topLevelAt(globalPos) + : 0; + } + + int desktopWidthPixels() + { + return m_desktopWidthPixels; + } + + int desktopHeightPixels() + { + return m_desktopHeightPixels; + } + + JavaVM *javaVM() + { + return m_javaVM; + } + + jclass findClass(const QString &className, JNIEnv *env) + { + return static_cast<jclass>(env->CallObjectMethod(m_classLoaderObject, + m_loadClassMethodID, + env->NewString(reinterpret_cast<const jchar *>(className.constData()), + jsize(className.length())))); + } + + AAssetManager *assetManager() + { + return m_assetManager; + } + + jclass applicationClass() + { + return m_applicationClass; + } + + jobject activity() + { + return m_activityObject; + } + + jobject createBitmap(QImage img, JNIEnv *env) + { + if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_RGB16) + img = img.convertToFormat(QImage::Format_ARGB32); + + jobject bitmap = env->CallStaticObjectMethod(m_bitmapClass, + m_createBitmapMethodID, + img.width(), + img.height(), + img.format() == QImage::Format_ARGB32 + ? m_ARGB_8888_BitmapConfigValue + : m_RGB_565_BitmapConfigValue); + if (!bitmap) + return 0; + + AndroidBitmapInfo info; + if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) { + env->DeleteLocalRef(bitmap); + return 0; + } + + void *pixels; + if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) { + env->DeleteLocalRef(bitmap); + return 0; + } + + if (info.stride == uint(img.bytesPerLine()) + && info.width == uint(img.width()) + && info.height == uint(img.height())) { + memcpy(pixels, img.constBits(), info.stride * info.height); + } else { + uchar *bmpPtr = static_cast<uchar *>(pixels); + const unsigned width = qMin(info.width, (uint)img.width()); //should be the same + const unsigned height = qMin(info.height, (uint)img.height()); //should be the same + for (unsigned y = 0; y < height; y++, bmpPtr += info.stride) + memcpy(bmpPtr, img.constScanLine(y), width); + } + AndroidBitmap_unlockPixels(env, bitmap); + return bitmap; + } + + jobject createBitmapDrawable(jobject bitmap, JNIEnv *env) + { + if (!bitmap) + return 0; + + return env->NewObject(m_bitmapDrawableClass, + m_bitmapDrawableConstructorMethodID, + m_resourcesObj, + bitmap); + } + + const char *classErrorMsgFmt() + { + return m_classErrorMsg; + } + + const char *methodErrorMsgFmt() + { + return m_methodErrorMsg; + } + + const char *qtTagText() + { + return m_qtTag; + } +} + +static jboolean startQtAndroidPlugin(JNIEnv* /*env*/, jobject /*object*//*, jobject applicationAssetManager*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + m_surface = 0; +#else + m_nativeWindow = 0; + m_waitForWindow = false; +#endif + + m_androidPlatformIntegration = 0; + m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler(); + +#ifdef ANDROID_PLUGIN_OPENGL + return true; +#else + return false; +#endif +} + +static void *startMainMethod(void */*data*/) +{ + char const **params; + params = static_cast<char const **>(malloc(m_applicationParams.length() * sizeof(char *))); + for (int i = 0; i < m_applicationParams.size(); i++) + params[i] = static_cast<const char *>(m_applicationParams[i].constData()); + + int ret = m_main(m_applicationParams.length(), const_cast<char **>(params)); + + free(params); + Q_UNUSED(ret); + + if (m_mainLibraryHnd) { + int res = dlclose(m_mainLibraryHnd); + if (res < 0) + qWarning() << "dlclose failed:" << dlerror(); + } + + QtAndroid::AttachedJNIEnv env; + if (!env.jniEnv) + return 0; + + if (m_applicationClass) { + jmethodID quitApp = env.jniEnv->GetStaticMethodID(m_applicationClass, "quitApp", "()V"); + env.jniEnv->CallStaticVoidMethod(m_applicationClass, quitApp); + } + + return 0; +} + +static jboolean startQtApplication(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString) +{ + m_mainLibraryHnd = NULL; + const char *nativeString = env->GetStringUTFChars(environmentString, 0); + QByteArray string = nativeString; + env->ReleaseStringUTFChars(environmentString, nativeString); + m_applicationParams=string.split('\t'); + foreach (string, m_applicationParams) { + if (putenv(string.constData())) + qWarning() << "Can't set environment" << string; + } + + nativeString = env->GetStringUTFChars(paramsString, 0); + string = nativeString; + env->ReleaseStringUTFChars(paramsString, nativeString); + + m_applicationParams=string.split('\t'); + + // Go home + QDir::setCurrent(QDir::homePath()); + + //look for main() + if (m_applicationParams.length()) { + // Obtain a handle to the main library (the library that contains the main() function). + // This library should already be loaded, and calling dlopen() will just return a reference to it. + m_mainLibraryHnd = dlopen(m_applicationParams.first().data(), 0); + if (m_mainLibraryHnd == NULL) { + qCritical() << "dlopen failed:" << dlerror(); + return false; + } + m_main = (Main)dlsym(m_mainLibraryHnd, "main"); + } else { + qWarning() << "No main library was specified; searching entire process (this is slow!)"; + m_main = (Main)dlsym(RTLD_DEFAULT, "main"); + } + + if (!m_main) { + qCritical() << "dlsym failed:" << dlerror(); + qCritical() << "Could not find main method"; + return false; + } + + pthread_t appThread; + return pthread_create(&appThread, NULL, startMainMethod, NULL) == 0; +} + +static void pauseQtApp(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.lock(); + m_pauseApplicationMutex.lock(); + + if (m_androidPlatformIntegration) + m_androidPlatformIntegration->pauseApp(); + m_pauseApplication = true; + + m_pauseApplicationMutex.unlock(); + m_surfaceMutex.unlock(); +} + +static void resumeQtApp(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.lock(); + m_pauseApplicationMutex.lock(); + if (m_androidPlatformIntegration) + m_androidPlatformIntegration->resumeApp(); + + if (m_pauseApplication) + m_pauseApplicationSemaphore.release(); + + m_pauseApplicationMutex.unlock(); + m_surfaceMutex.unlock(); +} + +static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) { + env->DeleteGlobalRef(m_surface); + m_surface = 0; + } +#else + Q_UNUSED(env); +#endif + + m_androidPlatformIntegration = 0; + delete m_androidAssetsFileEngineHandler; +} + +static void terminateQt(JNIEnv *env, jclass /*clazz*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) + env->DeleteGlobalRef(m_surface); +#endif + env->DeleteGlobalRef(m_applicationClass); + env->DeleteGlobalRef(m_classLoaderObject); + env->DeleteGlobalRef(m_resourcesObj); + env->DeleteGlobalRef(m_activityObject); + env->DeleteGlobalRef(m_bitmapClass); + env->DeleteGlobalRef(m_ARGB_8888_BitmapConfigValue); + env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue); + env->DeleteGlobalRef(m_bitmapDrawableClass); +} + +#ifdef ANDROID_PLUGIN_OPENGL +#if __ANDROID_API__ < 9 +struct FakeNativeWindow +{ + long long dummyNativeWindow;// force 64 bits alignment +}; + +class FakeSurface: public FakeNativeWindow +{ +public: + virtual void FakeSurfaceMethod() + { + fakeSurface = 0; + } + + int fakeSurface; +}; + +EGLNativeWindowType ANativeWindow_fromSurface(JNIEnv *env, jobject jSurface) +{ + FakeSurface *surface = static_cast<FakeSurface *>(env->GetIntField(jSurface, m_surfaceFieldID)); + return static_cast<EGLNativeWindowType>(static_cast<FakeNativeWindow*>(surface)); +} +#endif // __ANDROID_API__ < 9 +#endif // ANDROID_PLUGIN_OPENGL + +static void setSurface(JNIEnv *env, jobject /*thiz*/, jobject jSurface) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) + env->DeleteGlobalRef(m_surface); + m_surface = env->NewGlobalRef(jSurface); +#else + m_surfaceMutex.lock(); + EGLNativeWindowType nativeWindow = ANativeWindow_fromSurface(env, jSurface); + bool sameNativeWindow = (nativeWindow != 0 && nativeWindow == m_nativeWindow); + + m_nativeWindow = nativeWindow; + if (m_waitForWindow) + m_waitForWindowSemaphore.release(); + if (m_androidPlatformIntegration && !sameNativeWindow) { + m_surfaceMutex.unlock(); + m_androidPlatformIntegration->surfaceChanged(); + } else if (m_androidPlatformIntegration && sameNativeWindow) { + QAndroidOpenGLPlatformWindow *window = m_androidPlatformIntegration->primaryWindow(); + QPlatformScreen *screen = m_androidPlatformIntegration->screen(); + QSize size = QtAndroid::nativeWindowSize(); + + QRect geometry(QPoint(0, 0), size); + QWindowSystemInterface::handleScreenAvailableGeometryChange(screen->screen(), geometry); + QWindowSystemInterface::handleScreenGeometryChange(screen->screen(), geometry); + + if (window != 0) { + window->lock(); + window->scheduleResize(size); + + QWindowSystemInterface::handleExposeEvent(window->window(), + QRegion(window->window()->geometry())); + window->unlock(); + } + + m_surfaceMutex.unlock(); + } else { + m_surfaceMutex.unlock(); + } +#endif // for #ifndef ANDROID_PLUGIN_OPENGL +} + +static void destroySurface(JNIEnv *env, jobject /*thiz*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) { + env->DeleteGlobalRef(m_surface); + m_surface = 0; + } +#else + Q_UNUSED(env); + m_nativeWindow = 0; + if (m_androidPlatformIntegration != 0) + m_androidPlatformIntegration->invalidateNativeSurface(); +#endif +} + +static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/, + jint /*widthPixels*/, jint /*heightPixels*/, + jint desktopWidthPixels, jint desktopHeightPixels, + jdouble xdpi, jdouble ydpi) +{ + m_desktopWidthPixels = desktopWidthPixels; + m_desktopHeightPixels = desktopHeightPixels; + + if (!m_androidPlatformIntegration) { + QAndroidPlatformIntegration::setDefaultDisplayMetrics(desktopWidthPixels,desktopHeightPixels, + qRound(double(desktopWidthPixels) / xdpi * 25.4), + qRound(double(desktopHeightPixels) / ydpi * 25.4)); + } else { + m_androidPlatformIntegration->setDisplayMetrics(qRound(double(desktopWidthPixels) / xdpi * 25.4), + qRound(double(desktopHeightPixels) / ydpi * 25.4)); + m_androidPlatformIntegration->setDesktopSize(desktopWidthPixels, desktopHeightPixels); + } +} + +static void lockSurface(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.lock(); +} + +static void unlockSurface(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.unlock(); +} + +static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidPlatformIntegration) + return; + + if (qApp != 0) { + foreach (QWidget *w, qApp->topLevelWidgets()) + w->update(); + } + +#ifndef ANDROID_PLUGIN_OPENGL + QAndroidPlatformScreen *screen = static_cast<QAndroidPlatformScreen *>(m_androidPlatformIntegration->screen()); + QMetaObject::invokeMethod(screen, "setDirty", Qt::QueuedConnection, Q_ARG(QRect,screen->geometry())); +#else + qWarning("updateWindow: Dirty screen not implemented yet on OpenGL"); +#endif +} + +static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newOrientation) +{ + if (m_androidPlatformIntegration == 0) + return; + + Qt::ScreenOrientation screenOrientation = newOrientation == 1 + ? Qt::PortraitOrientation + : Qt::LandscapeOrientation; + QPlatformScreen *screen = m_androidPlatformIntegration->screen(); + QWindowSystemInterface::handleScreenOrientationChange(screen->screen(), + screenOrientation); +} + +static JNINativeMethod methods[] = { + {"startQtAndroidPlugin", "()Z", (void *)startQtAndroidPlugin}, + {"startQtApplication", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)startQtApplication}, + {"pauseQtApp", "()V", (void *)pauseQtApp}, + {"resumeQtApp", "()V", (void *)resumeQtApp}, + {"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin}, + {"terminateQt", "()V", (void *)terminateQt}, + {"setDisplayMetrics", "(IIIIDD)V", (void *)setDisplayMetrics}, + {"setSurface", "(Ljava/lang/Object;)V", (void *)setSurface}, + {"destroySurface", "()V", (void *)destroySurface}, + {"lockSurface", "()V", (void *)lockSurface}, + {"unlockSurface", "()V", (void *)unlockSurface}, + {"updateWindow", "()V", (void *)updateWindow}, + {"handleOrientationChanged", "(I)V", (void *)handleOrientationChanged} +}; + +#define FIND_AND_CHECK_CLASS(CLASS_NAME) \ +clazz = env->FindClass(CLASS_NAME); \ +if (!clazz) { \ + __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME); \ + return JNI_FALSE; \ +} + +#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ +VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ +if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \ + return JNI_FALSE; \ +} + +#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ +VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ +if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \ + return JNI_FALSE; \ +} + +#define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \ +VAR = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \ +if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \ + return JNI_FALSE; \ +} + +#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \ +VAR = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \ +if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \ + return JNI_FALSE; \ +} + +static int registerNatives(JNIEnv *env) +{ + jclass clazz; + FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/QtNative"); + m_applicationClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + + if (env->RegisterNatives(m_applicationClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed"); + return JNI_FALSE; + } + + GET_AND_CHECK_STATIC_METHOD(m_redrawSurfaceMethodID, m_applicationClass, "redrawSurface", "(IIII)V"); + GET_AND_CHECK_STATIC_METHOD(m_setFullScreenMethodID, m_applicationClass, "setFullScreen", "(Z)V"); + +#ifdef ANDROID_PLUGIN_OPENGL + FIND_AND_CHECK_CLASS("android/view/Surface"); +#if __ANDROID_API__ < 9 +# define ANDROID_VIEW_SURFACE_JNI_ID "mSurface" +#else +# define ANDROID_VIEW_SURFACE_JNI_ID "mNativeSurface" +#endif + GET_AND_CHECK_FIELD(m_surfaceFieldID, clazz, ANDROID_VIEW_SURFACE_JNI_ID, "I"); +#endif + + jmethodID methodID; + GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;"); + jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID); + m_activityObject = env->NewGlobalRef(activityObject); + GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "classLoader", "()Ljava/lang/ClassLoader;"); + m_classLoaderObject = env->NewGlobalRef(env->CallStaticObjectMethod(m_applicationClass, methodID)); + + clazz = env->GetObjectClass(m_classLoaderObject); + GET_AND_CHECK_METHOD(m_loadClassMethodID, clazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + + FIND_AND_CHECK_CLASS("android/content/ContextWrapper"); + GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;"); + m_assetManager = AAssetManager_fromJava(env, env->CallObjectMethod(activityObject, methodID)); + + GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;"); + m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(activityObject, methodID)); + + FIND_AND_CHECK_CLASS("android/graphics/Bitmap"); + m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass + , "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); + + FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config"); + jfieldID fieldId; + GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;"); + m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId)); + GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;"); + m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId)); + + FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable"); + m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID, + m_bitmapDrawableClass, + "<init>", + "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V"); + + return JNI_TRUE; +} + +Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/) +{ + typedef union { + JNIEnv *nativeEnvironment; + void *venv; + } UnionJNIEnvToVoid; + + __android_log_print(ANDROID_LOG_INFO, "Qt", "qt start"); + UnionJNIEnvToVoid uenv; + uenv.venv = NULL; + m_javaVM = 0; + + if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { + __android_log_print(ANDROID_LOG_FATAL, "Qt", "GetEnv failed"); + return -1; + } + + JNIEnv *env = uenv.nativeEnvironment; + if (!registerNatives(env) + || !QtAndroidInput::registerNatives(env) + || !QtAndroidClipboard::registerNatives(env) + || !QtAndroidMenu::registerNatives(env)) { + __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); + return -1; + } + + m_javaVM = vm; + return JNI_VERSION_1_4; +} diff --git a/src/plugins/platforms/android/src/androidjnimain.h b/src/plugins/platforms/android/src/androidjnimain.h new file mode 100644 index 0000000000..618bd87cdb --- /dev/null +++ b/src/plugins/platforms/android/src/androidjnimain.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 ANDROID_APP_H +#define ANDROID_APP_H + +#include <android/log.h> + +#ifdef ANDROID_PLUGIN_OPENGL +# include <EGL/eglplatform.h> +#endif + +#include <QtCore/qsize.h> + +#include <jni.h> +#include <android/asset_manager.h> + +class QImage; +class QRect; +class QPoint; +class QThread; +class QAndroidPlatformIntegration; +class QWidget; +class QString; +class QWindow; + +namespace QtAndroid +{ + QAndroidPlatformIntegration *androidPlatformIntegration(); + void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration); + void setQtThread(QThread *thread); + + void setFullScreen(QWidget *widget); + +#ifndef ANDROID_PLUGIN_OPENGL + void flushImage(const QPoint &pos, const QImage &image, const QRect &rect); +#else + EGLNativeWindowType nativeWindow(bool waitToCreate = true); + QSize nativeWindowSize(); +#endif + + QWindow *topLevelWindowAt(const QPoint &globalPos); + int desktopWidthPixels(); + int desktopHeightPixels(); + JavaVM *javaVM(); + jclass findClass(const QString &className, JNIEnv *env); + AAssetManager *assetManager(); + jclass applicationClass(); + jobject activity(); + + jobject createBitmap(QImage img, JNIEnv *env = 0); + jobject createBitmapDrawable(jobject bitmap, JNIEnv *env = 0); + + struct AttachedJNIEnv + { + AttachedJNIEnv() + { + attached = false; + if (QtAndroid::javaVM()->GetEnv((void**)&jniEnv, JNI_VERSION_1_6) < 0) { + if (QtAndroid::javaVM()->AttachCurrentThread(&jniEnv, NULL) < 0) { + __android_log_print(ANDROID_LOG_ERROR, "Qt", "AttachCurrentThread failed"); + jniEnv = 0; + return; + } + attached = true; + } + } + + ~AttachedJNIEnv() + { + if (attached) + QtAndroid::javaVM()->DetachCurrentThread(); + } + bool attached; + JNIEnv *jniEnv; + }; + const char *classErrorMsgFmt(); + const char *methodErrorMsgFmt(); + const char *qtTagText(); + +} +#endif // ANDROID_APP_H diff --git a/src/plugins/platforms/android/src/androidjnimenu.cpp b/src/plugins/platforms/android/src/androidjnimenu.cpp new file mode 100644 index 0000000000..e49af0fdac --- /dev/null +++ b/src/plugins/platforms/android/src/androidjnimenu.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "androidjnimenu.h" +#include "androidjnimain.h" +#include <qmutex.h> +#include <qset.h> +#include <qqueue.h> +#include <android/log.h> +#include "qandroidplatformmenubar.h" +#include "qandroidplatformmenu.h" +#include <qandroidplatformmenuitem.h> + +using namespace QtAndroid; + +namespace QtAndroidMenu +{ + static QQueue<QAndroidPlatformMenu *> pendingContextMenus; + static QAndroidPlatformMenu *visibleMenu = 0; + static QMutex visibleMenuMutex(QMutex::Recursive); + + static QSet<QAndroidPlatformMenuBar *> menuBars; + static QAndroidPlatformMenuBar *visibleMenuBar = 0; + static QWindow *activeTopLevelWindow = 0; + static QMutex menuBarMutex(QMutex::Recursive); + + static jmethodID openContextMenuMethodID = 0; + static jmethodID closeContextMenuMethodID = 0; + static jmethodID resetOptionsMenuMethodID = 0; + + static jmethodID clearMenuMethodID = 0; + static jmethodID addMenuItemMethodID = 0; + static int menuNoneValue = 0; + static jmethodID setHeaderTitleContextMenuMethodID = 0; + + static jmethodID setCheckableMenuItemMethodID = 0; + static jmethodID setCheckedMenuItemMethodID = 0; + static jmethodID setEnabledMenuItemMethodID = 0; + static jmethodID setIconMenuItemMethodID = 0; + static jmethodID setVisibleMenuItemMethodID = 0; + + void resetMenuBar() + { + AttachedJNIEnv env; + if (env.jniEnv) + env.jniEnv->CallStaticVoidMethod(applicationClass(), resetOptionsMenuMethodID); + } + + void showContextMenu(QAndroidPlatformMenu *menu, JNIEnv *env) + { + QMutexLocker lock(&visibleMenuMutex); + if (visibleMenu) { + pendingContextMenus.enqueue(menu); + } else { + visibleMenu = menu; + menu->aboutToShow(); + if (env) { + env->CallStaticVoidMethod(applicationClass(), openContextMenuMethodID); + } else { + AttachedJNIEnv aenv; + if (aenv.jniEnv) + aenv.jniEnv->CallStaticVoidMethod(applicationClass(), openContextMenuMethodID); + } + } + } + + void hideContextMenu(QAndroidPlatformMenu *menu) + { + QMutexLocker lock(&visibleMenuMutex); + if (visibleMenu == menu) { + AttachedJNIEnv env; + if (env.jniEnv) + env.jniEnv->CallStaticVoidMethod(applicationClass(), openContextMenuMethodID); + } else { + pendingContextMenus.removeOne(menu); + } + } + + void syncMenu(QAndroidPlatformMenu */*menu*/) + { +// QMutexLocker lock(&visibleMenuMutex); +// if (visibleMenu == menu) +// { +// hideContextMenu(menu); +// showContextMenu(menu); +// } + } + + void androidPlatformMenuDestroyed(QAndroidPlatformMenu *menu) + { + QMutexLocker lock(&visibleMenuMutex); + if (visibleMenu == menu) + visibleMenu = 0; + } + + void setMenuBar(QAndroidPlatformMenuBar *menuBar, QWindow *window) + { + if (activeTopLevelWindow == window && visibleMenuBar != menuBar) { + visibleMenuBar = menuBar; + resetMenuBar(); + } + } + + void setActiveTopLevelWindow(QWindow *window) + { + QMutexLocker lock(&menuBarMutex); + if (activeTopLevelWindow == window) + return; + + visibleMenuBar = 0; + activeTopLevelWindow = window; +#ifdef ANDROID_PLUGIN_OPENGL + //only one toplevel window, so the menu bar always belongs to us + if (menuBars.size() == 1) { + visibleMenuBar = *menuBars.constBegin(); //since QSet doesn't have first() + } else +#endif + foreach (QAndroidPlatformMenuBar *menuBar, menuBars) { + if (menuBar->parentWindow() == window) { + visibleMenuBar = menuBar; + break; + } + } + + resetMenuBar(); + } + + void addMenuBar(QAndroidPlatformMenuBar *menuBar) + { + QMutexLocker lock(&menuBarMutex); + menuBars.insert(menuBar); + } + + void removeMenuBar(QAndroidPlatformMenuBar *menuBar) + { + QMutexLocker lock(&menuBarMutex); + menuBars.remove(menuBar); + if (visibleMenuBar == menuBar) + resetMenuBar(); + } + + static void fillMenuItem(JNIEnv *env, jobject menuItem, bool checkable, bool checked, bool enabled, bool visible, const QIcon &icon=QIcon()) + { + env->CallObjectMethod(menuItem, setCheckableMenuItemMethodID, checkable); + env->CallObjectMethod(menuItem, setCheckedMenuItemMethodID, checked); + env->CallObjectMethod(menuItem, setEnabledMenuItemMethodID, enabled); + + if (!icon.isNull()) { + int sz = qMax(36, qgetenv("QT_ANDROID_APP_ICON_SIZE").toInt()); + QImage img = icon.pixmap(QSize(sz,sz), + enabled + ? QIcon::Normal + : QIcon::Disabled, + QIcon::On).toImage(); + env->CallObjectMethod(menuItem, + setIconMenuItemMethodID, + createBitmapDrawable(createBitmap(img, env), env)); + } + + env->CallObjectMethod(menuItem, setVisibleMenuItemMethodID, visible); + } + + static int addAllMenuItemsToMenu(JNIEnv *env, jobject menu, QAndroidPlatformMenu *platformMenu) { + int order = 0; + QMutexLocker lock(platformMenu->menuItemsMutex()); + foreach (QAndroidPlatformMenuItem *item, platformMenu->menuItems()) { + if (item->isSeparator()) + continue; + jstring jtext = env->NewString(reinterpret_cast<const jchar *>(item->text().data()), + item->text().length()); + jobject menuItem = env->CallObjectMethod(menu, + addMenuItemMethodID, + menuNoneValue, + int(item->tag()), + order++, + jtext); + env->DeleteLocalRef(jtext); + fillMenuItem(env, + menuItem, + item->isCheckable(), + item->isChecked(), + item->isEnabled(), + item->isVisible(), + item->icon()); + } + + return order; + } + + static jboolean onPrepareOptionsMenu(JNIEnv *env, jobject /*thiz*/, jobject menu) + { + env->CallVoidMethod(menu, clearMenuMethodID); + QMutexLocker lock(&menuBarMutex); + if (!visibleMenuBar) + return JNI_FALSE; + + const QAndroidPlatformMenuBar::PlatformMenusType &menus = visibleMenuBar->menus(); + int order = 0; + QMutexLocker lockMenuBarMutex(visibleMenuBar->menusListMutex()); + if (menus.size() == 1) { // Expand the menu + order = addAllMenuItemsToMenu(env, menu, static_cast<QAndroidPlatformMenu *>(menus.front())); + } else { + foreach (QAndroidPlatformMenu *item, menus) { + jstring jtext = env->NewString(reinterpret_cast<const jchar *>(item->text().data()), + item->text().length()); + jobject menuItem = env->CallObjectMethod(menu, + addMenuItemMethodID, + menuNoneValue, + int(item->tag()), + order++, + jtext); + env->DeleteLocalRef(jtext); + + fillMenuItem(env, + menuItem, + false, + false, + item->isEnabled(), + item->isVisible(), + item->icon()); + } + } + return order ? JNI_TRUE : JNI_FALSE; + } + + static jboolean onOptionsItemSelected(JNIEnv *env, jobject /*thiz*/, jint menuId, jboolean checked) + { + QMutexLocker lock(&menuBarMutex); + if (!visibleMenuBar) + return JNI_FALSE; + + const QAndroidPlatformMenuBar::PlatformMenusType &menus = visibleMenuBar->menus(); + if (menus.size() == 1) { // Expanded menu + QAndroidPlatformMenuItem *item = static_cast<QAndroidPlatformMenuItem *>(menus.front()->menuItemForTag(menuId)); + if (item) { + if (item->menu()) { + showContextMenu(item->menu(), env); + } else { + if (item->isCheckable()) + item->setChecked(checked); + item->activated(); + } + } + } else { + QAndroidPlatformMenu *menu = static_cast<QAndroidPlatformMenu *>(visibleMenuBar->menuForTag(menuId)); + if (menu) + showContextMenu(menu, env); + } + + return JNI_TRUE; + } + + static void onOptionsMenuClosed(JNIEnv */*env*/, jobject /*thiz*/, jobject /*menu*/) + { + } + + static void onCreateContextMenu(JNIEnv *env, jobject /*thiz*/, jobject menu) + { + env->CallVoidMethod(menu, clearMenuMethodID); + QMutexLocker lock(&visibleMenuMutex); + if (!visibleMenu) + return; + + jstring jtext = env->NewString(reinterpret_cast<const jchar*>(visibleMenu->text().data()), + visibleMenu->text().length()); + env->CallObjectMethod(menu, setHeaderTitleContextMenuMethodID, jtext); + env->DeleteLocalRef(jtext); + addAllMenuItemsToMenu(env, menu, visibleMenu); + } + + static jboolean onContextItemSelected(JNIEnv *env, jobject /*thiz*/, jint menuId, jboolean checked) + { + QMutexLocker lock(&visibleMenuMutex); + QAndroidPlatformMenuItem * item = static_cast<QAndroidPlatformMenuItem *>(visibleMenu->menuItemForTag(menuId)); + if (item) { + if (item->menu()) { + showContextMenu(item->menu(), env); + } else { + if (item->isCheckable()) + item->setChecked(checked); + item->activated(); + } + } + return JNI_TRUE; + } + + static void onContextMenuClosed(JNIEnv *env, jobject /*thiz*/, jobject /*menu*/) + { + QMutexLocker lock(&visibleMenuMutex); + if (!visibleMenu) + return; + visibleMenu->aboutToHide(); + visibleMenu = 0; + if (!pendingContextMenus.empty()) + showContextMenu(pendingContextMenus.dequeue(), env); + } + + static JNINativeMethod methods[] = { + {"onPrepareOptionsMenu", "(Landroid/view/Menu;)Z", (void *)onPrepareOptionsMenu}, + {"onOptionsItemSelected", "(IZ)Z", (void *)onOptionsItemSelected}, + {"onOptionsMenuClosed", "(Landroid/view/Menu;)V", (void*)onOptionsMenuClosed}, + {"onCreateContextMenu", "(Landroid/view/ContextMenu;)V", (void *)onCreateContextMenu}, + {"onContextItemSelected", "(IZ)Z", (void *)onContextItemSelected}, + {"onContextMenuClosed", "(Landroid/view/Menu;)V", (void*)onContextMenuClosed}, + }; + +#define FIND_AND_CHECK_CLASS(CLASS_NAME) \ + clazz = env->FindClass(CLASS_NAME); \ + if (!clazz) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), classErrorMsgFmt(), CLASS_NAME); \ + return false; \ + } + +#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ + VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ + if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), methodErrorMsgFmt(), METHOD_NAME, METHOD_SIGNATURE); \ + return false; \ + } + +#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ + VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ + if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), methodErrorMsgFmt(), METHOD_NAME, METHOD_SIGNATURE); \ + return false; \ + } + +#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \ + VAR = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \ + if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, qtTagText(), methodErrorMsgFmt(), FIELD_NAME, FIELD_SIGNATURE); \ + return false; \ + } + + bool registerNatives(JNIEnv *env) + { + jclass appClass = applicationClass(); + + if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed"); + return false; + } + + GET_AND_CHECK_STATIC_METHOD(openContextMenuMethodID, appClass, "openContextMenu", "()V"); + GET_AND_CHECK_STATIC_METHOD(closeContextMenuMethodID, appClass, "closeContextMenu", "()V"); + GET_AND_CHECK_STATIC_METHOD(resetOptionsMenuMethodID, appClass, "resetOptionsMenu", "()V"); + + jclass clazz; + FIND_AND_CHECK_CLASS("android/view/Menu"); + GET_AND_CHECK_METHOD(clearMenuMethodID, clazz, "clear", "()V"); + GET_AND_CHECK_METHOD(addMenuItemMethodID, clazz, "add", "(IIILjava/lang/CharSequence;)Landroid/view/MenuItem;"); + jfieldID menuNoneFiledId; + GET_AND_CHECK_STATIC_FIELD(menuNoneFiledId, clazz, "NONE", "I"); + menuNoneValue = env->GetStaticIntField(clazz, menuNoneFiledId); + + FIND_AND_CHECK_CLASS("android/view/ContextMenu"); + GET_AND_CHECK_METHOD(setHeaderTitleContextMenuMethodID, clazz, "setHeaderTitle","(Ljava/lang/CharSequence;)Landroid/view/ContextMenu;"); + + FIND_AND_CHECK_CLASS("android/view/MenuItem"); + GET_AND_CHECK_METHOD(setCheckableMenuItemMethodID, clazz, "setCheckable", "(Z)Landroid/view/MenuItem;"); + GET_AND_CHECK_METHOD(setCheckedMenuItemMethodID, clazz, "setChecked", "(Z)Landroid/view/MenuItem;"); + GET_AND_CHECK_METHOD(setEnabledMenuItemMethodID, clazz, "setEnabled", "(Z)Landroid/view/MenuItem;"); + GET_AND_CHECK_METHOD(setIconMenuItemMethodID, clazz, "setIcon", "(Landroid/graphics/drawable/Drawable;)Landroid/view/MenuItem;"); + GET_AND_CHECK_METHOD(setVisibleMenuItemMethodID, clazz, "setVisible", "(Z)Landroid/view/MenuItem;"); + return true; + } +} diff --git a/src/plugins/platforms/android/src/androidjnimenu.h b/src/plugins/platforms/android/src/androidjnimenu.h new file mode 100644 index 0000000000..7c5422f67b --- /dev/null +++ b/src/plugins/platforms/android/src/androidjnimenu.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 ANDROIDJNIMENU_H +#define ANDROIDJNIMENU_H + +#include <jni.h> + +class QAndroidPlatformMenuBar; +class QAndroidPlatformMenu; +class QAndroidPlatformMenuItem; +class QWindow; + +namespace QtAndroidMenu +{ + // Menu support + void showContextMenu(QAndroidPlatformMenu *menu, JNIEnv *env = 0); + void hideContextMenu(QAndroidPlatformMenu *menu); + void syncMenu(QAndroidPlatformMenu *menu); + void androidPlatformMenuDestroyed(QAndroidPlatformMenu *menu); + + void setMenuBar(QAndroidPlatformMenuBar *menuBar, QWindow *window); + void setActiveTopLevelWindow(QWindow *window); + void addMenuBar(QAndroidPlatformMenuBar *menuBar); + void removeMenuBar(QAndroidPlatformMenuBar *menuBar); + + // Menu support + bool registerNatives(JNIEnv *env); +} + +#endif // ANDROIDJNIMENU_H diff --git a/src/plugins/platforms/android/src/androidplatformplugin.cpp b/src/plugins/platforms/android/src/androidplatformplugin.cpp new file mode 100644 index 0000000000..71c5096e16 --- /dev/null +++ b/src/plugins/platforms/android/src/androidplatformplugin.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 <qpa/qplatformintegrationplugin.h> +#include "qandroidplatformintegration.h" + +QT_BEGIN_NAMESPACE + +class QAndroidPlatformIntegrationPlugin: public QPlatformIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.1" FILE "android.json") +public: + QPlatformIntegration *create(const QString &key, const QStringList ¶mList); +}; + + +QPlatformIntegration *QAndroidPlatformIntegrationPlugin::create(const QString &key, const QStringList ¶mList) +{ + Q_UNUSED(paramList); + if (key.toLower() == "android") + return new QAndroidPlatformIntegration(paramList); + return 0; +} + +QT_END_NAMESPACE +#include "androidplatformplugin.moc" + diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp new file mode 100644 index 0000000000..aa8ee57341 --- /dev/null +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** 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 "qandroidopenglcontext.h" +#include "qandroidopenglplatformwindow.h" +#include "qandroidplatformintegration.h" + +#include <QtCore/qdebug.h> +#include <qpa/qwindowsysteminterface.h> + +QT_BEGIN_NAMESPACE + +QAndroidOpenGLContext::QAndroidOpenGLContext(const QAndroidPlatformIntegration *integration, + const QSurfaceFormat &format, + QPlatformOpenGLContext *share, + EGLDisplay display, + EGLenum eglApi) + : QEglFSContext(format, share, display, eglApi) + , m_platformIntegration(integration) +{ +} + +void QAndroidOpenGLContext::swapBuffers(QPlatformSurface *surface) +{ + QEglFSContext::swapBuffers(surface); + + QAndroidOpenGLPlatformWindow *primaryWindow = m_platformIntegration->primaryWindow(); + if (primaryWindow == surface) { + primaryWindow->lock(); + QSize size = primaryWindow->scheduledResize(); + if (size.isValid()) { + QRect geometry(QPoint(0, 0), size); + primaryWindow->setGeometry(geometry); + primaryWindow->scheduleResize(QSize()); + } + primaryWindow->unlock(); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.h b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.h new file mode 100644 index 0000000000..c4c5a430ad --- /dev/null +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** 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 QANDROIDOPENGLCONTEXT_H +#define QANDROIDOPENGLCONTEXT_H + +#include <QtCore/qreadwritelock.h> +#include "qeglfscontext.h" + +QT_BEGIN_NAMESPACE + +class QAndroidPlatformIntegration; +class QAndroidOpenGLContext : public QEglFSContext +{ +public: + QAndroidOpenGLContext(const QAndroidPlatformIntegration *integration, + const QSurfaceFormat &format, + QPlatformOpenGLContext *share, + EGLDisplay display, + EGLenum eglApi = EGL_OPENGL_ES_API); + + void swapBuffers(QPlatformSurface *surface); + +private: + const QAndroidPlatformIntegration *m_platformIntegration; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDOPENGLCONTEXT_H diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp new file mode 100644 index 0000000000..15c6559157 --- /dev/null +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** 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 "qandroidopenglplatformwindow.h" +#include "androidjnimain.h" +#include <qpa/qwindowsysteminterface.h> + +QT_BEGIN_NAMESPACE + +QAndroidOpenGLPlatformWindow::QAndroidOpenGLPlatformWindow(QWindow *window) + : QEglFSWindow(window) +{ +} + +bool QAndroidOpenGLPlatformWindow::isExposed() const +{ + return QtAndroid::nativeWindow(false) != 0 && QEglFSWindow::isExposed(); +} + +void QAndroidOpenGLPlatformWindow::invalidateSurface() +{ + QWindowSystemInterface::handleExposeEvent(window(), QRegion()); // Obscure event + QWindowSystemInterface::flushWindowSystemEvents(); + QEglFSWindow::invalidateSurface(); +} + +void QAndroidOpenGLPlatformWindow::resetSurface() +{ + QEglFSWindow::resetSurface(); + QWindowSystemInterface::handleExposeEvent(window(), QRegion(geometry())); // Expose event + QWindowSystemInterface::flushWindowSystemEvents(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h new file mode 100644 index 0000000000..b835cb3246 --- /dev/null +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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 QANDROIDOPENGLPLATFORMWINDOW_H +#define QANDROIDOPENGLPLATFORMWINDOW_H + +#include "qeglfswindow.h" +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +class QAndroidOpenGLPlatformWindow : public QEglFSWindow +{ +public: + QAndroidOpenGLPlatformWindow(QWindow *window); + + QSize scheduledResize() const { return m_scheduledResize; } + void scheduleResize(const QSize &size) { m_scheduledResize = size; } + + void lock() { m_lock.lock(); } + void unlock() { m_lock.unlock(); } + + bool isExposed() const; + + void invalidateSurface(); + void resetSurface(); + +private: + QSize m_scheduledResize; + QMutex m_lock; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDOPENGLPLATFORMWINDOW_H diff --git a/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp b/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp new file mode 100644 index 0000000000..cd415843a7 --- /dev/null +++ b/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "qeglfshooks.h" +#include "androidjnimain.h" +#include "qandroidplatformintegration.h" + +#include <android/native_window.h> +#include <jni.h> + +QT_BEGIN_NAMESPACE + +class QEglFSAndroidHooks: public QEglFSHooks +{ +public: + void platformInit(); + void platformDestroy(); + EGLNativeDisplayType platformDisplay() const; + QSize screenSize() const; + QSizeF physicalScreenSize() const; + int screenDepth() const; + QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const; + EGLNativeWindowType createNativeWindow(const QSize &size, const QSurfaceFormat &format); + void destroyNativeWindow(EGLNativeWindowType window); + bool hasCapability(QPlatformIntegration::Capability cap) const; +}; + +void QEglFSAndroidHooks::platformInit() +{ +} + +void QEglFSAndroidHooks::platformDestroy() +{ +} + +EGLNativeDisplayType QEglFSAndroidHooks::platformDisplay() const +{ + return EGL_DEFAULT_DISPLAY; +} + +QSize QEglFSAndroidHooks::screenSize() const +{ + return QtAndroid::nativeWindowSize(); +} + +QSizeF QEglFSAndroidHooks::physicalScreenSize() const +{ + return QSizeF(QAndroidPlatformIntegration::m_defaultPhysicalSizeWidth, QAndroidPlatformIntegration::m_defaultPhysicalSizeHeight); +} + + +EGLNativeWindowType QEglFSAndroidHooks::createNativeWindow(const QSize &size, const QSurfaceFormat &format) +{ + ANativeWindow *window = QtAndroid::nativeWindow(); + if (window != 0) + ANativeWindow_acquire(window); + + return window; +} + +void QEglFSAndroidHooks::destroyNativeWindow(EGLNativeWindowType window) +{ + ANativeWindow_release(window); +} + +bool QEglFSAndroidHooks::hasCapability(QPlatformIntegration::Capability capability) const +{ + switch (capability) { + case QPlatformIntegration::OpenGL: return true; + case QPlatformIntegration::ThreadedOpenGL: return true; + default: return false; + }; +} + +int QEglFSAndroidHooks::screenDepth() const +{ + // ### Hardcoded + return 32; +} + +QSurfaceFormat QEglFSAndroidHooks::surfaceFormatFor(const QSurfaceFormat &inputFormat) const +{ + QSurfaceFormat ret(inputFormat); + ret.setAlphaBufferSize(8); + ret.setRedBufferSize(8); + ret.setGreenBufferSize(8); + ret.setBlueBufferSize(8); + return ret; +} + +static QEglFSAndroidHooks eglFSAndroidHooks; +QEglFSHooks *platformHooks = &eglFSAndroidHooks; + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.cpp new file mode 100644 index 0000000000..f3cb2586cc --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "qandroidassetsfileenginehandler.h" +#include "androidjnimain.h" + +#include <QCoreApplication> + +class AndroidAbstractFileEngineIterator: public QAbstractFileEngineIterator +{ +public: + AndroidAbstractFileEngineIterator(QDir::Filters filters, + const QStringList &nameFilters, + AAssetDir *asset, + const QString &path) + : QAbstractFileEngineIterator(filters, nameFilters) + { + AAssetDir_rewind(asset); + const char *fileName; + while ((fileName = AAssetDir_getNextFileName(asset))) + m_items << fileName; + m_index = -1; + m_path = path; + } + + virtual QFileInfo currentFileInfo() const + { + return QFileInfo(currentFilePath()); + } + + virtual QString currentFileName() const + { + if (m_index < 0 || m_index >= m_items.size()) + return QString(); + return m_items[m_index]; + } + + virtual QString currentFilePath() const + { + return m_path + currentFileName(); + } + + virtual bool hasNext() const + { + return m_items.size() && (m_index < m_items.size() - 1); + } + + virtual QString next() + { + if (!hasNext()) + return QString(); + m_index++; + return currentFileName(); + } + +private: + QString m_path; + QStringList m_items; + int m_index; +}; + +class AndroidAbstractFileEngine: public QAbstractFileEngine +{ +public: + explicit AndroidAbstractFileEngine(AAsset *asset, const QString &fileName) + { + m_assetDir = 0; + m_assetFile = asset; + m_fileName = fileName; + } + + explicit AndroidAbstractFileEngine(AAssetDir *asset, const QString &fileName) + { + m_assetFile = 0; + m_assetDir = asset; + m_fileName = fileName; + if (!m_fileName.endsWith(QChar(QLatin1Char('/')))) + m_fileName += "/"; + } + + ~AndroidAbstractFileEngine() + { + close(); + if (m_assetDir) + AAssetDir_close(m_assetDir); + } + + virtual bool open(QIODevice::OpenMode openMode) + { + if (m_assetFile) + return openMode & QIODevice::ReadOnly; + return false; + } + + virtual bool close() + { + if (m_assetFile) { + AAsset_close(m_assetFile); + m_assetFile = 0; + return true; + } + return false; + } + + virtual qint64 size() const + { + if (m_assetFile) + return AAsset_getLength(m_assetFile); + return -1; + } + + virtual qint64 pos() const + { + if (m_assetFile) + return AAsset_seek(m_assetFile, 0, SEEK_CUR); + return -1; + } + + virtual bool seek(qint64 pos) + { + if (m_assetFile) + return pos == AAsset_seek(m_assetFile, pos, SEEK_SET); + return false; + } + + virtual qint64 read(char *data, qint64 maxlen) + { + if (m_assetFile) + return AAsset_read(m_assetFile, data, maxlen); + return -1; + } + + virtual bool isSequential() const + { + return false; + } + + virtual bool caseSensitive() const + { + return true; + } + + virtual bool isRelativePath() const + { + return false; + } + + virtual FileFlags fileFlags(FileFlags type = FileInfoAll) const + { + FileFlags flags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag); + if (m_assetFile) + flags |= FileType; + if (m_assetDir) + flags |= DirectoryType; + + return type & flags; + } + + virtual QString fileName(FileName file = DefaultName) const + { + int pos; + switch (file) { + case DefaultName: + case AbsoluteName: + case CanonicalName: + return m_fileName; + case BaseName: + if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1) + return m_fileName.mid(pos); + else + return m_fileName; + case PathName: + case AbsolutePathName: + case CanonicalPathName: + if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1) + return m_fileName.left(pos); + else + return m_fileName; + default: + return QString(); + } + } + + virtual void setFileName(const QString &file) + { + if (file == m_fileName) + return; + + m_fileName = file; + if (!m_fileName.endsWith(QChar(QLatin1Char('/')))) + m_fileName += "/"; + + close(); + } + + virtual Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) + { + if (m_assetDir) + return new AndroidAbstractFileEngineIterator(filters, filterNames, m_assetDir, m_fileName); + return 0; + } + +private: + AAsset *m_assetFile; + AAssetDir *m_assetDir; + QString m_fileName; +}; + + +AndroidAssetsFileEngineHandler::AndroidAssetsFileEngineHandler() +{ + m_assetManager = QtAndroid::assetManager(); +} + +AndroidAssetsFileEngineHandler::~AndroidAssetsFileEngineHandler() +{ +} + +QAbstractFileEngine * AndroidAssetsFileEngineHandler::create(const QString &fileName) const +{ + if (fileName.isEmpty()) + return 0; + + if (!fileName.startsWith(QLatin1String("assets:/"))) + return 0; + + int prefixSize=8; + + m_path.clear(); + if (!fileName.endsWith(QLatin1Char('/'))) { + m_path = fileName.toUtf8(); + AAsset *asset = AAssetManager_open(m_assetManager, + m_path.constData() + prefixSize, + AASSET_MODE_BUFFER); + if (asset) + return new AndroidAbstractFileEngine(asset, fileName); + } + + if (!m_path.size()) + m_path = fileName.left(fileName.length() - 1).toUtf8(); + + AAssetDir *assetDir = AAssetManager_openDir(m_assetManager, m_path.constData() + prefixSize); + if (assetDir) { + if (AAssetDir_getNextFileName(assetDir)) + return new AndroidAbstractFileEngine(assetDir, fileName); + else + AAssetDir_close(assetDir); + } + return 0; +} diff --git a/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.h b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.h new file mode 100644 index 0000000000..9bff6a012e --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 QANDROIDASSETSFILEENGINEHANDLER_H +#define QANDROIDASSETSFILEENGINEHANDLER_H + +#include <QtCore/private/qabstractfileengine_p.h> +#include <android/asset_manager.h> + +class AndroidAssetsFileEngineHandler: public QAbstractFileEngineHandler +{ +public: + AndroidAssetsFileEngineHandler(); + virtual ~AndroidAssetsFileEngineHandler(); + QAbstractFileEngine *create(const QString &fileName) const; + +private: + AAssetManager *m_assetManager; + mutable QByteArray m_path; +}; + +#endif // QANDROIDASSETSFILEENGINEHANDLER_H diff --git a/src/plugins/platforms/android/src/qandroidinputcontext.cpp b/src/plugins/platforms/android/src/qandroidinputcontext.cpp new file mode 100644 index 0000000000..37fb605ea8 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidinputcontext.cpp @@ -0,0 +1,644 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 <android/log.h> + +#include "qandroidinputcontext.h" +#include "androidjnimain.h" +#include "androidjniinput.h" +#include <QDebug> +#include <qevent.h> +#include <qguiapplication.h> +#include <qsharedpointer.h> +#include <qthread.h> +#include <qinputmethod.h> +#include <qwindow.h> + +#include <QTextCharFormat> + +QT_BEGIN_NAMESPACE + +static QAndroidInputContext *m_androidInputContext = 0; +static char const *const QtNativeInputConnectionClassName = "org/qtproject/qt5/android/QtNativeInputConnection"; +static char const *const QtExtractedTextClassName = "org/qtproject/qt5/android/QtExtractedText"; +static jclass m_extractedTextClass = 0; +static jmethodID m_classConstructorMethodID = 0; +static jfieldID m_partialEndOffsetFieldID = 0; +static jfieldID m_partialStartOffsetFieldID = 0; +static jfieldID m_selectionEndFieldID = 0; +static jfieldID m_selectionStartFieldID = 0; +static jfieldID m_startOffsetFieldID = 0; +static jfieldID m_textFieldID = 0; + +static jboolean commitText(JNIEnv *env, jobject /*thiz*/, jstring text, jint newCursorPosition) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + jboolean isCopy; + const jchar *jstr = env->GetStringChars(text, &isCopy); + QString str(reinterpret_cast<const QChar *>(jstr), env->GetStringLength(text)); + env->ReleaseStringChars(text, jstr); + + return m_androidInputContext->commitText(str, newCursorPosition); +} + +static jboolean deleteSurroundingText(JNIEnv */*env*/, jobject /*thiz*/, jint leftLength, jint rightLength) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->deleteSurroundingText(leftLength, rightLength); +} + +static jboolean finishComposingText(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->finishComposingText(); +} + +static jint getCursorCapsMode(JNIEnv */*env*/, jobject /*thiz*/, jint reqModes) +{ + if (!m_androidInputContext) + return 0; + + return m_androidInputContext->getCursorCapsMode(reqModes); +} + +static jobject getExtractedText(JNIEnv *env, jobject /*thiz*/, int hintMaxChars, int hintMaxLines, jint flags) +{ + if (!m_androidInputContext) + return 0; + + const QAndroidInputContext::ExtractedText &extractedText = + m_androidInputContext->getExtractedText(hintMaxChars, hintMaxLines, flags); + + jobject object = env->NewObject(m_extractedTextClass, m_classConstructorMethodID); + env->SetIntField(object, m_partialStartOffsetFieldID, extractedText.partialStartOffset); + env->SetIntField(object, m_partialEndOffsetFieldID, extractedText.partialEndOffset); + env->SetIntField(object, m_selectionStartFieldID, extractedText.selectionStart); + env->SetIntField(object, m_selectionEndFieldID, extractedText.selectionEnd); + env->SetIntField(object, m_startOffsetFieldID, extractedText.startOffset); + env->SetObjectField(object, + m_textFieldID, + env->NewString(reinterpret_cast<const jchar *>(extractedText.text.constData()), + jsize(extractedText.text.length()))); + + return object; +} + +static jstring getSelectedText(JNIEnv *env, jobject /*thiz*/, jint flags) +{ + if (!m_androidInputContext) + return 0; + + const QString &text = m_androidInputContext->getSelectedText(flags); + return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length())); +} + +static jstring getTextAfterCursor(JNIEnv *env, jobject /*thiz*/, jint length, jint flags) +{ + if (!m_androidInputContext) + return 0; + + const QString &text = m_androidInputContext->getTextAfterCursor(length, flags); + return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length())); +} + +static jstring getTextBeforeCursor(JNIEnv *env, jobject /*thiz*/, jint length, jint flags) +{ + if (!m_androidInputContext) + return 0; + + const QString &text = m_androidInputContext->getTextBeforeCursor(length, flags); + return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length())); +} + +static jboolean setComposingText(JNIEnv *env, jobject /*thiz*/, jstring text, jint newCursorPosition) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + jboolean isCopy; + const jchar *jstr = env->GetStringChars(text, &isCopy); + QString str(reinterpret_cast<const QChar *>(jstr), env->GetStringLength(text)); + env->ReleaseStringChars(text, jstr); + + return m_androidInputContext->setComposingText(str, newCursorPosition); +} + +static jboolean setSelection(JNIEnv */*env*/, jobject /*thiz*/, jint start, jint end) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->setSelection(start, end); +} + +static jboolean selectAll(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->selectAll(); +} + +static jboolean cut(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->cut(); +} + +static jboolean copy(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->copy(); +} + +static jboolean copyURL(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->copyURL(); +} + +static jboolean paste(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + + return m_androidInputContext->paste(); +} + + +static JNINativeMethod methods[] = { + {"commitText", "(Ljava/lang/String;I)Z", (void *)commitText}, + {"deleteSurroundingText", "(II)Z", (void *)deleteSurroundingText}, + {"finishComposingText", "()Z", (void *)finishComposingText}, + {"getCursorCapsMode", "(I)I", (void *)getCursorCapsMode}, + {"getExtractedText", "(III)Lorg/qtproject/qt5/android/QtExtractedText;", (void *)getExtractedText}, + {"getSelectedText", "(I)Ljava/lang/String;", (void *)getSelectedText}, + {"getTextAfterCursor", "(II)Ljava/lang/String;", (void *)getTextAfterCursor}, + {"getTextBeforeCursor", "(II)Ljava/lang/String;", (void *)getTextBeforeCursor}, + {"setComposingText", "(Ljava/lang/String;I)Z", (void *)setComposingText}, + {"setSelection", "(II)Z", (void *)setSelection}, + {"selectAll", "()Z", (void *)selectAll}, + {"cut", "()Z", (void *)cut}, + {"copy", "()Z", (void *)copy}, + {"copyURL", "()Z", (void *)copyURL}, + {"paste", "()Z", (void *)paste} +}; + + +QAndroidInputContext::QAndroidInputContext():QPlatformInputContext() +{ + JNIEnv *env = 0; + if (QtAndroid::javaVM()->AttachCurrentThread(&env, NULL) < 0) { + qCritical() << "AttachCurrentThread failed"; + return; + } + + jclass clazz = QtAndroid::findClass(QtNativeInputConnectionClassName, env); + if (clazz == NULL) { + qCritical() << "Native registration unable to find class '" + << QtNativeInputConnectionClassName + << "'"; + return; + } + + if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + qCritical() << "RegisterNatives failed for '" + << QtNativeInputConnectionClassName + << "'"; + return; + } + + clazz = QtAndroid::findClass(QtExtractedTextClassName, env); + if (clazz == NULL) { + qCritical() << "Native registration unable to find class '" + << QtExtractedTextClassName + << "'"; + return; + } + + m_extractedTextClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + m_classConstructorMethodID = env->GetMethodID(m_extractedTextClass, "<init>", "()V"); + if (m_classConstructorMethodID == NULL) { + qCritical() << "GetMethodID failed"; + return; + } + + m_partialEndOffsetFieldID = env->GetFieldID(m_extractedTextClass, "partialEndOffset", "I"); + if (m_partialEndOffsetFieldID == NULL) { + qCritical() << "Can't find field partialEndOffset"; + return; + } + + m_partialStartOffsetFieldID = env->GetFieldID(m_extractedTextClass, "partialStartOffset", "I"); + if (m_partialStartOffsetFieldID == NULL) { + qCritical() << "Can't find field partialStartOffset"; + return; + } + + m_selectionEndFieldID = env->GetFieldID(m_extractedTextClass, "selectionEnd", "I"); + if (m_selectionEndFieldID == NULL) { + qCritical() << "Can't find field selectionEnd"; + return; + } + + m_selectionStartFieldID = env->GetFieldID(m_extractedTextClass, "selectionStart", "I"); + if (m_selectionStartFieldID == NULL) { + qCritical() << "Can't find field selectionStart"; + return; + } + + m_startOffsetFieldID = env->GetFieldID(m_extractedTextClass, "startOffset", "I"); + if (m_startOffsetFieldID == NULL) { + qCritical() << "Can't find field startOffset"; + return; + } + + m_textFieldID = env->GetFieldID(m_extractedTextClass, "text", "Ljava/lang/String;"); + if (m_textFieldID == NULL) { + qCritical() << "Can't find field text"; + return; + } + qRegisterMetaType<QInputMethodEvent *>("QInputMethodEvent*"); + qRegisterMetaType<QInputMethodQueryEvent *>("QInputMethodQueryEvent*"); + m_androidInputContext = this; +} + +QAndroidInputContext::~QAndroidInputContext() +{ + m_androidInputContext = 0; + m_extractedTextClass = 0; + m_partialEndOffsetFieldID = 0; + m_partialStartOffsetFieldID = 0; + m_selectionEndFieldID = 0; + m_selectionStartFieldID = 0; + m_startOffsetFieldID = 0; + m_textFieldID = 0; +} + +void QAndroidInputContext::reset() +{ + clear(); + if (qGuiApp->focusObject()) + QtAndroidInput::resetSoftwareKeyboard(); + else + QtAndroidInput::hideSoftwareKeyboard(); +} + +void QAndroidInputContext::commit() +{ + finishComposingText(); + + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); + if (!query.isNull()) { + const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + QtAndroidInput::updateSelection(cursorPos, cursorPos, -1, -1); //selection empty and no pre-edit text + } +} + +void QAndroidInputContext::update(Qt::InputMethodQueries queries) +{ + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(queries); + if (query.isNull()) + return; +#warning TODO extract the needed data from query +} + +void QAndroidInputContext::invokeAction(QInputMethod::Action action, int cursorPosition) +{ +#warning TODO Handle at least QInputMethod::ContextMenu action + Q_UNUSED(action) + Q_UNUSED(cursorPosition) + + if (action == QInputMethod::Click) + commit(); +} + +QRectF QAndroidInputContext::keyboardRect() const +{ + return QPlatformInputContext::keyboardRect(); +} + +bool QAndroidInputContext::isAnimating() const +{ + return false; +} + +void QAndroidInputContext::showInputPanel() +{ + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); + if (query.isNull()) + return; + QRectF itemRect = qGuiApp->inputMethod()->inputItemRectangle(); + QRect rect = qGuiApp->inputMethod()->inputItemTransform().mapRect(itemRect).toRect(); + QWindow *window = qGuiApp->focusWindow(); + if (window) + rect = QRect(window->mapToGlobal(rect.topLeft()), rect.size()); + + QtAndroidInput::showSoftwareKeyboard(rect.left(), + rect.top(), + rect.width(), + rect.height(), + query->value(Qt::ImHints).toUInt()); +} + +void QAndroidInputContext::hideInputPanel() +{ + QtAndroidInput::hideSoftwareKeyboard(); +} + +bool QAndroidInputContext::isInputPanelVisible() const +{ + return QtAndroidInput::isSoftwareKeyboardVisible(); +} + +bool QAndroidInputContext::isComposing() const +{ + return m_composingText.length(); +} + +void QAndroidInputContext::clear() +{ + m_composingText.clear(); + m_extractedText.clear(); +} + +void QAndroidInputContext::sendEvent(QObject *receiver, QInputMethodEvent *event) +{ + QCoreApplication::sendEvent(receiver, event); +} + +void QAndroidInputContext::sendEvent(QObject *receiver, QInputMethodQueryEvent *event) +{ + QCoreApplication::sendEvent(receiver, event); +} + +jboolean QAndroidInputContext::commitText(const QString &text, jint /*newCursorPosition*/) +{ + m_composingText = text; + return finishComposingText(); +} + +jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint rightLength) +{ + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); + if (query.isNull()) + return JNI_TRUE; + + m_composingText.clear(); + + QInputMethodEvent event; + event.setCommitString(QString(), -leftLength, leftLength+rightLength); + sendInputMethodEvent(&event); + clear(); + + return JNI_TRUE; +} + +jboolean QAndroidInputContext::finishComposingText() +{ + QInputMethodEvent event; + event.setCommitString(m_composingText); + sendInputMethodEvent(&event); + clear(); + + return JNI_TRUE; +} + +jint QAndroidInputContext::getCursorCapsMode(jint /*reqModes*/) +{ + jint res = 0; + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); + if (query.isNull()) + return res; + + const uint qtInputMethodHints = query->value(Qt::ImHints).toUInt(); + + if (qtInputMethodHints & Qt::ImhPreferUppercase) + res = CAP_MODE_SENTENCES; + + if (qtInputMethodHints & Qt::ImhUppercaseOnly) + res = CAP_MODE_CHARACTERS; + + return res; +} + +const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedText(jint hintMaxChars, jint /*hintMaxLines*/, jint /*flags*/) +{ + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); + if (query.isNull()) + return m_extractedText; + + if (hintMaxChars) + m_extractedText.text = query->value(Qt::ImSurroundingText).toString().right(hintMaxChars); + + m_extractedText.startOffset = query->value(Qt::ImCursorPosition).toInt(); + const QString &selection = query->value(Qt::ImCurrentSelection).toString(); + const int selLen = selection.length(); + if (selLen) { + m_extractedText.selectionStart = query->value(Qt::ImAnchorPosition).toInt(); + m_extractedText.selectionEnd = m_extractedText.startOffset; + } + + return m_extractedText; +} + +QString QAndroidInputContext::getSelectedText(jint /*flags*/) +{ + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); + if (query.isNull()) + return QString(); + + return query->value(Qt::ImCurrentSelection).toString(); +} + +QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/) +{ + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); + if (query.isNull()) + return QString(); + + QString text = query->value(Qt::ImSurroundingText).toString(); + if (!text.length()) + return text; + + int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + return text.mid(cursorPos, length); +} + +QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) +{ + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); + if (query.isNull()) + return QString(); + + QString text = query->value(Qt::ImSurroundingText).toString(); + if (!text.length()) + return text; + + int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + const int wordLeftPos = cursorPos - length; + return text.mid(wordLeftPos > 0 ? wordLeftPos : 0, cursorPos); +} + +jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCursorPosition) +{ + if (newCursorPosition > 0) + newCursorPosition += text.length() - 1; + m_composingText = text; + QList<QInputMethodEvent::Attribute> attributes; + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, + newCursorPosition, + 1, + QVariant())); + // Show compose text underlined + QTextCharFormat underlined; + underlined.setFontUnderline(true); + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,0, text.length(), + QVariant(underlined))); + + QInputMethodEvent event(m_composingText, attributes); + sendInputMethodEvent(&event); + + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); + if (!query.isNull()) { + int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + int preeditLength = text.length(); + QtAndroidInput::updateSelection(cursorPos+preeditLength, cursorPos+preeditLength, cursorPos, cursorPos+preeditLength); + } + + return JNI_TRUE; +} + +jboolean QAndroidInputContext::setSelection(jint start, jint end) +{ + QList<QInputMethodEvent::Attribute> attributes; + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, + start, + end - start, + QVariant())); + + QInputMethodEvent event(QString(), attributes); + sendInputMethodEvent(&event); + return JNI_TRUE; +} + +jboolean QAndroidInputContext::selectAll() +{ +#warning TODO + return JNI_FALSE; +} + +jboolean QAndroidInputContext::cut() +{ +#warning TODO + return JNI_FALSE; +} + +jboolean QAndroidInputContext::copy() +{ +#warning TODO + return JNI_FALSE; +} + +jboolean QAndroidInputContext::copyURL() +{ +#warning TODO + return JNI_FALSE; +} + +jboolean QAndroidInputContext::paste() +{ +#warning TODO + return JNI_FALSE; +} + +QSharedPointer<QInputMethodQueryEvent> QAndroidInputContext::focusObjectInputMethodQuery(Qt::InputMethodQueries queries) +{ +#warning TODO make qGuiApp->focusObject() thread safe !!! + QObject *focusObject = qGuiApp->focusObject(); + if (!focusObject) + return QSharedPointer<QInputMethodQueryEvent>(); + + QSharedPointer<QInputMethodQueryEvent> ret = QSharedPointer<QInputMethodQueryEvent>(new QInputMethodQueryEvent(queries)); + if (qGuiApp->thread()==QThread::currentThread()) { + QCoreApplication::sendEvent(focusObject, ret.data()); + } else { + QMetaObject::invokeMethod(this, + "sendEvent", + Qt::BlockingQueuedConnection, + Q_ARG(QObject*, focusObject), + Q_ARG(QInputMethodQueryEvent*, ret.data())); + } + + return ret; +} + +void QAndroidInputContext::sendInputMethodEvent(QInputMethodEvent *event) +{ +#warning TODO make qGuiApp->focusObject() thread safe !!! + QObject *focusObject = qGuiApp->focusObject(); + if (!focusObject) + return; + + if (qGuiApp->thread() == QThread::currentThread()) { + QCoreApplication::sendEvent(focusObject, event); + } else { + QMetaObject::invokeMethod(this, + "sendEvent", + Qt::BlockingQueuedConnection, + Q_ARG(QObject*, focusObject), + Q_ARG(QInputMethodEvent*, event)); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/src/qandroidinputcontext.h b/src/plugins/platforms/android/src/qandroidinputcontext.h new file mode 100644 index 0000000000..e2b8107044 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidinputcontext.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 ANDROIDINPUTCONTEXT_H +#define ANDROIDINPUTCONTEXT_H + +#include <qpa/qplatforminputcontext.h> +#include <jni.h> +#include <qevent.h> + +QT_BEGIN_NAMESPACE + +class QAndroidInputContext: public QPlatformInputContext +{ + Q_OBJECT + enum CapsMode + { + CAP_MODE_CHARACTERS = 0x00001000, + CAP_MODE_SENTENCES = 0x00004000, + CAP_MODE_WORDS = 0x00002000 + }; + +public: + struct ExtractedText + { + ExtractedText() { clear(); } + + void clear() + { + partialEndOffset = partialStartOffset = selectionEnd = selectionStart = startOffset = -1; + text.clear(); + } + + int partialEndOffset; + int partialStartOffset; + int selectionEnd; + int selectionStart; + int startOffset; + QString text; + }; + +public: + QAndroidInputContext(); + ~QAndroidInputContext(); + bool isValid() const { return true; } + + void reset(); + void commit(); + void update(Qt::InputMethodQueries queries); + void invokeAction(QInputMethod::Action action, int cursorPosition); + QRectF keyboardRect() const; + bool isAnimating() const; + void showInputPanel(); + void hideInputPanel(); + bool isInputPanelVisible() const; + + bool isComposing() const; + void clear(); + + //---------------// + jboolean commitText(const QString &text, jint newCursorPosition); + jboolean deleteSurroundingText(jint leftLength, jint rightLength); + jboolean finishComposingText(); + jint getCursorCapsMode(jint reqModes); + const ExtractedText &getExtractedText(jint hintMaxChars, jint hintMaxLines, jint flags); + QString getSelectedText(jint flags); + QString getTextAfterCursor(jint length, jint flags); + QString getTextBeforeCursor(jint length, jint flags); + jboolean setComposingText(const QString &text, jint newCursorPosition); + jboolean setSelection(jint start, jint end); + jboolean selectAll(); + jboolean cut(); + jboolean copy(); + jboolean copyURL(); + jboolean paste(); + +private: + QSharedPointer<QInputMethodQueryEvent> focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll); + void sendInputMethodEvent(QInputMethodEvent *event); + +private slots: + virtual void sendEvent(QObject *receiver, QInputMethodEvent *event); + virtual void sendEvent(QObject *receiver, QInputMethodQueryEvent *event); + +private: + ExtractedText m_extractedText; + QString m_composingText; +}; + +QT_END_NAMESPACE + +#endif // ANDROIDINPUTCONTEXT_H diff --git a/src/plugins/platforms/android/src/qandroidplatformclipboard.cpp b/src/plugins/platforms/android/src/qandroidplatformclipboard.cpp new file mode 100644 index 0000000000..bc48b4935b --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformclipboard.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "qandroidplatformclipboard.h" +#include "androidjniclipboard.h" +#ifndef QT_NO_CLIPBOARD +#include <QMimeData> + +QT_BEGIN_NAMESPACE + +QAndroidPlatformClipboard::QAndroidPlatformClipboard() +{ + QtAndroidClipboard::setClipboardListener(this); +} + +QMimeData *QAndroidPlatformClipboard::mimeData(QClipboard::Mode mode) +{ + if (QClipboard::Clipboard != mode || !QtAndroidClipboard::hasClipboardText()) + return 0; + + QMimeData *mimeData = new QMimeData(); + mimeData->setText(QtAndroidClipboard::clipboardText()); + return mimeData; +} + +void QAndroidPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) +{ + if (!data || !data->hasText() || QClipboard::Clipboard != mode) + return; + + QtAndroidClipboard::setClipboardText(data->text()); +} + +bool QAndroidPlatformClipboard::supportsMode(QClipboard::Mode mode) const +{ + return QClipboard::Clipboard == mode; +} + +QT_END_NAMESPACE + +#endif // QT_NO_CLIPBOARD diff --git a/src/plugins/platforms/android/src/qandroidplatformclipboard.h b/src/plugins/platforms/android/src/qandroidplatformclipboard.h new file mode 100644 index 0000000000..644f326934 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformclipboard.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 QANDROIDPLATFORMCLIPBOARD_H +#define QANDROIDPLATFORMCLIPBOARD_H + +#include <qpa/qplatformclipboard.h> + +#ifndef QT_NO_CLIPBOARD +QT_BEGIN_NAMESPACE + +class QAndroidPlatformClipboard: public QPlatformClipboard +{ +public: + QAndroidPlatformClipboard(); + + virtual QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard); + virtual void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard); + virtual bool supportsMode(QClipboard::Mode mode) const; +}; + +QT_END_NAMESPACE +#endif // QT_NO_CLIPBOARD + +#endif // QANDROIDPLATFORMCLIPBOARD_H diff --git a/src/plugins/platforms/android/src/qandroidplatformfontdatabase.cpp b/src/plugins/platforms/android/src/qandroidplatformfontdatabase.cpp new file mode 100644 index 0000000000..7f68b44ed8 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformfontdatabase.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 <QDir> + +#include "qandroidplatformfontdatabase.h" + +QString QAndroidPlatformFontDatabase::fontDir() const +{ + return QLatin1String("/system/fonts"); +} + +void QAndroidPlatformFontDatabase::populateFontDatabase() +{ + QString fontpath = fontDir(); + + if (!QFile::exists(fontpath)) { + qFatal("QFontDatabase: Cannot find font directory %s - is Qt installed correctly?", + qPrintable(fontpath)); + } + + QDir dir(fontpath, QLatin1String("*.ttf")); + for (int i = 0; i < int(dir.count()); ++i) { + const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); + addTTFile(QByteArray(), file); + } +} + +QStringList QAndroidPlatformFontDatabase::fallbacksForFamily(const QString &family, + QFont::Style style, + QFont::StyleHint styleHint, + QChar::Script script) const +{ + Q_UNUSED(family); + Q_UNUSED(style); + Q_UNUSED(script); + if (styleHint == QFont::Monospace) + return QString(qgetenv("QT_ANDROID_FONTS_MONOSPACE")).split(";"); + + return QString(qgetenv("QT_ANDROID_FONTS")).split(";"); +} diff --git a/src/plugins/platforms/android/src/qandroidplatformfontdatabase.h b/src/plugins/platforms/android/src/qandroidplatformfontdatabase.h new file mode 100644 index 0000000000..3cbfe95d36 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformfontdatabase.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 QANDROIDPLATFORMFONTDATABASE_H +#define QANDROIDPLATFORMFONTDATABASE_H + +#include <QtPlatformSupport/private/qbasicfontdatabase_p.h> + +class QAndroidPlatformFontDatabase: public QBasicFontDatabase +{ +public: + QString fontDir() const; + void populateFontDatabase(); + QStringList fallbacksForFamily(const QString &family, + QFont::Style style, + QFont::StyleHint styleHint, + QChar::Script script) const; +}; + +#endif // QANDROIDPLATFORMFONTDATABASE_H diff --git a/src/plugins/platforms/android/src/qandroidplatformintegration.cpp b/src/plugins/platforms/android/src/qandroidplatformintegration.cpp new file mode 100644 index 0000000000..cbd0f26835 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformintegration.cpp @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "qandroidplatformintegration.h" +#include "qabstracteventdispatcher.h" +#include "androidjnimain.h" +#include <QtGui/private/qpixmap_raster_p.h> +#include <qpa/qwindowsysteminterface.h> +#include <QThread> +#include <qpa/qplatformwindow.h> +#include "qandroidplatformservices.h" +#include "qandroidplatformfontdatabase.h" +#include "qandroidplatformclipboard.h" +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> + +#ifndef ANDROID_PLUGIN_OPENGL +# include "qandroidplatformscreen.h" +# include "qandroidplatformwindow.h" +# include <QtPlatformSupport/private/qfbbackingstore_p.h> +#else +# include "qeglfswindow.h" +# include "androidjnimenu.h" +# include "qandroidopenglcontext.h" +# include "qandroidopenglplatformwindow.h" +# include "qeglfshooks.h" +# include <QtGui/qopenglcontext.h> +#endif + +#include "qandroidplatformtheme.h" + +QT_BEGIN_NAMESPACE + +int QAndroidPlatformIntegration::m_defaultGeometryWidth = 320; +int QAndroidPlatformIntegration::m_defaultGeometryHeight = 455; +int QAndroidPlatformIntegration::m_defaultPhysicalSizeWidth = 50; +int QAndroidPlatformIntegration::m_defaultPhysicalSizeHeight = 71; + +void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteArray &resource) +{ + if (resource=="JavaVM") + return QtAndroid::javaVM(); + if (resource == "QtActivity") + return QtAndroid::activity(); + + return 0; +} + +QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶mList) + : m_touchDevice(0) +#ifdef ANDROID_PLUGIN_OPENGL + , m_primaryWindow(0) +#endif +{ + Q_UNUSED(paramList); + +#ifndef ANDROID_PLUGIN_OPENGL + m_eventDispatcher = createUnixEventDispatcher(); +#endif + + m_androidPlatformNativeInterface = new QAndroidPlatformNativeInterface(); + +#ifndef ANDROID_PLUGIN_OPENGL + m_primaryScreen = new QAndroidPlatformScreen(); + screenAdded(m_primaryScreen); + m_primaryScreen->setPhysicalSize(QSize(m_defaultPhysicalSizeWidth, m_defaultPhysicalSizeHeight)); + m_primaryScreen->setGeometry(QRect(0, 0, m_defaultGeometryWidth, m_defaultGeometryHeight)); +#endif + + m_mainThread = QThread::currentThread(); + QtAndroid::setAndroidPlatformIntegration(this); + + m_androidFDB = new QAndroidPlatformFontDatabase(); + m_androidPlatformServices = new QAndroidPlatformServices(); + m_androidPlatformClipboard = new QAndroidPlatformClipboard(); +} + +bool QAndroidPlatformIntegration::hasCapability(Capability cap) const +{ + switch (cap) { + case ThreadedPixmaps: return true; + default: +#ifndef ANDROID_PLUGIN_OPENGL + return QPlatformIntegration::hasCapability(cap); +#else + return QEglFSIntegration::hasCapability(cap); +#endif + } +} + +#ifndef ANDROID_PLUGIN_OPENGL +QPlatformBackingStore *QAndroidPlatformIntegration::createPlatformBackingStore(QWindow *window) const +{ + return new QFbBackingStore(window); +} + +QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *window) const +{ + return new QAndroidPlatformWindow(window); +} + +QAbstractEventDispatcher *QAndroidPlatformIntegration::guiThreadEventDispatcher() const +{ + return m_eventDispatcher; +} +#else // !ANDROID_PLUGIN_OPENGL +QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *window) const +{ + if (m_primaryWindow != 0) { + qWarning("QAndroidPlatformIntegration::createPlatformWindow: Unsupported case: More than " + "one top-level window created."); + } + + m_primaryWindow = new QAndroidOpenGLPlatformWindow(window); + m_primaryWindow->requestActivateWindow(); + QtAndroidMenu::setActiveTopLevelWindow(window); + + return m_primaryWindow; +} + +void QAndroidPlatformIntegration::invalidateNativeSurface() +{ + if (m_primaryWindow != 0) + m_primaryWindow->invalidateSurface(); +} + +void QAndroidPlatformIntegration::surfaceChanged() +{ + if (m_primaryWindow != 0) + m_primaryWindow->resetSurface(); +} + +QPlatformOpenGLContext *QAndroidPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const +{ + return new QAndroidOpenGLContext(this, + QEglFSHooks::hooks()->surfaceFormatFor(context->format()), + context->shareHandle(), + display()); +} +#endif // ANDROID_PLUGIN_OPENGL + +QAndroidPlatformIntegration::~QAndroidPlatformIntegration() +{ + delete m_androidPlatformNativeInterface; + delete m_androidFDB; + delete m_touchDevice; + QtAndroid::setAndroidPlatformIntegration(NULL); +} +QPlatformFontDatabase *QAndroidPlatformIntegration::fontDatabase() const +{ + return m_androidFDB; +} + +#ifndef QT_NO_CLIPBOARD +QPlatformClipboard *QAndroidPlatformIntegration::clipboard() const +{ +static QAndroidPlatformClipboard *clipboard = 0; + if (!clipboard) + clipboard = new QAndroidPlatformClipboard; + + return clipboard; +} +#endif + +QPlatformInputContext *QAndroidPlatformIntegration::inputContext() const +{ + return &m_platformInputContext; +} + +QPlatformNativeInterface *QAndroidPlatformIntegration::nativeInterface() const +{ + return m_androidPlatformNativeInterface; +} + +QPlatformServices *QAndroidPlatformIntegration::services() const +{ + return m_androidPlatformServices; +} + +static const QLatin1String androidThemeName("android"); +QStringList QAndroidPlatformIntegration::themeNames() const +{ + return QStringList(QString(androidThemeName)); +} + +QPlatformTheme *QAndroidPlatformIntegration::createPlatformTheme(const QString &name) const +{ + if (androidThemeName == name) + return new QAndroidPlatformTheme; + + return 0; +} + +void QAndroidPlatformIntegration::setDefaultDisplayMetrics(int gw, int gh, int sw, int sh) +{ + m_defaultGeometryWidth = gw; + m_defaultGeometryHeight = gh; + m_defaultPhysicalSizeWidth = sw; + m_defaultPhysicalSizeHeight = sh; +} + +void QAndroidPlatformIntegration::setDefaultDesktopSize(int gw, int gh) +{ + m_defaultGeometryWidth = gw; + m_defaultGeometryHeight = gh; +} + + +#ifndef ANDROID_PLUGIN_OPENGL +void QAndroidPlatformIntegration::setDesktopSize(int width, int height) +{ + if (m_primaryScreen) + QMetaObject::invokeMethod(m_primaryScreen, "setGeometry", Qt::AutoConnection, Q_ARG(QRect, QRect(0,0,width, height))); +} + +void QAndroidPlatformIntegration::setDisplayMetrics(int width, int height) +{ + if (m_primaryScreen) + QMetaObject::invokeMethod(m_primaryScreen, "setPhysicalSize", Qt::AutoConnection, Q_ARG(QSize, QSize(width, height))); +} +#else +void QAndroidPlatformIntegration::setDesktopSize(int width, int height) +{ + m_defaultGeometryWidth = width; + m_defaultGeometryHeight = height; +} + +void QAndroidPlatformIntegration::setDisplayMetrics(int width, int height) +{ + m_defaultPhysicalSizeWidth = width; + m_defaultPhysicalSizeHeight = height; +} + +#endif + +void QAndroidPlatformIntegration::pauseApp() +{ + if (QAbstractEventDispatcher::instance(m_mainThread)) + QAbstractEventDispatcher::instance(m_mainThread)->interrupt(); +} + +void QAndroidPlatformIntegration::resumeApp() +{ + if (QAbstractEventDispatcher::instance(m_mainThread)) + QAbstractEventDispatcher::instance(m_mainThread)->wakeUp(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/src/qandroidplatformintegration.h b/src/plugins/platforms/android/src/qandroidplatformintegration.h new file mode 100644 index 0000000000..3f8cc5a809 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformintegration.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 QANDROIDPLATFORMINTERATION_H +#define QANDROIDPLATFORMINTERATION_H + +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformmenu.h> +#include <qpa/qplatformnativeinterface.h> +#include <QtWidgets/QAction> + +#include <jni.h> +#include "qandroidinputcontext.h" + +#ifndef ANDROID_PLUGIN_OPENGL +# include "qandroidplatformscreen.h" +#else +# include "qeglfsintegration.h" +#endif + +QT_BEGIN_NAMESPACE + +class QDesktopWidget; +class QAndroidPlatformServices; + +#ifdef ANDROID_PLUGIN_OPENGL +class QAndroidOpenGLPlatformWindow; +#endif + +class QAndroidPlatformNativeInterface: public QPlatformNativeInterface +{ +public: + void *nativeResourceForIntegration(const QByteArray &resource); +}; + +class QAndroidPlatformIntegration +#ifndef ANDROID_PLUGIN_OPENGL + : public QPlatformIntegration +#else + : public QEglFSIntegration +#endif +{ + friend class QAndroidPlatformScreen; + +public: + QAndroidPlatformIntegration(const QStringList ¶mList); + ~QAndroidPlatformIntegration(); + + bool hasCapability(QPlatformIntegration::Capability cap) const; + +#ifndef ANDROID_PLUGIN_OPENGL + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + QPlatformWindow *createPlatformWindow(QWindow *window) const; + QAbstractEventDispatcher *guiThreadEventDispatcher() const; + QAndroidPlatformScreen *screen() { return m_primaryScreen; } +#else + QPlatformWindow *createPlatformWindow(QWindow *window) const; + void invalidateNativeSurface(); + void surfaceChanged(); + QAndroidOpenGLPlatformWindow *primaryWindow() const { return m_primaryWindow; } + QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; +#endif + + virtual void setDesktopSize(int width, int height); + virtual void setDisplayMetrics(int width, int height); + bool isVirtualDesktop() { return true; } + + QPlatformFontDatabase *fontDatabase() const; + +#ifndef QT_NO_CLIPBOARD + QPlatformClipboard *clipboard() const; +#endif + + QPlatformInputContext *inputContext() const; + QPlatformNativeInterface *nativeInterface() const; + QPlatformServices *services() const; + + QStringList themeNames() const; + QPlatformTheme *createPlatformTheme(const QString &name) const; + + void pauseApp(); + void resumeApp(); + static void setDefaultDisplayMetrics(int gw, int gh, int sw, int sh); + static void setDefaultDesktopSize(int gw, int gh); + + static QSize defaultDesktopSize() + { + return QSize(m_defaultGeometryWidth, m_defaultGeometryHeight); + } + + QTouchDevice *touchDevice() const { return m_touchDevice; } + void setTouchDevice(QTouchDevice *touchDevice) { m_touchDevice = touchDevice; } + +private: + + friend class QEglFSAndroidHooks; + + QTouchDevice *m_touchDevice; + +#ifndef ANDROID_PLUGIN_OPENGL + QAbstractEventDispatcher *m_eventDispatcher; + QAndroidPlatformScreen *m_primaryScreen; +#else + mutable QAndroidOpenGLPlatformWindow *m_primaryWindow; +#endif + + QThread *m_mainThread; + + static int m_defaultGeometryWidth; + static int m_defaultGeometryHeight; + static int m_defaultPhysicalSizeWidth; + static int m_defaultPhysicalSizeHeight; + + QPlatformFontDatabase *m_androidFDB; + QImage *m_FbScreenImage; + QPainter *m_compositePainter; + QAndroidPlatformNativeInterface *m_androidPlatformNativeInterface; + QAndroidPlatformServices *m_androidPlatformServices; + QPlatformClipboard *m_androidPlatformClipboard; + + mutable QAndroidInputContext m_platformInputContext; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/android/src/qandroidplatformmenu.cpp b/src/plugins/platforms/android/src/qandroidplatformmenu.cpp new file mode 100644 index 0000000000..36247e86f9 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenu.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "qandroidplatformmenu.h" +#include "qandroidplatformmenuitem.h" +#include "androidjnimenu.h" + +QAndroidPlatformMenu::QAndroidPlatformMenu() +{ + m_tag = reinterpret_cast<quintptr>(this); // QMenu will overwrite this later, but we need a unique ID for QtQuick + m_enabled = true; + m_isVisible = true; +} + +QAndroidPlatformMenu::~QAndroidPlatformMenu() +{ + QtAndroidMenu::androidPlatformMenuDestroyed(this); +} + +void QAndroidPlatformMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) +{ + QMutexLocker lock(&m_menuItemsMutex); + m_menuItems.insert(qFind(m_menuItems.begin(), + m_menuItems.end(), + static_cast<QAndroidPlatformMenuItem *>(before)), + static_cast<QAndroidPlatformMenuItem *>(menuItem)); +} + +void QAndroidPlatformMenu::removeMenuItem(QPlatformMenuItem *menuItem) +{ + QMutexLocker lock(&m_menuItemsMutex); + m_menuItems.erase(qFind(m_menuItems.begin(), + m_menuItems.end(), + static_cast<QAndroidPlatformMenuItem *>(menuItem))); +} + +void QAndroidPlatformMenu::syncMenuItem(QPlatformMenuItem *menuItem) +{ + PlatformMenuItemsType::iterator it; + for (it = m_menuItems.begin(); it != m_menuItems.end(); ++it) { + if ((*it)->tag() == menuItem->tag()) + break; + } + + if (it != m_menuItems.end()) + QtAndroidMenu::syncMenu(this); +} + +void QAndroidPlatformMenu::syncSeparatorsCollapsible(bool enable) +{ + Q_UNUSED(enable) +} + +void QAndroidPlatformMenu::setTag(quintptr tag) +{ + m_tag = tag; +} + +quintptr QAndroidPlatformMenu::tag() const +{ + return m_tag; +} + +void QAndroidPlatformMenu::setText(const QString &text) +{ + m_text = text; +} + +QString QAndroidPlatformMenu::text() const +{ + return m_text; +} + +void QAndroidPlatformMenu::setIcon(const QIcon &icon) +{ + m_icon = icon; +} + +QIcon QAndroidPlatformMenu::icon() const +{ + return m_icon; +} + +void QAndroidPlatformMenu::setEnabled(bool enabled) +{ + m_enabled = enabled; +} + +bool QAndroidPlatformMenu::isEnabled() const +{ + return m_enabled; +} + +void QAndroidPlatformMenu::setVisible(bool visible) +{ + m_isVisible = visible; +} + +bool QAndroidPlatformMenu::isVisible() const +{ + return m_isVisible; +} + +QPlatformMenuItem *QAndroidPlatformMenu::menuItemAt(int position) const +{ + if (position < m_menuItems.size()) + return m_menuItems[position]; + return 0; +} + +QPlatformMenuItem *QAndroidPlatformMenu::menuItemForTag(quintptr tag) const +{ + foreach (QPlatformMenuItem *menuItem, m_menuItems) { + if (menuItem->tag() == tag) + return menuItem; + } + + return 0; +} + +QAndroidPlatformMenu::PlatformMenuItemsType QAndroidPlatformMenu::menuItems() const +{ + return m_menuItems; +} + +QMutex *QAndroidPlatformMenu::menuItemsMutex() +{ + return &m_menuItemsMutex; +} diff --git a/src/plugins/platforms/android/src/qandroidplatformmenu.h b/src/plugins/platforms/android/src/qandroidplatformmenu.h new file mode 100644 index 0000000000..20236cb636 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenu.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 QANDROIDPLATFORMMENU_H +#define QANDROIDPLATFORMMENU_H + +#include <qpa/qplatformmenu.h> +#include <qvector.h> +#include <qmutex.h> + +class QAndroidPlatformMenuItem; +class QAndroidPlatformMenu: public QPlatformMenu +{ +public: + typedef QVector<QAndroidPlatformMenuItem *> PlatformMenuItemsType; + +public: + QAndroidPlatformMenu(); + ~QAndroidPlatformMenu(); + + void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before); + void removeMenuItem(QPlatformMenuItem *menuItem); + void syncMenuItem(QPlatformMenuItem *menuItem); + void syncSeparatorsCollapsible(bool enable); + + void setTag(quintptr tag); + quintptr tag() const; + void setText(const QString &text); + QString text() const; + void setIcon(const QIcon &icon); + QIcon icon() const; + void setEnabled(bool enabled); + bool isEnabled() const; + void setVisible(bool visible); + bool isVisible() const; + + QPlatformMenuItem *menuItemAt(int position) const; + QPlatformMenuItem *menuItemForTag(quintptr tag) const; + + PlatformMenuItemsType menuItems() const; + QMutex *menuItemsMutex(); + +private: + PlatformMenuItemsType m_menuItems; + quintptr m_tag; + QString m_text; + QIcon m_icon; + bool m_enabled; + bool m_isVisible; + QMutex m_menuItemsMutex; +}; + +#endif // QANDROIDPLATFORMMENU_H diff --git a/src/plugins/platforms/android/src/qandroidplatformmenubar.cpp b/src/plugins/platforms/android/src/qandroidplatformmenubar.cpp new file mode 100644 index 0000000000..ef1ac61356 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenubar.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "qandroidplatformmenubar.h" +#include "qandroidplatformmenu.h" +#include "androidjnimenu.h" + + +QAndroidPlatformMenuBar::QAndroidPlatformMenuBar() +{ + m_parentWindow = 0; + QtAndroidMenu::addMenuBar(this); +} + +QAndroidPlatformMenuBar::~QAndroidPlatformMenuBar() +{ + QtAndroidMenu::removeMenuBar(this); +} + +void QAndroidPlatformMenuBar::insertMenu(QPlatformMenu *menu, QPlatformMenu *before) +{ + QMutexLocker lock(&m_menusListMutex); + m_menus.insert(qFind(m_menus.begin(), + m_menus.end(), + static_cast<QAndroidPlatformMenu *>(before)), + static_cast<QAndroidPlatformMenu *>(menu)); +} + +void QAndroidPlatformMenuBar::removeMenu(QPlatformMenu *menu) +{ + QMutexLocker lock(&m_menusListMutex); + m_menus.erase(qFind(m_menus.begin(), + m_menus.end(), + static_cast<QAndroidPlatformMenu *>(menu))); +} + +void QAndroidPlatformMenuBar::syncMenu(QPlatformMenu *menu) +{ + QtAndroidMenu::syncMenu(static_cast<QAndroidPlatformMenu *>(menu)); +} + +void QAndroidPlatformMenuBar::handleReparent(QWindow *newParentWindow) +{ + m_parentWindow = newParentWindow; + QtAndroidMenu::setMenuBar(this, newParentWindow); +} + +QPlatformMenu *QAndroidPlatformMenuBar::menuForTag(quintptr tag) const +{ + foreach (QPlatformMenu *menu, m_menus) { + if (menu->tag() == tag) + return menu; + } + + return 0; +} + +QWindow *QAndroidPlatformMenuBar::parentWindow() const +{ + return m_parentWindow; +} + +QAndroidPlatformMenuBar::PlatformMenusType QAndroidPlatformMenuBar::menus() const +{ + return m_menus; +} + +QMutex *QAndroidPlatformMenuBar::menusListMutex() +{ + return &m_menusListMutex; +} diff --git a/src/plugins/platforms/android/src/qandroidplatformmenubar.h b/src/plugins/platforms/android/src/qandroidplatformmenubar.h new file mode 100644 index 0000000000..56915335c2 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenubar.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 QANDROIDPLATFORMMENUBAR_H +#define QANDROIDPLATFORMMENUBAR_H + +#include <qpa/qplatformmenu.h> +#include <qvector.h> +#include <qmutex.h> + +class QAndroidPlatformMenu; +class QAndroidPlatformMenuBar: public QPlatformMenuBar +{ +public: + typedef QVector<QAndroidPlatformMenu *> PlatformMenusType; +public: + QAndroidPlatformMenuBar(); + ~QAndroidPlatformMenuBar(); + + void insertMenu(QPlatformMenu *menu, QPlatformMenu *before); + void removeMenu(QPlatformMenu *menu); + void syncMenu(QPlatformMenu *menu); + void handleReparent(QWindow *newParentWindow); + QPlatformMenu *menuForTag(quintptr tag) const; + + QWindow *parentWindow() const; + PlatformMenusType menus() const; + QMutex *menusListMutex(); + +private: + PlatformMenusType m_menus; + QWindow *m_parentWindow; + QMutex m_menusListMutex; +}; + +#endif // QANDROIDPLATFORMMENUBAR_H diff --git a/src/plugins/platforms/android/src/qandroidplatformmenuitem.cpp b/src/plugins/platforms/android/src/qandroidplatformmenuitem.cpp new file mode 100644 index 0000000000..bd37834d2a --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenuitem.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "qandroidplatformmenuitem.h" +#include "qandroidplatformmenu.h" + +QAndroidPlatformMenuItem::QAndroidPlatformMenuItem() +{ + m_tag = reinterpret_cast<quintptr>(this); // QMenu will overwrite this later, but we need a unique ID for QtQuick + m_menu = 0; + m_isVisible = true; + m_isSeparator = false; + m_role = NoRole; + m_isCheckable = false; + m_isChecked = false; + m_isEnabled = true; +} + +void QAndroidPlatformMenuItem::setTag(quintptr tag) +{ + m_tag = tag; +} + +quintptr QAndroidPlatformMenuItem::tag() const +{ + return m_tag; +} + +void QAndroidPlatformMenuItem::setText(const QString &text) +{ + m_text = text; + if (m_menu) + m_menu->setText(m_text); +} + +QString QAndroidPlatformMenuItem::text() const +{ + return m_text; +} + +void QAndroidPlatformMenuItem::setIcon(const QIcon &icon) +{ + m_icon = icon; + if (m_menu) + m_menu->setIcon(m_icon); +} + +QIcon QAndroidPlatformMenuItem::icon() const +{ + return m_icon; +} + +void QAndroidPlatformMenuItem::setMenu(QPlatformMenu *menu) +{ + m_menu = static_cast<QAndroidPlatformMenu *>(menu); + if (!m_menu) + return; + + m_menu->setText(m_text); + m_menu->setIcon(m_icon); + m_menu->setVisible(m_isVisible); + m_menu->setEnabled(m_isEnabled); +} + +QAndroidPlatformMenu *QAndroidPlatformMenuItem::menu() const +{ + return m_menu; +} + +void QAndroidPlatformMenuItem::setVisible(bool isVisible) +{ + m_isVisible = isVisible; + if (m_menu) + m_menu->setVisible(m_isVisible); +} + +bool QAndroidPlatformMenuItem::isVisible() const +{ + return m_isVisible; +} + +void QAndroidPlatformMenuItem::setIsSeparator(bool isSeparator) +{ + m_isSeparator = isSeparator; +} + +bool QAndroidPlatformMenuItem::isSeparator() const +{ + return m_isSeparator; +} + +void QAndroidPlatformMenuItem::setFont(const QFont &font) +{ + Q_UNUSED(font) +} + +void QAndroidPlatformMenuItem::setRole(QPlatformMenuItem::MenuRole role) +{ + m_role = role; +} + +QPlatformMenuItem::MenuRole QAndroidPlatformMenuItem::role() const +{ + return m_role; +} + +void QAndroidPlatformMenuItem::setCheckable(bool checkable) +{ + m_isCheckable = checkable; +} + +bool QAndroidPlatformMenuItem::isCheckable() const +{ + return m_isCheckable; +} + +void QAndroidPlatformMenuItem::setChecked(bool isChecked) +{ + m_isChecked = isChecked; +} + +bool QAndroidPlatformMenuItem::isChecked() const +{ + return m_isChecked; +} + +void QAndroidPlatformMenuItem::setShortcut(const QKeySequence &shortcut) +{ + Q_UNUSED(shortcut) +} + +void QAndroidPlatformMenuItem::setEnabled(bool enabled) +{ + m_isEnabled = enabled; + if (m_menu) + m_menu->setEnabled(m_isEnabled); +} + +bool QAndroidPlatformMenuItem::isEnabled() const +{ + return m_isEnabled; +} diff --git a/src/plugins/platforms/android/src/qandroidplatformmenuitem.h b/src/plugins/platforms/android/src/qandroidplatformmenuitem.h new file mode 100644 index 0000000000..5861e8e195 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformmenuitem.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 QANDROIDPLATFORMMENUITEM_H +#define QANDROIDPLATFORMMENUITEM_H +#include <qpa/qplatformmenu.h> + +class QAndroidPlatformMenu; + +class QAndroidPlatformMenuItem: public QPlatformMenuItem +{ +public: + QAndroidPlatformMenuItem(); + void setTag(quintptr tag); + quintptr tag() const; + + void setText(const QString &text); + QString text() const; + + void setIcon(const QIcon &icon); + QIcon icon() const; + + void setMenu(QPlatformMenu *menu); + QAndroidPlatformMenu *menu() const; + + void setVisible(bool isVisible); + bool isVisible() const; + + void setIsSeparator(bool isSeparator); + bool isSeparator() const; + + void setFont(const QFont &font); + + void setRole(MenuRole role); + MenuRole role() const; + + void setCheckable(bool checkable); + bool isCheckable() const; + + void setChecked(bool isChecked); + bool isChecked() const; + + void setShortcut(const QKeySequence &shortcut); + + void setEnabled(bool enabled); + bool isEnabled() const; + +private: + quintptr m_tag; + QString m_text; + QIcon m_icon; + QAndroidPlatformMenu *m_menu; + bool m_isVisible; + bool m_isSeparator; + MenuRole m_role; + bool m_isCheckable; + bool m_isChecked; + bool m_isEnabled; +}; + +#endif // QANDROIDPLATFORMMENUITEM_H diff --git a/src/plugins/platforms/android/src/qandroidplatformservices.cpp b/src/plugins/platforms/android/src/qandroidplatformservices.cpp new file mode 100644 index 0000000000..841a9d4d51 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformservices.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "qandroidplatformservices.h" +#include <QUrl> +#include <QDir> +#include <QDebug> + +QAndroidPlatformServices::QAndroidPlatformServices() +{ + JNIEnv *env; + if (QtAndroid::javaVM()->AttachCurrentThread(&env, NULL) < 0) { + qCritical() << "AttachCurrentThread failed"; + return; + } + + m_openURIMethodID = env->GetStaticMethodID(QtAndroid::applicationClass(), + "openURL", + "(Ljava/lang/String;)V"); +} + +bool QAndroidPlatformServices::openUrl(const QUrl &url) +{ + JNIEnv *env; + if (QtAndroid::javaVM()->AttachCurrentThread(&env, NULL) < 0) { + qCritical() << "AttachCurrentThread failed"; + return false; + } + + jstring string = env->NewString(reinterpret_cast<const jchar *>(url.toString().constData()), + url.toString().length()); + env->CallStaticVoidMethod(QtAndroid::applicationClass(), m_openURIMethodID, string); + env->DeleteLocalRef(string); + return true; +} + +bool QAndroidPlatformServices::openDocument(const QUrl &url) +{ + return openUrl(url); +} + +QByteArray QAndroidPlatformServices::desktopEnvironment() const +{ + return QByteArray("Android"); +} diff --git a/src/plugins/platforms/android/src/qandroidplatformservices.h b/src/plugins/platforms/android/src/qandroidplatformservices.h new file mode 100644 index 0000000000..8368b19043 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformservices.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 ANDROIDPLATFORMDESKTOPSERVICE_H +#define ANDROIDPLATFORMDESKTOPSERVICE_H + +#include <qpa/qplatformservices.h> +#include "androidjnimain.h" +#include <jni.h> + +class QAndroidPlatformServices: public QPlatformServices +{ +public: + QAndroidPlatformServices(); + bool openUrl(const QUrl &url); + bool openDocument(const QUrl &url); + QByteArray desktopEnvironment() const; +private: + jmethodID m_openURIMethodID; + +}; + +#endif // ANDROIDPLATFORMDESKTOPSERVICE_H diff --git a/src/plugins/platforms/android/src/qandroidplatformtheme.cpp b/src/plugins/platforms/android/src/qandroidplatformtheme.cpp new file mode 100644 index 0000000000..25f2ade11a --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformtheme.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "qandroidplatformtheme.h" +#include "qandroidplatformmenubar.h" +#include "qandroidplatformmenu.h" +#include "qandroidplatformmenuitem.h" +#include <QVariant> +#include <QFileInfo> + +QPlatformMenuBar *QAndroidPlatformTheme::createPlatformMenuBar() const +{ + return new QAndroidPlatformMenuBar; +} + +QPlatformMenu *QAndroidPlatformTheme::createPlatformMenu() const +{ + return new QAndroidPlatformMenu; +} + +QPlatformMenuItem *QAndroidPlatformTheme::createPlatformMenuItem() const +{ + return new QAndroidPlatformMenuItem; +} + +QVariant QAndroidPlatformTheme::themeHint(ThemeHint hint) const +{ + switch (hint) { + case StyleNames: + if (qgetenv("QT_USE_ANDROID_NATIVE_STYLE").toInt() + && (!qgetenv("MINISTRO_ANDROID_STYLE_PATH").isEmpty() + || QFileInfo("/data/data/org.kde.necessitas.ministro/files/qt/style/style.json").exists())) { + return QStringList("android"); + } + return QStringList("fusion"); + break; + default: + return QPlatformTheme::themeHint(hint); + } +} diff --git a/src/plugins/platforms/android/src/qandroidplatformtheme.h b/src/plugins/platforms/android/src/qandroidplatformtheme.h new file mode 100644 index 0000000000..263878ee16 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformtheme.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 QANDROIDPLATFORMTHEME_H +#define QANDROIDPLATFORMTHEME_H + +#include <qpa/qplatformtheme.h> + +class QAndroidPlatformTheme: public QPlatformTheme +{ +public: + virtual QPlatformMenuBar *createPlatformMenuBar() const; + virtual QPlatformMenu *createPlatformMenu() const; + virtual QPlatformMenuItem *createPlatformMenuItem() const; + virtual QVariant themeHint(ThemeHint hint) const; +}; + +#endif // QANDROIDPLATFORMTHEME_H diff --git a/src/plugins/platforms/android/src/raster/qandroidplatformscreen.cpp b/src/plugins/platforms/android/src/raster/qandroidplatformscreen.cpp new file mode 100644 index 0000000000..2779d7cffd --- /dev/null +++ b/src/plugins/platforms/android/src/raster/qandroidplatformscreen.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "qandroidplatformscreen.h" +#include "qandroidplatformintegration.h" +#include "androidjnimain.h" +#include "androidjnimenu.h" + +QAndroidPlatformScreen::QAndroidPlatformScreen():QFbScreen() +{ + mGeometry = QRect(0, 0, QAndroidPlatformIntegration::m_defaultGeometryWidth, QAndroidPlatformIntegration::m_defaultGeometryHeight); + mFormat = QImage::Format_RGB16; + mDepth = 16; + mPhysicalSize.setHeight(QAndroidPlatformIntegration::m_defaultPhysicalSizeHeight); + mPhysicalSize.setWidth(QAndroidPlatformIntegration::m_defaultPhysicalSizeWidth); + initializeCompositor(); +} + +void QAndroidPlatformScreen::topWindowChanged(QWindow *w) +{ + QtAndroidMenu::setActiveTopLevelWindow(w); +} + +QRegion QAndroidPlatformScreen::doRedraw() +{ + QRegion touched; + touched = QFbScreen::doRedraw(); + if (touched.isEmpty()) + return touched; + + QtAndroid::flushImage(mGeometry.topLeft(), *mScreenImage, touched.boundingRect()); + return touched; +} diff --git a/src/plugins/platforms/android/src/raster/qandroidplatformscreen.h b/src/plugins/platforms/android/src/raster/qandroidplatformscreen.h new file mode 100644 index 0000000000..df08e43af4 --- /dev/null +++ b/src/plugins/platforms/android/src/raster/qandroidplatformscreen.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 QANDROIDPLATFORMSCREEN_H +#define QANDROIDPLATFORMSCREEN_H + +#include <QtPlatformSupport/private/qfbscreen_p.h> + +class QAndroidPlatformScreen: public QFbScreen +{ + Q_OBJECT +public: + QAndroidPlatformScreen(); + void topWindowChanged(QWindow *w); + +public slots: + QRegion doRedraw(); + +}; + +#endif diff --git a/src/plugins/platforms/android/src/raster/qandroidplatformwindow.cpp b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.cpp new file mode 100644 index 0000000000..94a69c10c7 --- /dev/null +++ b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 "qandroidplatformwindow.h" + +QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window) : QFbWindow(window) +{ +} + +void QAndroidPlatformWindow::setGeometry(const QRect &rect) +{ + QFbWindow::setGeometry(rect); +} + +void QAndroidPlatformWindow::propagateSizeHints() +{ + //shut up warning from default implementation +} diff --git a/src/plugins/platforms/android/src/raster/qandroidplatformwindow.h b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.h new file mode 100644 index 0000000000..3ee815fd69 --- /dev/null +++ b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** 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 ANDROIDPLATFORMWINDOW_H +#define ANDROIDPLATFORMWINDOW_H +#include <qobject.h> +#include <QtPlatformSupport/private/qfbwindow_p.h> + +class QAndroidPlatformWindow: public QObject, public QFbWindow +{ + Q_OBJECT +public: + explicit QAndroidPlatformWindow(QWindow *window); + + void propagateSizeHints(); + +public slots: + void setGeometry(const QRect &rect); + +}; + +#endif // ANDROIDPLATFORMWINDOW_H diff --git a/src/plugins/platforms/android/src/raster/raster.pri b/src/plugins/platforms/android/src/raster/raster.pri new file mode 100644 index 0000000000..86e5aa235f --- /dev/null +++ b/src/plugins/platforms/android/src/raster/raster.pri @@ -0,0 +1,7 @@ +INCLUDEPATH += $$PWD + +SOURCES += $$PWD/qandroidplatformscreen.cpp \ + $$PWD/qandroidplatformwindow.cpp + +HEADERS += $$PWD/qandroidplatformscreen.h \ + $$PWD/qandroidplatformwindow.h diff --git a/src/plugins/platforms/android/src/src.pri b/src/plugins/platforms/android/src/src.pri new file mode 100644 index 0000000000..9bf36b2337 --- /dev/null +++ b/src/plugins/platforms/android/src/src.pri @@ -0,0 +1,47 @@ +load(qt_plugin) + +QT += core-private gui-private widgets-private platformsupport-private + +CONFIG += qpa/genericunixfontdatabase + +OTHER_FILES += $$PWD/android.json + +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../../../3rdparty/android/src + +SOURCES += $$PWD/androidplatformplugin.cpp \ + $$PWD/androidjnimain.cpp \ + $$PWD/androidjniinput.cpp \ + $$PWD/androidjnimenu.cpp \ + $$PWD/androidjniclipboard.cpp \ + $$PWD/qandroidplatformintegration.cpp \ + $$PWD/qandroidplatformservices.cpp \ + $$PWD/qandroidassetsfileenginehandler.cpp \ + $$PWD/qandroidinputcontext.cpp \ + $$PWD/qandroidplatformfontdatabase.cpp \ + $$PWD/qandroidplatformclipboard.cpp \ + $$PWD/qandroidplatformtheme.cpp \ + $$PWD/qandroidplatformmenubar.cpp \ + $$PWD/qandroidplatformmenu.cpp \ + $$PWD/qandroidplatformmenuitem.cpp + + +HEADERS += $$PWD/qandroidplatformintegration.h \ + $$PWD/androidjnimain.h \ + $$PWD/androidjniinput.h \ + $$PWD/androidjnimenu.h \ + $$PWD/androidjniclipboard.h \ + $$PWD/qandroidplatformservices.h \ + $$PWD/qandroidassetsfileenginehandler.h \ + $$PWD/qandroidinputcontext.h \ + $$PWD/qandroidplatformfontdatabase.h \ + $$PWD/qandroidplatformclipboard.h \ + $$PWD/qandroidplatformtheme.h \ + $$PWD/qandroidplatformmenubar.h \ + $$PWD/qandroidplatformmenu.h \ + $$PWD/qandroidplatformmenuitem.h + + +#Non-standard install directory, QTBUG-29859 +DESTDIR = $$DESTDIR/android +target.path = $${target.path}/android diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index 83e2a88e6a..6ed26f9e6c 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -78,7 +78,7 @@ HEADERS += qcocoaintegration.h \ RESOURCES += qcocoaresources.qrc -LIBS += -framework Cocoa -framework IOKit +LIBS += -framework Cocoa -framework Carbon -framework IOKit QT += core-private gui-private platformsupport-private diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index 070f23f028..adeb423cf9 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -41,7 +41,7 @@ #include "qcocoaaccessibility.h" #include "qcocoaaccessibilityelement.h" #include <qaccessible.h> -#include <qaccessible2.h> +#include <QtGui/private/qaccessible2_p.h> #include <private/qcore_mac_p.h> #ifndef QT_NO_COCOA_ACCESSIBILITY diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h index 7ebe7a4e9d..3b1fbe042d 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h @@ -41,6 +41,7 @@ #ifndef QCOCOAACCESIBILITYELEMENT_H #define QCOCOAACCESIBILITYELEMENT_H +#include <QtCore/qglobal.h> #import <Cocoa/Cocoa.h> #import <AppKit/NSAccessibility.h> diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index f5f37c5b00..df496a413b 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -43,7 +43,7 @@ #include "qcocoahelpers.h" #include <QAccessible> -#include "QAccessibleActionInterface" +#include <QtGui/private/qaccessible2_p.h> #import <AppKit/NSAccessibility.h> diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h index 6dd7ea2fb3..e44b2d1b6d 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h @@ -93,19 +93,6 @@ @class QT_MANGLE_NAMESPACE(QCocoaMenuLoader); -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 - -@protocol NSApplicationDelegate <NSObject> -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification; -- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames; -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender; -- (void)applicationDidBecomeActive:(NSNotification *)notification; -- (void)applicationDidResignActive:(NSNotification *)notification; -@end - -#endif - @interface QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) : NSObject <NSApplicationDelegate> { bool startedQuit; NSMenu *dockMenu; diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 7f022da4c3..4881dcef71 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -73,7 +73,9 @@ QPaintDevice *QCocoaBackingStore::paintDevice() } #endif - m_qImage = QImage(m_requestedSize * scaleFactor, QImage::Format_ARGB32_Premultiplied); + QImage::Format format = window()->format().hasAlpha() + ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; + m_qImage = QImage(m_requestedSize * scaleFactor, format); m_qImage.setDevicePixelRatio(scaleFactor); } return &m_qImage; @@ -90,8 +92,10 @@ void QCocoaBackingStore::flush(QWindow *win, const QRegion ®ion, const QPoint // m_cgImage is only a reference to the data inside m_qImage, it is not a copy. CGImageRelease(m_cgImage); m_cgImage = 0; - if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(win->handle())) - [cocoaWindow->m_contentView flushBackingStore:this region:region offset:offset]; + if (!m_qImage.isNull()) { + if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(win->handle())) + [cocoaWindow->m_qtView flushBackingStore:this region:region offset:offset]; + } } void QCocoaBackingStore::resize(const QSize &size, const QRegion &) diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm index a36a855bcd..12808b7041 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.mm +++ b/src/plugins/platforms/cocoa/qcocoacursor.mm @@ -65,8 +65,9 @@ void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window) { Q_UNUSED(window); + const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; // Check for a suitable built-in NSCursor first: - switch (cursor->shape()) { + switch (newShape) { case Qt::ArrowCursor: [[NSCursor arrowCursor] set]; break; @@ -104,7 +105,7 @@ void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window) default : { // No suitable OS cursor exist, use cursors provided // by Qt for the rest. Check for a cached cursor: - NSCursor *cocoaCursor = m_cursors.value(cursor->shape()); + NSCursor *cocoaCursor = m_cursors.value(newShape); if (cocoaCursor && cursor->shape() == Qt::BitmapCursor) { [cocoaCursor release]; cocoaCursor = 0; @@ -115,7 +116,7 @@ void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window) [[NSCursor arrowCursor] set]; return; } - m_cursors.insert(cursor->shape(), cocoaCursor); + m_cursors.insert(newShape, cocoaCursor); } [cocoaCursor set]; diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h index f63ac0d205..93476ee1b4 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h @@ -93,6 +93,7 @@ #include <QtGui/qwindowdefs.h> #include <QtCore/private/qabstracteventdispatcher_p.h> #include <QtCore/private/qtimerinfo_unix_p.h> +#include <QtPlatformSupport/private/qcfsocketnotifier_p.h> #include <CoreFoundation/CoreFoundation.h> @@ -132,16 +133,9 @@ public: void wakeUp(); void interrupt(); void flush(); -}; -struct MacSocketInfo { - MacSocketInfo() : socket(0), runloop(0), readNotifier(0), writeNotifier(0) {} - CFSocketRef socket; - CFRunLoopSourceRef runloop; - QObject *readNotifier; - QObject *writeNotifier; + friend void qt_mac_maybeCancelWaitForMoreEventsForwarder(QAbstractEventDispatcher *eventDispatcher); }; -typedef QHash<int, MacSocketInfo *> MacSocketHash; class QCocoaEventDispatcherPrivate : public QAbstractEventDispatcherPrivate { @@ -183,7 +177,7 @@ public: void maybeCancelWaitForMoreEvents(); void ensureNSAppInitialized(); - MacSocketHash macSockets; + QCFSocketNotifier cfSocketNotifier; QList<void *> queuedUserInputEvents; // NSEvent * CFRunLoopSourceRef postedEventsSource; CFRunLoopObserverRef waitingObserver; diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index a0734530c5..5c487b0bdd 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -149,10 +149,10 @@ void QCocoaEventDispatcherPrivate::maybeStartCFRunLoopTimer() CFTimeInterval oneyear = CFTimeInterval(3600. * 24. * 365.); // Q: when should the CFRunLoopTimer fire for the first time? - struct timeval tv; + struct timespec tv; if (timerInfoList.timerWait(tv)) { // A: when we have timers to fire, of course - interval = qMax(tv.tv_sec + tv.tv_usec / 1000000., 0.0000001); + interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); } else { // this shouldn't really happen, but in case it does, set the timer to fire a some point in the distant future interval = oneyear; @@ -172,10 +172,10 @@ void QCocoaEventDispatcherPrivate::maybeStartCFRunLoopTimer() CFTimeInterval interval; // Q: when should the timer first next? - struct timeval tv; + struct timespec tv; if (timerInfoList.timerWait(tv)) { // A: when we have timers to fire, of course - interval = qMax(tv.tv_sec + tv.tv_usec / 1000000., 0.0000001); + interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); } else { // no timers can fire, but we cannot stop the CFRunLoopTimer, set the timer to fire at some // point in the distant future (the timer interval is one year) @@ -270,58 +270,6 @@ QCocoaEventDispatcher::registeredTimers(QObject *object) const return d->timerInfoList.registeredTimers(object); } -/************************************************************************** - Socket Notifiers - *************************************************************************/ -void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef, - const void *, void *info) { - QCocoaEventDispatcherPrivate *const eventDispatcher - = static_cast<QCocoaEventDispatcherPrivate *>(info); - int nativeSocket = CFSocketGetNative(s); - MacSocketInfo *socketInfo = eventDispatcher->macSockets.value(nativeSocket); - QEvent notifierEvent(QEvent::SockAct); - - // There is a race condition that happen where we disable the notifier and - // the kernel still has a notification to pass on. We then get this - // notification after we've successfully disabled the CFSocket, but our Qt - // notifier is now gone. The upshot is we have to check the notifier - // everytime. - if (callbackType == kCFSocketReadCallBack) { - if (socketInfo->readNotifier) - QGuiApplication::sendEvent(socketInfo->readNotifier, ¬ifierEvent); - } else if (callbackType == kCFSocketWriteCallBack) { - if (socketInfo->writeNotifier) - QGuiApplication::sendEvent(socketInfo->writeNotifier, ¬ifierEvent); - } - - eventDispatcher->maybeCancelWaitForMoreEvents(); -} - -/* - Adds a loop source for the given socket to the current run loop. -*/ -CFRunLoopSourceRef qt_mac_add_socket_to_runloop(const CFSocketRef socket) -{ - CFRunLoopSourceRef loopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0); - if (!loopSource) - return 0; - - CFRunLoopAddSource(mainRunLoop(), loopSource, kCFRunLoopCommonModes); - return loopSource; -} - -/* - Removes the loop source for the given socket from the current run loop. -*/ -void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSourceRef runloop) -{ - Q_ASSERT(runloop); - CFRunLoopRemoveSource(mainRunLoop(), runloop, kCFRunLoopCommonModes); - CFSocketDisableCallBacks(socket, kCFSocketReadCallBack); - CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack); - CFRunLoopSourceInvalidate(runloop); -} - /* Register a QSocketNotifier with the mac event system by creating a CFSocket with with a read/write callback. @@ -331,130 +279,14 @@ void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSource */ void QCocoaEventDispatcher::registerSocketNotifier(QSocketNotifier *notifier) { - Q_ASSERT(notifier); - int nativeSocket = notifier->socket(); - int type = notifier->type(); -#ifndef QT_NO_DEBUG - if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) { - qWarning("QSocketNotifier: Internal error"); - return; - } else if (notifier->thread() != thread() - || thread() != QThread::currentThread()) { - qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); - return; - } -#endif - Q_D(QCocoaEventDispatcher); - - if (type == QSocketNotifier::Exception) { - qWarning("QSocketNotifier::Exception is not supported on Mac OS X"); - return; - } - - // Check if we have a CFSocket for the native socket, create one if not. - MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket); - if (!socketInfo) { - socketInfo = new MacSocketInfo(); - - // Create CFSocket, specify that we want both read and write callbacks (the callbacks - // are enabled/disabled later on). - const int callbackTypes = kCFSocketReadCallBack | kCFSocketWriteCallBack; - CFSocketContext context = {0, d, 0, 0, 0}; - socketInfo->socket = CFSocketCreateWithNative(kCFAllocatorDefault, nativeSocket, callbackTypes, qt_mac_socket_callback, &context); - if (CFSocketIsValid(socketInfo->socket) == false) { - qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to create CFSocket"); - return; - } - - CFOptionFlags flags = CFSocketGetSocketFlags(socketInfo->socket); - flags |= kCFSocketAutomaticallyReenableWriteCallBack; //QSocketNotifier stays enabled after a write - flags &= ~kCFSocketCloseOnInvalidate; //QSocketNotifier doesn't close the socket upon destruction/invalidation - CFSocketSetSocketFlags(socketInfo->socket, flags); - - // Add CFSocket to runloop. - if(!(socketInfo->runloop = qt_mac_add_socket_to_runloop(socketInfo->socket))) { - qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to add CFSocket to runloop"); - CFSocketInvalidate(socketInfo->socket); - CFRelease(socketInfo->socket); - return; - } - - // Disable both callback types by default. This must be done after - // we add the CFSocket to the runloop, or else these calls will have - // no effect. - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); - - d->macSockets.insert(nativeSocket, socketInfo); - } - - // Increment read/write counters and select enable callbacks if necessary. - if (type == QSocketNotifier::Read) { - Q_ASSERT(socketInfo->readNotifier == 0); - socketInfo->readNotifier = notifier; - CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack); - } else if (type == QSocketNotifier::Write) { - Q_ASSERT(socketInfo->writeNotifier == 0); - socketInfo->writeNotifier = notifier; - CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); - } + d->cfSocketNotifier.registerSocketNotifier(notifier); } -/* - Unregister QSocketNotifer. The CFSocket correspoding to this notifier is - removed from the runloop of this is the last notifier that users - that CFSocket. -*/ void QCocoaEventDispatcher::unregisterSocketNotifier(QSocketNotifier *notifier) { - Q_ASSERT(notifier); - int nativeSocket = notifier->socket(); - int type = notifier->type(); -#ifndef QT_NO_DEBUG - if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) { - qWarning("QSocketNotifier: Internal error"); - return; - } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) { - qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); - return; - } -#endif - Q_D(QCocoaEventDispatcher); - - if (type == QSocketNotifier::Exception) { - qWarning("QSocketNotifier::Exception is not supported on Mac OS X"); - return; - } - MacSocketInfo *socketInfo = d->macSockets.value(nativeSocket); - if (!socketInfo) { - qWarning("QEventDispatcherMac::unregisterSocketNotifier: Tried to unregister a not registered notifier"); - return; - } - - // Decrement read/write counters and disable callbacks if necessary. - if (type == QSocketNotifier::Read) { - Q_ASSERT(notifier == socketInfo->readNotifier); - socketInfo->readNotifier = 0; - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack); - } else if (type == QSocketNotifier::Write) { - Q_ASSERT(notifier == socketInfo->writeNotifier); - socketInfo->writeNotifier = 0; - CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack); - } - - // Remove CFSocket from runloop if this was the last QSocketNotifier. - if (socketInfo->readNotifier == 0 && socketInfo->writeNotifier == 0) { - if (CFSocketIsValid(socketInfo->socket)) - qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop); - CFRunLoopSourceInvalidate(socketInfo->runloop); - CFRelease(socketInfo->runloop); - CFSocketInvalidate(socketInfo->socket); - CFRelease(socketInfo->socket); - delete socketInfo; - d->macSockets.remove(nativeSocket); - } + d->cfSocketNotifier.unregisterSocketNotifier(notifier); } bool QCocoaEventDispatcher::hasPendingEvents() @@ -467,37 +299,35 @@ bool QCocoaEventDispatcher::hasPendingEvents() static bool IsMouseOrKeyEvent( NSEvent* event ) { bool result = false; - + switch( [event type] ) { - case NSLeftMouseDown: - case NSLeftMouseUp: - case NSRightMouseDown: - case NSRightMouseUp: + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSRightMouseDown: + case NSRightMouseUp: case NSMouseMoved: // ?? - case NSLeftMouseDragged: + case NSLeftMouseDragged: case NSRightMouseDragged: - case NSMouseEntered: - case NSMouseExited: - case NSKeyDown: - case NSKeyUp: + case NSMouseEntered: + case NSMouseExited: + case NSKeyDown: + case NSKeyUp: case NSFlagsChanged: // key modifiers changed? case NSCursorUpdate: // ?? - case NSScrollWheel: - case NSTabletPoint: - case NSTabletProximity: - case NSOtherMouseDown: - case NSOtherMouseUp: + case NSScrollWheel: + case NSTabletPoint: + case NSTabletProximity: + case NSOtherMouseDown: + case NSOtherMouseUp: case NSOtherMouseDragged: #ifndef QT_NO_GESTURES -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 case NSEventTypeGesture: // touch events case NSEventTypeMagnify: case NSEventTypeSwipe: case NSEventTypeRotate: case NSEventTypeBeginGesture: case NSEventTypeEndGesture: -#endif #endif // QT_NO_GESTURES result = true; break; @@ -809,7 +639,7 @@ static void setChildrenWorksWhenModal(QWindow *window, bool worksWhenModal) Q_UNUSED(worksWhenModal) // For NSPanels (but not NSWindows, sadly), we can set the flag - // worksWhenModal, so that they are active even when they are not modal. + // worksWhenModal, so that they are active even when they are not modal. /* ### not ported QList<QDialog *> dialogs = window->findChildren<QDialog *>(); @@ -850,7 +680,7 @@ void QCocoaEventDispatcherPrivate::cleanupModalSessions() // this to actually end the sessions for real (rather than at the // point they were marked as stopped), is that ending a session // when no other session runs below it on the stack will make cocoa - // drop some events on the floor. + // drop some events on the floor. QCocoaAutoReleasePool pool; int stackSize = cocoaModalSessionStack.size(); @@ -942,11 +772,19 @@ QCocoaEventDispatcherPrivate::QCocoaEventDispatcherPrivate() { } +void qt_mac_maybeCancelWaitForMoreEventsForwarder(QAbstractEventDispatcher *eventDispatcher) +{ + static_cast<QCocoaEventDispatcher *>(eventDispatcher)->d_func()->maybeCancelWaitForMoreEvents(); +} + QCocoaEventDispatcher::QCocoaEventDispatcher(QObject *parent) : QAbstractEventDispatcher(*new QCocoaEventDispatcherPrivate, parent) { Q_D(QCocoaEventDispatcher); + d->cfSocketNotifier.setHostEventDispatcher(this); + d->cfSocketNotifier.setMaybeCancelWaitForMoreEventsCallback(qt_mac_maybeCancelWaitForMoreEventsForwarder); + // keep our sources running when modal loops are running CFRunLoopAddCommonMode(mainRunLoop(), (CFStringRef) NSModalPanelRunLoopMode); @@ -1021,7 +859,7 @@ void QCocoaEventDispatcherPrivate::processPostedEvents() if (currentExecIsNSAppRun) { // The event dispatcher has been interrupted. But since // [NSApplication run] is running the event loop, we - // delayed stopping it until now (to let cocoa process + // delayed stopping it until now (to let cocoa process // pending cocoa events first). if (currentModalSessionCached) temporarilyStopAllModalSessions(); @@ -1129,17 +967,8 @@ QCocoaEventDispatcher::~QCocoaEventDispatcher() [nsevent release]; } - // Remove CFSockets from the runloop. - for (MacSocketHash::ConstIterator it = d->macSockets.constBegin(); it != d->macSockets.constEnd(); ++it) { - MacSocketInfo *socketInfo = (*it); - if (CFSocketIsValid(socketInfo->socket)) { - qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop); - CFRunLoopSourceInvalidate(socketInfo->runloop); - CFRelease(socketInfo->runloop); - CFSocketInvalidate(socketInfo->socket); - CFRelease(socketInfo->socket); - } - } + d->cfSocketNotifier.removeSocketNotifiers(); + CFRunLoopRemoveSource(mainRunLoop(), d->postedEventsSource, kCFRunLoopCommonModes); CFRelease(d->postedEventsSource); diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index 61646041fb..9cc0353dc6 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -77,11 +77,7 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions; @class QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate); @interface QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 : NSObject<NSOpenSavePanelDelegate> -#else - : NSObject -#endif { @public NSOpenPanel *mOpenPanel; @@ -216,7 +212,7 @@ static QString strippedText(QString s) - (void)closePanel { *mCurrentSelection = QT_PREPEND_NAMESPACE(QCFString::toQString)([[mSavePanel URL] path]); - if ([mSavePanel respondsToSelector:@selector(closePanel:)]) + if ([mSavePanel respondsToSelector:@selector(close)]) [mSavePanel close]; if ([mSavePanel isSheet]) [NSApp endSheet: mSavePanel]; @@ -230,6 +226,7 @@ static QString strippedText(QString s) bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave) || [self panel:nil shouldShowFilename:filepath]; + [self updateProperties]; [mOpenPanel setAllowedFileTypes:nil]; [mOpenPanel setDirectoryURL:selectable ? [NSURL fileURLWithPath:QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.filePath())] : [NSURL fileURLWithPath:QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.path())]]; @@ -274,6 +271,7 @@ static QString strippedText(QString s) bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave) || [self panel:nil shouldShowFilename:filepath]; + [self updateProperties]; [mSavePanel setDirectoryURL:selectable ? [NSURL fileURLWithPath:QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.filePath())] : [NSURL fileURLWithPath:QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.path())]]; [mSavePanel setNameFieldStringValue:selectable ? QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.fileName()) : nil]; @@ -405,6 +403,9 @@ static QString strippedText(QString s) [mSavePanel setCanCreateDirectories:!(mOptions->testOption(QT_PREPEND_NAMESPACE(QFileDialogOptions::ReadOnly)))]; [mOpenPanel setAllowsMultipleSelection:(fileMode == QT_PREPEND_NAMESPACE(QFileDialogOptions::ExistingFiles))]; [mOpenPanel setResolvesAliases:!(mOptions->testOption(QT_PREPEND_NAMESPACE(QFileDialogOptions::DontResolveSymlinks)))]; + [mOpenPanel setTitle:QCFString::toNSString(mOptions->windowTitle())]; + [mSavePanel setTitle:QCFString::toNSString(mOptions->windowTitle())]; + [mPopUpButton setHidden:chooseDirsOnly]; // TODO hide the whole sunken pane instead? QStringList ext = [self acceptableExtensionsForSave]; const QString defaultSuffix = mOptions->defaultSuffix(); @@ -577,13 +578,16 @@ extern void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding void QCocoaFileDialogHelper::setDirectory(const QString &directory) { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); - [delegate->mSavePanel setDirectoryURL:[NSURL fileURLWithPath:QCFString::toNSString(directory)]]; + if (delegate) + [delegate->mSavePanel setDirectoryURL:[NSURL fileURLWithPath:QCFString::toNSString(directory)]]; } QString QCocoaFileDialogHelper::directory() const { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); - return QCFString::toQString([delegate->mSavePanel directory]); + if (delegate) + return QCFString::toQString([delegate->mSavePanel directory]); + return QString(); } void QCocoaFileDialogHelper::selectFile(const QString &filename) @@ -600,12 +604,16 @@ void QCocoaFileDialogHelper::selectFile(const QString &filename) QStringList QCocoaFileDialogHelper::selectedFiles() const { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); - return [delegate selectedFiles]; + if (delegate) + return [delegate selectedFiles]; + return QStringList(); } void QCocoaFileDialogHelper::setFilter() { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + if (!delegate) + return; const SharedPointerFileDialogOptions &opts = options(); [delegate->mSavePanel setTitle:QCFString::toNSString(opts->windowTitle())]; if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) @@ -618,9 +626,13 @@ void QCocoaFileDialogHelper::setFilter() void QCocoaFileDialogHelper::selectNameFilter(const QString &filter) { + if (!options()) + return; const int index = options()->nameFilters().indexOf(filter); if (index != -1) { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + if (!delegate) + return; [delegate->mPopUpButton selectItemAtIndex:index]; [delegate filterChanged:nil]; } @@ -629,7 +641,11 @@ void QCocoaFileDialogHelper::selectNameFilter(const QString &filter) QString QCocoaFileDialogHelper::selectedNameFilter() const { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + if (!delegate) + return QString(); int index = [delegate->mPopUpButton indexOfSelectedItem]; + if (index >= options()->nameFilters().count()) + return QString(); return index != -1 ? options()->nameFilters().at(index) : QString(); } @@ -675,6 +691,8 @@ bool QCocoaFileDialogHelper::showCocoaFilePanel(Qt::WindowModality windowModalit { createNSOpenSavePanelDelegate(); QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + if (!delegate) + return false; if (windowModality == Qt::NonModal) [delegate showModelessPanel]; else if (windowModality == Qt::WindowModal && parent) diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index d0f5a58e22..d4673baaef 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -49,11 +49,19 @@ #import <Cocoa/Cocoa.h> QCocoaGLContext::QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share) - : m_format(format) + : m_context(nil), + m_shareContext(nil), + m_format(format) { + // we only support OpenGL contexts under Cocoa + if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType) + m_format.setRenderableType(QSurfaceFormat::OpenGL); + if (m_format.renderableType() != QSurfaceFormat::OpenGL) + return; + QCocoaAutoReleasePool pool; // For the SG Canvas render thread - NSOpenGLPixelFormat *pixelFormat = static_cast <NSOpenGLPixelFormat *>(qcgl_createNSOpenGLPixelFormat(format)); + NSOpenGLPixelFormat *pixelFormat = static_cast <NSOpenGLPixelFormat *>(qcgl_createNSOpenGLPixelFormat(m_format)); m_shareContext = share ? static_cast<QCocoaGLContext *>(share)->nsOpenGLContext() : nil; m_context = [NSOpenGLContext alloc]; @@ -70,6 +78,10 @@ QCocoaGLContext::QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLCo const GLint interval = 1; [m_context setValues:&interval forParameter:NSOpenGLCPSwapInterval]; + if (format.alphaBufferSize() > 0) { + int zeroOpacity = 0; + [m_context setValues:&zeroOpacity forParameter:NSOpenGLCPSurfaceOpacity]; + } } QCocoaGLContext::~QCocoaGLContext() diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 831ab579f5..c801d9d926 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -84,8 +84,6 @@ HIMutableShapeRef qt_mac_QRegionToHIMutableShape(const QRegion ®ion); OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage); -CGFloat qt_mac_get_scalefactor(); - QChar qt_mac_qtKey2CocoaKey(Qt::Key key); Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 20702c6837..5ec2cea362 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -47,6 +47,7 @@ #include <QtGui> #include <qpa/qplatformscreen.h> #include <private/qguiapplication_p.h> +#include <private/qwindow_p.h> #ifndef QT_NO_WIDGETS #include <QtWidgets/QWidget> @@ -474,11 +475,9 @@ CGColorSpaceRef qt_mac_genericColorSpace() { #if 0 if (!m_genericColorSpace) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) { m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); } else -#endif { m_genericColorSpace = CGColorSpaceCreateDeviceRGB(); } @@ -586,10 +585,28 @@ QString qt_mac_applicationName() return appName; } +/* + Mac window coordinates are in the first quadrant: 0, 0 is at the lower-left + corner of the primary screen. This function converts the given rect to an + NSRect for the window geometry, flipping from 4th quadrant to 1st quadrant + and simultaneously ensuring that as much of the window as possible will be + onscreen. If the rect is too tall for the screen, the OS will reduce the + window's height anyway; but by moving the window upwards we can have more + of it onscreen. But the application can still control the y coordinate + in case it really wants the window to be positioned partially offscreen. +*/ NSRect qt_mac_flipRect(const QRect &rect, QWindow *window) { QPlatformScreen *onScreen = QPlatformScreen::platformScreenForWindow(window); - int flippedY = onScreen->geometry().height() - rect.y() - rect.height(); + int flippedY = onScreen->geometry().height() - (rect.y() + rect.height()); + + // In case of automatic positioning, try to put as much of the window onscreen as possible. + if (window->isTopLevel() && qt_window_private(const_cast<QWindow*>(window))->positionAutomatic && flippedY < 0) + flippedY = onScreen->geometry().height() - onScreen->availableGeometry().height() - onScreen->availableGeometry().y(); +#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG + qDebug() << Q_FUNC_INFO << rect << "flippedY" << flippedY << + "screen" << onScreen->geometry() << "available" << onScreen->availableGeometry(); +#endif return NSMakeRect(rect.x(), flippedY, rect.width(), rect.height()); } @@ -615,11 +632,6 @@ InvalidContext: return err; } -CGFloat qt_mac_get_scalefactor() -{ - return [[NSScreen mainScreen] userSpaceScaleFactor]; -} - Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum) { if (buttonNum == 0) diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 7fcdab4d97..d0fcf93b8c 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -105,7 +105,9 @@ void QCocoaScreen::updateGeometry() m_physicalSize = QSizeF(size.width, size.height); m_logicalDpi.first = 72; m_logicalDpi.second = 72; - m_refreshRate = CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(dpy)); + float refresh = CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(dpy)); + if (refresh > 0) + m_refreshRate = refresh; // Get m_name (brand/model of the monitor) NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(dpy), kIODisplayOnlyPreferredName); @@ -170,13 +172,13 @@ QPixmap QCocoaScreen::grabWindow(WId window, int x, int y, int width, int height windowSize.setHeight(windowRect.height()); } - QPixmap windowPixmap(windowSize); + QPixmap windowPixmap(windowSize * devicePixelRatio()); windowPixmap.fill(Qt::transparent); for (uint i = 0; i < displayCount; ++i) { const CGRect bounds = CGDisplayBounds(displays[i]); - int w = (width < 0 ? bounds.size.width : width); - int h = (height < 0 ? bounds.size.height : height); + int w = (width < 0 ? bounds.size.width : width) * devicePixelRatio(); + int h = (height < 0 ? bounds.size.height : height) * devicePixelRatio(); QRect displayRect = QRect(x, y, w, h); QCFType<CGImageRef> image = CGDisplayCreateImageForRect(displays[i], CGRectMake(displayRect.x(), displayRect.y(), displayRect.width(), displayRect.height())); diff --git a/src/plugins/platforms/cocoa/qcocoaintrospection.mm b/src/plugins/platforms/cocoa/qcocoaintrospection.mm index ffb6ae4294..806effc929 100644 --- a/src/plugins/platforms/cocoa/qcocoaintrospection.mm +++ b/src/plugins/platforms/cocoa/qcocoaintrospection.mm @@ -83,7 +83,6 @@ void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class prox if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) #endif { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 // The following code replaces the _implementation_ for the selector we want to hack // (originalSel) with the implementation found in proxyClass. Then it creates // a new 'backup' method inside baseClass containing the old, original, @@ -104,7 +103,6 @@ void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class prox Method backupMethod = class_getInstanceMethod(proxyClass, backupSel); class_addMethod(baseClass, backupSel, originalImp, method_getTypeEncoding(backupMethod)); } -#endif } } @@ -114,11 +112,9 @@ void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL b if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) #endif { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 Method originalMethod = class_getInstanceMethod(baseClass, originalSel); Method backupMethodInBaseClass = class_getInstanceMethod(baseClass, backupSel); method_setImplementation(originalMethod, method_getImplementation(backupMethodInBaseClass)); -#endif } } diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h index 09b62a9bcd..439b7f1a75 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.h +++ b/src/plugins/platforms/cocoa/qcocoamenu.h @@ -51,8 +51,6 @@ @class NSMenu; @class NSObject; -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QCocoaMenu : public QPlatformMenu @@ -71,11 +69,17 @@ public: void syncMenuItem(QPlatformMenuItem *menuItem); void setEnabled(bool enabled); void setVisible(bool visible); + void showPopup(const QWindow *parentWindow, QPoint pos, const QPlatformMenuItem *item); + void syncSeparatorsCollapsible(bool enable); void syncModalState(bool modal); - virtual void setText(const QString &text); + virtual void setIcon(const QIcon &icon) { Q_UNUSED(icon) } + + void setText(const QString &text); + void setMinimumWidth(int width); + void setFont(const QFont &font); void setParentItem(QCocoaMenuItem* item); @@ -102,6 +106,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index 676f0683fa..0fe4c48510 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -47,6 +47,8 @@ #include <QtCore/QtDebug> #include "qcocoaapplication.h" #include "qcocoamenuloader.h" +#include "qcocoawindow.h" +#import "qnsview.h" static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader() { @@ -133,6 +135,20 @@ void QCocoaMenu::setText(const QString &text) [m_nativeItem setTitle:QCFString::toNSString(stripped)]; } +void QCocoaMenu::setMinimumWidth(int width) +{ + m_nativeMenu.minimumWidth = width; +} + +void QCocoaMenu::setFont(const QFont &font) +{ + if (font.resolve()) { + NSFont *customMenuFont = [NSFont fontWithName:QCFString::toNSString(font.family()) + size:font.pointSize()]; + m_nativeMenu.font = customMenuFont; + } +} + void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) { QCocoaAutoReleasePool pool; @@ -290,9 +306,26 @@ void QCocoaMenu::setVisible(bool visible) [m_nativeItem setSubmenu:(visible ? m_nativeMenu : nil)]; } +void QCocoaMenu::showPopup(const QWindow *parentWindow, QPoint pos, const QPlatformMenuItem *item) +{ + QCocoaWindow *cocoaWindow = parentWindow ? static_cast<QCocoaWindow *>(parentWindow->handle()) : 0; + NSView *view = cocoaWindow ? cocoaWindow->contentView() : nil; + NSMenuItem *nsItem = item ? ((QCocoaMenuItem *)item)->nsItem() : nil; + NSPoint nsPos = NSMakePoint(pos.x(), pos.y()); + [m_nativeMenu popUpMenuPositioningItem:nsItem atLocation:nsPos inView:view]; + + // The call above blocks, and also swallows any mouse release event, + // so we need to clear any mouse button that triggered the menu popup. + if ([view isKindOfClass:[QNSView class]]) + [(QNSView *)view resetMouseButtons]; +} + QPlatformMenuItem *QCocoaMenu::menuItemAt(int position) const { - return m_menuItems.at(position); + if (0 <= position && position < m_menuItems.count()) + return m_menuItems.at(position); + + return 0; } QPlatformMenuItem *QCocoaMenu::menuItemForTag(quintptr tag) const diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm index bae52c91b8..c0c8caed05 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.mm +++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm @@ -77,8 +77,10 @@ QCocoaMenuBar::~QCocoaMenuBar() [m_nativeMenu release]; static_menubars.removeOne(this); - if (m_window) + if (m_window && m_window->menubar() == this) { m_window->setMenubar(0); + updateMenuBarImmediately(); + } } void QCocoaMenuBar::insertMenu(QPlatformMenu *platformMenu, QPlatformMenu *before) diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.h b/src/plugins/platforms/cocoa/qcocoamenuitem.h index fe5193a50c..0e6d17343d 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.h +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.h @@ -51,8 +51,6 @@ @class NSMenuItem; @class NSMenu; -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QCocoaMenu; @@ -76,6 +74,7 @@ public: void setFont(const QFont &font); void setRole(MenuRole role); void setShortcut(const QKeySequence& shortcut); + void setCheckable(bool checkable) { Q_UNUSED(checkable) } void setChecked(bool isChecked); void setEnabled(bool isEnabled); @@ -110,8 +109,6 @@ private: quintptr m_tag; }; -QT_END_HEADER - QT_END_NAMESPACE #endif diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index d78ff73bb6..40cffab3c9 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -195,7 +195,7 @@ NSMenuItem *QCocoaMenuItem::sync() QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); switch (m_role) { case ApplicationSpecificRole: - mergeItem = [loader appSpecificMenuItem]; + mergeItem = [loader appSpecificMenuItem:reinterpret_cast<NSInteger>(this)]; break; case AboutRole: mergeItem = [loader aboutMenuItem]; @@ -315,9 +315,12 @@ NSMenuItem *QCocoaMenuItem::sync() return m_native; } +QT_BEGIN_NAMESPACE +extern QString qt_mac_applicationmenu_string(int type); +QT_END_NAMESPACE + QString QCocoaMenuItem::mergeText() { - extern QString qt_mac_applicationmenu_string(int type); QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); if (m_native == [loader aboutMenuItem]) { return qt_mac_applicationmenu_string(6).arg(qt_mac_applicationName()); diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.h b/src/plugins/platforms/cocoa/qcocoamenuloader.h index f95f684e8c..a45ec0fa89 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuloader.h +++ b/src/plugins/platforms/cocoa/qcocoamenuloader.h @@ -79,7 +79,7 @@ - (NSMenuItem *)aboutMenuItem; - (NSMenuItem *)aboutQtMenuItem; - (NSMenuItem *)hideMenuItem; -- (NSMenuItem *)appSpecificMenuItem; +- (NSMenuItem *)appSpecificMenuItem:(NSInteger)tag; - (IBAction)terminate:(id)sender; - (IBAction)orderFrontStandardAboutPanel:(id)sender; - (IBAction)hideOtherApplications:(id)sender; diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.mm b/src/plugins/platforms/cocoa/qcocoamenuloader.mm index 2a9dcec64b..726fe5c6d2 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuloader.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuloader.mm @@ -246,11 +246,17 @@ QT_END_NAMESPACE return [[hideItem retain] autorelease]; } -- (NSMenuItem *)appSpecificMenuItem +- (NSMenuItem *)appSpecificMenuItem:(NSInteger)tag { + NSMenuItem *item = [appMenu itemWithTag:tag]; + + // No reason to create the item if it already exists. See QTBUG-27202. + if (item) + return [[item retain] autorelease]; + // Create an App-Specific menu item, insert it into the menu and return // it as an autorelease item. - NSMenuItem *item = [[NSMenuItem alloc] init]; + item = [[NSMenuItem alloc] init]; NSInteger location; if (lastAppSpecificItem == nil) { diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.h b/src/plugins/platforms/cocoa/qcocoanativeinterface.h index 9506f86238..2f79b49534 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.h +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.h @@ -102,6 +102,9 @@ private: // QImage <-> CGImage conversion functions static CGImageRef qImageToCGImage(const QImage &image); static QImage cgImageToQImage(CGImageRef image); + + // Embedding NSViews as child QWindows + static void setWindowContentView(QPlatformWindow *window, void *nsViewContentView); }; #endif // QCOCOANATIVEINTERFACE_H diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm index bd3a909137..9990537c1f 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm @@ -113,6 +113,8 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QCocoaNativeInter return NativeResourceForIntegrationFunction(QCocoaNativeInterface::qImageToCGImage); if (resource.toLower() == "cgimagetoqimage") return NativeResourceForIntegrationFunction(QCocoaNativeInterface::cgImageToQImage); + if (resource.toLower() == "setwindowcontentview") + return NativeResourceForIntegrationFunction(QCocoaNativeInterface::setWindowContentView); return 0; } @@ -198,5 +200,10 @@ QImage QCocoaNativeInterface::cgImageToQImage(CGImageRef image) return qt_mac_toQImage(image); } +void QCocoaNativeInterface::setWindowContentView(QPlatformWindow *window, void *contentView) +{ + QCocoaWindow *cocoaPlatformWindow = static_cast<QCocoaWindow *>(window); + cocoaPlatformWindow->setContentView(reinterpret_cast<NSView *>(contentView)); +} QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaprintersupport.h b/src/plugins/platforms/cocoa/qcocoaprintersupport.h index 040b687c4e..83cf1ffada 100644 --- a/src/plugins/platforms/cocoa/qcocoaprintersupport.h +++ b/src/plugins/platforms/cocoa/qcocoaprintersupport.h @@ -55,6 +55,7 @@ public: QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode) Q_DECL_OVERRIDE; QPaintEngine *createPaintEngine(QPrintEngine *, QPrinter::PrinterMode printerMode) Q_DECL_OVERRIDE; QList<QPrinter::PaperSize> supportedPaperSizes(const QPrinterInfo &) const Q_DECL_OVERRIDE; + QList<QPair<QString, QSizeF> > supportedSizesWithNames(const QPrinterInfo &) const Q_DECL_OVERRIDE; QList<QPrinterInfo> availablePrinters() Q_DECL_OVERRIDE; QPrinterInfo printerInfo(const QString &printerName) Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/cocoa/qcocoaprintersupport.mm b/src/plugins/platforms/cocoa/qcocoaprintersupport.mm index a48db02949..cfa23b7a30 100644 --- a/src/plugins/platforms/cocoa/qcocoaprintersupport.mm +++ b/src/plugins/platforms/cocoa/qcocoaprintersupport.mm @@ -138,3 +138,34 @@ QPrinterInfo QCocoaPrinterSupport::printerInfoFromPMPrinter(const PMPrinter &pri return createPrinterInfo(name, description, location, makeAndModel, isDefault, 0); } + +QList<QPair<QString, QSizeF> > QCocoaPrinterSupport::supportedSizesWithNames(const QPrinterInfo &printerInfo) const +{ + QList<QPair<QString, QSizeF> > returnValue; + if (printerInfo.isNull()) + return returnValue; + + PMPrinter printer = PMPrinterCreateFromPrinterID(QCFString::toCFStringRef(printerInfo.printerName())); + if (!printer) + return returnValue; + + CFArrayRef array; + if (PMPrinterGetPaperList(printer, &array) != noErr) { + PMRelease(printer); + return returnValue; + } + + int count = CFArrayGetCount(array); + for (int i = 0; i < count; ++i) { + PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(array, i))); + double width, height; + if (PMPaperGetWidth(paper, &width) == noErr && PMPaperGetHeight(paper, &height) == noErr) { + static const double OnePointInMillimeters = 1.0 / 72.0 * 25.4; + QCFString paperName; + if (PMPaperCreateLocalizedName(paper, printer, &paperName) == noErr) + returnValue.append(qMakePair(QString(paperName), QSizeF(width * OnePointInMillimeters, height * OnePointInMillimeters))); + } + } + PMRelease(printer); + return returnValue; +} diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm index 6f8d0fa22a..8b0c14a984 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm @@ -179,14 +179,14 @@ QHash<QPlatformTheme::Palette, QPalette*> qt_mac_createRolePalettes() if (mac_widget_colors[i].active != 0) { qc = qt_mac_colorForThemeTextColor(mac_widget_colors[i].active); pal.setColor(QPalette::Active, QPalette::Text, qc); + pal.setColor(QPalette::Inactive, QPalette::Text, qc); pal.setColor(QPalette::Active, QPalette::WindowText, qc); + pal.setColor(QPalette::Inactive, QPalette::WindowText, qc); pal.setColor(QPalette::Active, QPalette::HighlightedText, qc); + pal.setColor(QPalette::Inactive, QPalette::HighlightedText, qc); qc = qt_mac_colorForThemeTextColor(mac_widget_colors[i].inactive); - pal.setColor(QPalette::Inactive, QPalette::Text, qc); pal.setColor(QPalette::Disabled, QPalette::Text, qc); - pal.setColor(QPalette::Inactive, QPalette::WindowText, qc); pal.setColor(QPalette::Disabled, QPalette::WindowText, qc); - pal.setColor(QPalette::Inactive, QPalette::HighlightedText, qc); pal.setColor(QPalette::Disabled, QPalette::HighlightedText, qc); } if (mac_widget_colors[i].paletteRole == QPlatformTheme::MenuPalette) { diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h index 89ab51cf05..61a6ba3e2f 100755 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h @@ -43,6 +43,8 @@ #ifndef QCOCOASYSTEMTRAYICON_P_H #define QCOCOASYSTEMTRAYICON_P_H +#include <QtCore/qglobal.h> + #ifndef QT_NO_SYSTEMTRAYICON #include "QtCore/qstring.h" diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm index e092db8172..99f533b33a 100755 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm @@ -93,7 +93,11 @@ QT_USE_NAMESPACE @class QT_MANGLE_NAMESPACE(QNSMenu); @class QT_MANGLE_NAMESPACE(QNSImageView); -@interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject { +@interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + <NSUserNotificationCenterDelegate> +#endif + { @public QCocoaSystemTrayIcon *systray; NSStatusItem *item; @@ -108,6 +112,11 @@ QT_USE_NAMESPACE -(QRectF)geometry; - (void)triggerSelector:(id)sender button:(Qt::MouseButton)mouseButton; - (void)doubleClickSelector:(id)sender; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification; +- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification; +#endif @end @interface QT_MANGLE_NAMESPACE(QNSImageView) : NSImageView { @@ -132,9 +141,19 @@ class QSystemTrayIconSys public: QSystemTrayIconSys(QCocoaSystemTrayIcon *sys) { item = [[QT_MANGLE_NAMESPACE(QNSStatusItem) alloc] initWithSysTray:sys]; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) { + [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:item]; + } +#endif } ~QSystemTrayIconSys() { [[[item item] view] setHidden: YES]; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) { + [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil]; + } +#endif [item release]; } QT_MANGLE_NAMESPACE(QNSStatusItem) *item; @@ -223,6 +242,18 @@ void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &mess if (!m_sys) return; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) { + NSUserNotification *notification = [[NSUserNotification alloc] init]; + notification.title = [NSString stringWithUTF8String:title.toUtf8().data()]; + notification.informativeText = [NSString stringWithUTF8String:message.toUtf8().data()]; + + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; + + return; + } +#endif + #ifdef QT_MAC_SYSTEMTRAY_USE_GROWL // Make sure that we have Growl installed on the machine we are running on. QCFType<CFURLRef> cfurl; @@ -439,6 +470,20 @@ QT_END_NAMESPACE emit systray->activated(QPlatformSystemTrayIcon::DoubleClick); } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification { + Q_UNUSED(center); + Q_UNUSED(notification); + return YES; +} + +- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification { + Q_UNUSED(center); + Q_UNUSED(notification); + emit systray->messageClicked(); +} +#endif + @end class QSystemTrayIconQMenu : public QPlatformMenu diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 7140a68d49..fdb3f4672f 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -116,10 +116,13 @@ public: bool setMouseGrabEnabled(bool grab); QMargins frameMargins() const; + void requestActivateWindow(); + WId winId() const; void setParent(const QPlatformWindow *window); NSView *contentView() const; + void setContentView(NSView *contentView); void windowWillMove(); void windowDidMove(); @@ -158,13 +161,18 @@ protected: QCocoaWindow *parentCocoaWindow() const; void syncWindowState(Qt::WindowState newState); + void updateOpaque(); + // private: public: // for QNSView friend class QCocoaBackingStore; friend class QCocoaNativeInterface; - QNSView *m_contentView; + NSView *m_contentView; + QNSView *m_qtView; NSWindow *m_nsWindow; + bool m_contentViewIsEmbedded; // true if the m_contentView is embedded in a "foregin" NSView hiearchy + QNSWindowDelegate *m_nsWindowDelegate; Qt::WindowFlags m_windowFlags; Qt::WindowState m_synchedWindowState; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 7c665a35c5..9466b68bec 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -58,6 +58,11 @@ #include <QDebug> +enum { + defaultWindowWidth = 160, + defaultWindowHeight = 160 +}; + static bool isMouseEvent(NSEvent *ev) { switch ([ev type]) { @@ -186,6 +191,7 @@ static bool isMouseEvent(NSEvent *ev) QCocoaWindow::QCocoaWindow(QWindow *tlw) : QPlatformWindow(tlw) , m_nsWindow(0) + , m_contentViewIsEmbedded(false) , m_nsWindowDelegate(0) , m_synchedWindowState(Qt::WindowActive) , m_windowModality(Qt::NonModal) @@ -201,7 +207,8 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) #endif QCocoaAutoReleasePool pool; - m_contentView = [[QNSView alloc] initWithQWindow:tlw platformWindow:this]; + m_qtView = [[QNSView alloc] initWithQWindow:tlw platformWindow:this]; + m_contentView = m_qtView; setGeometry(tlw->geometry()); recreateWindow(parent()); @@ -238,6 +245,10 @@ void QCocoaWindow::setGeometry(const QRect &rect) void QCocoaWindow::setCocoaGeometry(const QRect &rect) { QCocoaAutoReleasePool pool; + + if (m_contentViewIsEmbedded) + return; + if (m_nsWindow) { NSRect bounds = qt_mac_flipRect(rect, window()); [m_nsWindow setContentSize : bounds.size]; @@ -367,7 +378,7 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) NSInteger styleMask = NSBorderlessWindowMask; if ((type & Qt::Popup) == Qt::Popup) { - if (!windowIsPopupType(type)) + if (!windowIsPopupType(type) && !(flags & Qt::FramelessWindowHint)) styleMask = (NSUtilityWindowMask | NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask); } else { @@ -409,6 +420,8 @@ 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); } @@ -523,20 +536,30 @@ void QCocoaWindow::propagateSizeHints() } } +void QCocoaWindow::updateOpaque() +{ + bool translucent = window()->format().alphaBufferSize() > 0 + || window()->opacity() < 1 + || (m_contentView && [m_contentView hasMask]); + [m_nsWindow setOpaque:!translucent]; +} + + void QCocoaWindow::setOpacity(qreal level) { - if (m_nsWindow) + if (m_nsWindow) { [m_nsWindow setAlphaValue:level]; + updateOpaque(); + } } void QCocoaWindow::setMask(const QRegion ®ion) { - if (m_nsWindow) { - [m_nsWindow setOpaque:NO]; + if (m_nsWindow) [m_nsWindow setBackgroundColor:[NSColor clearColor]]; - } - [m_contentView setMaskRegion:®ion]; + [m_qtView setMaskRegion:®ion]; + updateOpaque(); } bool QCocoaWindow::setKeyboardGrabEnabled(bool grab) @@ -580,6 +603,19 @@ NSView *QCocoaWindow::contentView() const return m_contentView; } +void QCocoaWindow::setContentView(NSView *contentView) +{ + // Remove and release the previous content view + [m_contentView removeFromSuperview]; + [m_contentView release]; + + // Insert and retain the new content view + [contentView retain]; + m_contentView = contentView; + m_qtView = 0; // The new content view is not a QNSView. + recreateWindow(parent()); // Adds the content view to parent NSView +} + void QCocoaWindow::windowWillMove() { // Close any open popups on window move @@ -592,7 +628,7 @@ void QCocoaWindow::windowWillMove() void QCocoaWindow::windowDidMove() { - [m_contentView updateGeometry]; + [m_qtView updateGeometry]; } void QCocoaWindow::windowDidResize() @@ -600,7 +636,7 @@ void QCocoaWindow::windowDidResize() if (!m_nsWindow) return; - [m_contentView updateGeometry]; + [m_qtView updateGeometry]; } void QCocoaWindow::windowWillClose() @@ -641,7 +677,9 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) m_nsWindowDelegate = 0; } - if (!parentWindow) { + if (window()->type() == Qt::SubWindow) { + // Subwindows don't have a NSWindow. + } else if (!parentWindow) { // Create a new NSWindow if this is a top-level window. m_nsWindow = createNSWindow(); setNSWindow(m_nsWindow); @@ -665,11 +703,19 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) setOpacity(opacity); } +void QCocoaWindow::requestActivateWindow() +{ + NSWindow *window = [m_contentView window]; + [ window makeFirstResponder : m_contentView ]; + [ window makeKeyWindow ]; +} + NSWindow * QCocoaWindow::createNSWindow() { QCocoaAutoReleasePool pool; - NSRect frame = qt_mac_flipRect(window()->geometry(), window()); + QRect rect = initialGeometry(window(), window()->geometry(), defaultWindowWidth, defaultWindowHeight); + NSRect frame = qt_mac_flipRect(rect, window()); Qt::WindowType type = window()->type(); Qt::WindowFlags flags = window()->flags(); @@ -726,6 +772,12 @@ NSWindow * QCocoaWindow::createNSWindow() NSInteger level = windowLevel(flags); [createdWindow setLevel:level]; + + if (window()->format().alphaBufferSize() > 0) { + [createdWindow setBackgroundColor:[NSColor clearColor]]; + [createdWindow setOpaque:NO]; + } + m_windowModality = window()->modality(); return createdWindow; } @@ -734,7 +786,6 @@ void QCocoaWindow::setNSWindow(NSWindow *window) { m_nsWindowDelegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this]; [window setDelegate:m_nsWindowDelegate]; - [window setAcceptsMouseMovedEvents:YES]; // Prevent Cocoa from releasing the window on close. Qt // handles the close event asynchronously and we want to diff --git a/src/plugins/platforms/cocoa/qmacdefines_mac.h b/src/plugins/platforms/cocoa/qmacdefines_mac.h index 18c1ac84de..c03b398836 100644 --- a/src/plugins/platforms/cocoa/qmacdefines_mac.h +++ b/src/plugins/platforms/cocoa/qmacdefines_mac.h @@ -79,15 +79,11 @@ */ /* This is just many defines. Therefore it doesn't need things like: -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE QT_END_NAMESPACE -QT_END_HEADER - Yes, it is an informative comment ;-) */ diff --git a/src/plugins/platforms/cocoa/qmultitouch_mac.mm b/src/plugins/platforms/cocoa/qmultitouch_mac.mm index 337d962c7f..fc0048f998 100644 --- a/src/plugins/platforms/cocoa/qmultitouch_mac.mm +++ b/src/plugins/platforms/cocoa/qmultitouch_mac.mm @@ -42,8 +42,6 @@ #include "qmultitouch_mac_p.h" #include "qcocoahelpers.h" -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - QT_BEGIN_NAMESPACE QHash<qint64, QCocoaTouch*> QCocoaTouch::_currentTouches; @@ -214,6 +212,3 @@ QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch) } QT_END_NAMESPACE - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - diff --git a/src/plugins/platforms/cocoa/qmultitouch_mac_p.h b/src/plugins/platforms/cocoa/qmultitouch_mac_p.h index 1244b8223f..736eb3f878 100644 --- a/src/plugins/platforms/cocoa/qmultitouch_mac_p.h +++ b/src/plugins/platforms/cocoa/qmultitouch_mac_p.h @@ -61,8 +61,6 @@ #include <qhash.h> #include <QtCore> -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - QT_BEGIN_NAMESPACE class QCocoaTouch @@ -92,7 +90,5 @@ class QCocoaTouch QT_END_NAMESPACE -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - #endif // QMULTITOUCH_MAC_P_H diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h index 853b4b99e1..5ccea3c387 100644 --- a/src/plugins/platforms/cocoa/qnsview.h +++ b/src/plugins/platforms/cocoa/qnsview.h @@ -86,6 +86,10 @@ QT_END_NAMESPACE - (BOOL)isFlipped; - (BOOL)acceptsFirstResponder; +- (BOOL)becomeFirstResponder; +- (BOOL)hasMask; + +- (void)resetMouseButtons; - (void)handleMouseEvent:(NSEvent *)theEvent; - (void)mouseDown:(NSEvent *)theEvent; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index fae68493f1..e608731cc1 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -174,6 +174,36 @@ static QTouchDevice *touchDevice = 0; QWindowSystemInterface::handleExposeEvent(m_window, m_window->geometry()); } +- (void)viewDidMoveToSuperview +{ + if (!(m_window->type() & Qt::SubWindow)) + return; + + if ([self superview]) { + m_platformWindow->m_contentViewIsEmbedded = true; + QWindowSystemInterface::handleGeometryChange(m_window, m_platformWindow->geometry()); + QWindowSystemInterface::handleExposeEvent(m_window, m_platformWindow->geometry()); + QWindowSystemInterface::flushWindowSystemEvents(); + } else { + m_platformWindow->m_contentViewIsEmbedded = false; + } +} + +- (void)viewWillMoveToWindow:(NSWindow *)newWindow +{ + // ### Merge "normal" window code path with this one for 5.1. + if (!(m_window->type() & Qt::SubWindow)) + return; + + if (newWindow) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowNotification:) + name:nil // Get all notifications + object:newWindow]; + } else { + [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:[self window]]; + } +} - (void)updateGeometry { QRect geometry; @@ -182,6 +212,9 @@ static QTouchDevice *touchDevice = 0; NSRect rect = [self frame]; NSRect windowRect = [[self window] frame]; geometry = QRect(windowRect.origin.x, qt_mac_flipYCoordinate(windowRect.origin.y + rect.size.height), rect.size.width, rect.size.height); + } else if (m_window->type() & Qt::SubWindow) { + // embedded child window, use the frame rect ### merge with case below + geometry = qt_mac_toQRect([self bounds]); } else { // child window, use the frame rect geometry = qt_mac_toQRect([self frame]); @@ -199,6 +232,12 @@ static QTouchDevice *touchDevice = 0; // an infinite loop when this notification is triggered again.) m_platformWindow->QPlatformWindow::setGeometry(geometry); + // Don't send the geometry change if the QWindow is designated to be + // embedded in a foregin view hiearchy but has not actually been + // embedded yet - it's too early. + if ((m_window->type() & Qt::SubWindow) && !m_platformWindow->m_contentViewIsEmbedded) + return; + // Send a geometry change event to Qt, if it's ready to handle events if (!m_platformWindow->m_inConstructor) { QWindowSystemInterface::handleGeometryChange(m_window, geometry); @@ -265,6 +304,11 @@ static QTouchDevice *touchDevice = 0; [self setNeedsDisplayInRect:NSMakeRect(br.x(), br.y(), br.width(), br.height())]; } +- (BOOL) hasMask +{ + return m_maskData != 0; +} + - (void) setMaskRegion:(const QRegion *)region { m_shouldInvalidateWindowShadow = true; @@ -337,7 +381,12 @@ static QTouchDevice *touchDevice = 0; ); CGImageRef bsCGImage = m_backingStore->getBackingStoreCGImage(); CGImageRef cleanImg = CGImageCreateWithImageInRect(bsCGImage, backingStoreRect); - CGContextSetBlendMode(cgContext, kCGBlendModeCopy); + + // Optimization: Copy frame buffer content instead of blending for + // top-level windows where Qt fills the entire window content area. + if (m_platformWindow->m_nsWindow) + CGContextSetBlendMode(cgContext, kCGBlendModeCopy); + CGContextDrawImage(cgContext, dirtyWindowRect, cleanImg); // Clean-up: @@ -353,8 +402,27 @@ static QTouchDevice *touchDevice = 0; return YES; } +- (BOOL)becomeFirstResponder +{ + QWindow *focusWindow = m_window; + + // For widgets we need to do a bit of trickery as the window + // to activate is the window of the top-level widget. + if (m_window->metaObject()->className() == QStringLiteral("QWidgetWindow")) { + while (focusWindow->parent()) { + focusWindow = focusWindow->parent(); + } + } + QWindowSystemInterface::handleWindowActivated(focusWindow); + return YES; +} + - (BOOL)acceptsFirstResponder { + if (m_window->flags() & Qt::WindowDoesNotAcceptFocus) + return NO; + if ((m_window->flags() & Qt::ToolTip) == Qt::ToolTip) + return NO; return YES; } @@ -403,6 +471,11 @@ static QTouchDevice *touchDevice = 0; } } +- (void)resetMouseButtons +{ + m_buttons = Qt::NoButton; +} + - (void)handleMouseEvent:(NSEvent *)theEvent { QPoint qtWindowPoint, qtScreenPoint; diff --git a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm index 8ee39bf767..e05ffbe343 100644 --- a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm +++ b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm @@ -46,7 +46,7 @@ #include "qcocoaaccessibility.h" #include "qcocoaaccessibilityelement.h" -#include "QAccessibleActionInterface" +#include <QtGui/private/qaccessible2_p.h> #include <QtCore/QDebug> #import <AppKit/NSAccessibility.h> diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.h b/src/plugins/platforms/cocoa/qnswindowdelegate.h index 53bbeb1318..98ad7b8c9d 100644 --- a/src/plugins/platforms/cocoa/qnswindowdelegate.h +++ b/src/plugins/platforms/cocoa/qnswindowdelegate.h @@ -46,26 +46,6 @@ #include "qcocoawindow.h" -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 -@protocol NSWindowDelegate <NSObject> -//- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize; -//- (void)windowDidMiniaturize:(NSNotification*)notification; -- (void)windowDidResize:(NSNotification *)notification; -- (void)windowWillClose:(NSNotification *)notification; -//- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame; -- (void)windowDidMove:(NSNotification *)notification; -//- (BOOL)windowShouldClose:(id)window; -//- (void)windowDidDeminiaturize:(NSNotification *)notification; -//- (void)windowDidBecomeMain:(NSNotification*)notification; -//- (void)windowDidResignMain:(NSNotification*)notification; -//- (void)windowDidBecomeKey:(NSNotification*)notification; -//- (void)windowDidResignKey:(NSNotification*)notification; -//- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu; -//- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard; -//- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame; -@end -#endif - @interface QNSWindowDelegate : NSObject <NSWindowDelegate> { QCocoaWindow *m_cocoaWindow; diff --git a/src/plugins/platforms/cocoa/qpaintengine_mac.mm b/src/plugins/platforms/cocoa/qpaintengine_mac.mm index 101be611ad..301beb11c1 100644 --- a/src/plugins/platforms/cocoa/qpaintengine_mac.mm +++ b/src/plugins/platforms/cocoa/qpaintengine_mac.mm @@ -90,14 +90,11 @@ static void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransfor if (rgn.isEmpty()) { CGContextAddRect(hd, CGRectMake(0, 0, 0, 0)); } else { -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { QCFType<HIMutableShapeRef> shape = qt_mac_QRegionToHIMutableShape(rgn); Q_ASSERT(!HIShapeIsEmpty(shape)); HIShapeReplacePathInCGContext(shape, hd); - } else -#endif - { + } else { QVector<QRect> rects = rgn.rects(); const int count = rects.size(); for (int i = 0; i < count; i++) { @@ -338,11 +335,9 @@ CGColorSpaceRef QCoreGraphicsPaintEngine::macGenericColorSpace() { #if 0 if (!m_genericColorSpace) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) { m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); } else -#endif { m_genericColorSpace = CGColorSpaceCreateDeviceRGB(); } @@ -1185,7 +1180,6 @@ extern "C" { void QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode) { -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { int cg_mode = kCGBlendModeNormal; switch (mode) { @@ -1267,11 +1261,9 @@ QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode) if (cg_mode > -1) { CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode)); } - } else -#endif - // The standard porter duff ops. - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_3 + } else if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_3 && mode <= QPainter::CompositionMode_Xor) { + // The standard porter duff ops. int cg_mode = kCGCompositeModeCopy; switch (mode) { case QPainter::CompositionMode_SourceOver: @@ -1317,7 +1309,6 @@ QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode) if (cg_mode > -1) CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode)); } else { -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) bool needPrivateAPI = false; if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) { int cg_mode = kCGBlendModeNormal; @@ -1367,7 +1358,6 @@ QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode) else CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode)); } -#endif } } @@ -1376,13 +1366,8 @@ QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints) { Q_D(QCoreGraphicsPaintEngine); CGContextSetShouldAntialias(d->hd, hints & QPainter::Antialiasing); - static const CGFloat ScaleFactor = qt_mac_get_scalefactor(); - if (ScaleFactor > 1.) { - CGContextSetInterpolationQuality(d->hd, kCGInterpolationHigh); - } else { - CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ? - kCGInterpolationHigh : kCGInterpolationNone); - } + CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ? + kCGInterpolationHigh : kCGInterpolationNone); bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing; if (!textAntialiasing || d->disabledSmoothFonts) { d->disabledSmoothFonts = !textAntialiasing; diff --git a/src/plugins/platforms/cocoa/qpaintengine_mac_p.h b/src/plugins/platforms/cocoa/qpaintengine_mac_p.h index 2763a14690..fa000b9f92 100644 --- a/src/plugins/platforms/cocoa/qpaintengine_mac_p.h +++ b/src/plugins/platforms/cocoa/qpaintengine_mac_p.h @@ -122,8 +122,6 @@ public: void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) { QPaintEngine::drawPolygon(points, pointCount, mode); } - bool supportsTransformations(qreal, const QTransform &) const { return true; }; - protected: friend class QMacPrintEngine; friend class QMacPrintEnginePrivate; diff --git a/src/plugins/platforms/cocoa/qprintengine_mac.mm b/src/plugins/platforms/cocoa/qprintengine_mac.mm index 54019372bc..4748005f1a 100644 --- a/src/plugins/platforms/cocoa/qprintengine_mac.mm +++ b/src/plugins/platforms/cocoa/qprintengine_mac.mm @@ -180,6 +180,38 @@ QPrinter::PaperSize QMacPrintEnginePrivate::paperSize() const return QPlatformPrinterSupport::convertQSizeFToPaperSize(sizef); } +void QMacPrintEnginePrivate::setPaperName(const QString &name) +{ + Q_Q(QMacPrintEngine); + PMPrinter printer; + + if (PMSessionGetCurrentPrinter(session(), &printer) == noErr) { + CFArrayRef array; + if (PMPrinterGetPaperList(printer, &array) != noErr) { + PMRelease(printer); + return; + } + int count = CFArrayGetCount(array); + for (int i = 0; i < count; ++i) { + PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(array, i))); + QCFString paperName; + if (PMPaperCreateLocalizedName(paper, printer, &paperName) == noErr) { + if (QString(paperName) == name) { + PMPageFormat tmp; + PMCreatePageFormatWithPMPaper(&tmp, paper); + PMCopyPageFormat(tmp, format()); + q->setProperty(QPrintEngine::PPK_Orientation, orient); + if (PMSessionValidatePageFormat(session(), format(), kPMDontWantBoolean) != noErr) { + // Don't know, warn for the moment. + qWarning("QMacPrintEngine, problem setting paper name"); + } + } + } + } + PMRelease(printer); + } +} + QList<QVariant> QMacPrintEnginePrivate::supportedResolutions() const { Q_ASSERT_X(printInfo, "QMacPrinterEngine::supportedResolutions", @@ -190,23 +222,7 @@ QList<QVariant> QMacPrintEnginePrivate::supportedResolutions() const if (PMSessionGetCurrentPrinter(session(), &printer) == noErr) { PMResolution res; OSStatus status = PMPrinterGetPrinterResolutionCount(printer, &resCount); - if (status == kPMNotImplemented) { -#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5) - // *Sigh* we have to use the non-indexed version. - if (PMPrinterGetPrinterResolution(printer, kPMMinSquareResolution, &res) == noErr) - resolutions.append(int(res.hRes)); - if (PMPrinterGetPrinterResolution(printer, kPMMaxSquareResolution, &res) == noErr) { - QVariant var(int(res.hRes)); - if (!resolutions.contains(var)) - resolutions.append(var); - } - if (PMPrinterGetPrinterResolution(printer, kPMDefaultResolution, &res) == noErr) { - QVariant var(int(res.hRes)); - if (!resolutions.contains(var)) - resolutions.append(var); - } -#endif - } else if (status == noErr) { + if (status == noErr) { // According to the docs, index start at 1. for (UInt32 i = 1; i <= resCount; ++i) { if (PMPrinterGetIndexedPrinterResolution(printer, i, &res) == noErr) @@ -617,6 +633,9 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va case PPK_PaperSize: d->setPaperSize(QPrinter::PaperSize(value.toInt())); break; + case PPK_PaperName: + d->setPaperName(value.toString()); + break; case PPK_PrinterName: { bool printerNameSet = false; OSStatus status = noErr; @@ -755,6 +774,9 @@ QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const case PPK_PaperSize: ret = d->paperSize(); break; + case PPK_PaperName: + ret = QCFString::toQString([d->printInfo localizedPaperName]); + break; case PPK_PaperRect: { QRect r; PMRect macrect; diff --git a/src/plugins/platforms/cocoa/qprintengine_mac_p.h b/src/plugins/platforms/cocoa/qprintengine_mac_p.h index e122cc5822..28183118d8 100644 --- a/src/plugins/platforms/cocoa/qprintengine_mac_p.h +++ b/src/plugins/platforms/cocoa/qprintengine_mac_p.h @@ -53,6 +53,8 @@ // We mean it. // +#include <QtCore/qglobal.h> + #ifndef QT_NO_PRINTER #include <QtPrintSupport/qprinter.h> @@ -142,6 +144,7 @@ public: bool newPage_helper(); void setPaperSize(QPrinter::PaperSize ps); QPrinter::PaperSize paperSize() const; + void setPaperName(const QString &name); QList<QVariant> supportedResolutions() const; inline bool isPrintSessionInitialized() const { diff --git a/src/plugins/platforms/directfb/qdirectfbcursor.cpp b/src/plugins/platforms/directfb/qdirectfbcursor.cpp index afddba3482..b03af51b1a 100644 --- a/src/plugins/platforms/directfb/qdirectfbcursor.cpp +++ b/src/plugins/platforms/directfb/qdirectfbcursor.cpp @@ -59,8 +59,9 @@ void QDirectFBCursor::changeCursor(QCursor *cursor, QWindow *) int ySpot; QPixmap map; - if (cursor->shape() != Qt::BitmapCursor) { - m_image->set(cursor->shape()); + const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; + if (newShape != Qt::BitmapCursor) { + m_image->set(newShape); xSpot = m_image->hotspot().x(); ySpot = m_image->hotspot().y(); QImage *i = m_image->image(); diff --git a/src/plugins/platforms/eglfs/eglfs.pri b/src/plugins/platforms/eglfs/eglfs.pri new file mode 100644 index 0000000000..eb66e17479 --- /dev/null +++ b/src/plugins/platforms/eglfs/eglfs.pri @@ -0,0 +1,44 @@ +QT += core-private gui-private platformsupport-private + +#DEFINES += QEGL_EXTRA_DEBUG + +#Avoid X11 header collision +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 + +SOURCES += $$PWD/qeglfsintegration.cpp \ + $$PWD/qeglfswindow.cpp \ + $$PWD/qeglfsbackingstore.cpp \ + $$PWD/qeglfsscreen.cpp \ + $$PWD/qeglfshooks_stub.cpp \ + $$PWD/qeglfscursor.cpp \ + $$PWD/qeglfscontext.cpp + +HEADERS += $$PWD/qeglfsintegration.h \ + $$PWD/qeglfswindow.h \ + $$PWD/qeglfsbackingstore.h \ + $$PWD/qeglfsscreen.h \ + $$PWD/qeglfscursor.h \ + $$PWD/qeglfshooks.h \ + $$PWD/qeglfscontext.h + +QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF + +INCLUDEPATH += $$PWD + +!isEmpty(EGLFS_PLATFORM_HOOKS_SOURCES) { + HEADERS += $$EGLFS_PLATFORM_HOOKS_HEADERS + SOURCES += $$EGLFS_PLATFORM_HOOKS_SOURCES + LIBS += $$EGLFS_PLATFORM_HOOKS_LIBS + DEFINES += EGLFS_PLATFORM_HOOKS +} + +CONFIG += egl qpa/genericunixfontdatabase + +RESOURCES += $$PWD/cursor.qrc + +OTHER_FILES += \ + $$PWD/eglfs.json diff --git a/src/plugins/platforms/eglfs/eglfs.pro b/src/plugins/platforms/eglfs/eglfs.pro index 45059a9cb9..8827f7680c 100644 --- a/src/plugins/platforms/eglfs/eglfs.pro +++ b/src/plugins/platforms/eglfs/eglfs.pro @@ -4,48 +4,6 @@ PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QEglFSIntegrationPlugin load(qt_plugin) -QT += core-private gui-private platformsupport-private +SOURCES += $$PWD/main.cpp -#DEFINES += QEGL_EXTRA_DEBUG - -#Avoid X11 header collision -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 - -SOURCES = main.cpp \ - qeglfsintegration.cpp \ - qeglfswindow.cpp \ - qeglfsbackingstore.cpp \ - qeglfsscreen.cpp \ - qeglfshooks_stub.cpp \ - qeglfscursor.cpp \ - qeglfscontext.cpp - -HEADERS = qeglfsintegration.h \ - qeglfswindow.h \ - qeglfsbackingstore.h \ - qeglfsscreen.h \ - qeglfscursor.h \ - qeglfshooks.h \ - qeglfscontext.h - -QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF - -INCLUDEPATH += $$PWD - -!isEmpty(EGLFS_PLATFORM_HOOKS_SOURCES) { - HEADERS += $$EGLFS_PLATFORM_HOOKS_HEADERS - SOURCES += $$EGLFS_PLATFORM_HOOKS_SOURCES - LIBS += $$EGLFS_PLATFORM_HOOKS_LIBS - DEFINES += EGLFS_PLATFORM_HOOKS -} - -CONFIG += egl qpa/genericunixfontdatabase - -RESOURCES += cursor.qrc - -OTHER_FILES += \ - eglfs.json +include(eglfs.pri) diff --git a/src/plugins/platforms/eglfs/qeglfscontext.cpp b/src/plugins/platforms/eglfs/qeglfscontext.cpp index 66b42d64e7..51439646c6 100644 --- a/src/plugins/platforms/eglfs/qeglfscontext.cpp +++ b/src/plugins/platforms/eglfs/qeglfscontext.cpp @@ -43,14 +43,17 @@ #include "qeglfswindow.h" #include "qeglfscursor.h" #include "qeglfshooks.h" +#include "qeglfsintegration.h" +#include <QtPlatformSupport/private/qeglpbuffer_p.h> +#include <QtGui/QSurface> #include <QtDebug> QT_BEGIN_NAMESPACE QEglFSContext::QEglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLenum eglApi) - : QEGLPlatformContext(hooks->surfaceFormatFor(format), share, display, eglApi) + : QEGLPlatformContext(QEglFSHooks::hooks()->surfaceFormatFor(format), share, display, QEglFSIntegration::chooseConfig(display, QEglFSHooks::hooks()->surfaceFormatFor(format)), eglApi) { } @@ -61,17 +64,22 @@ bool QEglFSContext::makeCurrent(QPlatformSurface *surface) EGLSurface QEglFSContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface) { - QEglFSWindow *window = static_cast<QEglFSWindow *>(surface); - return window->surface(); + if (surface->surface()->surfaceClass() == QSurface::Window) + return static_cast<QEglFSWindow *>(surface)->surface(); + else + return static_cast<QEGLPbuffer *>(surface)->pbuffer(); } void QEglFSContext::swapBuffers(QPlatformSurface *surface) { - QEglFSWindow *window = static_cast<QEglFSWindow *>(surface); - // draw the cursor - if (QEglFSCursor *cursor = static_cast<QEglFSCursor *>(window->screen()->cursor())) - cursor->paintOnScreen(); + if (surface->surface()->surfaceClass() == QSurface::Window) { + QEglFSWindow *window = static_cast<QEglFSWindow *>(surface); + // draw the cursor + if (QEglFSCursor *cursor = static_cast<QEglFSCursor *>(window->screen()->cursor())) + cursor->paintOnScreen(); + } + QEglFSHooks::hooks()->waitForVSync(); QEGLPlatformContext::swapBuffers(surface); } diff --git a/src/plugins/platforms/eglfs/qeglfscursor.cpp b/src/plugins/platforms/eglfs/qeglfscursor.cpp index c245b1b5bb..9dc836b8b1 100644 --- a/src/plugins/platforms/eglfs/qeglfscursor.cpp +++ b/src/plugins/platforms/eglfs/qeglfscursor.cpp @@ -196,15 +196,16 @@ void QEglFSCursor::changeCursor(QCursor *cursor, QWindow *window) bool QEglFSCursor::setCurrentCursor(QCursor *cursor) { - if (m_cursor.shape == cursor->shape() && cursor->shape() != Qt::BitmapCursor) + const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; + if (m_cursor.shape == newShape && newShape != Qt::BitmapCursor) return false; if (m_cursor.shape == Qt::BitmapCursor) { m_cursor.customCursorImage = QImage(); // in case render() never uploaded it } - m_cursor.shape = cursor->shape(); - if (cursor->shape() != Qt::BitmapCursor) { // standard cursor + m_cursor.shape = newShape; + if (newShape != Qt::BitmapCursor) { // standard cursor const float ws = (float)m_cursorAtlas.cursorWidth / m_cursorAtlas.width, hs = (float)m_cursorAtlas.cursorHeight / m_cursorAtlas.height; m_cursor.textureRect = QRectF(ws * (m_cursor.shape % m_cursorAtlas.cursorsPerRow), diff --git a/src/plugins/platforms/eglfs/qeglfshooks.h b/src/plugins/platforms/eglfs/qeglfshooks.h index 2d095ba934..c4ac7185fb 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks.h +++ b/src/plugins/platforms/eglfs/qeglfshooks.h @@ -55,10 +55,11 @@ class QEglFSScreen; class QEglFSHooks { public: - virtual ~QEglFSHooks() {}; + virtual ~QEglFSHooks() {} virtual void platformInit(); virtual void platformDestroy(); virtual EGLNativeDisplayType platformDisplay() const; + virtual QSizeF physicalScreenSize() const; virtual QSize screenSize() const; virtual int screenDepth() const; virtual QImage::Format screenFormat() const; @@ -67,15 +68,22 @@ public: virtual void destroyNativeWindow(EGLNativeWindowType window); virtual bool hasCapability(QPlatformIntegration::Capability cap) const; virtual QEglFSCursor *createCursor(QEglFSScreen *screen) const; -}; + virtual bool filterConfig(EGLDisplay display, EGLConfig config) const; + virtual void waitForVSync() const; + + virtual const char *fbDeviceName() const; + static QEglFSHooks *hooks() + { #ifdef EGLFS_PLATFORM_HOOKS -extern QEglFSHooks *platformHooks; -static QEglFSHooks *hooks = platformHooks; + extern QEglFSHooks *platformHooks; + return platformHooks; #else -extern QEglFSHooks stubHooks; -static QEglFSHooks *hooks = &stubHooks; + extern QEglFSHooks stubHooks; + return &stubHooks; #endif + } +}; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp index e9a22ea605..8200fa70b2 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp +++ b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp @@ -46,15 +46,32 @@ #include <linux/fb.h> #include <sys/ioctl.h> +#include <private/qmath_p.h> +#include <private/qcore_unix_p.h> + QT_BEGIN_NAMESPACE +// file descriptor for the frame buffer +// this is a global static to keep the QEglFSHooks interface as clean as possible +static int framebuffer = -1; + +const char *QEglFSHooks::fbDeviceName() const +{ + return "/dev/fb0"; +} + void QEglFSHooks::platformInit() { - Q_UNUSED(hooks); + framebuffer = qt_safe_open(fbDeviceName(), O_RDONLY); + + if (framebuffer == -1) + qWarning("EGLFS: Failed to open %s", fbDeviceName()); } void QEglFSHooks::platformDestroy() { + if (framebuffer != -1) + close(framebuffer); } EGLNativeDisplayType QEglFSHooks::platformDisplay() const @@ -62,6 +79,55 @@ EGLNativeDisplayType QEglFSHooks::platformDisplay() const return EGL_DEFAULT_DISPLAY; } +QSizeF QEglFSHooks::physicalScreenSize() const +{ + static QSizeF size; + if (size.isEmpty()) { + + // Note: in millimeters + int width = qgetenv("QT_QPA_EGLFS_PHYSICAL_WIDTH").toInt(); + int height = qgetenv("QT_QPA_EGLFS_PHYSICAL_HEIGHT").toInt(); + + if (width && height) { + // no need to read fb0 + size.setWidth(width); + size.setHeight(height); + return size; + } + + struct fb_var_screeninfo vinfo; + int w = -1; + int h = -1; + + if (framebuffer != -1) { + if (ioctl(framebuffer, FBIOGET_VSCREENINFO, &vinfo) == -1) { + qWarning("EGLFS: Could not query variable screen info."); + } else { + w = vinfo.width; + h = vinfo.height; + } + } + + const int defaultPhysicalDpi = 100; + size.setWidth(w <= 0 ? vinfo.xres * Q_MM_PER_INCH / defaultPhysicalDpi : qreal(w)); + size.setHeight(h <= 0 ? vinfo.yres * Q_MM_PER_INCH / defaultPhysicalDpi : qreal(h)); + + if (w <= 0 || h <= 0) { + qWarning("EGLFS: Unable to query physical screen size, defaulting to %d dpi.\n" + "EGLFS: To override, set QT_QPA_EGLFS_PHYSICAL_WIDTH " + "and QT_QPA_EGLFS_PHYSICAL_HEIGHT (in millimeters).", + defaultPhysicalDpi); + } + + // override fb0 from environment var setting + if (width) + size.setWidth(width); + if (height) + size.setWidth(height); + } + return size; +} + QSize QEglFSHooks::screenSize() const { static QSize size; @@ -78,17 +144,28 @@ QSize QEglFSHooks::screenSize() const } struct fb_var_screeninfo vinfo; - int fd = open("/dev/fb0", O_RDONLY); - if (fd != -1) { - if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) - qWarning("Could not query variable screen info."); - else - size = QSize(vinfo.xres, vinfo.yres); + int xres = -1; + int yres = -1; + + if (framebuffer != -1) { + if (ioctl(framebuffer, FBIOGET_VSCREENINFO, &vinfo) == -1) { + qWarning("EGLFS: Could not query variable screen info."); + } else { + xres = vinfo.xres; + yres = vinfo.yres; + } + } + + const int defaultWidth = 800; + const int defaultHeight = 600; + size.setWidth(xres <= 0 ? defaultWidth : xres); + size.setHeight(yres <= 0 ? defaultHeight : yres); - close(fd); - } else { - qWarning("Failed to open /dev/fb0 to detect screen resolution."); + if (xres <= 0 || yres <= 0) { + qWarning("EGLFS: Unable to query screen resolution, defaulting to %dx%d.\n" + "EGLFS: To override, set QT_QPA_EGLFS_WIDTH and QT_QPA_EGLFS_HEIGHT.", + defaultWidth, defaultHeight); } // override fb0 from environment var setting @@ -107,21 +184,25 @@ int QEglFSHooks::screenDepth() const if (depth == 0) { struct fb_var_screeninfo vinfo; - int fd = open("/dev/fb0", O_RDONLY); - if (fd != -1) { - if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) - qWarning("Could not query variable screen info."); + if (framebuffer != -1) { + if (ioctl(framebuffer, FBIOGET_VSCREENINFO, &vinfo) == -1) + qWarning("EGLFS: Could not query variable screen info."); else depth = vinfo.bits_per_pixel; + } + + const int defaultDepth = 32; + + if (depth <= 0) { + depth = defaultDepth; - close(fd); - } else { - qWarning("Failed to open /dev/fb0 to detect screen depth."); + qWarning("EGLFS: Unable to query screen depth, defaulting to %d.\n" + "EGLFS: To override, set QT_QPA_EGLFS_DEPTH.", defaultDepth); } } - return depth == 0 ? 32 : depth; + return depth; } QImage::Format QEglFSHooks::screenFormat() const @@ -134,6 +215,11 @@ QSurfaceFormat QEglFSHooks::surfaceFormatFor(const QSurfaceFormat &inputFormat) return inputFormat; } +bool QEglFSHooks::filterConfig(EGLDisplay, EGLConfig) const +{ + return true; +} + EGLNativeWindowType QEglFSHooks::createNativeWindow(const QSize &size, const QSurfaceFormat &format) { Q_UNUSED(size); @@ -158,6 +244,18 @@ QEglFSCursor *QEglFSHooks::createCursor(QEglFSScreen *screen) const return 0; } +void QEglFSHooks::waitForVSync() const +{ +#if defined(FBIO_WAITFORVSYNC) + static const bool forceSync = qgetenv("QT_QPA_EGLFS_FORCEVSYNC").toInt(); + if (forceSync && framebuffer != -1) { + int arg = 0; + if (ioctl(framebuffer, FBIO_WAITFORVSYNC, &arg) == -1) + qWarning("Could not wait for vsync."); + } +#endif +} + #ifndef EGLFS_PLATFORM_HOOKS QEglFSHooks stubHooks; #endif diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/qeglfsintegration.cpp index 0dcde9ca36..8cebe16775 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsintegration.cpp @@ -49,9 +49,11 @@ #include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> #include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> +#include <QtPlatformSupport/private/qeglconvenience_p.h> #include <QtPlatformSupport/private/qeglplatformcontext_p.h> +#include <QtPlatformSupport/private/qeglpbuffer_p.h> -#if !defined(QT_NO_EVDEV) +#if !defined(QT_NO_EVDEV) && !defined(Q_OS_ANDROID) #include <QtPlatformSupport/private/qevdevmousemanager_p.h> #include <QtPlatformSupport/private/qevdevkeyboardmanager_p.h> #include <QtPlatformSupport/private/qevdevtouch_p.h> @@ -61,6 +63,7 @@ #include <QtGui/QSurfaceFormat> #include <QtGui/QOpenGLContext> #include <QtGui/QScreen> +#include <QtGui/QOffscreenSurface> #include <qpa/qplatformcursor.h> #include "qeglfscontext.h" @@ -74,13 +77,13 @@ QEglFSIntegration::QEglFSIntegration() { QGuiApplicationPrivate::instance()->setEventDispatcher(mEventDispatcher); -#if !defined(QT_NO_EVDEV) +#if !defined(QT_NO_EVDEV) && !defined(Q_OS_ANDROID) new QEvdevKeyboardManager(QLatin1String("EvdevKeyboard"), QString() /* spec */, this); new QEvdevMouseManager(QLatin1String("EvdevMouse"), QString() /* spec */, this); new QEvdevTouchScreenHandlerThread(QString() /* spec */, this); #endif - hooks->platformInit(); + QEglFSHooks::hooks()->platformInit(); EGLint major, minor; @@ -89,7 +92,7 @@ QEglFSIntegration::QEglFSIntegration() qFatal("EGL error"); } - mDisplay = eglGetDisplay(hooks ? hooks->platformDisplay() : EGL_DEFAULT_DISPLAY); + mDisplay = eglGetDisplay(QEglFSHooks::hooks() ? QEglFSHooks::hooks()->platformDisplay() : EGL_DEFAULT_DISPLAY); if (mDisplay == EGL_NO_DISPLAY) { qWarning("Could not open egl display\n"); qFatal("EGL error"); @@ -119,13 +122,13 @@ QEglFSIntegration::~QEglFSIntegration() delete mScreen; eglTerminate(mDisplay); - hooks->platformDestroy(); + QEglFSHooks::hooks()->platformDestroy(); } bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) const { // We assume that devices will have more and not less capabilities - if (hooks && hooks->hasCapability(cap)) + if (QEglFSHooks::hooks() && QEglFSHooks::hooks()->hasCapability(cap)) return true; switch (cap) { @@ -150,7 +153,13 @@ QPlatformBackingStore *QEglFSIntegration::createPlatformBackingStore(QWindow *wi QPlatformOpenGLContext *QEglFSIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { - return new QEglFSContext(context->format(), 0 /*share*/, mDisplay); + return new QEglFSContext(QEglFSHooks::hooks()->surfaceFormatFor(context->format()), context->shareHandle(), mDisplay); +} + +QPlatformOffscreenSurface *QEglFSIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const +{ + QEglFSScreen *screen = static_cast<QEglFSScreen *>(surface->screen()->handle()); + return new QEGLPbuffer(screen->display(), QEglFSHooks::hooks()->surfaceFormatFor(surface->requestedFormat()), surface); } QPlatformFontDatabase *QEglFSIntegration::fontDatabase() const @@ -201,4 +210,29 @@ void *QEglFSIntegration::nativeResourceForContext(const QByteArray &resource, QO return 0; } +EGLConfig QEglFSIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format) +{ + class Chooser : public QEglConfigChooser { + public: + Chooser(EGLDisplay display, QEglFSHooks *hooks) + : QEglConfigChooser(display) + , m_hooks(hooks) + { + } + + protected: + bool filterConfig(EGLConfig config) const + { + return m_hooks->filterConfig(display(), config) && QEglConfigChooser::filterConfig(config); + } + + private: + QEglFSHooks *m_hooks; + }; + + Chooser chooser(display, QEglFSHooks::hooks()); + chooser.setSurfaceFormat(format); + return chooser.chooseConfig(); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.h b/src/plugins/platforms/eglfs/qeglfsintegration.h index 42b132b73a..5427137463 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.h +++ b/src/plugins/platforms/eglfs/qeglfsintegration.h @@ -48,8 +48,6 @@ #include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformscreen.h> -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QEglFSIntegration : public QPlatformIntegration, public QPlatformNativeInterface @@ -63,6 +61,7 @@ public: QPlatformWindow *createPlatformWindow(QWindow *window) const; QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; + QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const; QPlatformNativeInterface *nativeInterface() const; QPlatformFontDatabase *fontDatabase() const; @@ -75,6 +74,11 @@ public: void *nativeResourceForIntegration(const QByteArray &resource); void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context); + QPlatformScreen *screen() const { return mScreen; } + static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format); + + EGLDisplay display() const { return mDisplay; } + private: EGLDisplay mDisplay; QAbstractEventDispatcher *mEventDispatcher; @@ -83,6 +87,5 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // QEGLFSINTEGRATION_H diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.cpp b/src/plugins/platforms/eglfs/qeglfsscreen.cpp index 1bc1e944de..83f50dd382 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.cpp +++ b/src/plugins/platforms/eglfs/qeglfsscreen.cpp @@ -56,7 +56,7 @@ QEglFSScreen::QEglFSScreen(EGLDisplay dpy) static int hideCursor = qgetenv("QT_QPA_EGLFS_HIDECURSOR").toInt(); if (!hideCursor) { - if (QEglFSCursor *customCursor = hooks->createCursor(this)) + if (QEglFSCursor *customCursor = QEglFSHooks::hooks()->createCursor(this)) m_cursor = customCursor; else m_cursor = new QEglFSCursor(this); @@ -70,17 +70,22 @@ QEglFSScreen::~QEglFSScreen() QRect QEglFSScreen::geometry() const { - return QRect(QPoint(0, 0), hooks->screenSize()); + return QRect(QPoint(0, 0), QEglFSHooks::hooks()->screenSize()); } int QEglFSScreen::depth() const { - return hooks->screenDepth(); + return QEglFSHooks::hooks()->screenDepth(); } QImage::Format QEglFSScreen::format() const { - return hooks->screenFormat(); + return QEglFSHooks::hooks()->screenFormat(); +} + +QSizeF QEglFSScreen::physicalSize() const +{ + return QEglFSHooks::hooks()->physicalScreenSize(); } QPlatformCursor *QEglFSScreen::cursor() const diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.h b/src/plugins/platforms/eglfs/qeglfsscreen.h index 309791d6c2..8d3c5dbaec 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.h +++ b/src/plugins/platforms/eglfs/qeglfsscreen.h @@ -63,6 +63,8 @@ public: int depth() const; QImage::Format format() const; + QSizeF physicalSize() const; + QPlatformCursor *cursor() const; EGLDisplay display() const { return m_dpy; } diff --git a/src/plugins/platforms/eglfs/qeglfswindow.cpp b/src/plugins/platforms/eglfs/qeglfswindow.cpp index ceb04901c4..68cef6253e 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.cpp +++ b/src/plugins/platforms/eglfs/qeglfswindow.cpp @@ -76,18 +76,36 @@ void QEglFSWindow::create() return; if (window()->type() == Qt::Desktop) { - QRect rect(QPoint(), hooks->screenSize()); + QRect rect(QPoint(), QEglFSHooks::hooks()->screenSize()); QPlatformWindow::setGeometry(rect); QWindowSystemInterface::handleGeometryChange(window(), rect); return; } EGLDisplay display = (static_cast<QEglFSScreen *>(window()->screen()->handle()))->display(); - QSurfaceFormat platformFormat = hooks->surfaceFormatFor(window()->requestedFormat()); - EGLConfig config = q_configFromGLFormat(display, platformFormat); - m_format = q_glFormatFromConfig(display, config); - m_window = hooks->createNativeWindow(hooks->screenSize(), m_format); - m_surface = eglCreateWindowSurface(display, config, m_window, NULL); + QSurfaceFormat platformFormat = QEglFSHooks::hooks()->surfaceFormatFor(window()->requestedFormat()); + m_config = QEglFSIntegration::chooseConfig(display, platformFormat); + m_format = q_glFormatFromConfig(display, m_config); + resetSurface(); +} + +void QEglFSWindow::invalidateSurface() +{ + // Native surface has been deleted behind our backs + m_window = 0; + if (m_surface != 0) { + EGLDisplay display = (static_cast<QEglFSScreen *>(window()->screen()->handle()))->display(); + eglDestroySurface(display, m_surface); + m_surface = 0; + } +} + +void QEglFSWindow::resetSurface() +{ + EGLDisplay display = static_cast<QEglFSScreen *>(screen())->display(); + + m_window = QEglFSHooks::hooks()->createNativeWindow(QEglFSHooks::hooks()->screenSize(), m_format); + m_surface = eglCreateWindowSurface(display, m_config, m_window, NULL); if (m_surface == EGL_NO_SURFACE) { EGLint error = eglGetError(); eglTerminate(display); @@ -98,13 +116,13 @@ void QEglFSWindow::create() void QEglFSWindow::destroy() { if (m_surface) { - EGLDisplay display = (static_cast<QEglFSScreen *>(window()->screen()->handle()))->display(); + EGLDisplay display = static_cast<QEglFSScreen *>(screen())->display(); eglDestroySurface(display, m_surface); m_surface = 0; } if (m_window) { - hooks->destroyNativeWindow(m_window); + QEglFSHooks::hooks()->destroyNativeWindow(m_window); m_window = 0; } } @@ -113,9 +131,8 @@ void QEglFSWindow::setGeometry(const QRect &) { // We only support full-screen windows QRect rect(screen()->availableGeometry()); - QWindowSystemInterface::handleGeometryChange(window(), rect); - QPlatformWindow::setGeometry(rect); + QWindowSystemInterface::handleGeometryChange(window(), rect); } void QEglFSWindow::setWindowState(Qt::WindowState) diff --git a/src/plugins/platforms/eglfs/qeglfswindow.h b/src/plugins/platforms/eglfs/qeglfswindow.h index 706bbddd22..a351b4a6f4 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.h +++ b/src/plugins/platforms/eglfs/qeglfswindow.h @@ -65,10 +65,14 @@ public: void create(); void destroy(); + virtual void invalidateSurface(); + virtual void resetSurface(); + private: WId m_winid; EGLSurface m_surface; EGLNativeWindowType m_window; + EGLConfig m_config; QSurfaceFormat m_format; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/ios.json b/src/plugins/platforms/ios/ios.json new file mode 100644 index 0000000000..f0b766dae1 --- /dev/null +++ b/src/plugins/platforms/ios/ios.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "ios" ] +} diff --git a/src/plugins/platforms/ios/ios.pro b/src/plugins/platforms/ios/ios.pro new file mode 100644 index 0000000000..842ff17f1c --- /dev/null +++ b/src/plugins/platforms/ios/ios.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += plugin.pro qtmain.pro diff --git a/src/plugins/platforms/ios/plugin.mm b/src/plugins/platforms/ios/plugin.mm new file mode 100644 index 0000000000..a93b6037ad --- /dev/null +++ b/src/plugins/platforms/ios/plugin.mm @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** 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 <qpa/qplatformintegrationplugin.h> +#include <qpa/qplatformthemeplugin.h> +#include "qiosintegration.h" + +QT_BEGIN_NAMESPACE + +class QIOSIntegrationPlugin : public QPlatformIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.1" FILE "ios.json") + public: + QPlatformIntegration *create(const QString&, const QStringList&); +}; + +QPlatformIntegration * QIOSIntegrationPlugin::create(const QString& system, const QStringList& paramList) +{ + Q_UNUSED(paramList); + if (system.toLower() == "ios") + return new QIOSIntegration; + + return 0; +} + +QT_END_NAMESPACE + +#include "plugin.moc" + +Q_IMPORT_PLUGIN(QIOSIntegrationPlugin) diff --git a/src/plugins/platforms/ios/plugin.pro b/src/plugins/platforms/ios/plugin.pro new file mode 100644 index 0000000000..591a0a67ed --- /dev/null +++ b/src/plugins/platforms/ios/plugin.pro @@ -0,0 +1,36 @@ +TARGET = qios + +PLUGIN_TYPE = platforms +load(qt_plugin) + +QT += core-private gui-private platformsupport-private +LIBS += -framework UIKit -framework QuartzCore + +OBJECTIVE_SOURCES = \ + plugin.mm \ + qiosintegration.mm \ + qioswindow.mm \ + qiosscreen.mm \ + qioseventdispatcher.mm \ + qiosbackingstore.mm \ + qiosapplicationdelegate.mm \ + qiosviewcontroller.mm \ + qioscontext.mm \ + qiosinputcontext.mm \ + qiostheme.mm \ + qiosglobal.mm + +HEADERS = \ + qiosintegration.h \ + qioswindow.h \ + qiosscreen.h \ + qioseventdispatcher.h \ + qiosbackingstore.h \ + qiosapplicationdelegate.h \ + qiosviewcontroller.h \ + qioscontext.h \ + qiosinputcontext.h \ + qiostheme.h \ + qiosglobal.h + +#HEADERS = qiossoftwareinputhandler.h diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.h b/src/plugins/platforms/ios/qiosapplicationdelegate.h new file mode 100644 index 0000000000..442b37f1b3 --- /dev/null +++ b/src/plugins/platforms/ios/qiosapplicationdelegate.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#import <UIKit/UIKit.h> +#import <QtGui/QtGui> + +@interface QIOSApplicationDelegate : UIResponder <UIApplicationDelegate> + +@property (strong, nonatomic) UIWindow *window; + +@end + +@interface QIOSMainWrapperApplicationDelegate : QIOSApplicationDelegate +@end + diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.mm b/src/plugins/platforms/ios/qiosapplicationdelegate.mm new file mode 100644 index 0000000000..41a3fff84f --- /dev/null +++ b/src/plugins/platforms/ios/qiosapplicationdelegate.mm @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#import "qiosapplicationdelegate.h" +#include "qioswindow.h" +#include <QtCore/QtCore> + +@implementation QIOSApplicationDelegate + +@synthesize window; + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + Q_UNUSED(application) + Q_UNUSED(launchOptions) + + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + Q_UNUSED(application) +} + +- (void)applicationDidEnterBackground:(UIApplication *)application +{ + Q_UNUSED(application) +} + +- (void)applicationWillEnterForeground:(UIApplication *)application +{ + Q_UNUSED(application) + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + Q_UNUSED(application) + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + Q_UNUSED(application) + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +- (void)dealloc +{ + [window release]; + [super dealloc]; +} + +@end + + diff --git a/src/plugins/platforms/ios/qiosbackingstore.h b/src/plugins/platforms/ios/qiosbackingstore.h new file mode 100644 index 0000000000..c110f0e4d1 --- /dev/null +++ b/src/plugins/platforms/ios/qiosbackingstore.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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 QIOSBACKINGSTORE_H +#define QIOSBACKINGSTORE_H + +#include <qpa/qplatformbackingstore.h> + +QT_BEGIN_NAMESPACE + +class QIOSBackingStore : public QPlatformBackingStore +{ +public: + QIOSBackingStore(QWindow *window); + ~QIOSBackingStore(); + + QPaintDevice *paintDevice(); + + void beginPaint(const QRegion &); + void endPaint(); + + void flush(QWindow *window, const QRegion ®ion, const QPoint &offset); + void resize(const QSize &size, const QRegion &staticContents); + +private: + QOpenGLContext *m_context; + QPaintDevice *m_device; +}; + +QT_END_NAMESPACE + +#endif // QIOSBACKINGSTORE_H diff --git a/src/plugins/platforms/ios/qiosbackingstore.mm b/src/plugins/platforms/ios/qiosbackingstore.mm new file mode 100644 index 0000000000..566ff3a672 --- /dev/null +++ b/src/plugins/platforms/ios/qiosbackingstore.mm @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** 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 "qiosbackingstore.h" +#include "qioswindow.h" + +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLPaintDevice> + +#include <QtDebug> + +QIOSBackingStore::QIOSBackingStore(QWindow *window) + : QPlatformBackingStore(window) + , m_context(new QOpenGLContext) + , m_device(0) +{ + m_context->setFormat(window->requestedFormat()); + m_context->setScreen(window->screen()); + m_context->create(); +} + +QIOSBackingStore::~QIOSBackingStore() +{ + delete m_context; + delete m_device; +} + +void QIOSBackingStore::beginPaint(const QRegion &) +{ + // Needed to prevent QOpenGLContext::makeCurrent() from failing + window()->setSurfaceType(QSurface::OpenGLSurface); + + m_context->makeCurrent(window()); + + static_cast<QOpenGLPaintDevice *>(paintDevice())->setSize(window()->size()); + QIOSWindow *iosWindow = static_cast<QIOSWindow *>(window()->handle()); + static_cast<QOpenGLPaintDevice *>(paintDevice())->setSize(window()->size() * iosWindow->devicePixelRatio()); +} + +QPaintDevice *QIOSBackingStore::paintDevice() +{ + if (!m_device) { + QIOSWindow *iosWindow = static_cast<QIOSWindow *>(window()->handle()); + QOpenGLPaintDevice *openGLDevice = new QOpenGLPaintDevice(window()->size() * iosWindow->devicePixelRatio()); + openGLDevice->setDevicePixelRatio(iosWindow->devicePixelRatio()); + m_device = openGLDevice; + } + + return m_device; +} + +void QIOSBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(region); + Q_UNUSED(offset); + + if (window != this->window()) { + // We skip flushing raster-based child windows, to avoid the extra cost of copying from the + // parent FBO into the child FBO. Since the child is already drawn inside the parent FBO, it + // will become visible when flushing the parent. The only case we end up not supporting is if + // the child window overlaps a sibling window that's draws using a separate QOpenGLContext. + return; + } + m_context->swapBuffers(window); +} + +void QIOSBackingStore::endPaint() +{ + // Calling makeDone() on the context here would be an option, + // but is not needed, and would actually add some overhead. +} + +void QIOSBackingStore::resize(const QSize &size, const QRegion &staticContents) +{ + Q_UNUSED(staticContents); + + // Resizing the backing store would in our case mean resizing the QWindow, + // as we cheat and use an QOpenGLPaintDevice that we target at the window. + // That's probably not what the user intended, so we ignore resizes of the + // backing store and always keep the paint device's size in sync with the + // window size in beginPaint(). + + if (size != window()->size()) + qWarning() << "QIOSBackingStore needs to have the same size as its window"; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qioscontext.h b/src/plugins/platforms/ios/qioscontext.h new file mode 100644 index 0000000000..082ec4794c --- /dev/null +++ b/src/plugins/platforms/ios/qioscontext.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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 QIOSCONTEXT_H +#define QIOSCONTEXT_H + +#include <qpa/qplatformopenglcontext.h> + +@class EAGLContext; + +QT_BEGIN_NAMESPACE + +class QIOSContext : public QObject, public QPlatformOpenGLContext +{ + Q_OBJECT + +public: + QIOSContext(QOpenGLContext *context); + ~QIOSContext(); + + QSurfaceFormat format() const; + + void swapBuffers(QPlatformSurface *surface); + + bool makeCurrent(QPlatformSurface *surface); + void doneCurrent(); + + GLuint defaultFramebufferObject(QPlatformSurface *) const; + QFunctionPointer getProcAddress(const QByteArray &procName); + +private Q_SLOTS: + void windowDestroyed(QObject *object); + +private: + EAGLContext *m_eaglContext; + QSurfaceFormat m_format; + + struct FramebufferObject { + GLuint handle; + GLuint colorRenderbuffer; + GLuint depthRenderbuffer; + GLint renderbufferWidth; + GLint renderbufferHeight; + }; + + static void deleteBuffers(const FramebufferObject &framebufferObject); + + mutable QHash<QWindow *, FramebufferObject> m_framebufferObjects; +}; + +QT_END_NAMESPACE + +#endif // QIOSCONTEXT_H diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm new file mode 100644 index 0000000000..d3966964e0 --- /dev/null +++ b/src/plugins/platforms/ios/qioscontext.mm @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** 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 "qioscontext.h" +#include "qioswindow.h" + +#include <dlfcn.h> + +#include <QtGui/QOpenGlContext> + +#import <OpenGLES/EAGL.h> +#import <QuartzCore/CAEAGLLayer.h> + +QIOSContext::QIOSContext(QOpenGLContext *context) + : QPlatformOpenGLContext() + , m_eaglContext([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]) +{ + // Start out with the requested format + QSurfaceFormat format = context->format(); + + format.setRenderableType(QSurfaceFormat::OpenGLES); + format.setMajorVersion(2); + format.setMinorVersion(0); + + // Even though iOS internally double-buffers its rendering, we + // report single-buffered here since the buffer remains unchanged + // when swapping unlesss you manually clear it yourself. + format.setSwapBehavior(QSurfaceFormat::SingleBuffer); + + m_format = format; +} + +QIOSContext::~QIOSContext() +{ + [EAGLContext setCurrentContext:m_eaglContext]; + + foreach (const FramebufferObject &framebufferObject, m_framebufferObjects) + deleteBuffers(framebufferObject); + + [EAGLContext setCurrentContext:nil]; + [m_eaglContext release]; +} + +void QIOSContext::deleteBuffers(const FramebufferObject &framebufferObject) +{ + if (framebufferObject.handle) + glDeleteFramebuffers(1, &framebufferObject.handle); + if (framebufferObject.colorRenderbuffer) + glDeleteRenderbuffers(1, &framebufferObject.colorRenderbuffer); + if (framebufferObject.depthRenderbuffer) + glDeleteRenderbuffers(1, &framebufferObject.depthRenderbuffer); +} + +QSurfaceFormat QIOSContext::format() const +{ + return m_format; +} + +bool QIOSContext::makeCurrent(QPlatformSurface *surface) +{ + Q_ASSERT(surface && surface->surface()->surfaceType() == QSurface::OpenGLSurface); + + [EAGLContext setCurrentContext:m_eaglContext]; + glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject(surface)); + + return true; +} + +void QIOSContext::doneCurrent() +{ + [EAGLContext setCurrentContext:nil]; +} + +void QIOSContext::swapBuffers(QPlatformSurface *surface) +{ + Q_ASSERT(surface && surface->surface()->surfaceType() == QSurface::OpenGLSurface); + Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window); + QWindow *window = static_cast<QWindow *>(surface->surface()); + Q_ASSERT(m_framebufferObjects.contains(window)); + + [EAGLContext setCurrentContext:m_eaglContext]; + glBindRenderbuffer(GL_RENDERBUFFER, m_framebufferObjects[window].colorRenderbuffer); + [m_eaglContext presentRenderbuffer:GL_RENDERBUFFER]; +} + +GLuint QIOSContext::defaultFramebufferObject(QPlatformSurface *surface) const +{ + Q_ASSERT(surface && surface->surface()->surfaceClass() == QSurface::Window); + QWindow *window = static_cast<QWindow *>(surface->surface()); + + FramebufferObject &framebufferObject = m_framebufferObjects[window]; + + // Set up an FBO for the window if it hasn't been created yet + if (!framebufferObject.handle) { + [EAGLContext setCurrentContext:m_eaglContext]; + + glGenFramebuffers(1, &framebufferObject.handle); + glBindFramebuffer(GL_FRAMEBUFFER, framebufferObject.handle); + + glGenRenderbuffers(1, &framebufferObject.colorRenderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.colorRenderbuffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + framebufferObject.colorRenderbuffer); + + if (m_format.depthBufferSize() > 0 || m_format.stencilBufferSize() > 0) { + glGenRenderbuffers(1, &framebufferObject.depthRenderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.depthRenderbuffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, + framebufferObject.depthRenderbuffer); + + if (m_format.stencilBufferSize() > 0) + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, + framebufferObject.depthRenderbuffer); + } + + connect(window, SIGNAL(destroyed(QObject*)), this, SLOT(windowDestroyed(QObject*))); + } + + // Ensure that the FBO's buffers match the size of the window + QIOSWindow *platformWindow = static_cast<QIOSWindow *>(surface); + if (framebufferObject.renderbufferWidth != platformWindow->effectiveWidth() || + framebufferObject.renderbufferHeight != platformWindow->effectiveHeight()) { + + glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.colorRenderbuffer); + UIView *view = reinterpret_cast<UIView *>(platformWindow->winId()); + CAEAGLLayer *layer = static_cast<CAEAGLLayer *>(view.layer); + [m_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer]; + + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferObject.renderbufferWidth); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferObject.renderbufferHeight); + + if (framebufferObject.depthRenderbuffer) { + glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.depthRenderbuffer); + + // FIXME: Support more fine grained control over depth/stencil buffer sizes + if (m_format.stencilBufferSize() > 0) + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, + framebufferObject.renderbufferWidth, framebufferObject.renderbufferHeight); + else + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, + framebufferObject.renderbufferWidth, framebufferObject.renderbufferHeight); + } + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER)); + } + + return framebufferObject.handle; +} + +void QIOSContext::windowDestroyed(QObject *object) +{ + QWindow *window = static_cast<QWindow *>(object); + if (m_framebufferObjects.contains(window)) { + deleteBuffers(m_framebufferObjects[window]); + m_framebufferObjects.remove(window); + } +} + +QFunctionPointer QIOSContext::getProcAddress(const QByteArray& functionName) +{ + return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_NEXT, functionName.constData())); +} + +#include "moc_qioscontext.cpp" + diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h new file mode 100644 index 0000000000..53a75618ce --- /dev/null +++ b/src/plugins/platforms/ios/qioseventdispatcher.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (c) 2007-2008, Apple, Inc. +** +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** +** * Redistributions of source code must retain the above copyright notice, +** this list of conditions and the following disclaimer. +** +** * Redistributions in binary form must reproduce the above copyright notice, +** this list of conditions and the following disclaimer in the documentation +** and/or other materials provided with the distribution. +** +** * Neither the name of Apple, Inc. nor the names of its contributors +** may be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_IOS_P_H +#define QEVENTDISPATCHER_IOS_P_H + +#include <QtCore/qabstracteventdispatcher.h> +#include <QtCore/private/qtimerinfo_unix_p.h> +#include <QtPlatformSupport/private/qcfsocketnotifier_p.h> +#include <CoreFoundation/CoreFoundation.h> + +QT_BEGIN_NAMESPACE + +class QIOSEventDispatcher : public QAbstractEventDispatcher +{ + Q_OBJECT + +public: + explicit QIOSEventDispatcher(QObject *parent = 0); + ~QIOSEventDispatcher(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + bool hasPendingEvents(); + + void registerSocketNotifier(QSocketNotifier *notifier); + void unregisterSocketNotifier(QSocketNotifier *notifier); + + void registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object); + bool unregisterTimer(int timerId); + bool unregisterTimers(QObject *object); + QList<QAbstractEventDispatcher::TimerInfo> registeredTimers(QObject *object) const; + + int remainingTime(int timerId); + + void wakeUp(); + void interrupt(); + void flush(); + +private: + bool m_interrupted; + + CFRunLoopSourceRef m_postedEventsRunLoopSource; + CFRunLoopSourceRef m_blockingTimerRunLoopSource; + + QTimerInfoList m_timerInfoList; + CFRunLoopTimerRef m_runLoopTimerRef; + + QCFSocketNotifier m_cfSocketNotifier; + + void processPostedEvents(); + void maybeStartCFRunLoopTimer(); + void maybeStopCFRunLoopTimer(); + + static void postedEventsRunLoopCallback(void *info); + static void nonBlockingTimerRunLoopCallback(CFRunLoopTimerRef, void *info); + static void blockingTimerRunLoopCallback(void *info); +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_IOS_P_H diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm new file mode 100644 index 0000000000..e9bf039047 --- /dev/null +++ b/src/plugins/platforms/ios/qioseventdispatcher.mm @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** 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 "qioseventdispatcher.h" +#import "qiosapplicationdelegate.h" +#include <qdebug.h> +#include <qpa/qwindowsysteminterface.h> +#include <QtCore/QThread> +#include <QtCore/private/qcoreapplication_p.h> +#include <UIKit/UIApplication.h> + +QT_BEGIN_NAMESPACE +QT_USE_NAMESPACE + +static Boolean runLoopSourceEqualCallback(const void *info1, const void *info2) +{ + return info1 == info2; +} + +void QIOSEventDispatcher::postedEventsRunLoopCallback(void *info) +{ + QIOSEventDispatcher *self = static_cast<QIOSEventDispatcher *>(info); + self->processPostedEvents(); +} + +void QIOSEventDispatcher::nonBlockingTimerRunLoopCallback(CFRunLoopTimerRef, void *info) +{ + // The (one and only) CFRunLoopTimer has fired, which means that at least + // one QTimer should now fire as well. Note that CFRunLoopTimer's callback will + // never recurse. So if the app starts a new QEventLoop within this callback, other + // timers will stop working. The work-around is to forward the callback to a + // dedicated CFRunLoopSource that can recurse: + QIOSEventDispatcher *self = static_cast<QIOSEventDispatcher *>(info); + CFRunLoopSourceSignal(self->m_blockingTimerRunLoopSource); +} + +void QIOSEventDispatcher::blockingTimerRunLoopCallback(void *info) +{ + // TODO: + // We also need to block this new timer source + // along with the posted event source when calling processEvents() + // "manually" to prevent livelock deep in CFRunLoop. + + QIOSEventDispatcher *self = static_cast<QIOSEventDispatcher *>(info); + self->m_timerInfoList.activateTimers(); + self->maybeStartCFRunLoopTimer(); +} + +void QIOSEventDispatcher::maybeStartCFRunLoopTimer() +{ + // Find out when the next registered timer should fire, and schedule + // runLoopTimer accordingly. If the runLoopTimer does not yet exist, and + // at least one timer is registered, start by creating the timer: + if (m_timerInfoList.isEmpty()) { + Q_ASSERT(m_runLoopTimerRef == 0); + return; + } + + CFAbsoluteTime ttf = CFAbsoluteTimeGetCurrent(); + CFTimeInterval interval; + + if (m_runLoopTimerRef == 0) { + // start the CFRunLoopTimer + CFTimeInterval oneyear = CFTimeInterval(3600. * 24. * 365.); + + // calculate when the next timer should fire: + struct timespec tv; + if (m_timerInfoList.timerWait(tv)) { + interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); + } else { + // this shouldn't really happen, but in case it does, set the timer + // to fire a some point in the distant future: + interval = oneyear; + } + + ttf += interval; + CFRunLoopTimerContext info = { 0, this, 0, 0, 0 }; + // create the timer with a large interval, as recommended by the CFRunLoopTimerSetNextFireDate() + // documentation, since we will adjust the timer's time-to-fire as needed to keep Qt timers working + m_runLoopTimerRef = CFRunLoopTimerCreate(0, ttf, oneyear, 0, 0, QIOSEventDispatcher::nonBlockingTimerRunLoopCallback, &info); + Q_ASSERT(m_runLoopTimerRef != 0); + + CFRunLoopAddTimer(CFRunLoopGetMain(), m_runLoopTimerRef, kCFRunLoopCommonModes); + } else { + struct timespec tv; + // Calculate when the next timer should fire: + if (m_timerInfoList.timerWait(tv)) { + interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); + } else { + // no timers can fire, but we cannot stop the CFRunLoopTimer, set the timer to fire at some + // point in the distant future (the timer interval is one year) + interval = CFRunLoopTimerGetInterval(m_runLoopTimerRef); + } + + ttf += interval; + CFRunLoopTimerSetNextFireDate(m_runLoopTimerRef, ttf); + } +} + +void QIOSEventDispatcher::maybeStopCFRunLoopTimer() +{ + if (m_runLoopTimerRef == 0) + return; + + CFRunLoopTimerInvalidate(m_runLoopTimerRef); + CFRelease(m_runLoopTimerRef); + m_runLoopTimerRef = 0; +} + +void QIOSEventDispatcher::processPostedEvents() +{ + QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::AllEvents); +} + +QIOSEventDispatcher::QIOSEventDispatcher(QObject *parent) + : QAbstractEventDispatcher(parent) + , m_interrupted(false) + , m_runLoopTimerRef(0) +{ + m_cfSocketNotifier.setHostEventDispatcher(this); + + CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); + CFRunLoopSourceContext context; + bzero(&context, sizeof(CFRunLoopSourceContext)); + context.equal = runLoopSourceEqualCallback; + context.info = this; + + // source used to handle timers: + context.perform = QIOSEventDispatcher::blockingTimerRunLoopCallback; + m_blockingTimerRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); + Q_ASSERT(m_blockingTimerRunLoopSource); + CFRunLoopAddSource(mainRunLoop, m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); + + // source used to handle posted events: + context.perform = QIOSEventDispatcher::postedEventsRunLoopCallback; + m_postedEventsRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); + Q_ASSERT(m_postedEventsRunLoopSource); + CFRunLoopAddSource(mainRunLoop, m_postedEventsRunLoopSource, kCFRunLoopCommonModes); +} + +QIOSEventDispatcher::~QIOSEventDispatcher() +{ + CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); + CFRunLoopRemoveSource(mainRunLoop, m_postedEventsRunLoopSource, kCFRunLoopCommonModes); + CFRelease(m_postedEventsRunLoopSource); + + qDeleteAll(m_timerInfoList); + maybeStopCFRunLoopTimer(); + CFRunLoopRemoveSource(CFRunLoopGetMain(), m_blockingTimerRunLoopSource, kCFRunLoopCommonModes); + CFRelease(m_blockingTimerRunLoopSource); + + m_cfSocketNotifier.removeSocketNotifiers(); +} + +bool QIOSEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + m_interrupted = false; + bool eventsProcessed = false; + + bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents; + bool execFlagSet = (flags & QEventLoop::DialogExec) || (flags & QEventLoop::EventLoopExec); + bool useExecMode = execFlagSet && !excludeUserEvents; + + if (useExecMode) { + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + while ([runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]] && !m_interrupted); + eventsProcessed = true; + } else { + if (!(flags & QEventLoop::WaitForMoreEvents)) + wakeUp(); + eventsProcessed = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } + return eventsProcessed; +} + +bool QIOSEventDispatcher::hasPendingEvents() +{ + qDebug() << __FUNCTION__ << "not implemented"; + return false; +} + +void QIOSEventDispatcher::registerSocketNotifier(QSocketNotifier *notifier) +{ + m_cfSocketNotifier.registerSocketNotifier(notifier); +} + +void QIOSEventDispatcher::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + m_cfSocketNotifier.unregisterSocketNotifier(notifier); +} + +void QIOSEventDispatcher::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *obj) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1 || interval < 0 || !obj) { + qWarning("QIOSEventDispatcher::registerTimer: invalid arguments"); + return; + } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QIOSEventDispatcher: timers cannot be started from another thread"); + return; + } +#endif + + m_timerInfoList.registerTimer(timerId, interval, timerType, obj); + maybeStartCFRunLoopTimer(); +} + +bool QIOSEventDispatcher::unregisterTimer(int timerId) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1) { + qWarning("QIOSEventDispatcher::unregisterTimer: invalid argument"); + return false; + } else if (thread() != QThread::currentThread()) { + qWarning("QObject::killTimer: timers cannot be stopped from another thread"); + return false; + } +#endif + + bool returnValue = m_timerInfoList.unregisterTimer(timerId); + m_timerInfoList.isEmpty() ? maybeStopCFRunLoopTimer() : maybeStartCFRunLoopTimer(); + return returnValue; +} + +bool QIOSEventDispatcher::unregisterTimers(QObject *object) +{ +#ifndef QT_NO_DEBUG + if (!object) { + qWarning("QIOSEventDispatcher::unregisterTimers: invalid argument"); + return false; + } else if (object->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::killTimers: timers cannot be stopped from another thread"); + return false; + } +#endif + + bool returnValue = m_timerInfoList.unregisterTimers(object); + m_timerInfoList.isEmpty() ? maybeStopCFRunLoopTimer() : maybeStartCFRunLoopTimer(); + return returnValue; +} + +QList<QAbstractEventDispatcher::TimerInfo> QIOSEventDispatcher::registeredTimers(QObject *object) const +{ +#ifndef QT_NO_DEBUG + if (!object) { + qWarning("QIOSEventDispatcher:registeredTimers: invalid argument"); + return QList<TimerInfo>(); + } +#endif + + return m_timerInfoList.registeredTimers(object); +} + +int QIOSEventDispatcher::remainingTime(int timerId) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1) { + qWarning("QIOSEventDispatcher::remainingTime: invalid argument"); + return -1; + } +#endif + + return m_timerInfoList.timerRemainingTime(timerId); +} + +void QIOSEventDispatcher::wakeUp() +{ + CFRunLoopSourceSignal(m_postedEventsRunLoopSource); + CFRunLoopWakeUp(CFRunLoopGetMain()); +} + +void QIOSEventDispatcher::interrupt() +{ + wakeUp(); + m_interrupted = true; +} + +void QIOSEventDispatcher::flush() +{ + // X11 only. +} + +QT_END_NAMESPACE + diff --git a/src/plugins/platforms/ios/qiosglobal.h b/src/plugins/platforms/ios/qiosglobal.h new file mode 100644 index 0000000000..3be9f8bb21 --- /dev/null +++ b/src/plugins/platforms/ios/qiosglobal.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** 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 QIOSGLOBAL_H +#define QIOSGLOBAL_H + +#import <UIKit/UIKit.h> +#include <QtCore/QtCore> + +@class QIOSViewController; + +QT_BEGIN_NAMESPACE + +class QPlatformScreen; + +bool isQtApplication(); +QIOSViewController *rootViewController(); + +CGRect toCGRect(const QRect &rect); +QRect fromCGRect(const CGRect &rect); +CGPoint toCGPoint(const QPoint &point); +QPoint fromCGPoint(const CGPoint &point); +Qt::ScreenOrientation toQtScreenOrientation(UIDeviceOrientation uiDeviceOrientation); +UIDeviceOrientation fromQtScreenOrientation(Qt::ScreenOrientation qtOrientation); +QRect fromPortraitToPrimary(const QRect &rect, QPlatformScreen *screen); + +QT_END_NAMESPACE + +#endif // QIOSGLOBAL_H diff --git a/src/plugins/platforms/ios/qiosglobal.mm b/src/plugins/platforms/ios/qiosglobal.mm new file mode 100644 index 0000000000..5860078372 --- /dev/null +++ b/src/plugins/platforms/ios/qiosglobal.mm @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** 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 "qiosglobal.h" +#include "qiosapplicationdelegate.h" +#include "qiosviewcontroller.h" +#include "qiosscreen.h" + +QT_BEGIN_NAMESPACE + +bool isQtApplication() +{ + // Returns true if the plugin is in full control of the whole application. This means + // that we control the application delegate and the top view controller, and can take + // actions that impacts all parts of the application. The opposite means that we are + // embedded inside a native iOS application, and should be more focused on playing along + // with native UIControls, and less inclined to change structures that lies outside the + // scope of our QWindows/UIViews. + static bool isQt = ([[UIApplication sharedApplication].delegate isKindOfClass:[QIOSApplicationDelegate class]]); + return isQt; +} + +QIOSViewController *rootViewController() +{ + static QIOSViewController *c = isQtApplication() ? + static_cast<QIOSViewController *>([UIApplication sharedApplication].delegate.window.rootViewController) : nil; + return c; +} + +CGRect toCGRect(const QRect &rect) +{ + return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()); +} + +QRect fromCGRect(const CGRect &rect) +{ + return QRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); +} + +CGPoint toCGPoint(const QPoint &point) +{ + return CGPointMake(point.x(), point.y()); +} + +QPoint fromCGPoint(const CGPoint &point) +{ + return QPoint(point.x, point.y); +} + +Qt::ScreenOrientation toQtScreenOrientation(UIDeviceOrientation uiDeviceOrientation) +{ + Qt::ScreenOrientation qtOrientation; + switch (uiDeviceOrientation) { + case UIDeviceOrientationPortraitUpsideDown: + qtOrientation = Qt::InvertedPortraitOrientation; + break; + case UIDeviceOrientationLandscapeLeft: + qtOrientation = Qt::InvertedLandscapeOrientation; + break; + case UIDeviceOrientationLandscapeRight: + qtOrientation = Qt::LandscapeOrientation; + break; + case UIDeviceOrientationFaceUp: + case UIDeviceOrientationFaceDown: + qtOrientation = static_cast<Qt::ScreenOrientation>(-1); // not supported ATM. + break; + default: + qtOrientation = Qt::PortraitOrientation; + break; + } + return qtOrientation; +} + +UIDeviceOrientation fromQtScreenOrientation(Qt::ScreenOrientation qtOrientation) +{ + UIDeviceOrientation uiOrientation; + switch (qtOrientation) { + case Qt::LandscapeOrientation: + uiOrientation = UIDeviceOrientationLandscapeRight; + break; + case Qt::InvertedLandscapeOrientation: + uiOrientation = UIDeviceOrientationLandscapeLeft; + break; + case Qt::InvertedPortraitOrientation: + uiOrientation = UIDeviceOrientationPortraitUpsideDown; + break; + case Qt::PrimaryOrientation: + case Qt::PortraitOrientation: + default: + uiOrientation = UIDeviceOrientationPortrait; + break; + } + return uiOrientation; +} + +QRect fromPortraitToPrimary(const QRect &rect, QPlatformScreen *screen) +{ + // UIScreen is always in portrait. Use this function to convert CGRects + // aligned with UIScreen into whatever is the current orientation of QScreen. + QRect geometry = screen->geometry(); + return geometry.width() < geometry.height() ? rect + : QRect(rect.y(), geometry.width() - rect.width() - rect.x(), rect.height(), rect.width()); +} + +QT_END_NAMESPACE + diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h new file mode 100644 index 0000000000..176ad05733 --- /dev/null +++ b/src/plugins/platforms/ios/qiosinputcontext.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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 QIOSINPUTCONTEXT_H +#define QIOSINPUTCONTEXT_H + +#include <UIKit/UIKit.h> + +#include <qpa/qplatforminputcontext.h> + +QT_BEGIN_NAMESPACE + +@class QIOSKeyboardListener; + +class QIOSInputContext : public QPlatformInputContext +{ +public: + QIOSInputContext(); + ~QIOSInputContext(); + + QRectF keyboardRect() const; + void showInputPanel(); + void hideInputPanel(); + bool isInputPanelVisible() const; + + void focusViewChanged(UIView *view); + +private: + QIOSKeyboardListener *m_keyboardListener; + UIView *m_focusView; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm new file mode 100644 index 0000000000..1d3ab12de9 --- /dev/null +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** 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 "qiosglobal.h" +#include "qiosinputcontext.h" +#include "qioswindow.h" +#include <QGuiApplication> + +@interface QIOSKeyboardListener : NSObject { +@public + QIOSInputContext *m_context; + BOOL m_keyboardVisible; + QRectF m_keyboardRect; +} +@end + +@implementation QIOSKeyboardListener + +- (id)initWithQIOSInputContext:(QIOSInputContext *)context +{ + self = [super init]; + if (self) { + m_context = context; + m_keyboardVisible = NO; + // After the keyboard became undockable (iOS5), UIKeyboardWillShow/UIKeyboardWillHide + // no longer works for all cases. So listen to keyboard frame changes instead: + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(keyboardDidChangeFrame:) + name:@"UIKeyboardDidChangeFrameNotification" object:nil]; + } + return self; +} + +- (void) dealloc +{ + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"UIKeyboardDidChangeFrameNotification" object:nil]; + [super dealloc]; +} + +- (void) keyboardDidChangeFrame:(NSNotification *)notification +{ + CGRect frame; + [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&frame]; + + m_keyboardRect = fromPortraitToPrimary(fromCGRect(frame), QGuiApplication::primaryScreen()->handle()); + m_context->emitKeyboardRectChanged(); + + BOOL visible = CGRectIntersectsRect(frame, [UIScreen mainScreen].bounds); + if (m_keyboardVisible != visible) { + m_keyboardVisible = visible; + m_context->emitInputPanelVisibleChanged(); + } +} + +@end + +QIOSInputContext::QIOSInputContext() + : QPlatformInputContext() + , m_keyboardListener([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this]) + , m_focusView(0) +{ +} + +QIOSInputContext::~QIOSInputContext() +{ + [m_keyboardListener release]; + [m_focusView release]; +} + +QRectF QIOSInputContext::keyboardRect() const +{ + return m_keyboardListener->m_keyboardRect; +} + +void QIOSInputContext::showInputPanel() +{ + // Documentation tells that one should call (and recall, if necessary) becomeFirstResponder/resignFirstResponder + // to show/hide the keyboard. This is slightly inconvenient, since there exist no API to get the current first + // responder. Rather than searching for it from the top, we let the active QIOSWindow tell us which view to use. + // Note that Qt will forward keyevents to whichever QObject that needs it, regardless of which UIView the input + // actually came from. So in this respect, we're undermining iOS' responder chain. + [m_focusView becomeFirstResponder]; +} + +void QIOSInputContext::hideInputPanel() +{ + [m_focusView resignFirstResponder]; +} + +bool QIOSInputContext::isInputPanelVisible() const +{ + return m_keyboardListener->m_keyboardVisible; +} + +void QIOSInputContext::focusViewChanged(UIView *view) +{ + if ([m_focusView isFirstResponder]) + [view becomeFirstResponder]; + [m_focusView release]; + m_focusView = [view retain]; +} diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h new file mode 100644 index 0000000000..329a0a3d9b --- /dev/null +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** 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 QPLATFORMINTEGRATION_UIKIT_H +#define QPLATFORMINTEGRATION_UIKIT_H + +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformnativeinterface.h> +#include <qpa/qwindowsysteminterface.h> + +QT_BEGIN_NAMESPACE + +class QIOSIntegration : public QPlatformIntegration, public QPlatformNativeInterface +{ +public: + QIOSIntegration(); + ~QIOSIntegration(); + + bool hasCapability(Capability cap) const; + + QPlatformWindow *createPlatformWindow(QWindow *window) const; + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + + QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; + + QPlatformFontDatabase *fontDatabase() const; + QPlatformInputContext *inputContext() const; + + QVariant styleHint(StyleHint hint) const; + + QStringList themeNames() const; + QPlatformTheme *createPlatformTheme(const QString &name) const; + + QAbstractEventDispatcher *guiThreadEventDispatcher() const; + QPlatformNativeInterface *nativeInterface() const; + + void *nativeResourceForWindow(const QByteArray &resource, QWindow *window); + + QTouchDevice *touchDevice(); +private: + QPlatformFontDatabase *m_fontDatabase; + QPlatformInputContext *m_inputContext; + QPlatformScreen *m_screen; + QTouchDevice *m_touchDevice; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm new file mode 100644 index 0000000000..c7541c3e38 --- /dev/null +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** 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 "qiosintegration.h" +#include "qioswindow.h" +#include "qiosbackingstore.h" +#include "qiosscreen.h" +#include "qioseventdispatcher.h" +#include "qioscontext.h" +#include "qiosinputcontext.h" +#include "qiostheme.h" + +#include <QtPlatformSupport/private/qcoretextfontdatabase_p.h> + +#include <QtDebug> + +QT_BEGIN_NAMESPACE + +QIOSIntegration::QIOSIntegration() + : m_fontDatabase(new QCoreTextFontDatabase) + , m_inputContext(new QIOSInputContext) + , m_screen(new QIOSScreen(QIOSScreen::MainScreen)) +{ + if (![UIApplication sharedApplication]) { + qWarning() + << "Error: You are creating QApplication before calling UIApplicationMain.\n" + << "If you are writing a native iOS application, and only want to use Qt for\n" + << "parts of the application, a good place to create QApplication is from within\n" + << "'applicationDidFinishLaunching' inside your UIApplication delegate.\n" + << "If you instead create a cross-platform Qt application and do not intend to call\n" + << "UIApplicationMain, you need to link in libqtmain.a, and substitute main with qt_main.\n" + << "This is normally done automatically by qmake.\n"; + exit(-1); + } + + screenAdded(m_screen); + + m_touchDevice = new QTouchDevice; + m_touchDevice->setType(QTouchDevice::TouchScreen); + m_touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition); + QWindowSystemInterface::registerTouchDevice(m_touchDevice); +} + +QIOSIntegration::~QIOSIntegration() +{ + delete m_touchDevice; +} + +bool QIOSIntegration::hasCapability(Capability cap) const +{ + switch (cap) { + case OpenGL: + return true; + case MultipleWindows: + return true; + default: + return QPlatformIntegration::hasCapability(cap); + } +} + +QPlatformWindow *QIOSIntegration::createPlatformWindow(QWindow *window) const +{ + return new QIOSWindow(window); +} + +// Used when the QWindow's surface type is set by the client to QSurface::RasterSurface +QPlatformBackingStore *QIOSIntegration::createPlatformBackingStore(QWindow *window) const +{ + return new QIOSBackingStore(window); +} + +// Used when the QWindow's surface type is set by the client to QSurface::OpenGLSurface +QPlatformOpenGLContext *QIOSIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const +{ + Q_UNUSED(context); + return new QIOSContext(context); +} + +QAbstractEventDispatcher *QIOSIntegration::guiThreadEventDispatcher() const +{ + return new QIOSEventDispatcher(); +} + +QPlatformFontDatabase * QIOSIntegration::fontDatabase() const +{ + return m_fontDatabase; +} + +QPlatformInputContext *QIOSIntegration::inputContext() const +{ + return m_inputContext; +} + +QVariant QIOSIntegration::styleHint(StyleHint hint) const +{ + switch (hint) { + case ShowIsFullScreen: + return true; + default: + return QPlatformIntegration::styleHint(hint); + } +} + +QStringList QIOSIntegration::themeNames() const +{ + return QStringList(QLatin1String(QIOSTheme::name)); +} + +QPlatformTheme *QIOSIntegration::createPlatformTheme(const QString &name) const +{ + if (name == QLatin1String(QIOSTheme::name)) + return new QIOSTheme; + + return QPlatformIntegration::createPlatformTheme(name); +} + +QPlatformNativeInterface *QIOSIntegration::nativeInterface() const +{ + return const_cast<QIOSIntegration *>(this); +} + +void *QIOSIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window) +{ + if (!window || !window->handle()) + return 0; + + QByteArray lowerCaseResource = resource.toLower(); + + QIOSWindow *platformWindow = static_cast<QIOSWindow *>(window->handle()); + + if (lowerCaseResource == "uiview") + return reinterpret_cast<void *>(platformWindow->winId()); + + return 0; +} + +QTouchDevice *QIOSIntegration::touchDevice() +{ + return m_touchDevice; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosscreen.h b/src/plugins/platforms/ios/qiosscreen.h new file mode 100644 index 0000000000..40c7a3ccf7 --- /dev/null +++ b/src/plugins/platforms/ios/qiosscreen.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** 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 QIOSSCREEN_H +#define QIOSSCREEN_H + +#include <UIKit/UIKit.h> + +#include <qpa/qplatformscreen.h> + +@class QIOSOrientationListener; + +QT_BEGIN_NAMESPACE + +class QIOSScreen : public QPlatformScreen +{ +public: + QIOSScreen(unsigned int screenIndex); + ~QIOSScreen(); + + enum ScreenIndex { MainScreen = 0 }; + + QRect geometry() const; + QRect availableGeometry() const; + int depth() const; + QImage::Format format() const; + QSizeF physicalSize() const; + QDpi logicalDpi() const; + qreal devicePixelRatio() const; + + Qt::ScreenOrientation nativeOrientation() const; + Qt::ScreenOrientation orientation() const; + void setOrientationUpdateMask(Qt::ScreenOrientations mask); + + UIScreen *uiScreen() const; + + void setPrimaryOrientation(Qt::ScreenOrientation orientation); + +private: + UIScreen *m_uiScreen; + QRect m_geometry; + QRect m_availableGeometry; + int m_depth; + QSizeF m_physicalSize; + QIOSOrientationListener *m_orientationListener; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm new file mode 100644 index 0000000000..3265ed8e37 --- /dev/null +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** 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 "qiosglobal.h" +#include "qiosscreen.h" +#include "qioswindow.h" +#include <qpa/qwindowsysteminterface.h> +#include "qiosapplicationdelegate.h" +#include "qiosviewcontroller.h" + +#include <sys/sysctl.h> + +@interface QIOSOrientationListener : NSObject { + @public + QIOSScreen *m_screen; +} +- (id) initWithQIOSScreen:(QIOSScreen *)screen; +@end + +@implementation QIOSOrientationListener + +- (id) initWithQIOSScreen:(QIOSScreen *)screen +{ + self = [super init]; + if (self) { + m_screen = screen; + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(orientationChanged:) + name:@"UIDeviceOrientationDidChangeNotification" object:nil]; + } + return self; +} + +- (void) dealloc +{ + [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"UIDeviceOrientationDidChangeNotification" object:nil]; + [super dealloc]; +} + +- (void) orientationChanged:(NSNotification *)notification +{ + Q_UNUSED(notification); + Qt::ScreenOrientation orientation = toQtScreenOrientation([UIDevice currentDevice].orientation); + if (orientation != -1) + QWindowSystemInterface::handleScreenOrientationChange(m_screen->screen(), orientation); +} + +@end + +/*! + Returns the model identifier of the device. + + When running under the simulator, the identifier will not + match the simulated device, but will be x86_64 or i386. +*/ +static QString deviceModelIdentifier() +{ + static const char key[] = "hw.machine"; + + size_t size; + sysctlbyname(key, NULL, &size, NULL, 0); + + char value[size]; + sysctlbyname(key, &value, &size, NULL, 0); + + return QString::fromLatin1(value); +} + +QIOSScreen::QIOSScreen(unsigned int screenIndex) + : QPlatformScreen() + , m_uiScreen([[UIScreen screens] objectAtIndex:qMin(screenIndex, [[UIScreen screens] count] - 1)]) + , m_orientationListener(0) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + QString deviceIdentifier = deviceModelIdentifier(); + + if (deviceIdentifier == QStringLiteral("iPhone2,1") /* iPhone 3GS */ + || deviceIdentifier == QStringLiteral("iPod3,1") /* iPod touch 3G */) { + m_depth = 18; + } else { + m_depth = 24; + } + + int unscaledDpi = 163; // Regular iPhone DPI + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad + && deviceIdentifier != QStringLiteral("iPad2,5") /* iPad Mini */) { + unscaledDpi = 132; + }; + + CGRect bounds = [m_uiScreen bounds]; + m_geometry = QRect(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height); + + CGRect frame = m_uiScreen.applicationFrame; + m_availableGeometry = QRect(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); + + const qreal millimetersPerInch = 25.4; + m_physicalSize = QSizeF(m_geometry.size()) / unscaledDpi * millimetersPerInch; + + if (isQtApplication()) { + // When in a non-mixed environment, let QScreen follow the current interface orientation: + setPrimaryOrientation(toQtScreenOrientation(rootViewController().interfaceOrientation)); + } + + [pool release]; +} + +QIOSScreen::~QIOSScreen() +{ + [m_orientationListener release]; +} + +QRect QIOSScreen::geometry() const +{ + return m_geometry; +} + +QRect QIOSScreen::availableGeometry() const +{ + return m_availableGeometry; +} + +int QIOSScreen::depth() const +{ + return m_depth; +} + +QImage::Format QIOSScreen::format() const +{ + return QImage::Format_ARGB32_Premultiplied; +} + +QSizeF QIOSScreen::physicalSize() const +{ + return m_physicalSize; +} + +QDpi QIOSScreen::logicalDpi() const +{ + return QDpi(72, 72); +} + +qreal QIOSScreen::devicePixelRatio() const +{ + return [m_uiScreen scale]; +} + +Qt::ScreenOrientation QIOSScreen::nativeOrientation() const +{ + return Qt::PortraitOrientation; +} + +Qt::ScreenOrientation QIOSScreen::orientation() const +{ + return toQtScreenOrientation([UIDevice currentDevice].orientation); +} + +void QIOSScreen::setOrientationUpdateMask(Qt::ScreenOrientations mask) +{ + if (m_orientationListener && mask == Qt::PrimaryOrientation) { + [m_orientationListener release]; + m_orientationListener = 0; + } else if (!m_orientationListener) { + m_orientationListener = [[QIOSOrientationListener alloc] initWithQIOSScreen:this]; + } +} + +void QIOSScreen::setPrimaryOrientation(Qt::ScreenOrientation orientation) +{ + // Note that UIScreen never changes orientation, but QScreen should. To work around + // this, we let QIOSViewController call us whenever interface orientation changes, and + // use that as primary orientation. After all, the viewcontrollers geometry is what we + // place QWindows on top of. A problem with this approach is that QIOSViewController is + // not in use in a mixed environment, which results in no change to primary orientation. + // We see that as acceptable since Qt should most likely not interfere with orientation + // for that case anyway. + bool portrait = screen()->isPortrait(orientation); + if (portrait && m_geometry.width() < m_geometry.height()) + return; + + // Switching portrait/landscape means swapping width/height (and adjusting x/y): + m_geometry = QRect(0, 0, m_geometry.height(), m_geometry.width()); + m_physicalSize = QSizeF(m_physicalSize.height(), m_physicalSize.width()); + m_availableGeometry = fromPortraitToPrimary(fromCGRect(m_uiScreen.applicationFrame), this); + + QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry); + QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), m_availableGeometry); +} + +UIScreen *QIOSScreen::uiScreen() const +{ + return m_uiScreen; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiossoftwareinputhandler.h b/src/plugins/platforms/ios/qiossoftwareinputhandler.h new file mode 100644 index 0000000000..5dad6b8d86 --- /dev/null +++ b/src/plugins/platforms/ios/qiossoftwareinputhandler.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 QIOSSOFTWAREINPUTHANDLER_H +#define QIOSSOFTWAREINPUTHANDLER_H + +#include <QtCore/QObject> +#include <QtCore/QPointer> +#include <QtWidgets/QWidget> + +QT_BEGIN_NAMESPACE + +class QIOSSoftwareInputHandler : public QObject +{ + Q_OBJECT + +public: + QIOSSoftwareInputHandler() : m_CurrentFocusWidget(0), m_CurrentFocusObject(0) {} + bool eventFilter(QObject *obj, QEvent *event); + +private slots: + void activeFocusChanged(bool focus); + +private: + bool closeSoftwareInputPanel(QWidget *widget); + + QPointer<QWidget> m_currentFocusWidget; + QPointer<QObject> m_currentFocusObject; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/ios/qiostheme.h b/src/plugins/platforms/ios/qiostheme.h new file mode 100644 index 0000000000..5ccbcac710 --- /dev/null +++ b/src/plugins/platforms/ios/qiostheme.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** 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 QIOSTHEME_H +#define QIOSTHEME_H + +#include <qpa/qplatformtheme.h> + +QT_BEGIN_NAMESPACE + +class QIOSTheme : public QPlatformTheme +{ +public: + QIOSTheme(); + ~QIOSTheme(); + + QVariant themeHint(ThemeHint hint) const; + + const QFont *font(Font type = SystemFont) const; + + static const char *name; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/ios/qiostheme.mm b/src/plugins/platforms/ios/qiostheme.mm new file mode 100644 index 0000000000..f98781f8a7 --- /dev/null +++ b/src/plugins/platforms/ios/qiostheme.mm @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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 "qiostheme.h" + +#include <QtCore/QStringList> +#include <QtCore/QVariant> + +#include <QtGui/QFont> + +#include <UIKit/UIFont.h> +#include <UIKit/UIInterface.h> + +QT_BEGIN_NAMESPACE + +const char *QIOSTheme::name = "ios"; + +QIOSTheme::QIOSTheme() +{ +} + +QIOSTheme::~QIOSTheme() +{ +} + +QVariant QIOSTheme::themeHint(ThemeHint hint) const +{ + switch (hint) { + case QPlatformTheme::StyleNames: + return QStringList(QStringLiteral("fusion")); + default: + return QPlatformTheme::themeHint(hint); + } +} + +const QFont *QIOSTheme::font(Font type) const +{ + static QHash<QPlatformTheme::Font, QFont *> fonts; + if (fonts.isEmpty()) { + // The real system font on iOS is '.Helvetica Neue UI', as returned by both [UIFont systemFontOfSize] + // and CTFontCreateUIFontForLanguage(kCTFontSystemFontType, ...), but this font is not included when + // populating the available fonts in QCoreTextFontDatabase::populateFontDatabase(), since the font + // is internal to iOS and not supposed to be used by applications. We could potentially add this + // font to the font-database, but it would then show up when enumerating user fonts from Qt + // applications since we don't have a flag in Qt to mark a font as a private system font. + // For now we hard-code the font to Helvetica, which should be very close to the actual + // system font. + QLatin1String systemFontFamilyName("Helvetica"); + fonts.insert(QPlatformTheme::SystemFont, new QFont(systemFontFamilyName, [UIFont systemFontSize])); + fonts.insert(QPlatformTheme::SmallFont, new QFont(systemFontFamilyName, [UIFont smallSystemFontSize])); + fonts.insert(QPlatformTheme::LabelFont, new QFont(systemFontFamilyName, [UIFont labelFontSize])); + fonts.insert(QPlatformTheme::PushButtonFont, new QFont(systemFontFamilyName, [UIFont buttonFontSize])); + } + + return fonts.value(type, 0); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosviewcontroller.h b/src/plugins/platforms/ios/qiosviewcontroller.h new file mode 100644 index 0000000000..d5a61cb3f4 --- /dev/null +++ b/src/plugins/platforms/ios/qiosviewcontroller.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#import <UIKit/UIKit.h> + +@interface QIOSViewController : UIViewController +@end + diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm new file mode 100644 index 0000000000..c52bfd7345 --- /dev/null +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#import "qiosviewcontroller.h" + +#include <QtGui/QGuiApplication> +#include <QtGui/QScreen> +#include "qiosscreen.h" +#include "qiosglobal.h" + +@implementation QIOSViewController + +- (void)viewDidLoad +{ +#ifdef QT_DEBUG + if (!self.nibName) + self.view.backgroundColor = [UIColor magentaColor]; +#endif +} + +-(BOOL)shouldAutorotate +{ + // For now we assume that if the application doesn't listen to orientation + // updates it means it would like to enable auto-rotation, and vice versa. + if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp)) + return !guiApp->primaryScreen()->orientationUpdateMask(); + else + return YES; // Startup case: QGuiApplication is not ready yet. + + // FIXME: Investigate a proper Qt API for auto-rotation and orientation locking +} + +-(NSUInteger)supportedInterfaceOrientations +{ + // We need to tell iOS that we support all orientations in order to set + // status bar orientation when application content orientation changes. + return UIInterfaceOrientationMaskAll; +} + +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration +{ + Q_UNUSED(duration); + Qt::ScreenOrientation orientation = toQtScreenOrientation(toInterfaceOrientation); + if (orientation == -1) + return; + + QIOSScreen *qiosScreen = static_cast<QIOSScreen *>(QGuiApplication::primaryScreen()->handle()); + qiosScreen->setPrimaryOrientation(orientation); +} + +@end + diff --git a/src/plugins/platforms/ios/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h new file mode 100644 index 0000000000..cefb6f9388 --- /dev/null +++ b/src/plugins/platforms/ios/qioswindow.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** 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 QIOSWINDOW_H +#define QIOSWINDOW_H + +#include <qpa/qplatformwindow.h> +#include <qpa/qwindowsysteminterface.h> + +#import <UIKit/UIKit.h> + +class QIOSContext; +class QIOSWindow; + +@interface UIView (QIOS) +@property(readonly) QWindow *qwindow; +@end + +QT_BEGIN_NAMESPACE + +class QIOSWindow : public QPlatformWindow +{ +public: + explicit QIOSWindow(QWindow *window); + ~QIOSWindow(); + + void setGeometry(const QRect &rect); + + void setWindowState(Qt::WindowState state); + void setParent(const QPlatformWindow *window); + void handleContentOrientationChange(Qt::ScreenOrientation orientation); + void setVisible(bool visible); + + void raise() { raiseOrLower(true); } + void lower() { raiseOrLower(false); } + void requestActivateWindow(); + + qreal devicePixelRatio() const; + int effectiveWidth() const; + int effectiveHeight() const; + + 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; + qreal m_devicePixelRatio; + + void raiseOrLower(bool raise); + void updateWindowLevel(); + bool blockedByModal(); +}; + +QT_END_NAMESPACE + +#endif // QIOSWINDOW_H diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm new file mode 100644 index 0000000000..e4fb5e2e1c --- /dev/null +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -0,0 +1,523 @@ +/**************************************************************************** +** +** 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 "qiosglobal.h" +#include "qioswindow.h" +#include "qioscontext.h" +#include "qiosinputcontext.h" +#include "qiosscreen.h" +#include "qiosapplicationdelegate.h" +#include "qiosviewcontroller.h" +#include "qiosintegration.h" +#include <QtGui/private/qguiapplication_p.h> +#include <qpa/qplatformintegration.h> + +#import <QuartzCore/CAEAGLLayer.h> + +#include <QtGui/QKeyEvent> +#include <qpa/qwindowsysteminterface.h> + +#include <QtDebug> + +@interface EAGLView : UIView <UIKeyInput> +{ +@public + UITextAutocapitalizationType autocapitalizationType; + UITextAutocorrectionType autocorrectionType; + BOOL enablesReturnKeyAutomatically; + UIKeyboardAppearance keyboardAppearance; + UIKeyboardType keyboardType; + UIReturnKeyType returnKeyType; + BOOL secureTextEntry; + QIOSWindow *m_qioswindow; +} + +@property(nonatomic) UITextAutocapitalizationType autocapitalizationType; +@property(nonatomic) UITextAutocorrectionType autocorrectionType; +@property(nonatomic) BOOL enablesReturnKeyAutomatically; +@property(nonatomic) UIKeyboardAppearance keyboardAppearance; +@property(nonatomic) UIKeyboardType keyboardType; +@property(nonatomic) UIReturnKeyType returnKeyType; +@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry; + +@end + +@implementation EAGLView + ++ (Class)layerClass +{ + return [CAEAGLLayer class]; +} + +-(id)initWithQIOSWindow:(QIOSWindow *)window +{ + if (self = [self initWithFrame:toCGRect(window->geometry())]) + m_qioswindow = window; + + return self; +} + +- (id)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) { + // Set up EAGL layer + CAEAGLLayer *eaglLayer = static_cast<CAEAGLLayer *>(self.layer); + eaglLayer.opaque = TRUE; + eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking, + kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; + + // Set up text input + autocapitalizationType = UITextAutocapitalizationTypeNone; + autocorrectionType = UITextAutocorrectionTypeNo; + enablesReturnKeyAutomatically = NO; + keyboardAppearance = UIKeyboardAppearanceDefault; + keyboardType = UIKeyboardTypeDefault; + returnKeyType = UIReturnKeyDone; + secureTextEntry = NO; + + if (isQtApplication()) + self.hidden = YES; + + self.multipleTouchEnabled = YES; + } + + return self; +} + +- (void)layoutSubviews +{ + // This method is the de facto way to know that view has been resized, + // or otherwise needs invalidation of its buffers. Note though that we + // do not get this callback when the view just changes its position, so + // the position of our QWindow (and platform window) will only get updated + // when the size is also changed. + + if (!CGAffineTransformIsIdentity(self.transform)) + qWarning() << m_qioswindow->window() + << "is backed by a UIView that has a transform set. This is not supported."; + + QRect geometry = fromCGRect(self.frame); + m_qioswindow->QPlatformWindow::setGeometry(geometry); + QWindowSystemInterface::handleGeometryChange(m_qioswindow->window(), geometry); + + // If we have a new size here we need to resize the FBO's corresponding buffers, + // but we defer that to when the application calls makeCurrent. + + [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); + } +} + +- (void) sendTouchEventWithTimestamp:(ulong)timeStamp +{ + // Send touch event synchronously + QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration()); + QWindowSystemInterface::handleTouchEvent(m_qioswindow->window(), timeStamp, + iosIntegration->touchDevice(), m_qioswindow->touchPoints()); + QWindowSystemInterface::flushWindowSystemEvents(); +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + QWindow *window = m_qioswindow->window(); + + // Transfer focus to the touched window: + 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); + } + + [self updateTouchList:touches withState:Qt::TouchPointPressed]; + [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + [self updateTouchList:touches withState:Qt::TouchPointMoved]; + [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + [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; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + Q_UNUSED(touches) // ### can a subset of the active touches be cancelled? + + // Clear current touch points + m_qioswindow->activeTouches().clear(); + m_qioswindow->touchId() = 0; + + // Send cancel touch event synchronously + QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration()); + QWindowSystemInterface::handleTouchCancelEvent(m_qioswindow->window(), ulong(event.timestamp * 1000), iosIntegration->touchDevice()); + QWindowSystemInterface::flushWindowSystemEvents(); +} + +@synthesize autocapitalizationType; +@synthesize autocorrectionType; +@synthesize enablesReturnKeyAutomatically; +@synthesize keyboardAppearance; +@synthesize keyboardType; +@synthesize returnKeyType; +@synthesize secureTextEntry; + +- (BOOL)canBecomeFirstResponder +{ + return YES; +} + +- (BOOL)hasText +{ + return YES; +} + +- (void)insertText:(NSString *)text +{ + QString string = QString::fromUtf8([text UTF8String]); + int key = 0; + if ([text isEqualToString:@"\n"]) + key = (int)Qt::Key_Return; + + // Send key event to window system interface + QWindowSystemInterface::handleKeyEvent( + 0, QEvent::KeyPress, key, Qt::NoModifier, string, false, int(string.length())); + QWindowSystemInterface::handleKeyEvent( + 0, QEvent::KeyRelease, key, Qt::NoModifier, string, false, int(string.length())); +} + +- (void)deleteBackward +{ + // Send key event to window system interface + QWindowSystemInterface::handleKeyEvent( + 0, QEvent::KeyPress, (int)Qt::Key_Backspace, Qt::NoModifier); + QWindowSystemInterface::handleKeyEvent( + 0, QEvent::KeyRelease, (int)Qt::Key_Backspace, Qt::NoModifier); +} + +@end + +@implementation UIView (QIOS) + +- (QWindow *)qwindow +{ + if ([self isKindOfClass:[EAGLView class]]) + return static_cast<EAGLView *>(self)->m_qioswindow->window(); + return nil; +} + +@end + +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) +{ + setParent(parent()); + setWindowState(window->windowState()); + + // Retina support: get screen scale factor and set it in the content view. + // This will make framebufferObject() create a 2x frame buffer on retina + // displays. Also set m_devicePixelRatio which is used for scaling the + // paint device. + if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] == YES) { + m_devicePixelRatio = [[UIScreen mainScreen] scale]; + [m_view setContentScaleFactor: m_devicePixelRatio]; + } +} + +QIOSWindow::~QIOSWindow() +{ + [m_view removeFromSuperview]; + [m_view release]; +} + +bool QIOSWindow::blockedByModal() +{ + QWindow *modalWindow = QGuiApplication::modalWindow(); + return modalWindow && modalWindow != window(); +} + +void QIOSWindow::setVisible(bool visible) +{ + QPlatformWindow::setVisible(visible); + m_view.hidden = !visible; + + if (!isQtApplication()) + return; + + // Since iOS doesn't do window management the way a Qt application + // expects, we need to raise and activate windows ourselves: + if (visible) + updateWindowLevel(); + + if (blockedByModal()) { + if (visible) + raise(); + return; + } + + if (visible) { + requestActivateWindow(); + } else { + // Activate top-most visible QWindow: + NSArray *subviews = rootViewController().view.subviews; + for (int i = int(subviews.count) - 1; i >= 0; --i) { + UIView *view = [subviews objectAtIndex:i]; + if (!view.hidden) { + if (QWindow *window = view.qwindow) { + static_cast<QIOSWindow *>(window->handle())->requestActivateWindow(); + break; + } + } + } + } +} + +void QIOSWindow::setGeometry(const QRect &rect) +{ + // If the window is in fullscreen, just bookkeep the requested + // geometry in case the window goes into Qt::WindowNoState later: + m_requestedGeometry = rect; + if (window()->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) + return; + + // Since we don't support transformations on the UIView, we can set the frame + // directly and let UIKit deal with translating that into bounds and center. + // Changing the size of the view will end up in a call to -[EAGLView layoutSubviews] + // which will update QWindowSystemInterface with the new size. + m_view.frame = toCGRect(rect); +} + +void QIOSWindow::setWindowState(Qt::WindowState state) +{ + // FIXME: Figure out where or how we should disable/enable the statusbar. + // Perhaps setting QWindow to maximized should also mean that we'll show + // the statusbar, and vice versa for fullscreen? + + switch (state) { + case Qt::WindowMaximized: + case Qt::WindowFullScreen: { + // Since UIScreen does not take orientation into account when + // reporting geometry, we need to look at the top view instead: + CGSize fullscreenSize = m_view.window.rootViewController.view.bounds.size; + m_view.frame = CGRectMake(0, 0, fullscreenSize.width, fullscreenSize.height); + m_view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + break; } + default: + m_view.frame = toCGRect(m_requestedGeometry); + m_view.autoresizingMask = UIViewAutoresizingNone; + break; + } +} + +void QIOSWindow::setParent(const QPlatformWindow *parentWindow) +{ + if (parentWindow) { + UIView *parentView = reinterpret_cast<UIView *>(parentWindow->winId()); + [parentView addSubview:m_view]; + } else if (isQtApplication()) { + [rootViewController().view addSubview:m_view]; + } +} + +void QIOSWindow::requestActivateWindow() +{ + // Note that several windows can be active at the same time if they exist in the same + // hierarchy (transient children). But only one window can be QGuiApplication::focusWindow(). + // Dispite the name, 'requestActivateWindow' means raise and transfer focus to the window: + if (blockedByModal()) + return; + + raise(); + QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext(); + static_cast<QIOSInputContext *>(context)->focusViewChanged(m_view); + QPlatformWindow::requestActivateWindow(); +} + +void QIOSWindow::raiseOrLower(bool raise) +{ + // Re-insert m_view at the correct index among its sibling views + // (QWindows) according to their current m_windowLevel: + if (!isQtApplication()) + return; + + NSArray *subviews = m_view.superview.subviews; + if (subviews.count == 1) + return; + + for (int i = int(subviews.count) - 1; i >= 0; --i) { + UIView *view = static_cast<UIView *>([subviews objectAtIndex:i]); + if (view.hidden || view == m_view) + continue; + int level = static_cast<QIOSWindow *>(view.qwindow->handle())->m_windowLevel; + if (m_windowLevel > level || (raise && m_windowLevel == level)) { + [m_view.superview insertSubview:m_view aboveSubview:view]; + return; + } + } + [m_view.superview insertSubview:m_view atIndex:0]; +} + +void QIOSWindow::updateWindowLevel() +{ + Qt::WindowType type = static_cast<Qt::WindowType>(int(window()->flags() & Qt::WindowType_Mask)); + + if (type == Qt::ToolTip) + m_windowLevel = 120; + else if (window()->flags() & Qt::WindowStaysOnTopHint) + m_windowLevel = 100; + else if (window()->isModal()) + m_windowLevel = 30; + else if (type & Qt::Popup & ~Qt::Window) + m_windowLevel = 20; + else if (type == Qt::Tool) + m_windowLevel = 10; + else + m_windowLevel = 0; + + // A window should be in at least the same m_windowLevel as its parent: + QWindow *transientParent = window()->transientParent(); + QIOSWindow *transientParentWindow = transientParent ? static_cast<QIOSWindow *>(transientParent->handle()) : 0; + if (transientParentWindow) + m_windowLevel = qMax(transientParentWindow->m_windowLevel, m_windowLevel); +} + +void QIOSWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation) +{ + // Keep the status bar in sync with content orientation. This will ensure + // that the task bar (and associated gestures) are aligned correctly: + UIDeviceOrientation uiOrientation = fromQtScreenOrientation(orientation); + [[UIApplication sharedApplication] setStatusBarOrientation:uiOrientation animated:NO]; +} + +qreal QIOSWindow::devicePixelRatio() const +{ + return m_devicePixelRatio; +} + +int QIOSWindow::effectiveWidth() const +{ + return geometry().width() * m_devicePixelRatio; +} + +int QIOSWindow::effectiveHeight() const +{ + return geometry().height() * m_devicePixelRatio; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qtmain.mm b/src/plugins/platforms/ios/qtmain.mm new file mode 100644 index 0000000000..916224f936 --- /dev/null +++ b/src/plugins/platforms/ios/qtmain.mm @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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 "qiosapplicationdelegate.h" +#include "qiosviewcontroller.h" + +int main(int argc, char *argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSMainWrapperApplicationDelegate class])); + } +} + +extern int qt_main(int argc, char *argv[]); + +@implementation QIOSMainWrapperApplicationDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; + self.window.rootViewController = [[[QIOSViewController alloc] init] autorelease]; + +#ifdef QT_DEBUG + self.window.backgroundColor = [UIColor cyanColor]; +#endif + + [self.window makeKeyAndVisible]; + + // We schedule the main-redirection for the next eventloop pass so that we + // can return from this function and let UIApplicationMain finish its job. + [NSTimer scheduledTimerWithTimeInterval:.01f target:self + selector:@selector(runUserMain) userInfo:nil repeats:NO]; + + if ([QIOSApplicationDelegate instancesRespondToSelector:_cmd]) + return [super application:application didFinishLaunchingWithOptions:launchOptions]; + else + return YES; +} + +- (void)runUserMain +{ + NSArray *arguments = [[NSProcessInfo processInfo] arguments]; + int argc = arguments.count; + char **argv = new char*[argc]; + for (int i = 0; i < argc; ++i) { + NSString *arg = [arguments objectAtIndex:i]; + argv[i] = reinterpret_cast<char *>(malloc([arg lengthOfBytesUsingEncoding:[NSString defaultCStringEncoding]])); + strcpy(argv[i], [arg cStringUsingEncoding:[NSString defaultCStringEncoding]]); + } + + qt_main(argc, argv); + delete[] argv; +} + +@end diff --git a/src/plugins/platforms/ios/qtmain.pro b/src/plugins/platforms/ios/qtmain.pro new file mode 100644 index 0000000000..cbcb272217 --- /dev/null +++ b/src/plugins/platforms/ios/qtmain.pro @@ -0,0 +1,8 @@ +TARGET = qiosmain + +PLUGIN_TYPE = platforms +load(qt_plugin) + +QT += gui-private + +OBJECTIVE_SOURCES = qtmain.mm diff --git a/src/plugins/platforms/kms/kms.pro b/src/plugins/platforms/kms/kms.pro index c0300e0960..711cf9e5c7 100644 --- a/src/plugins/platforms/kms/kms.pro +++ b/src/plugins/platforms/kms/kms.pro @@ -4,9 +4,10 @@ PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QKmsIntegrationPlugin load(qt_plugin) -QT += core-private gui-private platformsupport-private opengl-private +QT += core-private gui-private platformsupport-private +qtHaveModule(opengl):QT += opengl-private -DEFINES += MESA_EGL_NO_X11_HEADERS +DEFINES += MESA_EGL_NO_X11_HEADERS __GBM__ CONFIG += link_pkgconfig egl qpa/genericunixfontdatabase diff --git a/src/plugins/platforms/kms/qkmscursor.cpp b/src/plugins/platforms/kms/qkmscursor.cpp index d9345ce89b..da5dc9bb06 100644 --- a/src/plugins/platforms/kms/qkmscursor.cpp +++ b/src/plugins/platforms/kms/qkmscursor.cpp @@ -48,22 +48,17 @@ QT_BEGIN_NAMESPACE QKmsCursor::QKmsCursor(QKmsScreen *screen) : m_screen(screen), m_graphicsBufferManager(screen->device()->gbmDevice()), + m_cursorBufferObject(gbm_bo_create(m_graphicsBufferManager, 64, 64, GBM_FORMAT_ARGB8888, + GBM_BO_USE_CURSOR_64X64|GBM_BO_USE_WRITE)), + m_cursorImage(new QPlatformCursorImage(0, 0, 0, 0, 0, 0)), m_moved(false) { - gbm_bo *bo = gbm_bo_create(m_graphicsBufferManager, 64, 64, - GBM_BO_FORMAT_ARGB8888, - GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_RENDERING); - - m_eglImage = eglCreateImageKHR(m_screen->device()->eglDisplay(), 0, EGL_NATIVE_PIXMAP_KHR, - bo, 0); - gbm_bo_destroy(bo); - m_cursorImage = new QPlatformCursorImage(0, 0, 0, 0, 0, 0); } QKmsCursor::~QKmsCursor() { - drmModeSetCursor(m_screen->device()->fd(), m_screen->crtcId(), - 0, 0, 0); + drmModeSetCursor(m_screen->device()->fd(), m_screen->crtcId(), 0, 0, 0); + gbm_bo_destroy(m_cursorBufferObject); } void QKmsCursor::pointerEvent(const QMouseEvent &event) @@ -78,57 +73,30 @@ void QKmsCursor::pointerEvent(const QMouseEvent &event) } } -void QKmsCursor::changeCursor(QCursor *widgetCursor, QWindow *window) +void QKmsCursor::changeCursor(QCursor *windowCursor, QWindow *window) { Q_UNUSED(window) if (!m_moved) drmModeMoveCursor(m_screen->device()->fd(), m_screen->crtcId(), 0, 0); - if (widgetCursor->shape() != Qt::BitmapCursor) { - m_cursorImage->set(widgetCursor->shape()); - } else { - m_cursorImage->set(widgetCursor->pixmap().toImage(), - widgetCursor->hotSpot().x(), - widgetCursor->hotSpot().y()); - } - - if ((m_cursorImage->image()->width() > 64) || (m_cursorImage->image()->width() > 64)) { - qWarning("failed to set hardware cursor: larger than 64x64."); - return; - } - - QImage cursorImage = m_cursorImage->image()->convertToFormat(QImage::Format_RGB32); - - //Load cursor image into EGLImage - GLuint cursorTexture; - glGenTextures(1, &cursorTexture); - glBindTexture(GL_TEXTURE_2D, cursorTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - - //TODO: Format may be wrong here, need a color icon to test. - if (m_eglImage != EGL_NO_IMAGE_KHR) { - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, cursorImage.width(), - cursorImage.height(), GL_RGBA, - GL_UNSIGNED_BYTE, cursorImage.constBits()); + const Qt::CursorShape newShape = windowCursor ? windowCursor->shape() : Qt::ArrowCursor; + if (newShape != Qt::BitmapCursor) { + m_cursorImage->set(newShape); } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cursorImage.width(), - cursorImage.height(), 0, GL_RGBA, - GL_UNSIGNED_BYTE, cursorImage.constBits()); + m_cursorImage->set(windowCursor->pixmap().toImage(), + windowCursor->hotSpot().x(), + windowCursor->hotSpot().y()); } - //EGLImage needs to contain sprite before calling this: - gbm_bo *bufferObject = gbm_bo_import(m_graphicsBufferManager, GBM_BO_IMPORT_EGL_IMAGE, - m_eglImage, GBM_BO_USE_CURSOR_64X64); - quint32 handle = gbm_bo_get_handle(bufferObject).u32; + if ((m_cursorImage->image()->width() > 64) || (m_cursorImage->image()->width() > 64)) + qWarning("Warning: cursor larger than 64x64; only 64x64 pixels will be shown."); - gbm_bo_destroy(bufferObject); + QImage cursorImage = m_cursorImage->image()-> + convertToFormat(QImage::Format_ARGB32).copy(0, 0, 64, 64); + gbm_bo_write(m_cursorBufferObject, cursorImage.constBits(), cursorImage.byteCount()); + quint32 handle = gbm_bo_get_handle(m_cursorBufferObject).u32; int status = drmModeSetCursor(m_screen->device()->fd(), m_screen->crtcId(), handle, 64, 64); diff --git a/src/plugins/platforms/kms/qkmscursor.h b/src/plugins/platforms/kms/qkmscursor.h index fda47ebedc..ee65b01e36 100644 --- a/src/plugins/platforms/kms/qkmscursor.h +++ b/src/plugins/platforms/kms/qkmscursor.h @@ -44,15 +44,11 @@ #include <qpa/qplatformcursor.h> -#define EGL_EGLEXT_PROTOTYPES 1 - -#include <EGL/egl.h> -#include <EGL/eglext.h> - QT_BEGIN_NAMESPACE class QKmsScreen; class gbm_device; +class gbm_bo; class QKmsCursor : public QPlatformCursor { @@ -61,12 +57,12 @@ public: ~QKmsCursor(); void pointerEvent(const QMouseEvent &event); - void changeCursor(QCursor *widgetCursor, QWindow *window); + void changeCursor(QCursor *windowCursor, QWindow *window); private: QKmsScreen *m_screen; gbm_device *m_graphicsBufferManager; - EGLImageKHR m_eglImage; + gbm_bo *m_cursorBufferObject; QPlatformCursorImage *m_cursorImage; bool m_moved; }; diff --git a/src/plugins/platforms/kms/qkmsscreen.cpp b/src/plugins/platforms/kms/qkmsscreen.cpp index 123dcc283f..892f7bb01f 100644 --- a/src/plugins/platforms/kms/qkmsscreen.cpp +++ b/src/plugins/platforms/kms/qkmsscreen.cpp @@ -124,10 +124,14 @@ void QKmsScreen::initializeScreenMode() drmModeConnector *connector = drmModeGetConnector(m_device->fd(), m_connectorId); drmModeModeInfo *mode = 0; - if (connector->count_modes > 0) - mode = &connector->modes[0]; - else - mode = &builtin_1024x768; + for (int i = 0; i < connector->count_modes; ++i) { + if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) { + mode = &connector->modes[i]; + break; + } + } + if (!mode) + mode = mode = &builtin_1024x768; drmModeEncoder *encoder = drmModeGetEncoder(m_device->fd(), connector->encoders[0]); if (encoder == 0) diff --git a/src/plugins/platforms/minimalegl/qminimaleglbackingstore.cpp b/src/plugins/platforms/minimalegl/qminimaleglbackingstore.cpp index da89100359..cb245f2e5c 100644 --- a/src/plugins/platforms/minimalegl/qminimaleglbackingstore.cpp +++ b/src/plugins/platforms/minimalegl/qminimaleglbackingstore.cpp @@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE QMinimalEglBackingStore::QMinimalEglBackingStore(QWindow *window) : QPlatformBackingStore(window) , m_context(new QOpenGLContext) + , m_device(0) { m_context->setFormat(window->requestedFormat()); m_context->setScreen(window->screen()); diff --git a/src/plugins/platforms/minimalegl/qminimaleglintegration.h b/src/plugins/platforms/minimalegl/qminimaleglintegration.h index fb86c967e1..dba7504033 100644 --- a/src/plugins/platforms/minimalegl/qminimaleglintegration.h +++ b/src/plugins/platforms/minimalegl/qminimaleglintegration.h @@ -47,8 +47,6 @@ #include <qpa/qplatformintegration.h> #include <qpa/qplatformscreen.h> -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QMinimalEglIntegration : public QPlatformIntegration @@ -75,6 +73,5 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // QMINIMALEGLINTEGRATION_H diff --git a/src/plugins/platforms/offscreen/main.cpp b/src/plugins/platforms/offscreen/main.cpp new file mode 100644 index 0000000000..ca7dc1d18b --- /dev/null +++ b/src/plugins/platforms/offscreen/main.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 <qpa/qplatformintegrationplugin.h> +#include "qoffscreenintegration.h" + +QT_BEGIN_NAMESPACE + +class QOffscreenIntegrationPlugin : public QPlatformIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.1" FILE "offscreen.json") +public: + QPlatformIntegration *create(const QString&, const QStringList&); +}; + +QPlatformIntegration *QOffscreenIntegrationPlugin::create(const QString& system, const QStringList& paramList) +{ + Q_UNUSED(paramList); + if (system.toLower() == "offscreen") + return QOffscreenIntegration::createOffscreenIntegration(); + + return 0; +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/platforms/offscreen/offscreen.json b/src/plugins/platforms/offscreen/offscreen.json new file mode 100644 index 0000000000..6e87744de0 --- /dev/null +++ b/src/plugins/platforms/offscreen/offscreen.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "offscreen" ] +} diff --git a/src/plugins/platforms/offscreen/offscreen.pro b/src/plugins/platforms/offscreen/offscreen.pro new file mode 100644 index 0000000000..270e3ce228 --- /dev/null +++ b/src/plugins/platforms/offscreen/offscreen.pro @@ -0,0 +1,25 @@ +TARGET = qoffscreen + +PLUGIN_TYPE = platforms +load(qt_plugin) + +QT += core-private gui-private platformsupport-private + +SOURCES = main.cpp \ + qoffscreenintegration.cpp \ + qoffscreenwindow.cpp \ + qoffscreencommon.cpp + +HEADERS = qoffscreenintegration.h \ + qoffscreenwindow.h \ + qoffscreencommon.h + +OTHER_FILES += offscreen.json + +contains(QT_CONFIG, xcb):contains(QT_CONFIG, opengl):!contains(QT_CONFIG, opengles2) { + SOURCES += qoffscreenintegration_x11.cpp + HEADERS += qoffscreenintegration_x11.h + system(echo "Using X11 offscreen integration with GLX") +} else { + SOURCES += qoffscreenintegration_dummy.cpp +} diff --git a/src/plugins/platforms/offscreen/qoffscreencommon.cpp b/src/plugins/platforms/offscreen/qoffscreencommon.cpp new file mode 100644 index 0000000000..069b20693d --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreencommon.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "qoffscreencommon.h" +#include "qoffscreenwindow.h" + +#include <QtGui/private/qpixmap_raster_p.h> +#include <QtGui/private/qguiapplication_p.h> + +#include <qpa/qplatformcursor.h> +#include <qpa/qplatformwindow.h> + +QT_BEGIN_NAMESPACE + +QPlatformWindow *QOffscreenScreen::windowContainingCursor = 0; + +class QOffscreenCursor : public QPlatformCursor +{ +public: + QOffscreenCursor() : m_pos(10, 10) {} + + QPoint pos() const { return m_pos; } + void setPos(const QPoint &pos) + { + m_pos = pos; + QWindowList wl = QGuiApplication::topLevelWindows(); + QWindow *containing = 0; + foreach (QWindow *w, wl) { + if (w->type() != Qt::Desktop && w->isExposed() && w->geometry().contains(pos)) { + containing = w; + break; + } + } + + QPoint local = pos; + if (containing) + local -= containing->position(); + + QWindow *previous = QOffscreenScreen::windowContainingCursor ? QOffscreenScreen::windowContainingCursor->window() : 0; + + if (containing != previous) + QWindowSystemInterface::handleEnterLeaveEvent(containing, previous, local, pos); + + QWindowSystemInterface::handleMouseEvent(containing, local, pos, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); + + QOffscreenScreen::windowContainingCursor = containing ? containing->handle() : 0; + } + + void changeCursor(QCursor *windowCursor, QWindow *window) + { + Q_UNUSED(windowCursor); + Q_UNUSED(window); + } + +private: + QPoint m_pos; +}; + +QOffscreenScreen::QOffscreenScreen() + : m_geometry(0, 0, 800, 600) + , m_cursor(new QOffscreenCursor) +{ +} + +QPixmap QOffscreenScreen::grabWindow(WId id, int x, int y, int width, int height) const +{ + QRect rect(x, y, width, height); + + QOffscreenWindow *window = QOffscreenWindow::windowForWinId(id); + if (!window || window->window()->type() == Qt::Desktop) { + QWindowList wl = QGuiApplication::topLevelWindows(); + QWindow *containing = 0; + foreach (QWindow *w, wl) { + if (w->type() != Qt::Desktop && w->isExposed() && w->geometry().contains(rect)) { + containing = w; + break; + } + } + + if (!containing) + return QPixmap(); + + id = containing->winId(); + rect = rect.translated(-containing->geometry().topLeft()); + } + + QOffscreenBackingStore *store = QOffscreenBackingStore::backingStoreForWinId(id); + if (store) + return store->grabWindow(id, rect); + return QPixmap(); +} + +QOffscreenBackingStore::QOffscreenBackingStore(QWindow *window) + : QPlatformBackingStore(window) +{ +} + +QOffscreenBackingStore::~QOffscreenBackingStore() +{ + clearHash(); +} + +QPaintDevice *QOffscreenBackingStore::paintDevice() +{ + return &m_image; +} + +void QOffscreenBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(region); + + if (m_image.size().isEmpty()) + return; + + QSize imageSize = m_image.size(); + + QRegion clipped = QRect(0, 0, window->width(), window->height()); + clipped &= QRect(0, 0, imageSize.width(), imageSize.height()).translated(-offset); + + QRect bounds = clipped.boundingRect().translated(offset); + + if (bounds.isNull()) + return; + + WId id = window->winId(); + + m_windowAreaHash[id] = bounds; + m_backingStoreForWinIdHash[id] = this; +} + +void QOffscreenBackingStore::resize(const QSize &size, const QRegion &) +{ + QImage::Format format = QGuiApplication::primaryScreen()->handle()->format(); + if (m_image.size() != size) + m_image = QImage(size, format); + clearHash(); +} + +extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset); + +bool QOffscreenBackingStore::scroll(const QRegion &area, int dx, int dy) +{ + if (m_image.isNull()) + return false; + + const QVector<QRect> rects = area.rects(); + for (int i = 0; i < rects.size(); ++i) + qt_scrollRectInImage(m_image, rects.at(i), QPoint(dx, dy)); + + return true; +} + +QPixmap QOffscreenBackingStore::grabWindow(WId window, const QRect &rect) const +{ + QRect area = m_windowAreaHash.value(window, QRect()); + if (area.isNull()) + return QPixmap(); + + QRect adjusted = rect; + if (adjusted.width() <= 0) + adjusted.setWidth(area.width()); + if (adjusted.height() <= 0) + adjusted.setHeight(area.height()); + + adjusted = adjusted.translated(area.topLeft()) & area; + + if (adjusted.isEmpty()) + return QPixmap(); + + return QPixmap::fromImage(m_image.copy(adjusted)); +} + +QOffscreenBackingStore *QOffscreenBackingStore::backingStoreForWinId(WId id) +{ + return m_backingStoreForWinIdHash.value(id, 0); +} + +void QOffscreenBackingStore::clearHash() +{ + QList<WId> ids = m_windowAreaHash.keys(); + foreach (WId id, ids) { + QHash<WId, QOffscreenBackingStore *>::iterator it = m_backingStoreForWinIdHash.find(id); + if (it.value() == this) + m_backingStoreForWinIdHash.remove(id); + } + m_windowAreaHash.clear(); +} + +QHash<WId, QOffscreenBackingStore *> QOffscreenBackingStore::m_backingStoreForWinIdHash; + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/offscreen/qoffscreencommon.h b/src/plugins/platforms/offscreen/qoffscreencommon.h new file mode 100644 index 0000000000..a5df7d05d3 --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreencommon.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 QOFFSCREENCOMMON_H +#define QOFFSCREENCOMMON_H + +#include <qpa/qplatformbackingstore.h> +#include <qpa/qplatformdrag.h> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformscreen.h> +#include <qpa/qplatformwindow.h> + +#include <qscopedpointer.h> +#include <qimage.h> +#include <qhash.h> + +QT_BEGIN_NAMESPACE + +class QOffscreenScreen : public QPlatformScreen +{ +public: + QOffscreenScreen(); + + QRect geometry() const { return m_geometry; } + int depth() const { return 32; } + QImage::Format format() const { return QImage::Format_RGB32; } + QPlatformCursor *cursor() const { return m_cursor.data(); } + + QPixmap grabWindow(WId window, int x, int y, int width, int height) const; + + static QPlatformWindow *windowContainingCursor; + +public: + QRect m_geometry; + QScopedPointer<QPlatformCursor> m_cursor; +}; + +class QOffscreenDrag : public QPlatformDrag +{ +public: + QMimeData *platformDropData() { return 0; } + Qt::DropAction drag(QDrag *) { return Qt::IgnoreAction; } +}; + +class QOffscreenBackingStore : public QPlatformBackingStore +{ +public: + QOffscreenBackingStore(QWindow *window); + ~QOffscreenBackingStore(); + + QPaintDevice *paintDevice(); + void flush(QWindow *window, const QRegion ®ion, const QPoint &offset); + void resize(const QSize &size, const QRegion &staticContents); + bool scroll(const QRegion &area, int dx, int dy); + + QPixmap grabWindow(WId window, const QRect &rect) const; + + static QOffscreenBackingStore *backingStoreForWinId(WId id); + +private: + void clearHash(); + + QImage m_image; + QHash<WId, QRect> m_windowAreaHash; + + static QHash<WId, QOffscreenBackingStore *> m_backingStoreForWinIdHash; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp new file mode 100644 index 0000000000..e3fcc7ebb0 --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "qoffscreenintegration.h" +#include "qoffscreenwindow.h" +#include "qoffscreencommon.h" + +#if defined(Q_OS_UNIX) +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> +#if defined(Q_OS_MAC) +#include <qpa/qplatformfontdatabase.h> +#else +#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> +#endif +#elif defined(Q_OS_WIN) +#include <QtPlatformSupport/private/qbasicfontdatabase_p.h> +#include <QtCore/private/qeventdispatcher_win_p.h> +#endif + +#include <QtGui/private/qpixmap_raster_p.h> +#include <QtGui/private/qguiapplication_p.h> + +#include <qpa/qplatformservices.h> + +QT_BEGIN_NAMESPACE + +template <typename BaseEventDispatcher> +class QOffscreenEventDispatcher : public BaseEventDispatcher +{ +public: + explicit QOffscreenEventDispatcher(QObject *parent = 0) + : BaseEventDispatcher(parent) + { + } + + bool processEvents(QEventLoop::ProcessEventsFlags flags) + { + bool didSendEvents = QWindowSystemInterface::sendWindowSystemEvents(flags); + + return BaseEventDispatcher::processEvents(flags) || didSendEvents; + } + + bool hasPendingEvents() + { + return BaseEventDispatcher::hasPendingEvents() + || QWindowSystemInterface::windowSystemEventsQueued(); + } + + void flush() + { + if (qApp) + qApp->sendPostedEvents(); + BaseEventDispatcher::flush(); + } +}; + +QOffscreenIntegration::QOffscreenIntegration() +{ +#if defined(Q_OS_UNIX) + m_eventDispatcher = createUnixEventDispatcher(); +#if defined(Q_OS_MAC) + m_fontDatabase.reset(new QPlatformFontDatabase()); +#else + m_fontDatabase.reset(new QGenericUnixFontDatabase()); +#endif +#elif defined(Q_OS_WIN) + m_eventDispatcher = new QOffscreenEventDispatcher<QEventDispatcherWin32>(); + m_fontDatabase.reset(new QBasicFontDatabase()); +#endif + + m_drag.reset(new QOffscreenDrag); + m_services.reset(new QPlatformServices); + + QGuiApplicationPrivate::instance()->setEventDispatcher(m_eventDispatcher); + screenAdded(new QOffscreenScreen); +} + +QOffscreenIntegration::~QOffscreenIntegration() +{ +} + +bool QOffscreenIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case ThreadedPixmaps: return true; + case MultipleWindows: return true; + default: return QPlatformIntegration::hasCapability(cap); + } +} + +QPlatformWindow *QOffscreenIntegration::createPlatformWindow(QWindow *window) const +{ + Q_UNUSED(window); + QPlatformWindow *w = new QOffscreenWindow(window); + w->requestActivateWindow(); + return w; +} + +QPlatformBackingStore *QOffscreenIntegration::createPlatformBackingStore(QWindow *window) const +{ + return new QOffscreenBackingStore(window); +} + +QAbstractEventDispatcher *QOffscreenIntegration::guiThreadEventDispatcher() const +{ + return m_eventDispatcher; +} + +QPlatformFontDatabase *QOffscreenIntegration::fontDatabase() const +{ + return m_fontDatabase.data(); +} + +QPlatformDrag *QOffscreenIntegration::drag() const +{ + return m_drag.data(); +} + +QPlatformServices *QOffscreenIntegration::services() const +{ + return m_services.data(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.h b/src/plugins/platforms/offscreen/qoffscreenintegration.h new file mode 100644 index 0000000000..eb03100ec9 --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenintegration.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 QOFFSCREENINTEGRATION_H +#define QOFFSCREENINTEGRATION_H + +#include <qpa/qplatformintegration.h> + +#include <qscopedpointer.h> + +QT_BEGIN_NAMESPACE + +class QOffscreenBackendData; + +class QOffscreenIntegration : public QPlatformIntegration +{ +public: + QOffscreenIntegration(); + ~QOffscreenIntegration(); + + bool hasCapability(QPlatformIntegration::Capability cap) const; + + QPlatformWindow *createPlatformWindow(QWindow *window) const; + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + QPlatformDrag *drag() const; + QPlatformServices *services() const; + + QPlatformFontDatabase *fontDatabase() const; + QAbstractEventDispatcher *guiThreadEventDispatcher() const; + + static QOffscreenIntegration *createOffscreenIntegration(); + +private: + QAbstractEventDispatcher *m_eventDispatcher; + QScopedPointer<QPlatformFontDatabase> m_fontDatabase; + QScopedPointer<QPlatformDrag> m_drag; + QScopedPointer<QPlatformServices> m_services; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_dummy.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration_dummy.cpp new file mode 100644 index 0000000000..8bc1b17a56 --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenintegration_dummy.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "qoffscreenintegration.h" + +QOffscreenIntegration *QOffscreenIntegration::createOffscreenIntegration() +{ + return new QOffscreenIntegration; +} diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp new file mode 100644 index 0000000000..6c6c516a4e --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "qoffscreenintegration_x11.h" + +#include <QByteArray> +#include <QOpenGLContext> + +#include <X11/Xlib.h> +#include <GL/glx.h> + +#include <QtPlatformSupport/private/qglxconvenience_p.h> + +#include <qpa/qplatformsurface.h> +#include <qsurface.h> + +QT_BEGIN_NAMESPACE + +QOffscreenIntegration *QOffscreenIntegration::createOffscreenIntegration() +{ + return new QOffscreenX11Integration; +} + +bool QOffscreenX11Integration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case OpenGL: return true; + case ThreadedOpenGL: return true; + default: return QOffscreenIntegration::hasCapability(cap); + } +} + +QPlatformOpenGLContext *QOffscreenX11Integration::createPlatformOpenGLContext(QOpenGLContext *context) const +{ + if (!m_connection) + m_connection.reset(new QOffscreenX11Connection); + + return new QOffscreenX11GLXContext(m_connection->x11Info(), context); +} + +QOffscreenX11Connection::QOffscreenX11Connection() +{ + XInitThreads(); + + QByteArray displayName = qgetenv("DISPLAY"); + Display *display = XOpenDisplay(displayName.constData()); + m_display = display; + m_screenNumber = DefaultScreen(display); +} + +QOffscreenX11Connection::~QOffscreenX11Connection() +{ + XCloseDisplay((Display *)m_display); +} + +class QOffscreenX11Info +{ +public: + QOffscreenX11Info(QOffscreenX11Connection *connection) + : m_connection(connection) + { + } + + Display *display() const { + return (Display *)m_connection->display(); + } + + Window root() const { + return DefaultRootWindow(display()); + } + + int screenNumber() const { + return m_connection->screenNumber(); + } + +private: + QOffscreenX11Connection *m_connection; +}; + +QOffscreenX11Info *QOffscreenX11Connection::x11Info() +{ + if (!m_x11Info) + m_x11Info.reset(new QOffscreenX11Info(this)); + return m_x11Info.data(); +} + +class QOffscreenX11GLXContextData +{ +public: + QOffscreenX11Info *x11; + QSurfaceFormat format; + GLXContext context; + GLXContext shareContext; + Window window; +}; + +static Window createDummyWindow(QOffscreenX11Info *x11, XVisualInfo *visualInfo) +{ + Colormap cmap = XCreateColormap(x11->display(), x11->root(), visualInfo->visual, AllocNone); + XSetWindowAttributes a; + a.background_pixel = WhitePixel(x11->display(), x11->screenNumber()); + a.border_pixel = BlackPixel(x11->display(), x11->screenNumber()); + a.colormap = cmap; + + Window window = XCreateWindow(x11->display(), x11->root(), + 0, 0, 100, 100, + 0, visualInfo->depth, InputOutput, visualInfo->visual, + CWBackPixel|CWBorderPixel|CWColormap, &a); + XFreeColormap(x11->display(), cmap); + return window; +} + +static Window createDummyWindow(QOffscreenX11Info *x11, GLXFBConfig config) +{ + XVisualInfo *visualInfo = glXGetVisualFromFBConfig(x11->display(), config); + if (!visualInfo) + qFatal("Could not initialize GLX"); + Window window = createDummyWindow(x11, visualInfo); + XFree(visualInfo); + return window; +} + +QOffscreenX11GLXContext::QOffscreenX11GLXContext(QOffscreenX11Info *x11, QOpenGLContext *context) + : d(new QOffscreenX11GLXContextData) +{ + d->x11 = x11; + d->format = context->format(); + + d->shareContext = 0; + if (context->shareHandle()) + d->shareContext = static_cast<QOffscreenX11GLXContext *>(context->shareHandle())->d->context; + + GLXFBConfig config = qglx_findConfig(x11->display(), x11->screenNumber(), d->format); + if (config) { + d->context = glXCreateNewContext(x11->display(), config, GLX_RGBA_TYPE, d->shareContext, true); + if (!d->context && d->shareContext) { + d->shareContext = 0; + // re-try without a shared glx context + d->context = glXCreateNewContext(x11->display(), config, GLX_RGBA_TYPE, 0, true); + } + + // Get the basic surface format details + if (d->context) + qglx_surfaceFormatFromGLXFBConfig(&d->format, x11->display(), config, d->context); + + // Create a temporary window so that we can make the new context current + d->window = createDummyWindow(x11, config); + } else { + XVisualInfo *visualInfo = qglx_findVisualInfo(x11->display(), 0, &d->format); + if (!visualInfo) + qFatal("Could not initialize GLX"); + d->context = glXCreateContext(x11->display(), visualInfo, d->shareContext, true); + if (!d->context && d->shareContext) { + // re-try without a shared glx context + d->shareContext = 0; + d->context = glXCreateContext(x11->display(), visualInfo, 0, true); + } + + d->window = createDummyWindow(x11, visualInfo); + XFree(visualInfo); + } +} + +QOffscreenX11GLXContext::~QOffscreenX11GLXContext() +{ + glXDestroyContext(d->x11->display(), d->context); + XDestroyWindow(d->x11->display(), d->window); +} + +bool QOffscreenX11GLXContext::makeCurrent(QPlatformSurface *surface) +{ + QSize size = surface->surface()->size(); + + XResizeWindow(d->x11->display(), d->window, size.width(), size.height()); + XSync(d->x11->display(), true); + + if (glXMakeCurrent(d->x11->display(), d->window, d->context)) { + glViewport(0, 0, size.width(), size.height()); + return true; + } + + return false; +} + +void QOffscreenX11GLXContext::doneCurrent() +{ + glXMakeCurrent(d->x11->display(), 0, 0); +} + +void QOffscreenX11GLXContext::swapBuffers(QPlatformSurface *) +{ +} + +void (*QOffscreenX11GLXContext::getProcAddress(const QByteArray &procName)) () +{ + return (void (*)())glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(procName.constData())); +} + +QSurfaceFormat QOffscreenX11GLXContext::format() const +{ + return d->format; +} + +bool QOffscreenX11GLXContext::isSharing() const +{ + return d->shareContext; +} + +bool QOffscreenX11GLXContext::isValid() const +{ + return d->context && d->window; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h new file mode 100644 index 0000000000..9afa302b04 --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 QOFFSCREENINTEGRATION_X11_H +#define QOFFSCREENINTEGRATION_X11_H + +#include "qoffscreenintegration.h" + +#include <qglobal.h> +#include <qscopedpointer.h> + +#include <qpa/qplatformopenglcontext.h> + +QT_BEGIN_NAMESPACE + +class QOffscreenX11Connection; +class QOffscreenX11Info; + +class QOffscreenX11Integration : public QOffscreenIntegration +{ +public: + bool hasCapability(QPlatformIntegration::Capability cap) const; + + QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; + +private: + mutable QScopedPointer<QOffscreenX11Connection> m_connection; +}; + +class QOffscreenX11Connection { +public: + QOffscreenX11Connection(); + ~QOffscreenX11Connection(); + + QOffscreenX11Info *x11Info(); + + void *display() const { return m_display; } + int screenNumber() const { return m_screenNumber; } + +private: + void *m_display; + int m_screenNumber; + + QScopedPointer<QOffscreenX11Info> m_x11Info; +}; + +class QOffscreenX11GLXContextData; + +class QOffscreenX11GLXContext : public QPlatformOpenGLContext +{ +public: + QOffscreenX11GLXContext(QOffscreenX11Info *x11, QOpenGLContext *context); + ~QOffscreenX11GLXContext(); + + bool makeCurrent(QPlatformSurface *surface); + void doneCurrent(); + void swapBuffers(QPlatformSurface *surface); + void (*getProcAddress(const QByteArray &procName)) (); + + QSurfaceFormat format() const; + bool isSharing() const; + bool isValid() const; + +private: + QScopedPointer<QOffscreenX11GLXContextData> d; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/offscreen/qoffscreenwindow.cpp b/src/plugins/platforms/offscreen/qoffscreenwindow.cpp new file mode 100644 index 0000000000..702ef2300c --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenwindow.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 "qoffscreenwindow.h" +#include "qoffscreencommon.h" + +#include <qpa/qplatformscreen.h> +#include <qpa/qwindowsysteminterface.h> + +#include <private/qwindow_p.h> + +QT_BEGIN_NAMESPACE + +QOffscreenWindow::QOffscreenWindow(QWindow *window) + : QPlatformWindow(window) + , m_positionIncludesFrame(false) + , m_visible(false) + , m_pendingGeometryChangeOnShow(true) +{ + if (window->windowState() == Qt::WindowNoState) + setGeometry(window->geometry()); + else + setWindowState(window->windowState()); + + QWindowSystemInterface::flushWindowSystemEvents(); + + static WId counter = 0; + m_winId = ++counter; + + m_windowForWinIdHash[m_winId] = this; +} + +QOffscreenWindow::~QOffscreenWindow() +{ + if (QOffscreenScreen::windowContainingCursor == this) + QOffscreenScreen::windowContainingCursor = 0; + m_windowForWinIdHash.remove(m_winId); +} + +void QOffscreenWindow::setGeometry(const QRect &rect) +{ + if (window()->windowState() != Qt::WindowNoState) + return; + + m_positionIncludesFrame = qt_window_private(window())->positionPolicy == QWindowPrivate::WindowFrameInclusive; + + setFrameMarginsEnabled(true); + setGeometryImpl(rect); + + m_normalGeometry = geometry(); +} + +void QOffscreenWindow::setGeometryImpl(const QRect &rect) +{ + QRect adjusted = rect; + if (adjusted.width() <= 0) + adjusted.setWidth(1); + if (adjusted.height() <= 0) + adjusted.setHeight(1); + + if (m_positionIncludesFrame) { + adjusted.translate(m_margins.left(), m_margins.top()); + } else { + // make sure we're not placed off-screen + if (adjusted.left() < m_margins.left()) + adjusted.translate(m_margins.left(), 0); + if (adjusted.top() < m_margins.top()) + adjusted.translate(0, m_margins.top()); + } + + QPlatformWindow::setGeometry(adjusted); + + if (m_visible) { + QWindowSystemInterface::handleGeometryChange(window(), adjusted); + QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), adjusted.size())); + } else { + m_pendingGeometryChangeOnShow = true; + } +} + +void QOffscreenWindow::setVisible(bool visible) +{ + if (visible == m_visible) + return; + + if (visible) { + if (window()->type() != Qt::ToolTip) + QWindowSystemInterface::handleWindowActivated(window()); + + if (m_pendingGeometryChangeOnShow) { + m_pendingGeometryChangeOnShow = false; + QWindowSystemInterface::handleGeometryChange(window(), geometry()); + } + } + + if (visible) { + QRect rect(QPoint(), geometry().size()); + QWindowSystemInterface::handleExposeEvent(window(), rect); + } else { + QWindowSystemInterface::handleExposeEvent(window(), QRegion()); + } + + m_visible = visible; +} + +void QOffscreenWindow::requestActivateWindow() +{ + if (m_visible) + QWindowSystemInterface::handleWindowActivated(window()); +} + +WId QOffscreenWindow::winId() const +{ + return m_winId; +} + +QMargins QOffscreenWindow::frameMargins() const +{ + return m_margins; +} + +void QOffscreenWindow::setFrameMarginsEnabled(bool enabled) +{ + if (enabled && !(window()->flags() & Qt::FramelessWindowHint)) + m_margins = QMargins(2, 2, 2, 2); + else + m_margins = QMargins(0, 0, 0, 0); +} + +void QOffscreenWindow::setWindowState(Qt::WindowState state) +{ + setFrameMarginsEnabled(state != Qt::WindowFullScreen); + m_positionIncludesFrame = false; + + switch (state) { + case Qt::WindowFullScreen: + setGeometryImpl(screen()->geometry()); + break; + case Qt::WindowMaximized: + setGeometryImpl(screen()->availableGeometry().adjusted(m_margins.left(), m_margins.top(), -m_margins.right(), -m_margins.bottom())); + break; + case Qt::WindowMinimized: + break; + case Qt::WindowNoState: + setGeometryImpl(m_normalGeometry); + break; + default: + break; + } + + QWindowSystemInterface::handleWindowStateChanged(window(), state); +} + +QOffscreenWindow *QOffscreenWindow::windowForWinId(WId id) +{ + return m_windowForWinIdHash.value(id, 0); +} + +QHash<WId, QOffscreenWindow *> QOffscreenWindow::m_windowForWinIdHash; + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/offscreen/qoffscreenwindow.h b/src/plugins/platforms/offscreen/qoffscreenwindow.h new file mode 100644 index 0000000000..cd1cf8e0aa --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenwindow.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 QOFFSCREENWINDOW_H +#define QOFFSCREENWINDOW_H + +#include <qpa/qplatformbackingstore.h> +#include <qpa/qplatformwindow.h> + +#include <qhash.h> + +QT_BEGIN_NAMESPACE + +class QOffscreenWindow : public QPlatformWindow +{ +public: + QOffscreenWindow(QWindow *window); + ~QOffscreenWindow(); + + void setGeometry(const QRect &rect); + void setWindowState(Qt::WindowState state); + + QMargins frameMargins() const; + + void setVisible(bool visible); + void requestActivateWindow(); + + WId winId() const; + + static QOffscreenWindow *windowForWinId(WId id); + +private: + void setFrameMarginsEnabled(bool enabled); + void setGeometryImpl(const QRect &rect); + + QRect m_normalGeometry; + QMargins m_margins; + bool m_positionIncludesFrame; + bool m_visible; + bool m_pendingGeometryChangeOnShow; + WId m_winId; + + static QHash<WId, QOffscreenWindow *> m_windowForWinIdHash; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index 828867c3a7..92664826ab 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -1,12 +1,17 @@ TEMPLATE = subdirs -SUBDIRS += minimal +android:!android-no-sdk: SUBDIRS += android + +SUBDIRS += minimal offscreen contains(QT_CONFIG, xcb) { SUBDIRS += xcb } -mac:!ios: SUBDIRS += cocoa +mac { + ios: SUBDIRS += ios + else: SUBDIRS += cocoa +} win32: SUBDIRS += windows diff --git a/src/plugins/platforms/qnx/qnx.pro b/src/plugins/platforms/qnx/qnx.pro index 8367513fc5..203cdebda9 100644 --- a/src/plugins/platforms/qnx/qnx.pro +++ b/src/plugins/platforms/qnx/qnx.pro @@ -39,6 +39,7 @@ CONFIG(blackberry) { #DEFINES += QQNXSCREEN_DEBUG #DEFINES += QQNXVIRTUALKEYBOARD_DEBUG #DEFINES += QQNXWINDOW_DEBUG +#DEFINES += QQNXCURSOR_DEBUG SOURCES = main.cpp \ @@ -54,7 +55,8 @@ SOURCES = main.cpp \ qqnxnavigatoreventhandler.cpp \ qqnxabstractnavigator.cpp \ qqnxabstractvirtualkeyboard.cpp \ - qqnxservices.cpp + qqnxservices.cpp \ + qqnxcursor.cpp HEADERS = main.h \ qqnxbuffer.h \ @@ -70,7 +72,8 @@ HEADERS = main.h \ qqnxnavigatoreventhandler.h \ qqnxabstractnavigator.h \ qqnxabstractvirtualkeyboard.h \ - qqnxservices.h + qqnxservices.h \ + qqnxcursor.h LIBS += -lscreen diff --git a/src/plugins/platforms/qnx/qqnxbpseventfilter.cpp b/src/plugins/platforms/qnx/qqnxbpseventfilter.cpp index 2d19537896..765853e392 100644 --- a/src/plugins/platforms/qnx/qqnxbpseventfilter.cpp +++ b/src/plugins/platforms/qnx/qqnxbpseventfilter.cpp @@ -87,7 +87,7 @@ void QQnxBpsEventFilter::installOnEventDispatcher(QAbstractEventDispatcher *disp { qBpsEventFilterDebug() << Q_FUNC_INFO << "dispatcher=" << dispatcher; - if (navigator_request_events(0) != BPS_SUCCESS) + if (navigator_request_events(NAVIGATOR_EXTENDED_DATA) != BPS_SUCCESS) qWarning("QQNX: failed to register for navigator events"); dispatcher->installNativeEventFilter(this); @@ -210,6 +210,26 @@ bool QQnxBpsEventFilter::handleNavigatorEvent(bps_event_t *event) m_navigatorEventHandler->handleExit(); break; + case NAVIGATOR_WINDOW_STATE: { + qBpsEventFilterDebug() << Q_FUNC_INFO << "WINDOW STATE event"; + const navigator_window_state_t state = navigator_event_get_window_state(event); + const QByteArray id(navigator_event_get_groupid(event)); + + switch (state) { + case NAVIGATOR_WINDOW_FULLSCREEN: + m_navigatorEventHandler->handleWindowGroupStateChanged(id, Qt::WindowFullScreen); + break; + case NAVIGATOR_WINDOW_THUMBNAIL: + m_navigatorEventHandler->handleWindowGroupStateChanged(id, Qt::WindowMinimized); + break; + case NAVIGATOR_WINDOW_INVISIBLE: + m_navigatorEventHandler->handleWindowGroupDeactivated(id); + break; + } + + break; + } + case NAVIGATOR_WINDOW_ACTIVE: { qBpsEventFilterDebug() << Q_FUNC_INFO << "WINDOW ACTIVE event"; const QByteArray id(navigator_event_get_groupid(event)); diff --git a/src/plugins/platforms/qnx/qqnxclipboard.h b/src/plugins/platforms/qnx/qqnxclipboard.h index 0b75393efd..e069355adc 100644 --- a/src/plugins/platforms/qnx/qqnxclipboard.h +++ b/src/plugins/platforms/qnx/qqnxclipboard.h @@ -42,6 +42,8 @@ #ifndef QQNXCLIPBOARD_H #define QQNXCLIPBOARD_H +#include <QtCore/qglobal.h> + #ifndef QT_NO_CLIPBOARD #include <qpa/qplatformclipboard.h> diff --git a/src/plugins/platforms/qnx/qqnxcursor.cpp b/src/plugins/platforms/qnx/qqnxcursor.cpp new file mode 100644 index 0000000000..4fdff666d7 --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxcursor.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** 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 "qqnxcursor.h" + +#include <QtCore/QDebug> + +#ifdef QQNXCURSOR_DEBUG +#define qCursorDebug qDebug +#else +#define qCursorDebug QT_NO_QDEBUG_MACRO +#endif + +QT_BEGIN_NAMESPACE + +QQnxCursor::QQnxCursor() +{ +} + +#ifndef QT_NO_CURSOR +void QQnxCursor::changeCursor(QCursor *windowCursor, QWindow *window) +{ + Q_UNUSED(windowCursor); + Q_UNUSED(window); +} +#endif + +void QQnxCursor::setPos(const QPoint &pos) +{ + qCursorDebug() << "QQnxCursor::setPos -" << pos; + m_pos = pos; +} + +QPoint QQnxCursor::pos() const +{ + qCursorDebug() << "QQnxCursor::pos -" << m_pos; + return m_pos; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxcursor.h b/src/plugins/platforms/qnx/qqnxcursor.h new file mode 100644 index 0000000000..5d6a8b2c30 --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxcursor.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** 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 QQNXCURSOR_H +#define QQNXCURSOR_H + +#include <qpa/qplatformcursor.h> + +QT_BEGIN_NAMESPACE + +class QQnxCursor : public QPlatformCursor +{ +public: + QQnxCursor(); + +#ifndef QT_NO_CURSOR + void changeCursor(QCursor *windowCursor, QWindow *window); +#endif + void setPos(const QPoint &pos); + + QPoint pos() const; + +private: + QPoint m_pos; +}; + +QT_END_NAMESPACE + +#endif // QQNXCURSOR_H diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index f3cfdab9c6..2d5c2e54e7 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -495,6 +495,8 @@ void QQnxIntegration::createDisplay(screen_display_t display, bool isPrimary) QObject::connect(m_navigatorEventHandler, SIGNAL(rotationChanged(int)), screen, SLOT(setRotation(int))); QObject::connect(m_navigatorEventHandler, SIGNAL(windowGroupActivated(QByteArray)), screen, SLOT(activateWindowGroup(QByteArray))); QObject::connect(m_navigatorEventHandler, SIGNAL(windowGroupDeactivated(QByteArray)), screen, SLOT(deactivateWindowGroup(QByteArray))); + QObject::connect(m_navigatorEventHandler, SIGNAL(windowGroupStateChanged(QByteArray,Qt::WindowState)), + screen, SLOT(windowGroupStateChanged(QByteArray,Qt::WindowState))); } void QQnxIntegration::removeDisplay(QQnxScreen *screen) diff --git a/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.cpp b/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.cpp index 19fa9c5a9f..d561482b47 100644 --- a/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.cpp @@ -106,4 +106,10 @@ void QQnxNavigatorEventHandler::handleWindowGroupDeactivated(const QByteArray &i Q_EMIT windowGroupDeactivated(id); } +void QQnxNavigatorEventHandler::handleWindowGroupStateChanged(const QByteArray &id, Qt::WindowState state) +{ + qNavigatorEventHandlerDebug() << Q_FUNC_INFO << id; + Q_EMIT windowGroupStateChanged(id, state); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.h b/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.h index 2068163473..cce3921a27 100644 --- a/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.h +++ b/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.h @@ -58,11 +58,13 @@ public: void handleExit(); void handleWindowGroupActivated(const QByteArray &id); void handleWindowGroupDeactivated(const QByteArray &id); + void handleWindowGroupStateChanged(const QByteArray &id, Qt::WindowState state); Q_SIGNALS: void rotationChanged(int angle); void windowGroupActivated(const QByteArray &id); void windowGroupDeactivated(const QByteArray &id); + void windowGroupStateChanged(const QByteArray &id, Qt::WindowState state); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxscreen.cpp b/src/plugins/platforms/qnx/qqnxscreen.cpp index 1e58f047ab..f9efbde40c 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.cpp +++ b/src/plugins/platforms/qnx/qqnxscreen.cpp @@ -41,6 +41,7 @@ #include "qqnxscreen.h" #include "qqnxwindow.h" +#include "qqnxcursor.h" #include <QtCore/QThread> #include <QtCore/QDebug> @@ -110,7 +111,8 @@ QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, m_posted(false), m_keyboardHeight(0), m_nativeOrientation(Qt::PrimaryOrientation), - m_platformContext(0) + m_platformContext(0), + m_cursor(new QQnxCursor()) { qScreenDebug() << Q_FUNC_INFO; // Cache initial orientation of this display @@ -149,6 +151,10 @@ QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, QQnxScreen::~QQnxScreen() { qScreenDebug() << Q_FUNC_INFO; + Q_FOREACH (QQnxWindow *childWindow, m_childWindows) + childWindow->setScreen(0); + + delete m_cursor; } static int defaultDepth() @@ -492,6 +498,11 @@ void QQnxScreen::onWindowPost(QQnxWindow *window) } } +QPlatformCursor * QQnxScreen::cursor() const +{ + return m_cursor; +} + void QQnxScreen::keyboardHeightChanged(int height) { if (height == m_keyboardHeight) @@ -542,6 +553,21 @@ void QQnxScreen::windowClosed(void *window) removeOverlayWindow(windowHandle); } +void QQnxScreen::windowGroupStateChanged(const QByteArray &id, Qt::WindowState state) +{ + qScreenDebug() << Q_FUNC_INFO; + + if (!rootWindow() || id != rootWindow()->groupName()) + return; + + QWindow * const window = topMostChildWindow(); + + if (!window) + return; + + QWindowSystemInterface::handleWindowStateChanged(window, state); +} + void QQnxScreen::activateWindowGroup(const QByteArray &id) { qScreenDebug() << Q_FUNC_INFO; @@ -549,13 +575,12 @@ void QQnxScreen::activateWindowGroup(const QByteArray &id) if (!rootWindow() || id != rootWindow()->groupName()) return; - if (!m_childWindows.isEmpty()) { - // We're picking up the last window of the list here - // because this list is ordered by stacking order. - // Last window is effectively the one on top. - QWindow * const window = m_childWindows.last()->window(); - QWindowSystemInterface::handleWindowActivated(window); - } + QWindow * const window = topMostChildWindow(); + + if (!window) + return; + + QWindowSystemInterface::handleWindowActivated(window); } void QQnxScreen::deactivateWindowGroup(const QByteArray &id) @@ -577,4 +602,17 @@ QSharedPointer<QQnxRootWindow> QQnxScreen::rootWindow() const return m_rootWindow; } +QWindow * QQnxScreen::topMostChildWindow() const +{ + if (!m_childWindows.isEmpty()) { + + // We're picking up the last window of the list here + // because this list is ordered by stacking order. + // Last window is effectively the one on top. + return m_childWindows.last()->window(); + } + + return 0; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxscreen.h b/src/plugins/platforms/qnx/qqnxscreen.h index 2851c13c52..41dc675599 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.h +++ b/src/plugins/platforms/qnx/qqnxscreen.h @@ -95,10 +95,13 @@ public: QSharedPointer<QQnxRootWindow> rootWindow() const; + QPlatformCursor *cursor() const; + public Q_SLOTS: void setRotation(int rotation); void newWindowCreated(void *window); void windowClosed(void *window); + void windowGroupStateChanged(const QByteArray &id, Qt::WindowState state); void activateWindowGroup(const QByteArray &id); void deactivateWindowGroup(const QByteArray &id); @@ -112,6 +115,8 @@ private: void addOverlayWindow(screen_window_t window); void removeOverlayWindow(screen_window_t window); + QWindow *topMostChildWindow() const; + screen_context_t m_screenContext; screen_display_t m_display; mutable QSharedPointer<QQnxRootWindow> m_rootWindow; @@ -130,6 +135,8 @@ private: QList<QQnxWindow *> m_childWindows; QList<screen_window_t> m_overlays; + + QPlatformCursor *m_cursor; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp index 4412bb34bd..2d3c7608bf 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp @@ -347,6 +347,8 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType) qFatal("QQNX: failed to query event position, errno=%d", errno); } + QCursor::setPos(pos[0], pos[1]); + // get window coordinates of touch errno = 0; int windowPos[2]; diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp index ab9c94772b..51435a1e55 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxwindow.cpp @@ -53,6 +53,11 @@ #include <errno.h> +#ifdef Q_OS_BLACKBERRY +#include <sys/pps.h> +#include <bps/navigator.h> +#endif + #ifdef QQNXWINDOW_DEBUG #define qWindowDebug qDebug #else @@ -156,7 +161,8 @@ QQnxWindow::~QQnxWindow() // Remove from parent's Hierarchy. removeFromParent(); - m_screen->updateHierarchy(); + if (m_screen) + m_screen->updateHierarchy(); // Cleanup QNX window and its buffers screen_destroy_window(m_window); @@ -272,9 +278,9 @@ void QQnxWindow::setVisible(bool visible) window()->requestActivate(); if (window()->isTopLevel()) { - if (visible) { - QWindowSystemInterface::handleExposeEvent(window(), window()->geometry()); - } else { + QWindowSystemInterface::handleExposeEvent(window(), window()->geometry()); + + if (!visible) { // Flush the context, otherwise it won't disappear immediately screen_flush_context(m_screenContext, 0); } @@ -497,6 +503,11 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen) { qWindowDebug() << Q_FUNC_INFO << "window =" << window() << "platformScreen =" << platformScreen; + if (platformScreen == 0) { // The screen has been destroyed + m_screen = 0; + return; + } + if (m_screen == platformScreen) return; @@ -539,7 +550,7 @@ void QQnxWindow::removeFromParent() m_parentWindow = 0; else qFatal("QQnxWindow: Window Hierarchy broken; window has parent, but parent hasn't got child."); - } else { + } else if (m_screen) { m_screen->removeWindow(this); } } @@ -618,12 +629,20 @@ void QQnxWindow::setWindowState(Qt::WindowState state) switch (state) { - // WindowMinimized is not supported - navigator does not have an API to minimize a window // WindowActive is not an accepted parameter according to the docs - case Qt::WindowMinimized: case Qt::WindowActive: return; + case Qt::WindowMinimized: + minimize(); + + if (m_unmaximizedGeometry.isValid()) + setGeometry(m_unmaximizedGeometry); + else + setGeometry(m_screen->geometry()); + + break; + case Qt::WindowMaximized: case Qt::WindowFullScreen: m_unmaximizedGeometry = geometry(); @@ -683,6 +702,27 @@ void QQnxWindow::blitFrom(QQnxWindow *sourceWindow, const QPoint &sourceOffset, blitHelper(sourceBuffer, targetBuffer, sourceOffset, QPoint(0, 0), targetRegion, true); } +void QQnxWindow::minimize() +{ +#if defined(Q_OS_BLACKBERRY) && !defined(Q_OS_BLACKBERRY_TABLET) + qWindowDebug() << Q_FUNC_INFO; + + pps_encoder_t encoder; + + pps_encoder_initialize(&encoder, false); + pps_encoder_add_string(&encoder, "msg", "minimizeWindow"); + + if (navigator_raw_write(pps_encoder_buffer(&encoder), + pps_encoder_length(&encoder)) != BPS_SUCCESS) { + qWindowDebug() << Q_FUNC_INFO << "navigator_raw_write failed:" << strerror(errno); + } + + pps_encoder_cleanup(&encoder); +#else + qWarning("Qt::WindowMinimized is not supported by this OS version"); +#endif +} + void QQnxWindow::updateZorder(int &topZorder) { errno = 0; diff --git a/src/plugins/platforms/qnx/qqnxwindow.h b/src/plugins/platforms/qnx/qqnxwindow.h index 90226bb9a4..ad136227e3 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.h +++ b/src/plugins/platforms/qnx/qqnxwindow.h @@ -116,6 +116,7 @@ public: QQnxWindow *findWindow(screen_window_t windowHandle); void blitFrom(QQnxWindow *sourceWindow, const QPoint &sourceOffset, const QRegion &targetRegion); + void minimize(); private: QRect setGeometryHelper(const QRect &rect); diff --git a/src/plugins/platforms/windows/accessible/iaccessible2.cpp b/src/plugins/platforms/windows/accessible/iaccessible2.cpp index 164bd6eed9..fb08daa38e 100644 --- a/src/plugins/platforms/windows/accessible/iaccessible2.cpp +++ b/src/plugins/platforms/windows/accessible/iaccessible2.cpp @@ -44,7 +44,7 @@ #include "iaccessible2.h" #include "qwindowsaccessibility.h" -#include <QtGui/qaccessible2.h> +#include <QtGui/private/qaccessible2_p.h> #include <QtGui/qclipboard.h> #include <QtWidgets/qapplication.h> #include <QtCore/qdebug.h> diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp index 3ccc6212a9..752b9e7c20 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp +++ b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp @@ -51,7 +51,7 @@ #include <QtCore/qpointer.h> #include <QtCore/qsettings.h> #include <QtGui/qaccessible.h> -#include <QtGui/qaccessible2.h> +#include <QtGui/private/qaccessible2_p.h> #include <qpa/qplatformnativeinterface.h> #include <QtGui/qwindow.h> #include <QtGui/qguiapplication.h> @@ -162,21 +162,10 @@ void QWindowsAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) } } - typedef void (WINAPI *PtrNotifyWinEvent)(DWORD, HWND, LONG, LONG); - #if defined(Q_OS_WINCE) // ### TODO: check for NotifyWinEvent in CE 6.0 // There is no user32.lib nor NotifyWinEvent for CE return; #else - static PtrNotifyWinEvent ptrNotifyWinEvent = 0; - static bool resolvedNWE = false; - if (!resolvedNWE) { - resolvedNWE = true; - ptrNotifyWinEvent = (PtrNotifyWinEvent)QSystemLibrary::resolve(QLatin1String("user32"), "NotifyWinEvent"); - } - if (!ptrNotifyWinEvent) - return; - // An event has to be associated with a window, // so find the first parent that is a widget and that has a WId QAccessibleInterface *iface = event->accessibleInterface(); @@ -213,7 +202,7 @@ void QWindowsAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) eventNum %= 50; //[0..49] int eventId = - (eventNum - 1); qAccessibleRecentSentEvents()->insert(eventId, qMakePair(QPointer<QObject>(event->object()), event->child())); - ptrNotifyWinEvent(event->type(), hWnd, OBJID_CLIENT, eventId); + ::NotifyWinEvent(event->type(), hWnd, OBJID_CLIENT, eventId); ++eventNum; } #endif // Q_OS_WINCE @@ -283,7 +272,7 @@ bool QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(HWND hwnd, W { if (static_cast<long>(lParam) == static_cast<long>(UiaRootObjectId)) { /* For UI Automation */ - } else if ((DWORD)lParam == OBJID_CLIENT) { + } else if ((DWORD)lParam == DWORD(OBJID_CLIENT)) { #if 1 // Ignoring all requests while starting up // ### Maybe QPA takes care of this??? diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h index af0bd65c80..5a6dc0c2e5 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h +++ b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h @@ -48,7 +48,6 @@ #include <oleacc.h> -QT_BEGIN_HEADER QT_BEGIN_NAMESPACE class QWindowsAccessibility : public QPlatformAccessibility @@ -69,6 +68,5 @@ public: }; QT_END_NAMESPACE -QT_END_HEADER #endif // QWINDOWSACCESSIBILITY_H diff --git a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp index c23902014c..bb5d5d13a7 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp +++ b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp @@ -54,7 +54,7 @@ #include <QtCore/qpair.h> #include <QtCore/qsettings.h> #include <QtGui/qaccessible.h> -#include <QtGui/qaccessible2.h> +#include <QtGui/private/qaccessible2_p.h> #include <QtGui/qguiapplication.h> #include <qpa/qplatformnativeinterface.h> #include <QtGui/qwindow.h> diff --git a/src/plugins/platforms/windows/qtwindows_additional.h b/src/plugins/platforms/windows/qtwindows_additional.h index 8d59fbd7c6..3b2e9787a2 100644 --- a/src/plugins/platforms/windows/qtwindows_additional.h +++ b/src/plugins/platforms/windows/qtwindows_additional.h @@ -126,6 +126,10 @@ typedef struct tagUPDATELAYEREDWINDOWINFO { // IME. #define IMR_CONFIRMRECONVERTSTRING 0x0005 +#ifndef MAPVK_VK_TO_CHAR +# define MAPVK_VK_TO_CHAR 2 +#endif + #endif // if defined(Q_CC_MINGW) /* Touch is supported from Windows 7 onwards and data structures diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 99ef3aacf3..7e6b55dead 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -368,56 +368,53 @@ void QWindowsContext::setKeyGrabber(QWindow *w) } // Window class registering code (from qapplication_win.cpp) -// If 0 is passed as the widget pointer, register a window class -// for QWidget as default. This is used in QGLTemporaryContext -// during GL initialization, where we don't want to use temporary -// QWidgets or QGLWidgets, neither do we want to have separate code -// to register window classes. QString QWindowsContext::registerWindowClass(const QWindow *w, bool isGL) { - const Qt::WindowFlags flags = w ? w->flags() : (Qt::WindowFlags)0; + Q_ASSERT(w); + const Qt::WindowFlags flags = w->flags(); const Qt::WindowFlags type = flags & Qt::WindowType_Mask; - - uint style = 0; - bool icon = false; - QString cname = QStringLiteral("Qt5"); - if (w && isGL) { - cname += QStringLiteral("QGLWindow"); - style = CS_DBLCLKS|CS_OWNDC; - icon = true; - } else if (w && (flags & Qt::MSWindowsOwnDC)) { - cname += QStringLiteral("QWindowOwnDC"); - style = CS_DBLCLKS|CS_OWNDC; - icon = true; - } else if (w && (type == Qt::Tool || type == Qt::ToolTip)) { - style = CS_DBLCLKS; - if (w->inherits("QTipLabel") || w->inherits("QAlphaWidget")) { - if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP - && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based))) { - style |= CS_DROPSHADOW; - } - cname += QStringLiteral("QToolTip"); - } else { - cname += QStringLiteral("QTool"); - } - style |= CS_SAVEBITS; - icon = false; - } else if (w && (type == Qt::Popup)) { - cname += QStringLiteral("QPopup"); - style = CS_DBLCLKS|CS_SAVEBITS; - if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP - && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based))) - style |= CS_DROPSHADOW; + // Determine style and icon. + uint style = CS_DBLCLKS; + bool icon = true; + if (isGL || (flags & Qt::MSWindowsOwnDC)) + style |= CS_OWNDC; + if ((QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) + && (type == Qt::Popup || w->property("_q_windowsDropShadow").toBool())) { + style |= CS_DROPSHADOW; + } + if (type == Qt::Tool || type == Qt::ToolTip || type == Qt::Popup) { + style |= CS_SAVEBITS; // Save/restore background icon = false; - } else { - cname += QStringLiteral("QWindow"); - style = CS_DBLCLKS; - icon = true; } + // Create a unique name for the flag combination + QString cname = QStringLiteral("Qt5QWindow"); + switch (type) { + case Qt::Tool: + cname += QStringLiteral("Tool"); + break; + case Qt::ToolTip: + cname += QStringLiteral("ToolTip"); + break; + case Qt::Popup: + cname += QStringLiteral("Popup"); + break; + default: + break; + } + if (isGL) + cname += QStringLiteral("GL"); + if (style & CS_DROPSHADOW) + cname += QStringLiteral("DropShadow"); + if (style & CS_SAVEBITS) + cname += QStringLiteral("SaveBits"); + if (style & CS_OWNDC) + cname += QStringLiteral("OwnDC"); + if (icon) + cname += QStringLiteral("Icon"); HBRUSH brush = 0; - if (w && !isGL) + if (!isGL) brush = GetSysColorBrush(COLOR_WINDOW); return registerWindowClass(cname, qWindowsWndProc, style, brush, icon); } @@ -783,7 +780,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, d->m_creationContext->obtainedGeometry.moveTo(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return true; case QtWindows::CalculateSize: - return false; + return QWindowsGeometryHint::handleCalculateSize(d->m_creationContext->customMargins, msg, result); default: break; } @@ -818,12 +815,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, platformWindow->getSizeHints(reinterpret_cast<MINMAXINFO *>(lParam)); return true;// maybe available on some SDKs revisit WM_NCCALCSIZE case QtWindows::CalculateSize: - // NCCALCSIZE_PARAMS structure if wParam==TRUE - if (wParam && QWindowsContext::verboseWindows) { - const NCCALCSIZE_PARAMS *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(lParam); - qDebug() << platformWindow->window() << *ncp; - } - break; + return QWindowsGeometryHint::handleCalculateSize(platformWindow->customMargins(), msg, result); #endif case QtWindows::ExposeEvent: return platformWindow->handleWmPaint(hwnd, message, wParam, lParam); @@ -854,10 +846,10 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, return true; case QtWindows::ShowEvent: platformWindow->handleShown(); - return true; + return false; // Indicate transient children should be shown by windows (SW_PARENTOPENING) case QtWindows::HideEvent: platformWindow->handleHidden(); - return true; + return false;// Indicate transient children should be hidden by windows (SW_PARENTCLOSING) case QtWindows::CloseEvent: QWindowSystemInterface::handleCloseEvent(platformWindow->window()); return true; diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp index 4dc9af61d8..5b2a3acbae 100644 --- a/src/plugins/platforms/windows/qwindowscursor.cpp +++ b/src/plugins/platforms/windows/qwindowscursor.cpp @@ -395,6 +395,19 @@ QWindowsWindowCursor QWindowsCursor::standardWindowCursor(Qt::CursorShape shape) } /*! + \brief Return cached pixmap cursor or create new one. +*/ + +QWindowsWindowCursor QWindowsCursor::pixmapWindowCursor(const QCursor &c) +{ + const qint64 cacheKey = c.pixmap().cacheKey(); + PixmapCursorCache::iterator it = m_pixmapCursorCache.find(cacheKey); + if (it == m_pixmapCursorCache.end()) + it = m_pixmapCursorCache.insert(cacheKey, QWindowsWindowCursor(c)); + return it.value(); +} + +/*! \brief Set a cursor on a window. This is called frequently as the mouse moves over widgets in the window @@ -406,11 +419,15 @@ void QWindowsCursor::changeCursor(QCursor *cursorIn, QWindow *window) if (QWindowsContext::verboseWindows > 1) qDebug() << __FUNCTION__ << cursorIn << window; - if (!cursorIn || !window) + if (!window) + return; + if (!cursorIn) { + QWindowsWindow::baseWindowOf(window)->setCursor(QWindowsWindowCursor()); return; + } const QWindowsWindowCursor wcursor = cursorIn->shape() == Qt::BitmapCursor ? - QWindowsWindowCursor(*cursorIn) : standardWindowCursor(cursorIn->shape()); + pixmapWindowCursor(*cursorIn) : standardWindowCursor(cursorIn->shape()); if (wcursor.handle()) { QWindowsWindow::baseWindowOf(window)->setCursor(wcursor); } else { @@ -449,6 +466,7 @@ void QWindowsCursor::setPos(const QPoint &pos) class QWindowsWindowCursorData : public QSharedData { public: + QWindowsWindowCursorData() : m_cursor(Qt::ArrowCursor), m_handle(0) {} explicit QWindowsWindowCursorData(const QCursor &c); ~QWindowsWindowCursorData(); @@ -464,7 +482,13 @@ QWindowsWindowCursorData::QWindowsWindowCursorData(const QCursor &c) : QWindowsWindowCursorData::~QWindowsWindowCursorData() { - DestroyCursor(m_handle); + if (m_handle) + DestroyCursor(m_handle); +} + +QWindowsWindowCursor::QWindowsWindowCursor() : + m_data(new QWindowsWindowCursorData) +{ } QWindowsWindowCursor::QWindowsWindowCursor(const QCursor &c) : @@ -488,6 +512,11 @@ QWindowsWindowCursor & QWindowsWindowCursor::operator =(const QWindowsWindowCurs return *this; } +bool QWindowsWindowCursor::isNull() const +{ + return m_data->m_handle == 0; +} + QCursor QWindowsWindowCursor::cursor() const { return m_data->m_cursor; diff --git a/src/plugins/platforms/windows/qwindowscursor.h b/src/plugins/platforms/windows/qwindowscursor.h index b0256d4203..1e818bc9b8 100644 --- a/src/plugins/platforms/windows/qwindowscursor.h +++ b/src/plugins/platforms/windows/qwindowscursor.h @@ -55,11 +55,13 @@ class QWindowsWindowCursorData; class QWindowsWindowCursor { public: + QWindowsWindowCursor(); explicit QWindowsWindowCursor(const QCursor &c); ~QWindowsWindowCursor(); QWindowsWindowCursor(const QWindowsWindowCursor &c); QWindowsWindowCursor &operator=(const QWindowsWindowCursor &c); + bool isNull() const; QCursor cursor() const; HCURSOR handle() const; @@ -81,11 +83,14 @@ public: static QPoint mousePosition(); QWindowsWindowCursor standardWindowCursor(Qt::CursorShape s = Qt::ArrowCursor); + QWindowsWindowCursor pixmapWindowCursor(const QCursor &c); private: typedef QHash<Qt::CursorShape, QWindowsWindowCursor> StandardCursorCache; + typedef QHash<qint64, QWindowsWindowCursor> PixmapCursorCache; StandardCursorCache m_standardCursorCache; + PixmapCursorCache m_pixmapCursorCache; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index daa4369d88..8565bf0204 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -58,6 +58,10 @@ #include <QtCore/QObject> #include <QtCore/QThread> #include <QtCore/QSysInfo> +#include <QtCore/QSharedData> +#include <QtCore/QExplicitlySharedDataPointer> +#include <QtCore/QMutex> +#include <QtCore/QMutexLocker> #include <QtCore/private/qsystemlibrary_p.h> #include "qtwindows_additional.h" @@ -82,6 +86,8 @@ static const IID q_IID_IShellItem = {0x43826d1e, 0xe718, 0x42ee, {0xbc, 0 #define IID_IShellItem q_IID_IShellItem #else static const IID IID_IShellItem = {0x43826d1e, 0xe718, 0x42ee, {0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe}}; +static const IID IID_IShellItemArray = {0xb63ea76d, 0x1f85, 0x456f, {0xa1, 0x9c, 0x48, 0x15, 0x9e, 0xfa, 0x85, 0x8b}}; +# define LFF_FORCEFILESYSTEM 1 #endif static const IID IID_IFileDialogEvents = {0x973510db, 0x7d7f, 0x452b,{0x89, 0x75, 0x74, 0xa8, 0x58, 0x28, 0xd3, 0x54}}; static const CLSID CLSID_FileOpenDialog = {0xdc1c5a9c, 0xe88a, 0x4dde, {0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7}}; @@ -249,6 +255,34 @@ DECLARE_INTERFACE_(IShellItemArray, IUnknown) }; #endif +#ifndef __IShellLibrary_INTERFACE_DEFINED__ + +enum LIBRARYOPTIONFLAGS {}; +enum DEFAULTSAVEFOLDERTYPE { DSFT_DETECT = 1 }; +enum LIBRARYSAVEFLAGS {}; + +DECLARE_INTERFACE_(IShellLibrary, IUnknown) +{ + STDMETHOD(LoadLibraryFromItem)(THIS_ IShellItem *psiLibrary, DWORD grfMode) PURE; + STDMETHOD(LoadLibraryFromKnownFolder)(THIS_ const GUID &kfidLibrary, DWORD grfMode) PURE; + STDMETHOD(AddFolder)(THIS_ IShellItem *psiLocation) PURE; + STDMETHOD(RemoveFolder)(THIS_ IShellItem *psiLocation) PURE; + STDMETHOD(GetFolders)(THIS_ int lff, REFIID riid, void **ppv) PURE; + STDMETHOD(ResolveFolder)(THIS_ IShellItem *psiFolderToResolve, DWORD dwTimeout, REFIID riid, void **ppv) PURE; + STDMETHOD(GetDefaultSaveFolder)(THIS_ DEFAULTSAVEFOLDERTYPE dsft, REFIID riid, void **ppv) PURE; + STDMETHOD(SetDefaultSaveFolder)(THIS_ DEFAULTSAVEFOLDERTYPE dsft, IShellItem *psi) PURE; + STDMETHOD(GetOptions)(THIS_ LIBRARYOPTIONFLAGS *plofOptions) PURE; + STDMETHOD(SetOptions)(THIS_ LIBRARYOPTIONFLAGS lofMask, LIBRARYOPTIONFLAGS lofOptions) PURE; + STDMETHOD(GetFolderType)(THIS_ GUID *pftid) PURE; + STDMETHOD(SetFolderType)(THIS_ const GUID &ftid) PURE; + STDMETHOD(GetIcon)(THIS_ LPWSTR *ppszIcon) PURE; + STDMETHOD(SetIcon)(THIS_ LPCWSTR pszIcon) PURE; + STDMETHOD(Commit)(THIS_) PURE; + STDMETHOD(Save)(THIS_ IShellItem *psiFolderToSaveIn, LPCWSTR pszLibraryName, LIBRARYSAVEFLAGS lsf, IShellItem **ppsiSavedTo) PURE; + STDMETHOD(SaveInKnownFolder)(THIS_ const GUID &kfidToSaveIn, LPCWSTR pszLibraryName, LIBRARYSAVEFLAGS lsf,IShellItem **ppsiSavedTo) PURE; +}; +#endif + #ifndef __IModalWindow_INTERFACE_DEFINED__ DECLARE_INTERFACE_(IModalWindow, IUnknown) { @@ -387,6 +421,9 @@ void eatMouseMove() Vista on) that mimick the behaviour of their QDialog counterparts as close as possible. + Instances of derived classes are controlled by + QWindowsDialogHelperBase-derived classes. + A major difference is that there is only an exec(), which is a modal, blocking call; there is no non-blocking show(). There 2 types of native dialogs: @@ -401,6 +438,7 @@ void eatMouseMove() like close() can be called on them from event handlers. \endlist + \sa QWindowsDialogHelperBase \internal \ingroup qt-lighthouse-win */ @@ -411,7 +449,6 @@ class QWindowsNativeDialogBase : public QObject public: virtual void setWindowTitle(const QString &title) = 0; virtual void exec(HWND owner = 0) = 0; - virtual QPlatformDialogHelper::DialogCode result() const = 0; signals: void accepted(); @@ -432,12 +469,10 @@ protected: The native dialog is created in setVisible_sys() since then modality and the state of DontUseNativeDialog is known. - Modal dialogs are then started via the platformNativeDialogModalHelp(), - platformNativeDialogModalHelp() slots. - Non-modal dialogs are shown using a separate thread should - they support it. + Modal dialogs are then run by exec(). Non-modal dialogs are shown using a + separate thread started in show() should they support it. - \sa QWindowsDialogThread + \sa QWindowsDialogThread, QWindowsNativeDialogBase \internal \ingroup qt-lighthouse-win */ @@ -445,17 +480,12 @@ protected: template <class BaseClass> QWindowsDialogHelperBase<BaseClass>::QWindowsDialogHelperBase() : m_nativeDialog(0), - m_ownerWindow(0) + m_ownerWindow(0), + m_timerId(0) { } template <class BaseClass> -QWindowsDialogHelperBase<BaseClass>::~QWindowsDialogHelperBase() -{ - delete m_nativeDialog; -} - -template <class BaseClass> QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::nativeDialog() const { if (!m_nativeDialog) { @@ -466,6 +496,19 @@ QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::nativeDialog() co } template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::deleteNativeDialog() +{ + delete m_nativeDialog; + m_nativeDialog = 0; +} + +template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::timerEvent(QTimerEvent *) +{ + startDialogThread(); +} + +template <class BaseClass> QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::ensureNativeDialog() { // Create dialog and apply common settings. @@ -486,22 +529,18 @@ QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::ensureNativeDialo class QWindowsDialogThread : public QThread { public: - QWindowsDialogThread(QWindowsNativeDialogBase *dialog, - HWND owner = 0) : - m_dialog(dialog), m_owner(owner) {} - + QWindowsDialogThread(QPlatformDialogHelper *h) : m_helper(h) {} void run(); private: - QWindowsNativeDialogBase *m_dialog; - const HWND m_owner; + QPlatformDialogHelper *m_helper; }; void QWindowsDialogThread::run() { if (QWindowsContext::verboseDialogs) qDebug(">%s" , __FUNCTION__); - m_dialog->exec(m_owner); + m_helper->exec(); deleteLater(); if (QWindowsContext::verboseDialogs) qDebug("<%s" , __FUNCTION__); @@ -512,7 +551,7 @@ bool QWindowsDialogHelperBase<BaseClass>::show(Qt::WindowFlags, Qt::WindowModality windowModality, QWindow *parent) { - const bool modal = (windowModality == Qt::ApplicationModal); + const bool modal = (windowModality != Qt::NonModal); if (parent) { m_ownerWindow = QWindowsWindow::handleOf(parent); } else { @@ -521,18 +560,40 @@ bool QWindowsDialogHelperBase<BaseClass>::show(Qt::WindowFlags, if (QWindowsContext::verboseDialogs) qDebug("%s modal=%d native=%p parent=%p" , __FUNCTION__, modal, m_nativeDialog, m_ownerWindow); - if (!modal && !supportsNonModalDialog()) + if (!modal && !supportsNonModalDialog(parent)) return false; // Was it changed in-between? if (!ensureNativeDialog()) return false; - if (!modal) { // Modal dialogs are shown in separate slot. - QWindowsDialogThread *thread = new QWindowsDialogThread(m_nativeDialog, m_ownerWindow); - thread->start(); + // Start a background thread to show the dialog. For modal dialogs, + // a subsequent call to exec() may follow. So, start an idle timer + // which will start the dialog thread. If exec() is then called, the + // timer is stopped and dialog->exec() is called directly. + if (modal) { + m_timerId = this->startTimer(0); + } else { + startDialogThread(); } return true; } template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::startDialogThread() +{ + QWindowsDialogThread *thread = new QWindowsDialogThread(this); + thread->start(); + stopTimer(); +} + +template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::stopTimer() +{ + if (m_timerId) { + this->killTimer(m_timerId); + m_timerId = 0; + } +} + +template <class BaseClass> void QWindowsDialogHelperBase<BaseClass>::hide() { if (m_nativeDialog) @@ -545,8 +606,11 @@ void QWindowsDialogHelperBase<BaseClass>::exec() { if (QWindowsContext::verboseDialogs) qDebug("%s" , __FUNCTION__); - if (QWindowsNativeDialogBase *nd = nativeDialog()) + stopTimer(); + if (QWindowsNativeDialogBase *nd = nativeDialog()) { nd->exec(m_ownerWindow); + deleteNativeDialog(); + } } static inline bool snapToDefaultButtonHint() @@ -568,6 +632,101 @@ QVariant QWindowsDialogHelperBase<BaseClass>::styleHint(QPlatformDialogHelper::S } /*! + \class QWindowsFileDialogSharedData + \brief Explicitly shared file dialog parameters that are not in QFileDialogOptions. + + Contain Parameters that need to be cached while the native dialog does not + exist yet. In addition, the data are updated by the change notifications of the + IFileDialogEvent, as querying them after the dialog has closed + does not reliably work. Provides thread-safe setters (for the non-modal case). + + \internal + \ingroup qt-lighthouse-win + \sa QFileDialogOptions +*/ + +class QWindowsFileDialogSharedData +{ +public: + QWindowsFileDialogSharedData() : m_data(new Data) {} + void fromOptions(const QSharedPointer<QFileDialogOptions> &o); + + QString directory() const; + void setDirectory(const QString &); + QString selectedNameFilter() const; + void setSelectedNameFilter(const QString &); + QStringList selectedFiles() const; + void setSelectedFiles(const QStringList &); + QString selectedFile() const; + +private: + class Data : public QSharedData { + public: + QString directory; + QString selectedNameFilter; + QStringList selectedFiles; + QMutex mutex; + }; + QExplicitlySharedDataPointer<Data> m_data; +}; + +inline QString QWindowsFileDialogSharedData::directory() const +{ + m_data->mutex.lock(); + const QString result = m_data->directory; + m_data->mutex.unlock(); + return result; +} + +inline void QWindowsFileDialogSharedData::setDirectory(const QString &d) +{ + QMutexLocker (&m_data->mutex); + m_data->directory = d; +} + +inline QString QWindowsFileDialogSharedData::selectedNameFilter() const +{ + m_data->mutex.lock(); + const QString result = m_data->selectedNameFilter; + m_data->mutex.unlock(); + return result; +} + +inline void QWindowsFileDialogSharedData::setSelectedNameFilter(const QString &f) +{ + QMutexLocker (&m_data->mutex); + m_data->selectedNameFilter = f; +} + +inline QStringList QWindowsFileDialogSharedData::selectedFiles() const +{ + m_data->mutex.lock(); + const QStringList result = m_data->selectedFiles; + m_data->mutex.unlock(); + return result; +} + +inline QString QWindowsFileDialogSharedData::selectedFile() const +{ + const QStringList files = selectedFiles(); + return files.isEmpty() ? QString() : files.front(); +} + +inline void QWindowsFileDialogSharedData::setSelectedFiles(const QStringList &f) +{ + QMutexLocker (&m_data->mutex); + m_data->selectedFiles = f; +} + +inline void QWindowsFileDialogSharedData::fromOptions(const QSharedPointer<QFileDialogOptions> &o) +{ + QMutexLocker (&m_data->mutex); + m_data->directory = o->initialDirectory(); + m_data->selectedFiles = o->initiallySelectedFiles(); + m_data->selectedNameFilter = o->initiallySelectedNameFilter(); +} + +/*! \class QWindowsNativeFileDialogEventHandler \brief Listens to IFileDialog events and forwards them to QWindowsNativeFileDialogBase @@ -609,7 +768,7 @@ public: } // IFileDialogEvents methods - IFACEMETHODIMP OnFileOk(IFileDialog *) { return S_OK; } + IFACEMETHODIMP OnFileOk(IFileDialog *); IFACEMETHODIMP OnFolderChange(IFileDialog *) { return S_OK; } IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *); IFACEMETHODIMP OnHelp(IFileDialog *) { return S_OK; } @@ -658,15 +817,17 @@ class QWindowsNativeFileDialogBase : public QWindowsNativeDialogBase public: ~QWindowsNativeFileDialogBase(); - inline static QWindowsNativeFileDialogBase *create(QFileDialogOptions::AcceptMode am); + inline static QWindowsNativeFileDialogBase *create(QFileDialogOptions::AcceptMode am, const QWindowsFileDialogSharedData &data); virtual void setWindowTitle(const QString &title); inline void setMode(QFileDialogOptions::FileMode mode, QFileDialogOptions::FileDialogOptions options); inline void setDirectory(const QString &directory); + inline void updateDirectory() { setDirectory(m_data.directory()); } inline QString directory() const; virtual void exec(HWND owner = 0); inline void setNameFilters(const QStringList &f); inline void selectNameFilter(const QString &filter); + inline void updateSelectedNameFilter() { selectNameFilter(m_data.selectedNameFilter()); } inline QString selectedNameFilter() const; void selectFile(const QString &fileName) const; bool hideFiltersDetails() const { return m_hideFiltersDetails; } @@ -674,14 +835,16 @@ public: void setDefaultSuffix(const QString &s); inline void setLabelText(QFileDialogOptions::DialogLabel l, const QString &text); - virtual QPlatformDialogHelper::DialogCode result() const - { return fileResult(); } - virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const = 0; + // Return the selected files for tracking in OnSelectionChanged(). virtual QStringList selectedFiles() const = 0; + // Return the result for tracking in OnFileOk(). Differs from selection for + // example by appended default suffixes, etc. + virtual QStringList dialogResult() const = 0; inline void onFolderChange(IShellItem *); inline void onSelectionChange(); inline void onTypeChange(); + inline bool onFileOk(); signals: void directoryEntered(const QString& directory); @@ -692,23 +855,30 @@ public slots: virtual void close() { m_fileDialog->Close(S_OK); } protected: - QWindowsNativeFileDialogBase(); + explicit QWindowsNativeFileDialogBase(const QWindowsFileDialogSharedData &data); bool init(const CLSID &clsId, const IID &iid); inline IFileDialog * fileDialog() const { return m_fileDialog; } static QString itemPath(IShellItem *item); + static QStringList libraryItemFolders(IShellItem *item); + static QString libraryItemDefaultSaveFolder(IShellItem *item); static int itemPaths(IShellItemArray *items, QStringList *fileResult = 0); static IShellItem *shellItem(const QString &path); + const QWindowsFileDialogSharedData &data() const { return m_data; } + QWindowsFileDialogSharedData &data() { return m_data; } + private: IFileDialog *m_fileDialog; IFileDialogEvents *m_dialogEvents; DWORD m_cookie; QStringList m_nameFilters; bool m_hideFiltersDetails; + QWindowsFileDialogSharedData m_data; }; -QWindowsNativeFileDialogBase::QWindowsNativeFileDialogBase() : - m_fileDialog(0), m_dialogEvents(0), m_cookie(0), m_hideFiltersDetails(false) +QWindowsNativeFileDialogBase::QWindowsNativeFileDialogBase(const QWindowsFileDialogSharedData &data) : + m_fileDialog(0), m_dialogEvents(0), m_cookie(0), m_hideFiltersDetails(false), + m_data(data) { } @@ -764,15 +934,17 @@ IShellItem *QWindowsNativeFileDialogBase::shellItem(const QString &path) return result; } #endif - qErrnoWarning("%s: SHCreateItemFromParsingName()) failed", __FUNCTION__); + qErrnoWarning("%s: SHCreateItemFromParsingName(%s)) failed", __FUNCTION__, qPrintable(path)); return 0; } void QWindowsNativeFileDialogBase::setDirectory(const QString &directory) { - if (IShellItem *psi = QWindowsNativeFileDialogBase::shellItem(directory)) { - m_fileDialog->SetFolder(psi); - psi->Release(); + if (!directory.isEmpty()) { + if (IShellItem *psi = QWindowsNativeFileDialogBase::shellItem(directory)) { + m_fileDialog->SetFolder(psi); + psi->Release(); + } } } @@ -832,17 +1004,94 @@ void QWindowsNativeFileDialogBase::setMode(QFileDialogOptions::FileMode mode, QF qErrnoWarning("%s: SetOptions() failed", __FUNCTION__); } -QString QWindowsNativeFileDialogBase::itemPath(IShellItem *item) +#if !defined(Q_OS_WINCE) && defined(__IShellLibrary_INTERFACE_DEFINED__) // Windows SDK 7 + +// Helper for "Libraries": collections of folders appearing from Windows 7 +// on, visible in the file dialogs. + +// Load a library from a IShellItem (sanitized copy of the inline function +// SHLoadLibraryFromItem from ShObjIdl.h, which does not exist for MinGW). +static IShellLibrary *sHLoadLibraryFromItem(IShellItem *libraryItem, DWORD mode) +{ + // ID symbols present from Windows 7 on: + static const CLSID classId_ShellLibrary = {0xd9b3211d, 0xe57f, 0x4426, {0xaa, 0xef, 0x30, 0xa8, 0x6, 0xad, 0xd3, 0x97}}; + static const IID iId_IShellLibrary = {0x11a66efa, 0x382e, 0x451a, {0x92, 0x34, 0x1e, 0xe, 0x12, 0xef, 0x30, 0x85}}; + + IShellLibrary *helper = 0; + IShellLibrary *result = 0; + if (SUCCEEDED(CoCreateInstance(classId_ShellLibrary, NULL, CLSCTX_INPROC_SERVER, iId_IShellLibrary, reinterpret_cast<void **>(&helper)))) + if (SUCCEEDED(helper->LoadLibraryFromItem(libraryItem, mode))) + helper->QueryInterface(iId_IShellLibrary, reinterpret_cast<void **>(&result)); + if (helper) + helper->Release(); + return result; +} + +// Return all folders of a library-type item. +QStringList QWindowsNativeFileDialogBase::libraryItemFolders(IShellItem *item) +{ + QStringList result; + if (IShellLibrary *library = sHLoadLibraryFromItem(item, STGM_READ | STGM_SHARE_DENY_WRITE)) { + IShellItemArray *itemArray = 0; + if (SUCCEEDED(library->GetFolders(LFF_FORCEFILESYSTEM, IID_IShellItemArray, reinterpret_cast<void **>(&itemArray)))) { + QWindowsNativeFileDialogBase::itemPaths(itemArray, &result); + itemArray->Release(); + } + library->Release(); + } + return result; +} + +// Return default save folders of a library-type item. +QString QWindowsNativeFileDialogBase::libraryItemDefaultSaveFolder(IShellItem *item) { QString result; - LPWSTR name = 0; - if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &name))) { - result = QDir::cleanPath(QString::fromWCharArray(name)); - CoTaskMemFree(name); + if (IShellLibrary *library = sHLoadLibraryFromItem(item, STGM_READ | STGM_SHARE_DENY_WRITE)) { + IShellItem *item = 0; + if (SUCCEEDED(library->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem, reinterpret_cast<void **>(&item)))) { + result = QWindowsNativeFileDialogBase::itemPath(item); + item->Release(); + } + library->Release(); } return result; } +#else // !Q_OS_WINCE && __IShellLibrary_INTERFACE_DEFINED__ + +QStringList QWindowsNativeFileDialogBase::libraryItemFolders(IShellItem *) +{ + return QStringList(); +} + +QString QWindowsNativeFileDialogBase::libraryItemDefaultSaveFolder(IShellItem *) +{ + return QString(); +} + +#endif // Q_OS_WINCE || !__IShellLibrary_INTERFACE_DEFINED__ + +QString QWindowsNativeFileDialogBase::itemPath(IShellItem *item) +{ + SFGAOF attributes = 0; + // Check whether it has a file system representation? + if (FAILED(item->GetAttributes(SFGAO_FILESYSTEM, &attributes))) + return QString(); + if (attributes & SFGAO_FILESYSTEM) { + LPWSTR name = 0; + QString result; + if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &name))) { + result = QDir::cleanPath(QString::fromWCharArray(name)); + CoTaskMemFree(name); + } + return result; + } + // Check for a "Library" item + if ((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) && QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) + return QWindowsNativeFileDialogBase::libraryItemDefaultSaveFolder(item); + return QString(); +} + int QWindowsNativeFileDialogBase::itemPaths(IShellItemArray *items, QStringList *result /* = 0 */) { @@ -983,14 +1232,16 @@ static int indexOfNameFilter(const QStringList &filters, const QString &needle) void QWindowsNativeFileDialogBase::selectNameFilter(const QString &filter) { + if (filter.isEmpty()) + return; const int index = indexOfNameFilter(m_nameFilters, filter); - if (index >= 0) { - m_fileDialog->SetFileTypeIndex(index + 1); // one-based. - } else { + if (index < 0) { qWarning("%s: Invalid parameter '%s' not found in '%s'.", __FUNCTION__, qPrintable(filter), qPrintable(m_nameFilters.join(QStringLiteral(", ")))); + return; } + m_fileDialog->SetFileTypeIndex(index + 1); // one-based. } QString QWindowsNativeFileDialogBase::selectedNameFilter() const @@ -1008,6 +1259,7 @@ void QWindowsNativeFileDialogBase::onFolderChange(IShellItem *item) { if (item) { const QString directory = QWindowsNativeFileDialogBase::itemPath(item); + m_data.setDirectory(directory); emit directoryEntered(directory); } } @@ -1015,13 +1267,23 @@ void QWindowsNativeFileDialogBase::onFolderChange(IShellItem *item) void QWindowsNativeFileDialogBase::onSelectionChange() { const QStringList current = selectedFiles(); + m_data.setSelectedFiles(current); if (current.size() == 1) emit currentChanged(current.front()); } void QWindowsNativeFileDialogBase::onTypeChange() { - emit filterSelected(selectedNameFilter()); + const QString filter = selectedNameFilter(); + m_data.setSelectedNameFilter(filter); + emit filterSelected(filter); +} + +bool QWindowsNativeFileDialogBase::onFileOk() +{ + // Store selected files as GetResults() returns invalid data after the dialog closes. + m_data.setSelectedFiles(dialogResult()); + return true; } HRESULT QWindowsNativeFileDialogEventHandler::OnFolderChanging(IFileDialog *, IShellItem *item) @@ -1042,6 +1304,11 @@ HRESULT QWindowsNativeFileDialogEventHandler::OnTypeChange(IFileDialog *) return S_OK; } +HRESULT QWindowsNativeFileDialogEventHandler::OnFileOk(IFileDialog *) +{ + return m_nativeFileDialog->onFileOk() ? S_OK : S_FALSE; +} + /*! \class QWindowsNativeSaveFileDialog \brief Windows native file save dialog wrapper around IFileSaveDialog. @@ -1055,8 +1322,10 @@ HRESULT QWindowsNativeFileDialogEventHandler::OnTypeChange(IFileDialog *) class QWindowsNativeSaveFileDialog : public QWindowsNativeFileDialogBase { public: - virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const; + explicit QWindowsNativeSaveFileDialog(const QWindowsFileDialogSharedData &data) : + QWindowsNativeFileDialogBase(data) {} virtual QStringList selectedFiles() const; + virtual QStringList dialogResult() const; }; // Append a suffix from the name filter "Foo files (*.foo;*.bar)" @@ -1081,17 +1350,13 @@ static inline QString appendSuffix(const QString &fileName, const QString &filte return fileName + QLatin1Char('.') + filter.mid(suffixPos, endPos - suffixPos); } -QPlatformDialogHelper::DialogCode QWindowsNativeSaveFileDialog::fileResult(QStringList *result /* = 0 */) const +QStringList QWindowsNativeSaveFileDialog::dialogResult() const { - if (result) - result->clear(); + QStringList result; IShellItem *item = 0; - const HRESULT hr = fileDialog()->GetResult(&item); - if (FAILED(hr) || !item) - return QPlatformDialogHelper::Rejected; - if (result) - result->push_back(appendSuffix(QWindowsNativeFileDialogBase::itemPath(item), selectedNameFilter())); - return QPlatformDialogHelper::Accepted; + if (SUCCEEDED(fileDialog()->GetResult(&item)) && item) + result.push_back(appendSuffix(QWindowsNativeFileDialogBase::itemPath(item), selectedNameFilter())); + return result; } QStringList QWindowsNativeSaveFileDialog::selectedFiles() const @@ -1117,23 +1382,23 @@ QStringList QWindowsNativeSaveFileDialog::selectedFiles() const class QWindowsNativeOpenFileDialog : public QWindowsNativeFileDialogBase { public: - virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const; + explicit QWindowsNativeOpenFileDialog(const QWindowsFileDialogSharedData &data) : + QWindowsNativeFileDialogBase(data) {} virtual QStringList selectedFiles() const; + virtual QStringList dialogResult() const; private: inline IFileOpenDialog *openFileDialog() const { return static_cast<IFileOpenDialog *>(fileDialog()); } }; -QPlatformDialogHelper::DialogCode QWindowsNativeOpenFileDialog::fileResult(QStringList *result /* = 0 */) const +QStringList QWindowsNativeOpenFileDialog::dialogResult() const { - if (result) - result->clear(); + QStringList result; IShellItemArray *items = 0; - const HRESULT hr = openFileDialog()->GetResults(&items); - if (SUCCEEDED(hr) && items && QWindowsNativeFileDialogBase::itemPaths(items, result) > 0) - return QPlatformDialogHelper::Accepted; - return QPlatformDialogHelper::Rejected; + if (SUCCEEDED(openFileDialog()->GetResults(&items)) && items) + QWindowsNativeFileDialogBase::itemPaths(items, &result); + return result; } QStringList QWindowsNativeOpenFileDialog::selectedFiles() const @@ -1152,17 +1417,18 @@ QStringList QWindowsNativeOpenFileDialog::selectedFiles() const QFileDialog::AcceptMode. */ -QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOptions::AcceptMode am) +QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOptions::AcceptMode am, + const QWindowsFileDialogSharedData &data) { QWindowsNativeFileDialogBase *result = 0; if (am == QFileDialogOptions::AcceptOpen) { - result = new QWindowsNativeOpenFileDialog; + result = new QWindowsNativeOpenFileDialog(data); if (!result->init(CLSID_FileOpenDialog, IID_IFileOpenDialog)) { delete result; return 0; } } else { - result = new QWindowsNativeSaveFileDialog; + result = new QWindowsNativeSaveFileDialog(data); if (!result->init(CLSID_FileSaveDialog, IID_IFileSaveDialog)) { delete result; return 0; @@ -1171,16 +1437,17 @@ QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOp return result; } +static inline bool isQQuickWindow(const QWindow *w = 0) +{ + return w && w->inherits("QQuickWindow"); +} + /*! \class QWindowsFileDialogHelper \brief Helper for native Windows file dialogs - Non-modal dialogs are disabled for now. The functionality is - implemented in principle, however there are failures - when querying the results from a dialog run in another thread. - This could probably be fixed be calling CoInitializeEx() with - the right parameters from each thread. The problem is though - that calls to CoInitialize() occur in several places in Qt. + For Qt 4 compatibility, do not create native non-modal dialogs on widgets, + but only on QQuickWindows, which do not have a fallback. \internal \ingroup qt-lighthouse-win @@ -1190,8 +1457,9 @@ class QWindowsFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDi { public: QWindowsFileDialogHelper() {} - virtual bool supportsNonModalDialog() const { return false; } - + // For Qt 4 compatibility, do not create native non-modal dialogs on widgets, + // but only on QQuickWindows, which do not have a fallback. + virtual bool supportsNonModalDialog(const QWindow *parent = 0) const { return isQQuickWindow(parent); } virtual bool defaultNameFilterDisables() const { return true; } virtual void setDirectory(const QString &directory); @@ -1207,11 +1475,14 @@ private: virtual QWindowsNativeDialogBase *createNativeDialog(); inline QWindowsNativeFileDialogBase *nativeFileDialog() const { return static_cast<QWindowsNativeFileDialogBase *>(nativeDialog()); } + + // Cache for the case no native dialog is created. + QWindowsFileDialogSharedData m_data; }; QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog() { - QWindowsNativeFileDialogBase *result = QWindowsNativeFileDialogBase::create(options()->acceptMode()); + QWindowsNativeFileDialogBase *result = QWindowsNativeFileDialogBase::create(options()->acceptMode(), m_data); if (!result) return 0; QObject::connect(result, SIGNAL(accepted()), this, SIGNAL(accept())); @@ -1225,8 +1496,10 @@ QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog() // Apply settings. const QSharedPointer<QFileDialogOptions> &opts = options(); + m_data.fromOptions(opts); + const QFileDialogOptions::FileMode mode = opts->fileMode(); result->setWindowTitle(opts->windowTitle()); - result->setMode(opts->fileMode(), opts->options()); + result->setMode(mode, opts->options()); result->setHideFiltersDetails(opts->testOption(QFileDialogOptions::HideNameFilterDetails)); const QStringList nameFilters = opts->nameFilters(); if (!nameFilters.isEmpty()) @@ -1235,18 +1508,20 @@ QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog() result->setLabelText(QFileDialogOptions::FileName, opts->labelText(QFileDialogOptions::FileName)); if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) result->setLabelText(QFileDialogOptions::Accept, opts->labelText(QFileDialogOptions::Accept)); - const QString initialDirectory = opts->initialDirectory(); - if (!initialDirectory.isEmpty()) - result->setDirectory(initialDirectory); + result->updateDirectory(); + result->updateSelectedNameFilter(); const QStringList initialSelection = opts->initiallySelectedFiles(); if (initialSelection.size() > 0) { QFileInfo info(initialSelection.front()); if (!info.isDir()) result->selectFile(info.fileName()); } - const QString initialNameFilter = opts->initiallySelectedNameFilter(); - if (!initialNameFilter.isEmpty()) - result->selectNameFilter(initialNameFilter); + // No need to select initialNameFilter if mode is Dir + if (mode != QFileDialogOptions::Directory && mode != QFileDialogOptions::DirectoryOnly) { + const QString initialNameFilter = opts->initiallySelectedNameFilter(); + if (!initialNameFilter.isEmpty()) + result->selectNameFilter(initialNameFilter); + } const QString defaultSuffix = opts->defaultSuffix(); if (!defaultSuffix.isEmpty()) result->setDefaultSuffix(defaultSuffix); @@ -1258,15 +1533,14 @@ void QWindowsFileDialogHelper::setDirectory(const QString &directory) if (QWindowsContext::verboseDialogs) qDebug("%s %s" , __FUNCTION__, qPrintable(directory)); - if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - nfd->setDirectory(directory); + m_data.setDirectory(directory); + if (hasNativeDialog()) + nativeFileDialog()->updateDirectory(); } QString QWindowsFileDialogHelper::directory() const { - if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - return nfd->directory(); - return QString(); + return m_data.directory(); } void QWindowsFileDialogHelper::selectFile(const QString &fileName) @@ -1280,13 +1554,7 @@ void QWindowsFileDialogHelper::selectFile(const QString &fileName) QStringList QWindowsFileDialogHelper::selectedFiles() const { - QStringList files; - if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - nfd->fileResult(&files); - if (QWindowsContext::verboseDialogs) - qDebug("%s files='%s'" , __FUNCTION__, - qPrintable(files.join(QStringLiteral(", ")))); - return files; + return m_data.selectedFiles(); } void QWindowsFileDialogHelper::setFilter() @@ -1305,15 +1573,14 @@ void QWindowsFileDialogHelper::setNameFilters(const QStringList &filters) void QWindowsFileDialogHelper::selectNameFilter(const QString &filter) { - if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - nfd->selectNameFilter(filter); + m_data.setSelectedNameFilter(filter); + if (hasNativeDialog()) + nativeFileDialog()->updateSelectedNameFilter(); } QString QWindowsFileDialogHelper::selectedNameFilter() const { - if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - return nfd->selectedNameFilter(); - return QString(); + return m_data.selectedNameFilter(); } #ifndef Q_OS_WINCE @@ -1337,20 +1604,12 @@ class QWindowsXpNativeFileDialog : public QWindowsNativeDialogBase public: typedef QSharedPointer<QFileDialogOptions> OptionsPtr; - static QWindowsXpNativeFileDialog *create(const OptionsPtr &options); + static QWindowsXpNativeFileDialog *create(const OptionsPtr &options, const QWindowsFileDialogSharedData &data); virtual void setWindowTitle(const QString &t) { m_title = t; } virtual void exec(HWND owner = 0); virtual QPlatformDialogHelper::DialogCode result() const { return m_result; } - void setDirectory(const QString &d) { m_directory = d; } - QString directory() const { return m_directory; } - void selectFile(const QString &f) { m_initialFile = f; } - QStringList selectedFiles() const { return m_selectedFiles; } - void setNameFilters(const QStringList &n) { m_nameFilters = n; } - void selectNameFilter(const QString &f); - QString selectedNameFilter() const { return m_selectedNameFilter; } - int existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam); public slots: @@ -1360,19 +1619,15 @@ private: typedef BOOL (APIENTRY *PtrGetOpenFileNameW)(LPOPENFILENAMEW); typedef BOOL (APIENTRY *PtrGetSaveFileNameW)(LPOPENFILENAMEW); - explicit QWindowsXpNativeFileDialog(const OptionsPtr &options); + explicit QWindowsXpNativeFileDialog(const OptionsPtr &options, const QWindowsFileDialogSharedData &data); void populateOpenFileName(OPENFILENAME *ofn, HWND owner) const; QStringList execExistingDir(HWND owner); QStringList execFileNames(HWND owner, int *selectedFilterIndex) const; const OptionsPtr m_options; QString m_title; - QString m_directory; - QString m_initialFile; - QStringList m_selectedFiles; - QString m_selectedNameFilter; - QStringList m_nameFilters; QPlatformDialogHelper::DialogCode m_result; + QWindowsFileDialogSharedData m_data; static PtrGetOpenFileNameW m_getOpenFileNameW; static PtrGetSaveFileNameW m_getSaveFileNameW; @@ -1381,7 +1636,7 @@ private: QWindowsXpNativeFileDialog::PtrGetOpenFileNameW QWindowsXpNativeFileDialog::m_getOpenFileNameW = 0; QWindowsXpNativeFileDialog::PtrGetSaveFileNameW QWindowsXpNativeFileDialog::m_getSaveFileNameW = 0; -QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options) +QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options, const QWindowsFileDialogSharedData &data) { // GetOpenFileNameW() GetSaveFileName() are resolved // dynamically as not to create a dependency on Comdlg32, which @@ -1392,51 +1647,33 @@ QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr m_getSaveFileNameW = (PtrGetSaveFileNameW)(library.resolve("GetSaveFileNameW")); } if (m_getOpenFileNameW && m_getSaveFileNameW) - return new QWindowsXpNativeFileDialog(options); + return new QWindowsXpNativeFileDialog(options, data); return 0; } -QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options) : - m_options(options), m_result(QPlatformDialogHelper::Rejected) +QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options, + const QWindowsFileDialogSharedData &data) : + m_options(options), m_result(QPlatformDialogHelper::Rejected), m_data(data) { - const QStringList nameFilters = m_options->nameFilters(); - if (!nameFilters.isEmpty()) - setNameFilters(nameFilters); - const QString initialDirectory = m_options->initialDirectory(); - if (!initialDirectory.isEmpty()) - setDirectory(initialDirectory); - const QString initialNameFilter = m_options->initiallySelectedNameFilter(); - if (!initialNameFilter.isEmpty()) - selectNameFilter(initialNameFilter); - const QStringList selectedFiles = m_options->initiallySelectedFiles(); - if (!selectedFiles.isEmpty()) - selectFile(selectedFiles.front()); setWindowTitle(m_options->windowTitle()); } -void QWindowsXpNativeFileDialog::selectNameFilter(const QString &f) -{ - const int index = indexOfNameFilter(m_nameFilters, f); - if (index >= 0) - m_selectedNameFilter = m_nameFilters.at(index); -} - void QWindowsXpNativeFileDialog::exec(HWND owner) { int selectedFilterIndex = -1; - m_selectedFiles = m_options->fileMode() == QFileDialogOptions::DirectoryOnly ? + const QStringList selectedFiles = + m_options->fileMode() == QFileDialogOptions::DirectoryOnly ? execExistingDir(owner) : execFileNames(owner, &selectedFilterIndex); + m_data.setSelectedFiles(selectedFiles); QWindowsDialogs::eatMouseMove(); - if (m_selectedFiles.isEmpty()) { + if (selectedFiles.isEmpty()) { m_result = QPlatformDialogHelper::Rejected; emit rejected(); } else { - if (selectedFilterIndex >= 0 && selectedFilterIndex < m_nameFilters.size()) { - m_selectedNameFilter = m_nameFilters.at(selectedFilterIndex); - } else { - m_selectedNameFilter.clear(); - } - m_directory = QFileInfo(m_selectedFiles.front()).absolutePath(); + const QStringList nameFilters = m_options->nameFilters(); + if (selectedFilterIndex >= 0 && selectedFilterIndex < nameFilters.size()) + m_data.setSelectedNameFilter(nameFilters.at(selectedFilterIndex)); + m_data.setDirectory(QFileInfo(selectedFiles.front()).absolutePath()); m_result = QPlatformDialogHelper::Accepted; emit accepted(); } @@ -1460,9 +1697,11 @@ typedef PIDLIST_ABSOLUTE qt_LpItemIdList; int QWindowsXpNativeFileDialog::existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam) { switch (uMsg) { - case BFFM_INITIALIZED: - if (!m_initialFile.isEmpty()) - SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(m_initialFile.utf16())); + case BFFM_INITIALIZED: { + const QString initialFile = m_data.selectedFile(); + if (!initialFile.isEmpty()) + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initialFile.utf16())); + } break; case BFFM_SELCHANGED: { wchar_t path[MAX_PATH]; @@ -1534,7 +1773,7 @@ void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND ow *ptr++ = 0; } *ptr = 0; - const int nameFilterIndex = indexOfNameFilter(m_nameFilters, m_selectedNameFilter); + const int nameFilterIndex = indexOfNameFilter(m_options->nameFilters(), m_data.selectedNameFilter()); if (nameFilterIndex >= 0) ofn->nFilterIndex = nameFilterIndex + 1; // 1..n based. // lpstrFile receives the initial selection and is the buffer @@ -1542,10 +1781,10 @@ void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND ow // will not show. ofn->nMaxFile = 65535; const QString initiallySelectedFile = - QDir::toNativeSeparators(m_initialFile).remove(QLatin1Char('<')). + QDir::toNativeSeparators(m_data.selectedFile()).remove(QLatin1Char('<')). remove(QLatin1Char('>')).remove(QLatin1Char('"')).remove(QLatin1Char('|')); ofn->lpstrFile = qStringToWCharArray(initiallySelectedFile, ofn->nMaxFile); - ofn->lpstrInitialDir = qStringToWCharArray(QDir::toNativeSeparators(m_directory)); + ofn->lpstrInitialDir = qStringToWCharArray(QDir::toNativeSeparators(m_data.directory())); ofn->lpstrTitle = (wchar_t*)m_title.utf16(); // Determine lpstrDefExt. Note that the current MSDN docs document this // member wrong. It should rather be documented as "the default extension @@ -1614,8 +1853,7 @@ class QWindowsXpFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFile { public: QWindowsXpFileDialogHelper() {} - virtual bool supportsNonModalDialog() const { return false; } - + virtual bool supportsNonModalDialog(const QWindow *parent = 0) const { return isQQuickWindow(parent); } virtual bool defaultNameFilterDisables() const { return true; } virtual void setDirectory(const QString &directory); @@ -1631,11 +1869,14 @@ private: virtual QWindowsNativeDialogBase *createNativeDialog(); inline QWindowsXpNativeFileDialog *nativeFileDialog() const { return static_cast<QWindowsXpNativeFileDialog *>(nativeDialog()); } + + QWindowsFileDialogSharedData m_data; }; QWindowsNativeDialogBase *QWindowsXpFileDialogHelper::createNativeDialog() { - if (QWindowsNativeDialogBase *result = QWindowsXpNativeFileDialog::create(options())) { + m_data.fromOptions(options()); + if (QWindowsXpNativeFileDialog *result = QWindowsXpNativeFileDialog::create(options(), m_data)) { QObject::connect(result, SIGNAL(accepted()), this, SIGNAL(accept())); QObject::connect(result, SIGNAL(rejected()), this, SIGNAL(reject())); return result; @@ -1645,47 +1886,37 @@ QWindowsNativeDialogBase *QWindowsXpFileDialogHelper::createNativeDialog() void QWindowsXpFileDialogHelper::setDirectory(const QString &directory) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->setDirectory(directory); + m_data.setDirectory(directory); // Dialog cannot be updated at run-time. } QString QWindowsXpFileDialogHelper::directory() const { - if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - return nfd->directory(); - return QString(); + return m_data.directory(); } void QWindowsXpFileDialogHelper::selectFile(const QString &filename) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->selectFile(filename); + m_data.setSelectedFiles(QStringList(filename)); // Dialog cannot be updated at run-time. } QStringList QWindowsXpFileDialogHelper::selectedFiles() const { - if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - return nfd->selectedFiles(); - return QStringList(); + return m_data.selectedFiles(); } -void QWindowsXpFileDialogHelper::setNameFilters(const QStringList &n) +void QWindowsXpFileDialogHelper::setNameFilters(const QStringList &) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->setNameFilters(n); + // Dialog cannot be updated at run-time. } void QWindowsXpFileDialogHelper::selectNameFilter(const QString &f) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->selectNameFilter(f); + m_data.setSelectedNameFilter(f); // Dialog cannot be updated at run-time. } QString QWindowsXpFileDialogHelper::selectedNameFilter() const { - if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - return nfd->selectedNameFilter(); - return QString(); + return m_data.selectedNameFilter(); } #endif // Q_OS_WINCE diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.h b/src/plugins/platforms/windows/qwindowsdialoghelpers.h index 07f105cb35..7884f398f3 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.h +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.h @@ -65,6 +65,7 @@ template <class BaseClass> class QWindowsDialogHelperBase : public BaseClass { public: + ~QWindowsDialogHelperBase() { deleteNativeDialog(); } virtual void exec(); virtual bool show(Qt::WindowFlags windowFlags, @@ -73,19 +74,24 @@ public: virtual void hide(); virtual QVariant styleHint(QPlatformDialogHelper::StyleHint) const; - virtual bool supportsNonModalDialog() const { return true; } + virtual bool supportsNonModalDialog(const QWindow * /* parent */ = 0) const { return true; } protected: QWindowsDialogHelperBase(); - ~QWindowsDialogHelperBase(); QWindowsNativeDialogBase *nativeDialog() const; + inline bool hasNativeDialog() const { return m_nativeDialog; } + void deleteNativeDialog(); + void timerEvent(QTimerEvent *); private: virtual QWindowsNativeDialogBase *createNativeDialog() = 0; inline QWindowsNativeDialogBase *ensureNativeDialog(); + inline void startDialogThread(); + inline void stopTimer(); QWindowsNativeDialogBase *m_nativeDialog; HWND m_ownerWindow; + int m_timerId; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 1286f5c106..fedda750d9 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -304,10 +304,10 @@ private: class DragCursorHandle { Q_DISABLE_COPY(DragCursorHandle) public: - DragCursorHandle(HCURSOR c, quint64 k) : cursor(c), cacheKey(k) {} + DragCursorHandle(HCURSOR c, qint64 k) : cursor(c), cacheKey(k) {} ~DragCursorHandle() { DestroyCursor(cursor); } HCURSOR cursor; - quint64 cacheKey; + qint64 cacheKey; }; typedef QMap <Qt::DropAction, QSharedPointer<DragCursorHandle> > ActionCursorMap; @@ -490,7 +490,7 @@ QWindowsOleDropSource::GiveFeedback(DWORD dwEffect) qDebug("%s dwEffect=%lu, action=%d", __FUNCTION__, dwEffect, action); QSharedPointer<DragCursorHandle> cursorHandler = m_cursors.value(action); - quint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey(); + qint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey(); if (cursorHandler.isNull() || currentCacheKey != cursorHandler->cacheKey) createCursors(); diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp index b4fb19e8e8..c59b0edf78 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp @@ -606,130 +606,6 @@ QDebug operator<<(QDebug d, const QFontDef &def) return d; } -/* From QFontDatabase.cpp, qt_determine_writing_systems_from_truetype_bits(). - * Fixme: Make public? */ - -// see the Unicode subset bitfields in the MSDN docs -static int requiredUnicodeBits[QFontDatabase::WritingSystemsCount][2] = { - // Any, - { 127, 127 }, - // Latin, - { 0, 127 }, - // Greek, - { 7, 127 }, - // Cyrillic, - { 9, 127 }, - // Armenian, - { 10, 127 }, - // Hebrew, - { 11, 127 }, - // Arabic, - { 13, 127 }, - // Syriac, - { 71, 127 }, - //Thaana, - { 72, 127 }, - //Devanagari, - { 15, 127 }, - //Bengali, - { 16, 127 }, - //Gurmukhi, - { 17, 127 }, - //Gujarati, - { 18, 127 }, - //Oriya, - { 19, 127 }, - //Tamil, - { 20, 127 }, - //Telugu, - { 21, 127 }, - //Kannada, - { 22, 127 }, - //Malayalam, - { 23, 127 }, - //Sinhala, - { 73, 127 }, - //Thai, - { 24, 127 }, - //Lao, - { 25, 127 }, - //Tibetan, - { 70, 127 }, - //Myanmar, - { 74, 127 }, - // Georgian, - { 26, 127 }, - // Khmer, - { 80, 127 }, - // SimplifiedChinese, - { 126, 127 }, - // TraditionalChinese, - { 126, 127 }, - // Japanese, - { 126, 127 }, - // Korean, - { 56, 127 }, - // Vietnamese, - { 0, 127 }, // same as latin1 - // Other, - { 126, 127 }, - // Ogham, - { 78, 127 }, - // Runic, - { 79, 127 }, - // Nko, - { 14, 127 }, -}; - -enum -{ - SimplifiedChineseCsbBit = 18, - TraditionalChineseCsbBit = 20, - JapaneseCsbBit = 17, - KoreanCsbBit = 21 -}; - -static inline void writingSystemsFromTrueTypeBits(quint32 unicodeRange[4], - quint32 codePageRange[2], - QSupportedWritingSystems *ws) -{ - bool hasScript = false; - for(int i = 0; i < QFontDatabase::WritingSystemsCount; i++) { - int bit = requiredUnicodeBits[i][0]; - int index = bit/32; - int flag = 1 << (bit&31); - if (bit != 126 && unicodeRange[index] & flag) { - bit = requiredUnicodeBits[i][1]; - index = bit/32; - - flag = 1 << (bit&31); - if (bit == 127 || unicodeRange[index] & flag) { - ws->setSupported(QFontDatabase::WritingSystem(i), true); - hasScript = true; - } - } - } - if(codePageRange[0] & (1 << SimplifiedChineseCsbBit)) { - ws->setSupported(QFontDatabase::SimplifiedChinese, true); - hasScript = true; - } - if(codePageRange[0] & (1 << TraditionalChineseCsbBit)) { - ws->setSupported(QFontDatabase::TraditionalChinese, true); - hasScript = true; - } - if(codePageRange[0] & (1 << JapaneseCsbBit)) { - ws->setSupported(QFontDatabase::Japanese, true); - hasScript = true; - //qDebug("font %s supports Japanese", familyName.latin1()); - } - if(codePageRange[0] & (1 << KoreanCsbBit)) { - ws->setSupported(QFontDatabase::Korean, true); - hasScript = true; - } - if (!hasScript) - ws->setSupported(QFontDatabase::Symbol, true); -} - // convert 0 ~ 1000 integer to QFont::Weight static inline QFont::Weight weightFromInteger(long weight) { @@ -1019,7 +895,7 @@ static bool addFontToDatabase(QString familyName, const QString &scriptName, unicodeRange[3] = 0xffffffff; } #endif - writingSystemsFromTrueTypeBits(unicodeRange, codePageRange, &writingSystems); + writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); // ### Hack to work around problem with Thai text on Windows 7. Segoe UI contains // the symbol for Baht, and Windows thus reports that it supports the Thai script. // Since it's the default UI font on this platform, most widgets will be unable to @@ -1160,9 +1036,7 @@ QWindowsFontDatabase::~QWindowsFontDatabase() removeApplicationFonts(); } -QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, - QUnicodeTables::Script script, - void *handle) +QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle) { QFontEngine *fe = QWindowsFontDatabase::createEngine(script, fontDef, 0, QWindowsContext::instance()->defaultDPI(), false, @@ -1215,7 +1089,7 @@ QFontEngine *QWindowsFontDatabase::fontEngine(const QByteArray &fontData, qreal request.styleStrategy = QFont::NoFontMerging | QFont::PreferMatch; request.hintingPreference = hintingPreference; - fontEngine = QWindowsFontDatabase::createEngine(QUnicodeTables::Common, request, 0, + fontEngine = QWindowsFontDatabase::createEngine(QChar::Script_Common, request, 0, QWindowsContext::instance()->defaultDPI(), false, QStringList(), sharedFontData()); @@ -1223,11 +1097,11 @@ QFontEngine *QWindowsFontDatabase::fontEngine(const QByteArray &fontData, qreal if (request.family != fontEngine->fontDef.family) { qWarning("%s: Failed to load font. Got fallback instead: %s", __FUNCTION__, qPrintable(fontEngine->fontDef.family)); - if (fontEngine->cache_count == 0 && fontEngine->ref.load() == 0) + if (fontEngine->ref.load() == 0) delete fontEngine; fontEngine = 0; } else { - Q_ASSERT(fontEngine->cache_count == 0 && fontEngine->ref.load() == 0); + Q_ASSERT(fontEngine->ref.load() == 0); // Override the generated font name static_cast<QWindowsFontEngine *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName); @@ -1529,8 +1403,8 @@ HFONT QWindowsFontDatabase::systemFont() static inline bool scriptRequiresOpenType(int script) { - return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala) - || script == QUnicodeTables::Khmer || script == QUnicodeTables::Nko); + return ((script >= QChar::Script_Syriac && script <= QChar::Script_Sinhala) + || script == QChar::Script_Khmer || script == QChar::Script_Nko); } static const char *other_tryFonts[] = { @@ -1715,7 +1589,7 @@ static QStringList extraTryFontsForFamily(const QString& family) return result; } -QStringList QWindowsFontDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const +QStringList QWindowsFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const { QStringList result = QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script); if (!result.isEmpty()) @@ -1885,8 +1759,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(int script, const QFontDef &requ // for scripts that do not require OpenType we should just look at the list of // supported writing systems in the font's OS/2 table. if (scriptRequiresOpenType(script)) { - HB_Face hbFace = few->harfbuzzFace(); - if (!hbFace || !hbFace->supported_scripts[script]) { + if (!few->supportsScript(QChar::Script(script))) { qWarning(" OpenType support missing for script\n"); delete few; return 0; @@ -1917,7 +1790,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(int script, const QFontDef &requ directWriteFont->Release(); #endif - if (script == QUnicodeTables::Common + if (script == QChar::Script_Common && !(request.styleStrategy & QFont::NoFontMerging)) { QStringList extraFonts = extraTryFontsForFamily(request.family); if (extraFonts.size()) { diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.h b/src/plugins/platforms/windows/qwindowsfontdatabase.h index c14d6027c2..b9e6c38eaa 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.h +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.h @@ -78,9 +78,9 @@ public: ~QWindowsFontDatabase(); virtual void populateFontDatabase(); - virtual QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle); + virtual QFontEngine *fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle); virtual QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference); - virtual QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const; + virtual QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const; virtual QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName); virtual void releaseHandle(void *handle); virtual QString fontDir() const; diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp index 406dc636b8..99b8d215f2 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp @@ -175,7 +175,7 @@ static bool addFontToDatabase(QString familyName, const QString &scriptName, quint32 codePageRange[2] = { signature->fsCsb[0], signature->fsCsb[1] }; - writingSystems = QBasicFontDatabase::determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); // ### Hack to work around problem with Thai text on Windows 7. Segoe UI contains // the symbol for Baht, and Windows thus reports that it supports the Thai script. // Since it's the default UI font on this platform, most widgets will be unable to @@ -362,7 +362,7 @@ void QWindowsFontDatabaseFT::populate(const QString &family) ReleaseDC(0, dummy); } -QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle) +QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle) { QFontEngine *fe = QBasicFontDatabase::fontEngine(fontDef, script, handle); if (QWindowsContext::verboseFonts) @@ -430,9 +430,9 @@ static const char *kr_tryFonts[] = { static const char **tryFonts = 0; -QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const +QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const { - if(script == QUnicodeTables::Common) { + if (script == QChar::Script_Common) { // && !(request.styleStrategy & QFont::NoFontMerging)) { QFontDatabase db; if (!db.writingSystems(family).contains(QFontDatabase::Symbol)) { @@ -518,8 +518,8 @@ HFONT QWindowsFontDatabaseFT::systemFont() static inline bool scriptRequiresOpenType(int script) { - return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala) - || script == QUnicodeTables::Khmer || script == QUnicodeTables::Nko); + return ((script >= QChar::Script_Syriac && script <= QChar::Script_Sinhala) + || script == QChar::Script_Khmer || script == QChar::Script_Nko); } static inline int verticalDPI() diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h index 2ce429d012..d3cbc0210a 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h @@ -52,10 +52,10 @@ class QWindowsFontDatabaseFT : public QBasicFontDatabase { public: void populateFontDatabase(); - QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle); + QFontEngine *fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle); QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference); - QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const; + QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const; virtual QString fontDir() const; virtual QFont defaultFont() const; diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index 7fcd9814bd..c402f00453 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -66,7 +66,6 @@ #include <QtCore/QThreadStorage> #include <QtCore/private/qsystemlibrary_p.h> -#include <QtCore/private/qunicodetables_p.h> #include <QtCore/QDebug> #include <limits.h> @@ -155,9 +154,20 @@ static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc) return otm; } +bool QWindowsFontEngine::hasCFFTable() const +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + return GetFontData(hdc, MAKE_TAG('C', 'F', 'F', ' '), 0, 0, 0) != GDI_ERROR; +} + void QWindowsFontEngine::getCMap() { ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE); + + // TMPF_TRUETYPE is not set for fonts with CFF tables + cffTable = !ttf && hasCFFTable(); + HDC hdc = m_fontEngineData->hdc; SelectObject(hdc, hfont); bool symb = false; @@ -1041,7 +1051,7 @@ void QWindowsFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gly bool QWindowsFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const { - if (!ttf) + if (!ttf && !cffTable) return false; HDC hdc = m_fontEngineData->hdc; SelectObject(hdc, hfont); @@ -1216,13 +1226,13 @@ QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed, const QTra { HFONT font = hfont; - int contrast; + UINT contrast; SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0); SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) 1000, 0); int margin = glyphMargin(QFontEngineGlyphCache::Raster_RGBMask); QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, margin, t, QImage::Format_RGB32); - SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) contrast, 0); + SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) quintptr(contrast), 0); if (mask == 0) return QImage(); @@ -1258,7 +1268,7 @@ QFontEngine *QWindowsFontEngine::cloneWithSize(qreal pixelSize) const request.styleStrategy |= QFont::NoFontMerging; QFontEngine *fontEngine = - QWindowsFontDatabase::createEngine(QUnicodeTables::Common, request, 0, + QWindowsFontDatabase::createEngine(QChar::Script_Common, request, 0, QWindowsContext::instance()->defaultDPI(), false, QStringList(), m_fontEngineData); diff --git a/src/plugins/platforms/windows/qwindowsfontengine.h b/src/plugins/platforms/windows/qwindowsfontengine.h index a23db2f235..2bf6ead503 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.h +++ b/src/plugins/platforms/windows/qwindowsfontengine.h @@ -145,6 +145,7 @@ public: private: QWindowsNativeImage *drawGDIGlyph(HFONT font, glyph_t, int margin, const QTransform &xform, QImage::Format mask_format); + bool hasCFFTable() const; const QSharedPointer<QWindowsFontEngineData> m_fontEngineData; @@ -155,6 +156,7 @@ private: uint stockFont : 1; uint ttf : 1; uint hasOutline : 1; + uint cffTable : 1; TEXTMETRIC tm; int lw; const unsigned char *cmap; diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp index 183acedfc8..5b6ce695d8 100644 --- a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp @@ -376,10 +376,12 @@ void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEn if (SUCCEEDED(hr)) { for (int i=0; i<glyphs->numGlyphs; ++i) { glyphs->advances_x[i] = DESIGN_TO_LOGICAL(glyphMetrics[i].advanceWidth); - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - glyphs->advances_x[i] = glyphs->advances_x[i].round(); glyphs->advances_y[i] = 0; } + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + for (int i = 0; i < glyphs->numGlyphs; ++i) + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + } } else { qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__); } diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h index 35ed6f3891..106087f757 100644 --- a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h @@ -42,6 +42,8 @@ #ifndef QWINDOWSFONTENGINEDIRECTWRITE_H #define QWINDOWSFONTENGINEDIRECTWRITE_H +#include <QtCore/qglobal.h> + #ifndef QT_NO_DIRECTWRITE #include <QtGui/private/qfontengine_p.h> diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp index 081b42ce65..6f59b33e62 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -232,6 +232,7 @@ static QSurfaceFormat QWindowsOpenGLAdditionalFormat *additionalIn = 0) { QSurfaceFormat format; + format.setRenderableType(QSurfaceFormat::OpenGL); if (pfd.dwFlags & PFD_DOUBLEBUFFER) format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); format.setDepthBufferSize(pfd.cDepthBits); @@ -500,6 +501,7 @@ static QSurfaceFormat enum { attribSize =40 }; QSurfaceFormat result; + result.setRenderableType(QSurfaceFormat::OpenGL); if (!staticContext.hasExtensions()) return result; int iAttributes[attribSize]; @@ -875,6 +877,12 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex m_renderingContext(0), m_pixelFormat(0), m_extensionsUsed(false) { + QSurfaceFormat format = context->format(); + if (format.renderableType() == QSurfaceFormat::DefaultRenderableType) + format.setRenderableType(QSurfaceFormat::OpenGL); + if (format.renderableType() != QSurfaceFormat::OpenGL) + return; + // workaround for matrox driver: // make a cheap call to opengl to force loading of DLL static bool opengl32dll = false; @@ -912,7 +920,7 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex QWindowsOpenGLAdditionalFormat obtainedAdditional; if (tryExtensions) { m_pixelFormat = - ARB::choosePixelFormat(hdc, *m_staticContext, context->format(), + ARB::choosePixelFormat(hdc, *m_staticContext, format, requestedAdditional, &m_obtainedPixelFormatDescriptor); if (m_pixelFormat > 0) { m_obtainedFormat = @@ -922,7 +930,7 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex } } // tryExtensions if (!m_pixelFormat) { // Failed, try GDI - m_pixelFormat = GDI::choosePixelFormat(hdc, context->format(), requestedAdditional, + m_pixelFormat = GDI::choosePixelFormat(hdc, format, requestedAdditional, &m_obtainedPixelFormatDescriptor); if (m_pixelFormat) m_obtainedFormat = @@ -945,7 +953,7 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex if (m_extensionsUsed) m_renderingContext = ARB::createContext(*m_staticContext, hdc, - context->format(), + format, requestedAdditional, sharingRenderingContext); if (!m_renderingContext) diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index da4519199f..30e0478e64 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -77,6 +77,7 @@ #include <QtCore/private/qeventdispatcher_win_p.h> #include <QtCore/QDebug> +#include <QtCore/QVariant> QT_BEGIN_NAMESPACE @@ -114,6 +115,11 @@ public: bool asyncExpose() const; void setAsyncExpose(bool value); + + QVariantMap windowProperties(QPlatformWindow *window) const; + QVariant windowProperty(QPlatformWindow *window, const QString &name) const; + QVariant windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const; + void setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value); }; void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resource, QWindow *window) @@ -150,6 +156,37 @@ void *QWindowsNativeInterface::nativeResourceForBackingStore(const QByteArray &r return 0; } +static const char customMarginPropertyC[] = "WindowsCustomMargins"; + +QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name) const +{ + QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window); + if (name == QLatin1String(customMarginPropertyC)) + return qVariantFromValue(platformWindow->customMargins()); + return QVariant(); +} + +QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const +{ + const QVariant result = windowProperty(window, name); + return result.isValid() ? result : defaultValue; +} + +void QWindowsNativeInterface::setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value) +{ + QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window); + if (name == QLatin1String(customMarginPropertyC)) + platformWindow->setCustomMargins(qvariant_cast<QMargins>(value)); +} + +QVariantMap QWindowsNativeInterface::windowProperties(QPlatformWindow *window) const +{ + QVariantMap result; + const QString customMarginProperty = QLatin1String(customMarginPropertyC); + result.insert(customMarginProperty, windowProperty(window, customMarginProperty)); + return result; +} + #ifndef QT_NO_OPENGL void *QWindowsNativeInterface::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) { @@ -372,6 +409,11 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons QWindowsWindow::WindowData requested; requested.flags = window->flags(); requested.geometry = window->geometry(); + // Apply custom margins (see QWindowsWindow::setCustomMargins())). + const QVariant customMarginsV = window->property("_q_windowsCustomMargins"); + if (customMarginsV.isValid()) + requested.customMargins = qvariant_cast<QMargins>(customMarginsV); + const QWindowsWindow::WindowData obtained = QWindowsWindow::WindowData::create(window, requested, window->title()); if (QWindowsContext::verboseIntegration || QWindowsContext::verboseWindows) diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h index 24dc01f0bd..ca484415be 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -78,7 +78,7 @@ public: # ifndef QT_NO_DRAGANDDROP virtual QPlatformDrag *drag() const; # endif -#endif !QT_NO_CLIPBOARD +#endif // !QT_NO_CLIPBOARD virtual QPlatformInputContext *inputContext() const; #ifndef QT_NO_ACCESSIBILITY virtual QPlatformAccessibility *accessibility() const; diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 56deb27442..924d604641 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -856,9 +856,15 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms if (isNumpad && (nModifiers & AltAny)) { code = winceKeyBend(msg.wParam); } else if (!isDeadKey) { - unsigned char kbdBuffer[256]; // Will hold the complete keyboard state - GetKeyboardState(kbdBuffer); - code = toKeyOrUnicode(msg.wParam, scancode, kbdBuffer); + // QTBUG-8764, QTBUG-10032 + // Can't call toKeyOrUnicode because that would call ToUnicode, and, if a dead key + // is pressed at the moment, Windows would NOT use it to compose a character for the next + // WM_CHAR event. + + // Instead, use MapVirtualKey, which will provide adequate values. + code = MapVirtualKey(msg.wParam, MAPVK_VK_TO_CHAR); + if (code < 0x20 || code == 0x7f) // The same logic as in toKeyOrUnicode() + code = winceKeyBend(msg.wParam); } // Invert state logic: @@ -1120,7 +1126,7 @@ QList<int> QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const } result << int(baseKey + keyMods); // The base key is _always_ valid, of course - for (int i = 1; i < NumMods; ++i) { + for (size_t i = 1; i < NumMods; ++i) { Qt::KeyboardModifiers neededMods = ModsTbl[i]; quint32 key = kbItem.qtKey[i]; if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp index 7fb32d3513..a8bacd631d 100644 --- a/src/plugins/platforms/windows/qwindowsmime.cpp +++ b/src/plugins/platforms/windows/qwindowsmime.cpp @@ -703,14 +703,14 @@ QWindowsMimeURI::QWindowsMimeURI() bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const { - if (getCf(formatetc) == CF_HDROP) { + if (mimeData->hasUrls() && getCf(formatetc) == CF_HDROP) { QList<QUrl> urls = mimeData->urls(); for (int i=0; i<urls.size(); i++) { if (!urls.at(i).toLocalFile().isEmpty()) return true; } } - return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasFormat(QStringLiteral("text/uri-list")); + return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasUrls(); } bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index e1f4f4b143..dd16ea1c6f 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -49,6 +49,7 @@ #include <qpa/qwindowsysteminterface.h> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> +#include <QtGui/QWindow> #include <QtCore/QDebug> #include <QtCore/QScopedArrayPointer> @@ -236,6 +237,9 @@ 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) + window->requestActivate(); } else if (platformWindow->hasMouseCapture() && platformWindow->testFlag(QWindowsWindow::AutoMouseCapture) && (msg.message == WM_LBUTTONUP || msg.message == WM_MBUTTONUP diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.h b/src/plugins/platforms/windows/qwindowsmousehandler.h index b3bfa03380..caf30e6b1a 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.h +++ b/src/plugins/platforms/windows/qwindowsmousehandler.h @@ -109,6 +109,8 @@ Qt::KeyboardModifiers QWindowsMouseHandler::keyStateToModifiers(int wParam) mods |= Qt::ControlModifier; if (wParam & MK_SHIFT) mods |= Qt::ShiftModifier; + if (GetKeyState(VK_MENU) < 0) + mods |= Qt::AltModifier; return mods; } diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp index bcae2e0816..250fea56b1 100644 --- a/src/plugins/platforms/windows/qwindowsservices.cpp +++ b/src/plugins/platforms/windows/qwindowsservices.cpp @@ -44,6 +44,7 @@ #include <QtCore/QUrl> #include <QtCore/QDebug> +#include <QtCore/QDir> #include <shlobj.h> #ifndef Q_OS_WINCE @@ -57,7 +58,8 @@ enum { debug = 0 }; static inline bool shellExecute(const QString &file) { #ifndef Q_OS_WINCE - const quintptr result = (quintptr)ShellExecute(0, 0, (wchar_t*)file.utf16(), 0, 0, SW_SHOWNORMAL); + const QString nativeFilePath = QDir::toNativeSeparators(file); + const quintptr result = (quintptr)ShellExecute(0, 0, (wchar_t*)nativeFilePath.utf16(), 0, 0, SW_SHOWNORMAL); // ShellExecute returns a value greater than 32 if successful if (result <= 32) { qWarning("ShellExecute '%s' failed (error %s).", qPrintable(file), qPrintable(QString::number(result))); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 07dc668b3f..6cbe3e8cf7 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -64,6 +64,11 @@ QT_BEGIN_NAMESPACE +enum { + defaultWindowWidth = 160, + defaultWindowHeight = 160 +}; + Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &); static QByteArray debugWinStyle(DWORD style) @@ -198,17 +203,16 @@ static inline QSize clientSize(HWND hwnd) return qSizeOfRect(rect); } -// from qwidget_win.cpp/maximum layout size check removed. -static bool shouldShowMaximizeButton(Qt::WindowFlags flags) +// from qwidget_win.cpp +static bool shouldShowMaximizeButton(const QWindow *w) { - if (flags & Qt::MSWindowsFixedSizeDialogHint) + 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 // it even if the window has fixed size. - if (flags & Qt::CustomizeWindowHint && - flags & Qt::WindowMaximizeButtonHint) - return true; - return flags & Qt::WindowMaximizeButtonHint; + return (flags & Qt::CustomizeWindowHint) || + w->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX); } // Set the WS_EX_LAYERED flag on a HWND if required. This is required for @@ -253,6 +257,8 @@ static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qr } else { QWindowsContext::user32dll.setLayeredWindowAttributes(hwnd, 0, (int)(level * 255), LWA_ALPHA); } + } else if (IsWindowVisible(hwnd)) { // Repaint when switching from layered. + InvalidateRect(hwnd, NULL, TRUE); } #endif // !Q_OS_WINCE } @@ -296,7 +302,7 @@ struct WindowCreationData tool(false), embedded(false), hasAlpha(false) {} void fromWindow(const QWindow *w, const Qt::WindowFlags flags, unsigned creationFlags = 0); - inline WindowData create(const QWindow *w, const QRect &geometry, QString title) const; + inline WindowData create(const QWindow *w, const WindowData &data, QString title) const; inline void applyWindowFlags(HWND hwnd) const; void initialize(HWND h, bool frameChange, qreal opacityLevel) const; @@ -427,7 +433,7 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag style |= WS_SYSMENU; if (flags & Qt::WindowMinimizeButtonHint) style |= WS_MINIMIZEBOX; - if (shouldShowMaximizeButton(flags)) + if (shouldShowMaximizeButton(w)) style |= WS_MAXIMIZEBOX; if (tool) exStyle |= WS_EX_TOOLWINDOW; @@ -448,7 +454,7 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag } QWindowsWindow::WindowData - WindowCreationData::create(const QWindow *w, const QRect &geometry, QString title) const + WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const { typedef QSharedPointer<QWindowCreationContext> QWindowCreationContextPtr; @@ -468,30 +474,38 @@ QWindowsWindow::WindowData const QString windowClassName = QWindowsContext::instance()->registerWindowClass(w, isGL); + QRect rect = QPlatformWindow::initialGeometry(w, data.geometry, defaultWindowWidth, defaultWindowHeight); + if (title.isEmpty() && (result.flags & Qt::WindowTitleHint)) title = topLevel ? qAppName() : w->objectName(); const wchar_t *titleUtf16 = reinterpret_cast<const wchar_t *>(title.utf16()); const wchar_t *classNameUtf16 = reinterpret_cast<const wchar_t *>(windowClassName.utf16()); - // Capture events before CreateWindowEx() returns. - const QWindowCreationContextPtr context(new QWindowCreationContext(w, geometry, style, exStyle)); + // Capture events before CreateWindowEx() returns. The context is cleared in + // the QWindowsWindow constructor. + const QWindowCreationContextPtr context(new QWindowCreationContext(w, rect, data.customMargins, style, exStyle)); QWindowsContext::instance()->setWindowCreationContext(context); + if (context->frameX < 0) + context->frameX = 0; + if (context->frameY < 0) + context->frameY = 0; + if (QWindowsContext::verboseWindows) qDebug().nospace() << "CreateWindowEx: " << w << *this << " class=" <<windowClassName << " title=" << title - << "\nrequested: " << geometry << ": " + << "\nrequested: " << rect << ": " << context->frameWidth << 'x' << context->frameHeight - << '+' << context->frameX << '+' << context->frameY; + << '+' << context->frameX << '+' << context->frameY + << " custom margins: " << context->customMargins; result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16, style, context->frameX, context->frameY, context->frameWidth, context->frameHeight, parentHandle, NULL, appinst, NULL); - QWindowsContext::instance()->setWindowCreationContext(QWindowCreationContextPtr()); if (QWindowsContext::verboseWindows) qDebug().nospace() << "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: " @@ -505,6 +519,7 @@ QWindowsWindow::WindowData result.geometry = context->obtainedGeometry; result.frame = context->margins; result.embedded = embedded; + result.customMargins = context->customMargins; return result; } @@ -543,6 +558,8 @@ void WindowCreationData::initialize(HWND hwnd, bool frameChange, qreal opacityLe qWarning() << "QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time"; } else if (flags & Qt::WindowStaysOnBottomHint) { SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, swpFlags); + } else if (frameChange) { // Force WM_NCCALCSIZE with wParam=1 in case of custom margins. + SetWindowPos(hwnd, 0, 0, 0, 0, 0, swpFlags); } if (flags & (Qt::CustomizeWindowHint|Qt::WindowTitleHint)) { HMENU systemMenu = GetSystemMenu(hwnd, FALSE); @@ -571,9 +588,10 @@ void WindowCreationData::initialize(HWND hwnd, bool frameChange, qreal opacityLe #define QWINDOWSIZE_MAX ((1<<24)-1) -QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w) : +QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w, const QMargins &cm) : minimumSize(w->minimumSize()), - maximumSize(w->maximumSize()) + maximumSize(w->maximumSize()), + customMargins(cm) { } @@ -603,6 +621,33 @@ QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle) return result; } +bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result) +{ +#ifndef Q_OS_WINCE + // NCCALCSIZE_PARAMS structure if wParam==TRUE + if (!msg.wParam || customMargins.isNull()) + return false; + *result = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + NCCALCSIZE_PARAMS *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg.lParam); + const RECT oldClientArea = ncp->rgrc[0]; + ncp->rgrc[0].left += customMargins.left(); + ncp->rgrc[0].top += customMargins.top(); + ncp->rgrc[0].right -= customMargins.right(); + ncp->rgrc[0].bottom -= customMargins.bottom(); + result = 0; + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->" + << ncp->rgrc[0] << ' ' << ncp->rgrc[1] << ' ' << ncp->rgrc[2] + << ' ' << ncp->lppos->cx << ',' << ncp->lppos->cy; + return true; +#else + Q_UNUSED(customMargins) + Q_UNUSED(msg) + Q_UNUSED(result) + return false; +#endif +} + #ifndef Q_OS_WINCE void QWindowsGeometryHint::applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const { @@ -619,8 +664,8 @@ void QWindowsGeometryHint::applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXI << " in " << *mmi; const QMargins margins = QWindowsGeometryHint::frame(style, exStyle); - const int frameWidth = margins.left() + margins.right(); - const int frameHeight = margins.top() + margins.bottom(); + const int frameWidth = margins.left() + margins.right() + customMargins.left() + customMargins.right(); + const int frameHeight = margins.top() + margins.bottom() + customMargins.top() + customMargins.bottom(); if (minimumSize.width() > 0) mmi->ptMinTrackSize.x = minimumSize.width() + frameWidth; if (minimumSize.height() > 0) @@ -669,10 +714,11 @@ bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w) QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QRect &geometry, + const QMargins &cm, DWORD style_, DWORD exStyle_) : - geometryHint(w), style(style_), exStyle(exStyle_), + geometryHint(w, cm), style(style_), exStyle(exStyle_), requestedGeometry(geometry), obtainedGeometry(geometry), - margins(QWindowsGeometryHint::frame(style, exStyle)), + margins(QWindowsGeometryHint::frame(style, exStyle)), customMargins(cm), frameX(CW_USEDEFAULT), frameY(CW_USEDEFAULT), frameWidth(CW_USEDEFAULT), frameHeight(CW_USEDEFAULT) { @@ -683,14 +729,16 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, if (geometry.isValid()) { frameX = geometry.x(); frameY = geometry.y(); - frameWidth = margins.left() + geometry.width() + margins.right(); - frameHeight = margins.top() + geometry.height() + margins.bottom(); + const QMargins effectiveMargins = margins + customMargins; + frameWidth = effectiveMargins.left() + geometry.width() + effectiveMargins.right(); + frameHeight = effectiveMargins.top() + geometry.height() + effectiveMargins.bottom(); const bool isDefaultPosition = !frameX && !frameY && w->isTopLevel(); if (!QWindowsGeometryHint::positionIncludesFrame(w) && !isDefaultPosition) { - frameX -= margins.left(); - frameY -= margins.top(); + frameX -= effectiveMargins.left(); + frameY -= effectiveMargins.top(); } } + if (QWindowsContext::verboseWindows) qDebug().nospace() << __FUNCTION__ << ' ' << w << geometry @@ -698,7 +746,8 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, << " frame: " << frameWidth << 'x' << frameHeight << '+' << frameX << '+' << frameY << " min" << geometryHint.minimumSize - << " max" << geometryHint.maximumSize; + << " max" << geometryHint.maximumSize + << " custom margins " << customMargins; } /*! @@ -730,9 +779,6 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : m_hdc(0), m_windowState(Qt::WindowNoState), m_opacity(1.0), -#ifndef QT_NO_CURSOR - m_cursor(QWindowsScreen::screenOf(aWindow)->windowsCursor()->standardWindowCursor()), -#endif m_dropTarget(0), m_savedStyle(0), m_format(aWindow->format()), @@ -747,6 +793,8 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : { if (aWindow->surfaceType() == QWindow::OpenGLSurface) setFlag(OpenGLSurface); + // Clear the creation context as the window can be found in QWindowsContext's map. + QWindowsContext::instance()->setWindowCreationContext(QSharedPointer<QWindowCreationContext>()); QWindowsContext::instance()->addWindow(m_data.hwnd, this); if (aWindow->isTopLevel()) { switch (aWindow->type()) { @@ -775,6 +823,7 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : QWindowsWindow::~QWindowsWindow() { #ifndef Q_OS_WINCE + QWindowSystemInterface::flushWindowSystemEvents(); if (QWindowsContext::instance()->systemInfo() & QWindowsContext::SI_SupportsTouch) QWindowsContext::user32dll.unregisterTouchWindow(m_data.hwnd); #endif // !Q_OS_WINCE @@ -872,8 +921,9 @@ QWindowsWindow::WindowData { WindowCreationData creationData; creationData.fromWindow(w, parameters.flags); - WindowData result = creationData.create(w, parameters.geometry, title); - creationData.initialize(result.hwnd, false, 1); + WindowData result = creationData.create(w, parameters, title); + // Force WM_NCCALCSIZE (with wParam=1) via SWP_FRAMECHANGED for custom margin. + creationData.initialize(result.hwnd, !parameters.customMargins.isNull(), 1); return result; } @@ -1060,7 +1110,7 @@ void QWindowsWindow::setGeometry(const QRect &rectIn) const QSize newSize = rect.size(); // Check on hint. if (newSize != oldSize) { - const QWindowsGeometryHint hint(window()); + const QWindowsGeometryHint hint(window(), m_data.customMargins); if (!hint.validSize(newSize)) { qWarning("%s: Attempt to set a size (%dx%d) violating the constraints" "(%dx%d - %dx%d) on window '%s'.", __FUNCTION__, @@ -1078,14 +1128,19 @@ void QWindowsWindow::setGeometry(const QRect &rectIn) if (m_data.geometry != rect) { qWarning("%s: Unable to set geometry %dx%d+%d+%d on '%s'." " Resulting geometry: %dx%d+%d+%d " - "(frame: %d, %d, %d, %d).", + "(frame: %d, %d, %d, %d, custom margin: %d, %d, %d, %d" + ", minimum size: %dx%d, maximum size: %dx%d).", __FUNCTION__, rect.width(), rect.height(), rect.x(), rect.y(), qPrintable(window()->objectName()), m_data.geometry.width(), m_data.geometry.height(), m_data.geometry.x(), m_data.geometry.y(), m_data.frame.left(), m_data.frame.top(), - m_data.frame.right(), m_data.frame.bottom()); + m_data.frame.right(), m_data.frame.bottom(), + m_data.customMargins.left(), m_data.customMargins.top(), + m_data.customMargins.right(), m_data.customMargins.bottom(), + window()->minimumWidth(), window()->minimumHeight(), + window()->maximumWidth(), window()->maximumHeight()); } } else { QPlatformWindow::setGeometry(rect); @@ -1163,7 +1218,7 @@ QRect QWindowsWindow::frameGeometry_sys() const QRect QWindowsWindow::geometry_sys() const { - return frameGeometry_sys() - frameMargins(); + return frameGeometry_sys().marginsRemoved(frameMargins()); } /*! @@ -1490,7 +1545,7 @@ QMargins QWindowsWindow::frameMargins() const m_data.frame = QWindowsGeometryHint::frame(style(), exStyle()); clearFlag(FrameDirty); } - return m_data.frame; + return m_data.frame + m_data.customMargins; } void QWindowsWindow::setOpacity(qreal level) @@ -1648,13 +1703,23 @@ void QWindowsWindow::setFrameStrutEventsEnabled(bool enabled) #ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_GETMINMAXINFO void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const { - const QWindowsGeometryHint hint(window()); + const QWindowsGeometryHint hint(window(), m_data.customMargins); hint.applyToMinMaxInfo(m_data.hwnd, mmi); if (QWindowsContext::verboseWindows) qDebug() << __FUNCTION__ << window() << *mmi; } #endif // !Q_OS_WINCE +// Return the default cursor (Arrow) from QWindowsCursor's cache. +static inline QWindowsWindowCursor defaultCursor(const QWindow *w) +{ + if (QScreen *screen = w->screen()) + if (const QPlatformScreen *platformScreen = screen->handle()) + if (QPlatformCursor *cursor = platformScreen->cursor()) + return static_cast<QWindowsCursor *>(cursor)->standardWindowCursor(Qt::ArrowCursor); + return QWindowsWindowCursor(Qt::ArrowCursor); +} + /*! \brief Applies to cursor property set on the window to the global cursor. @@ -1664,20 +1729,45 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const void QWindowsWindow::applyCursor() { #ifndef QT_NO_CURSOR - SetCursor(m_cursor.handle()); + if (m_cursor.isNull()) { // Recurse up to parent with non-null cursor. Set default for toplevel. + if (const QWindow *p = window()->parent()) { + QWindowsWindow::baseWindowOf(p)->applyCursor(); + } else { + SetCursor(defaultCursor(window()).handle()); + } + } else { + SetCursor(m_cursor.handle()); + } #endif } +// Check whether to apply a new cursor. Either the window in question is +// currently under mouse, or it is the parent of the window under mouse and +// there is no other window with an explicitly set cursor in-between. +static inline bool applyNewCursor(const QWindow *w) +{ + const QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse(); + if (underMouse == w) + return true; + for (const QWindow *p = underMouse; p ; p = p->parent()) { + if (p == w) + return true; + if (!QWindowsWindow::baseWindowOf(p)->cursor().isNull()) + return false; + } + return false; +} + void QWindowsWindow::setCursor(const QWindowsWindowCursor &c) { #ifndef QT_NO_CURSOR if (c.handle() != m_cursor.handle()) { - const bool underMouse = QWindowsContext::instance()->windowUnderMouse() == window(); + const bool apply = applyNewCursor(window()); if (QWindowsContext::verboseWindows) qDebug() << window() << __FUNCTION__ << "Shape=" << c.cursor().shape() - << " isWUM=" << underMouse; + << " doApply=" << apply; m_cursor = c; - if (underMouse) + if (apply) applyCursor(); } #endif @@ -1850,4 +1940,32 @@ void QWindowsWindow::setWindowIcon(const QIcon &icon) } } +/*! + \brief Sets custom margins to be added to the default margins determined by + the windows style in the handling of the WM_NCCALCSIZE message. + + This is currently used to give the Aero-style QWizard a smaller top margin. + The property can be set using QPlatformNativeInterface::setWindowProperty() or, + before platform window creation, by setting a dynamic property + on the QWindow (see QWindowsIntegration::createPlatformWindow()). +*/ + +void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) +{ + if (newCustomMargins != m_data.customMargins) { + const QMargins oldCustomMargins = m_data.customMargins; + m_data.customMargins = newCustomMargins; + // Re-trigger WM_NCALCSIZE with wParam=1 by passing SWP_FRAMECHANGED + const QRect currentFrameGeometry = frameGeometry_sys(); + const QPoint topLeft = currentFrameGeometry.topLeft(); + QRect newFrame = currentFrameGeometry.marginsRemoved(oldCustomMargins) + m_data.customMargins; + newFrame.moveTo(topLeft); + setFlag(FrameDirty); + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins + << currentFrameGeometry << "->" << newFrame; + SetWindowPos(m_data.hwnd, 0, newFrame.x(), newFrame.y(), newFrame.width(), newFrame.height(), SWP_NOZORDER | SWP_FRAMECHANGED); + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index f3b480b0af..87397f1c1d 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -67,8 +67,9 @@ class QWindowsEGLStaticContext; struct QWindowsGeometryHint { QWindowsGeometryHint() {} - explicit QWindowsGeometryHint(const QWindow *w); + explicit QWindowsGeometryHint(const QWindow *w, const QMargins &customMargins); static QMargins frame(DWORD style, DWORD exStyle); + static bool handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result); #ifndef Q_OS_WINCE //MinMax maybe define struct if not available void applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const; void applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const; @@ -84,11 +85,13 @@ struct QWindowsGeometryHint QSize minimumSize; QSize maximumSize; + QMargins customMargins; }; struct QWindowCreationContext { QWindowCreationContext(const QWindow *w, const QRect &r, + const QMargins &customMargins, DWORD style, DWORD exStyle); #ifndef Q_OS_WINCE //MinMax maybe define struct if not available void applyToMinMaxInfo(MINMAXINFO *mmi) const @@ -101,6 +104,7 @@ struct QWindowCreationContext QRect requestedGeometry; QRect obtainedGeometry; QMargins margins; + QMargins customMargins; // User-defined, additional frame for WM_NCCALCSIZE int frameX; // Passed on to CreateWindowEx(), including frame. int frameY; int frameWidth; @@ -137,6 +141,7 @@ public: Qt::WindowFlags flags; QRect geometry; QMargins frame; // Do not use directly for windows, see FrameDirty. + QMargins customMargins; // User-defined, additional frame for NCCALCSIZE HWND hwnd; bool embedded; @@ -191,6 +196,9 @@ public: void setFrameStrutEventsEnabled(bool enabled); bool frameStrutEventsEnabled() const { return testFlag(FrameStrutEventsEnabled); } + QMargins customMargins() const { return m_data.customMargins; } + void setCustomMargins(const QMargins &m); + #ifdef QT_OPENGL_ES_2 EGLSurface eglSurfaceHandle() const { return m_eglSurface;} EGLSurface ensureEglSurfaceHandle(const QWindowsEGLStaticContextPtr &staticContext, EGLConfig config); @@ -290,17 +298,6 @@ private: HICON m_iconBig; }; -// Conveniences for window frames. -inline QRect operator+(const QRect &r, const QMargins &m) -{ - return r.adjusted(-m.left(), -m.top(), m.right(), m.bottom()); -} - -inline QRect operator-(const QRect &r, const QMargins &m) -{ - return r.adjusted(m.left(), m.top(), -m.right(), -m.bottom()); -} - // Debug QDebug operator<<(QDebug d, const RECT &r); #ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_GETMINMAXINFO/WM_NCCALCSIZE @@ -371,4 +368,6 @@ inline void QWindowsWindow::destroyIcon() QT_END_NAMESPACE +Q_DECLARE_METATYPE(QMargins) + #endif // QWINDOWSWINDOW_H diff --git a/src/plugins/platforms/windows/windows.pro b/src/plugins/platforms/windows/windows.pro index ff162e2d41..3aa9caaa0f 100644 --- a/src/plugins/platforms/windows/windows.pro +++ b/src/plugins/platforms/windows/windows.pro @@ -102,12 +102,8 @@ contains(QT_CONFIG, opengles2) { } } -# Enable access to HB_Face in harfbuzz includes included by qfontengine_p.h. -DEFINES *= QT_COMPILES_IN_HARFBUZZ - contains(QT_CONFIG, freetype) { DEFINES *= QT_NO_FONTCONFIG - DEFINES *= QT_COMPILES_IN_HARFBUZZ QT_FREETYPE_DIR = $$QT_SOURCE_TREE/src/3rdparty/freetype HEADERS += \ diff --git a/src/plugins/platforms/xcb/qglxintegration.cpp b/src/plugins/platforms/xcb/qglxintegration.cpp index 11c74edc45..516b35dac8 100644 --- a/src/plugins/platforms/xcb/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/qglxintegration.cpp @@ -50,6 +50,7 @@ #include <GL/glx.h> #include <QtGui/QOpenGLContext> +#include <QtGui/QOffscreenSurface> #include "qglxintegration.h" #include <QtPlatformSupport/private/qglxconvenience_p.h> @@ -70,6 +71,10 @@ typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXC #define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 #endif +#ifndef GLX_CONTEXT_ES2_PROFILE_BIT_EXT +#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#endif + #ifndef GLX_CONTEXT_PROFILE_MASK_ARB #define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 #endif @@ -104,57 +109,6 @@ static Window createDummyWindow(QXcbScreen *screen, GLXFBConfig config) return window; } -// Per-window data for active OpenGL contexts. -struct QOpenGLContextData -{ - QOpenGLContextData(Display *display, Window window, GLXContext context) - : m_display(display), - m_window(window), - m_context(context) - {} - - QOpenGLContextData() - : m_display(0), - m_window(0), - m_context(0) - {} - - Display *m_display; - Window m_window; - GLXContext m_context; -}; - -static inline QOpenGLContextData currentOpenGLContextData() -{ - QOpenGLContextData result; - result.m_display = glXGetCurrentDisplay(); - result.m_window = glXGetCurrentDrawable(); - result.m_context = glXGetCurrentContext(); - return result; -} - -static inline QOpenGLContextData createDummyWindowOpenGLContextData(QXcbScreen *screen) -{ - QOpenGLContextData result; - result.m_display = DISPLAY_FROM_XCB(screen); - - QSurfaceFormat format; - GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(screen), screen->screenNumber(), format); - if (config) { - result.m_context = glXCreateNewContext(DISPLAY_FROM_XCB(screen), config, GLX_RGBA_TYPE, 0, true); - result.m_window = createDummyWindow(screen, config); - } else { - XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(screen), screen->screenNumber(), &format); - if (!visualInfo) - qFatal("Could not initialize GLX"); - result.m_context = glXCreateContext(DISPLAY_FROM_XCB(screen), visualInfo, 0, true); - result.m_window = createDummyWindow(screen, visualInfo); - XFree(visualInfo); - } - - return result; -} - static inline QByteArray getGlString(GLenum param) { if (const GLubyte *s = glGetString(param)) @@ -173,109 +127,53 @@ static void updateFormatFromContext(QSurfaceFormat &format) } format.setProfile(QSurfaceFormat::NoProfile); + format.setOption(QSurfaceFormat::FormatOptions()); - const int version = (major << 8) + minor; - if (version < 0x0300) { - format.setProfile(QSurfaceFormat::NoProfile); - format.setOption(QSurfaceFormat::DeprecatedFunctions); - return; - } - - // Version 3.0 onwards - check if it includes deprecated functionality or is - // a debug context - GLint value = 0; - glGetIntegerv(GL_CONTEXT_FLAGS, &value); - if (!(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)) - format.setOption(QSurfaceFormat::DeprecatedFunctions); - if (value & GL_CONTEXT_FLAG_DEBUG_BIT) - format.setOption(QSurfaceFormat::DebugContext); - if (version < 0x0302) - return; - - // Version 3.2 and newer have a profile - value = 0; - glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value); - - if (value & GL_CONTEXT_CORE_PROFILE_BIT) - format.setProfile(QSurfaceFormat::CoreProfile); - else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) - format.setProfile(QSurfaceFormat::CompatibilityProfile); -} - -/*! - \class QOpenGLTemporaryContext - \brief A temporary context that can be instantiated on the stack. - - Functions like glGetString() only work if there is a current GL context. - - \internal - \ingroup qt-lighthouse-xcb -*/ -class QOpenGLTemporaryContext -{ - Q_DISABLE_COPY(QOpenGLTemporaryContext) -public: - QOpenGLTemporaryContext(QXcbScreen *screen); - ~QOpenGLTemporaryContext(); - -private: - const QOpenGLContextData m_previous; - const QOpenGLContextData m_current; -}; - -QOpenGLTemporaryContext::QOpenGLTemporaryContext(QXcbScreen *screen) - : m_previous(currentOpenGLContextData()), - m_current(createDummyWindowOpenGLContextData(screen)) -{ - // Make our temporary context current on our temporary window - glXMakeCurrent(m_current.m_display, m_current.m_window, m_current.m_context); -} - -QOpenGLTemporaryContext::~QOpenGLTemporaryContext() -{ - // Restore the previous context if possible, otherwise just release our temporary context - if (m_previous.m_display) - glXMakeCurrent(m_previous.m_display, m_previous.m_window, m_previous.m_context); - else - glXMakeCurrent(m_current.m_display, 0, 0); - - // Destroy our temporary window - XDestroyWindow(m_current.m_display, m_current.m_window); - - // Finally destroy our temporary context itself - glXDestroyContext(m_current.m_display, m_current.m_context); -} - -QOpenGLDefaultContextInfo::QOpenGLDefaultContextInfo() - : vendor(getGlString(GL_VENDOR)), - renderer(getGlString(GL_RENDERER)) -{ - updateFormatFromContext(format); -} + if (format.renderableType() == QSurfaceFormat::OpenGL) { + if (format.version() < qMakePair(3, 0)) { + format.setOption(QSurfaceFormat::DeprecatedFunctions); + return; + } -QOpenGLDefaultContextInfo *QOpenGLDefaultContextInfo::create(QXcbScreen *screen) -{ - // We need a current context for getGLString() to work. To have - // the QOpenGLDefaultContextInfo contain the latest supported - // context version, we rely upon the QOpenGLTemporaryContext to - // correctly obtain a context with the latest version - QScopedPointer<QOpenGLTemporaryContext> temporaryContext(new QOpenGLTemporaryContext(screen)); - QOpenGLDefaultContextInfo *result = new QOpenGLDefaultContextInfo; - return result; + // Version 3.0 onwards - check if it includes deprecated functionality or is + // a debug context + GLint value = 0; + glGetIntegerv(GL_CONTEXT_FLAGS, &value); + if (!(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)) + format.setOption(QSurfaceFormat::DeprecatedFunctions); + if (value & GL_CONTEXT_FLAG_DEBUG_BIT) + format.setOption(QSurfaceFormat::DebugContext); + if (format.version() < qMakePair(3, 2)) + return; + + // Version 3.2 and newer have a profile + value = 0; + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value); + + if (value & GL_CONTEXT_CORE_PROFILE_BIT) + format.setProfile(QSurfaceFormat::CoreProfile); + else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) + format.setProfile(QSurfaceFormat::CompatibilityProfile); + } } - -QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share, QOpenGLDefaultContextInfo *defaultContextInfo) +QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share) : QPlatformOpenGLContext() , m_screen(screen) , m_context(0) + , m_shareContext(0) , m_format(format) + , m_isPBufferCurrent(false) { - m_shareContext = 0; + if (m_format.renderableType() == QSurfaceFormat::DefaultRenderableType) + m_format.setRenderableType(QSurfaceFormat::OpenGL); + if (m_format.renderableType() != QSurfaceFormat::OpenGL && m_format.renderableType() != QSurfaceFormat::OpenGLES) + return; + if (share) m_shareContext = static_cast<const QGLXContext*>(share)->glxContext(); - GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(screen),screen->screenNumber(),format); + GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(screen),screen->screenNumber(),m_format); XVisualInfo *visualInfo = 0; Window window = 0; // Temporary window used to query OpenGL context @@ -287,56 +185,88 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat QList<QByteArray> glxExt = QByteArray(glXQueryExtensionsString(DISPLAY_FROM_XCB(m_screen), m_screen->screenNumber())).split(' '); bool supportsProfiles = glxExt.contains("GLX_ARB_create_context_profile"); - // Use glXCreateContextAttribsARB if is available - if (glxExt.contains("GLX_ARB_create_context") && glXCreateContextAttribsARB != 0) { - // We limit the requested version by the version of the static context as - // glXCreateContextAttribsARB fails and returns NULL if the requested context - // version is not supported. This means that we will get the closest supported - // context format that that which was requested and is supported by the driver - const int maxSupportedVersion = (defaultContextInfo->format.majorVersion() << 8) - + defaultContextInfo->format.minorVersion(); - const int requestedVersion = qMin((format.majorVersion() << 8) + format.minorVersion(), - maxSupportedVersion); - const int majorVersion = requestedVersion >> 8; - const int minorVersion = requestedVersion & 0xFF; - - QVector<int> contextAttributes; - contextAttributes << GLX_CONTEXT_MAJOR_VERSION_ARB << majorVersion - << GLX_CONTEXT_MINOR_VERSION_ARB << minorVersion; - - // If asking for OpenGL 3.2 or newer we should also specify a profile - if (supportsProfiles && (m_format.majorVersion() > 3 || (m_format.majorVersion() == 3 && m_format.minorVersion() > 1))) { - if (m_format.profile() == QSurfaceFormat::CoreProfile) - contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_CORE_PROFILE_BIT_ARB; - else - contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + // Use glXCreateContextAttribsARB if available + // Also, GL ES context creation requires GLX_EXT_create_context_es2_profile + if (glxExt.contains("GLX_ARB_create_context") && glXCreateContextAttribsARB != 0 + && (m_format.renderableType() != QSurfaceFormat::OpenGLES || (supportsProfiles && glxExt.contains("GLX_EXT_create_context_es2_profile")))) { + // Try to create an OpenGL context for each known OpenGL version in descending + // order from the requested version. + const int requestedVersion = format.majorVersion() * 10 + qMin(format.minorVersion(), 9); + + QVector<int> glVersions; + if (m_format.renderableType() == QSurfaceFormat::OpenGL) { + if (requestedVersion > 43) + glVersions << requestedVersion; + + // Don't bother with versions below 2.0 + glVersions << 43 << 42 << 41 << 40 << 33 << 32 << 31 << 30 << 21 << 20; + } else if (m_format.renderableType() == QSurfaceFormat::OpenGLES) { + if (requestedVersion > 30) + glVersions << requestedVersion; + + // Don't bother with versions below ES 2.0 + glVersions << 30 << 20; + // ES does not support any format option + m_format.setOption(QSurfaceFormat::FormatOptions()); } - int flags = 0; + Q_ASSERT(glVersions.count() > 0); - if (m_format.testOption(QSurfaceFormat::DebugContext)) - flags |= GLX_CONTEXT_DEBUG_BIT_ARB; + for (int i = 0; !m_context && i < glVersions.count(); i++) { + const int version = glVersions[i]; + if (version > requestedVersion) + continue; - // A forward-compatible context may be requested for 3.0 and later - if (m_format.majorVersion() >= 3 && !m_format.testOption(QSurfaceFormat::DeprecatedFunctions)) - flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + const int majorVersion = version / 10; + const int minorVersion = version % 10; - if (flags != 0) - contextAttributes << GLX_CONTEXT_FLAGS_ARB << flags; + QVector<int> contextAttributes; + contextAttributes << GLX_CONTEXT_MAJOR_VERSION_ARB << majorVersion + << GLX_CONTEXT_MINOR_VERSION_ARB << minorVersion; - contextAttributes << None; - m_context = glXCreateContextAttribsARB(DISPLAY_FROM_XCB(screen), config, m_shareContext, true, contextAttributes.data()); - if (!m_context && m_shareContext) { - // re-try without a shared glx context - m_context = glXCreateContextAttribsARB(DISPLAY_FROM_XCB(screen), config, 0, true, contextAttributes.data()); - if (m_context) - m_shareContext = 0; + if (m_format.renderableType() == QSurfaceFormat::OpenGL) { + // If asking for OpenGL 3.2 or newer we should also specify a profile + if (version >= 32 && supportsProfiles) { + if (m_format.profile() == QSurfaceFormat::CoreProfile) + contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + else + contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + } + + int flags = 0; + + if (m_format.testOption(QSurfaceFormat::DebugContext)) + flags |= GLX_CONTEXT_DEBUG_BIT_ARB; + + // A forward-compatible context may be requested for 3.0 and later + if (version >= 30 && !m_format.testOption(QSurfaceFormat::DeprecatedFunctions)) + flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + + if (flags != 0) + contextAttributes << GLX_CONTEXT_FLAGS_ARB << flags; + } else if (m_format.renderableType() == QSurfaceFormat::OpenGLES) { + contextAttributes << GLX_CONTEXT_PROFILE_MASK_ARB << GLX_CONTEXT_ES2_PROFILE_BIT_EXT; + } + + contextAttributes << None; + + m_context = glXCreateContextAttribsARB(DISPLAY_FROM_XCB(screen), config, m_shareContext, true, contextAttributes.data()); + if (!m_context && m_shareContext) { + // re-try without a shared glx context + m_context = glXCreateContextAttribsARB(DISPLAY_FROM_XCB(screen), config, 0, true, contextAttributes.data()); + if (m_context) + m_shareContext = 0; + } } } // Could not create a context using glXCreateContextAttribsARB, falling back to glXCreateNewContext. if (!m_context) { + // requesting an OpenGL ES context requires glXCreateContextAttribsARB, so bail out + if (m_format.renderableType() == QSurfaceFormat::OpenGLES) + return; + m_context = glXCreateNewContext(DISPLAY_FROM_XCB(screen), config, GLX_RGBA_TYPE, m_shareContext, true); if (!m_context && m_shareContext) { // re-try without a shared glx context @@ -348,11 +278,15 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat // Get the basic surface format details if (m_context) - m_format = qglx_surfaceFormatFromGLXFBConfig(DISPLAY_FROM_XCB(screen), config, m_context); + qglx_surfaceFormatFromGLXFBConfig(&m_format, DISPLAY_FROM_XCB(screen), config, m_context); // Create a temporary window so that we can make the new context current window = createDummyWindow(screen, config); } else { + // requesting an OpenGL ES context requires glXCreateContextAttribsARB, so bail out + if (m_format.renderableType() == QSurfaceFormat::OpenGLES) + return; + // Note that m_format gets updated with the used surface format visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(screen), screen->screenNumber(), &m_format); if (!visualInfo) @@ -391,19 +325,35 @@ bool QGLXContext::makeCurrent(QPlatformSurface *surface) { Q_ASSERT(surface->surface()->surfaceType() == QSurface::OpenGLSurface); - GLXDrawable glxDrawable = static_cast<QXcbWindow *>(surface)->xcb_window(); - - return glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), glxDrawable, m_context); + QSurface::SurfaceClass surfaceClass = surface->surface()->surfaceClass(); + if (surfaceClass == QSurface::Window) { + m_isPBufferCurrent = false; + QXcbWindow *window = static_cast<QXcbWindow *>(surface); + return glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), window->xcb_window(), m_context); + } else if (surfaceClass == QSurface::Offscreen) { + m_isPBufferCurrent = true; + QGLXPbuffer *pbuffer = static_cast<QGLXPbuffer *>(surface); + return glXMakeContextCurrent(DISPLAY_FROM_XCB(m_screen), pbuffer->pbuffer(), pbuffer->pbuffer(), m_context); + } + return false; } void QGLXContext::doneCurrent() { - glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), 0, 0); + if (m_isPBufferCurrent) + glXMakeContextCurrent(DISPLAY_FROM_XCB(m_screen), 0, 0, 0); + else + glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), 0, 0); + m_isPBufferCurrent = false; } void QGLXContext::swapBuffers(QPlatformSurface *surface) { - GLXDrawable glxDrawable = static_cast<QXcbWindow *>(surface)->xcb_window(); + GLXDrawable glxDrawable = 0; + if (surface->surface()->surfaceClass() == QSurface::Offscreen) + glxDrawable = static_cast<QGLXPbuffer *>(surface)->pbuffer(); + else + glxDrawable = static_cast<QXcbWindow *>(surface)->xcb_window(); glXSwapBuffers(DISPLAY_FROM_XCB(m_screen), glxDrawable); } @@ -455,4 +405,36 @@ bool QGLXContext::isValid() const return m_context != 0; } + +QGLXPbuffer::QGLXPbuffer(QOffscreenSurface *offscreenSurface) + : QPlatformOffscreenSurface(offscreenSurface) + , m_format(offscreenSurface->requestedFormat()) + , m_screen(static_cast<QXcbScreen *>(offscreenSurface->screen()->handle())) + , m_pbuffer(0) +{ + GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(m_screen), m_screen->screenNumber(), m_format); + + if (config) { + const int attributes[] = { + GLX_PBUFFER_WIDTH, offscreenSurface->size().width(), + GLX_PBUFFER_HEIGHT, offscreenSurface->size().height(), + GLX_LARGEST_PBUFFER, False, + GLX_PRESERVED_CONTENTS, False, + GLX_NONE + }; + + m_pbuffer = glXCreatePbuffer(DISPLAY_FROM_XCB(m_screen), config, attributes); + + if (m_pbuffer) + qglx_surfaceFormatFromGLXFBConfig(&m_format, DISPLAY_FROM_XCB(m_screen), config); + } +} + +QGLXPbuffer::~QGLXPbuffer() +{ + if (m_pbuffer) + glXDestroyPbuffer(DISPLAY_FROM_XCB(m_screen), m_pbuffer); +} + + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qglxintegration.h b/src/plugins/platforms/xcb/qglxintegration.h index 78e9985334..7116b2389d 100644 --- a/src/plugins/platforms/xcb/qglxintegration.h +++ b/src/plugins/platforms/xcb/qglxintegration.h @@ -46,6 +46,7 @@ #include "qxcbscreen.h" #include <qpa/qplatformopenglcontext.h> +#include <qpa/qplatformoffscreensurface.h> #include <QtGui/QSurfaceFormat> #include <QtCore/QMutex> @@ -54,23 +55,10 @@ QT_BEGIN_NAMESPACE -class QOpenGLDefaultContextInfo -{ - Q_DISABLE_COPY(QOpenGLDefaultContextInfo) - QOpenGLDefaultContextInfo(); -public: - static QOpenGLDefaultContextInfo *create(QXcbScreen *screen); - - const QByteArray vendor; - const QByteArray renderer; - QSurfaceFormat format; -}; - - class QGLXContext : public QPlatformOpenGLContext { public: - QGLXContext(QXcbScreen *xd, const QSurfaceFormat &format, QPlatformOpenGLContext *share, QOpenGLDefaultContextInfo *defaultContextInfo); + QGLXContext(QXcbScreen *xd, const QSurfaceFormat &format, QPlatformOpenGLContext *share); ~QGLXContext(); bool makeCurrent(QPlatformSurface *surface); @@ -89,6 +77,25 @@ private: GLXContext m_context; GLXContext m_shareContext; QSurfaceFormat m_format; + bool m_isPBufferCurrent; +}; + + +class QGLXPbuffer : public QPlatformOffscreenSurface +{ +public: + explicit QGLXPbuffer(QOffscreenSurface *offscreenSurface); + ~QGLXPbuffer(); + + QSurfaceFormat format() const { return m_format; } + bool isValid() const { return m_pbuffer != 0; } + + GLXPbuffer pbuffer() const { return m_pbuffer; } + +private: + QSurfaceFormat m_format; + QXcbScreen *m_screen; + GLXPbuffer m_pbuffer; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 32de54562a..1504bd99d2 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -184,7 +184,7 @@ void QXcbConnection::updateScreens() activeScreens << screen; ++screenNumber; if (!primaryScreen && primary) { - if (primary->output == XCB_NONE || outputs[i] == primary->output) { + if (m_primaryScreen == xcbScreenNumber && (primary->output == XCB_NONE || outputs[i] == primary->output)) { primaryScreen = screen; siblings.prepend(siblings.takeLast()); #ifdef Q_XCB_DEBUG @@ -222,7 +222,6 @@ void QXcbConnection::updateScreens() // Delete any existing screens which are not in activeScreens for (int i = m_screens.count() - 1; i >= 0; --i) { if (!activeScreens.contains(m_screens[i])) { - ((QXcbIntegration*)QGuiApplicationPrivate::platformIntegration())->removeDefaultOpenGLContextInfo(m_screens[i]); delete m_screens[i]; m_screens.removeAt(i); } @@ -1067,7 +1066,7 @@ void QXcbConnection::processXcbEvents() while (it != m_peekFuncs.end()) { // These callbacks return true if the event is what they were // waiting for, remove them from the list in that case. - if ((*it)(event)) + if ((*it)(this, event)) it = m_peekFuncs.erase(it); else ++it; @@ -1087,7 +1086,7 @@ void QXcbConnection::processXcbEvents() // Indicate with a null event that the event the callbacks are waiting for // is not in the queue currently. Q_FOREACH (PeekFunc f, m_peekFuncs) - f(0); + f(this, 0); m_peekFuncs.clear(); xcb_flush(xcb_connection()); diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index b499f75b78..f69a8a9f35 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -364,7 +364,7 @@ public: template<typename T> inline xcb_generic_event_t *checkEvent(T &checker); - typedef bool (*PeekFunc)(xcb_generic_event_t *); + typedef bool (*PeekFunc)(QXcbConnection *, xcb_generic_event_t *); void addPeekFunc(PeekFunc f); inline xcb_timestamp_t time() const { return m_time; } diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index b40fdb0e92..3fd2ca70e3 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -287,8 +287,14 @@ QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) QXcbCursor::~QXcbCursor() { + xcb_connection_t *conn = xcb_connection(); if (!--cursorCount) - xcb_close_font(xcb_connection(), cursorFont); + xcb_close_font(conn, cursorFont); + + foreach (xcb_cursor_t cursor, m_bitmapCursorMap) + xcb_free_cursor(conn, cursor); + foreach (xcb_cursor_t cursor, m_shapeCursorMap) + xcb_free_cursor(conn, cursor); } #ifndef QT_NO_CURSOR @@ -300,18 +306,20 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *widget) else // No X11 cursor control when there is no widget under the cursor return; - - xcb_cursor_t c; - if (cursor->shape() == Qt::BitmapCursor) { - qint64 id = cursor->pixmap().cacheKey(); - if (!m_bitmapCursorMap.contains(id)) - m_bitmapCursorMap.insert(id, createBitmapCursor(cursor)); - c = m_bitmapCursorMap.value(id); - } else { - int id = cursor->shape(); - if (!m_shapeCursorMap.contains(id)) - m_shapeCursorMap.insert(id, createFontCursor(cursor->shape())); - c = m_shapeCursorMap.value(id); + + xcb_cursor_t c = XCB_CURSOR_NONE; + if (cursor) { + if (cursor->shape() == Qt::BitmapCursor) { + qint64 id = cursor->pixmap().cacheKey(); + if (!m_bitmapCursorMap.contains(id)) + m_bitmapCursorMap.insert(id, createBitmapCursor(cursor)); + c = m_bitmapCursorMap.value(id); + } else { + int id = cursor->shape(); + if (!m_shapeCursorMap.contains(id)) + m_shapeCursorMap.insert(id, createFontCursor(cursor->shape())); + c = m_shapeCursorMap.value(id); + } } w->setCursor(c); diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index 1840dd1ce5..f0cabea43d 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -78,10 +78,12 @@ #elif defined(XCB_USE_EGL) #include "qxcbeglsurface.h" #include <QtPlatformSupport/private/qeglplatformcontext_p.h> +#include <QtPlatformSupport/private/qeglpbuffer_p.h> #endif #include <QtGui/QOpenGLContext> #include <QtGui/QScreen> +#include <QtGui/QOffscreenSurface> #ifndef QT_NO_ACCESSIBILITY #include <qpa/qplatformaccessibility.h> #ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE @@ -121,9 +123,6 @@ QXcbIntegration::QXcbIntegration(const QStringList ¶meters) QXcbIntegration::~QXcbIntegration() { -#if !defined(QT_NO_OPENGL) && defined(XCB_USE_GLX) - qDeleteAll(m_defaultContextInfos); -#endif qDeleteAll(m_connections); } @@ -168,7 +167,10 @@ public: EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) { - return static_cast<QXcbWindow *>(surface)->eglSurface()->surface(); + if (surface->surface()->surfaceClass() == QSurface::Window) + return static_cast<QXcbWindow *>(surface)->eglSurface()->surface(); + else + return static_cast<QEGLPbuffer *>(surface)->pbuffer(); } private: @@ -181,14 +183,7 @@ QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLCont { QXcbScreen *screen = static_cast<QXcbScreen *>(context->screen()->handle()); #if defined(XCB_USE_GLX) - QOpenGLDefaultContextInfo *defaultContextInfo; - if (m_defaultContextInfos.contains(screen)) { - defaultContextInfo = m_defaultContextInfos.value(screen); - } else { - defaultContextInfo = QOpenGLDefaultContextInfo::create(screen); - m_defaultContextInfos.insert(screen, defaultContextInfo); - } - return new QGLXContext(screen, context->format(), context->shareHandle(), defaultContextInfo); + return new QGLXContext(screen, context->format(), context->shareHandle()); #elif defined(XCB_USE_EGL) return new QEGLXcbPlatformContext(context->format(), context->shareHandle(), screen->connection()->egl_display(), screen->connection()); @@ -205,6 +200,20 @@ QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *wind return new QXcbBackingStore(window); } +QPlatformOffscreenSurface *QXcbIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const +{ +#if defined(XCB_USE_GLX) + return new QGLXPbuffer(surface); +#elif defined(XCB_USE_EGL) + QXcbScreen *screen = static_cast<QXcbScreen *>(surface->screen()->handle()); + return new QEGLPbuffer(screen->connection()->egl_display(), surface->requestedFormat(), surface); +#else + Q_UNUSED(surface); + qWarning("QXcbIntegration: Cannot create platform offscreen surface, neither GLX nor EGL are enabled"); + return 0; +#endif +} + bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const { switch (cap) { @@ -219,6 +228,7 @@ bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const case ThreadedOpenGL: return m_connections.at(0)->supportsThreadedRendering(); case WindowMasks: return true; case MultipleWindows: return true; + case ForeignWindows: return true; default: return QPlatformIntegration::hasCapability(cap); } } @@ -293,21 +303,4 @@ QPlatformTheme *QXcbIntegration::createPlatformTheme(const QString &name) const return QGenericUnixTheme::createUnixTheme(name); } -/*! - Called by QXcbConnection prior to a QQnxScreen being deleted. - - Destroys and cleans up any default OpenGL context info for this screen. -*/ -void QXcbIntegration::removeDefaultOpenGLContextInfo(QXcbScreen *screen) -{ -#if !defined(QT_NO_OPENGL) && defined(XCB_USE_GLX) - if (!m_defaultContextInfos.contains(screen)) - return; - QOpenGLDefaultContextInfo* info = m_defaultContextInfos.take(screen); - delete info; -#else - Q_UNUSED(screen); -#endif -} - QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h index cd6c2fd73c..6db9d82cca 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -52,10 +52,6 @@ class QAbstractEventDispatcher; class QXcbNativeInterface; class QXcbScreen; -#if !defined(QT_NO_OPENGL) && defined(XCB_USE_GLX) -class QOpenGLDefaultContextInfo; -#endif - class QXcbIntegration : public QPlatformIntegration { public: @@ -68,6 +64,8 @@ public: #endif QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const; + bool hasCapability(Capability cap) const; QAbstractEventDispatcher *guiThreadEventDispatcher() const; @@ -97,8 +95,6 @@ public: QStringList themeNames() const; QPlatformTheme *createPlatformTheme(const QString &name) const; - void removeDefaultOpenGLContextInfo(QXcbScreen *screen); - private: QList<QXcbConnection *> m_connections; @@ -108,10 +104,6 @@ private: QScopedPointer<QPlatformInputContext> m_inputContext; QAbstractEventDispatcher *m_eventDispatcher; -#if !defined(QT_NO_OPENGL) && defined(XCB_USE_GLX) - mutable QHash<QXcbScreen *, QOpenGLDefaultContextInfo *> m_defaultContextInfos; -#endif - #ifndef QT_NO_ACCESSIBILITY QScopedPointer<QPlatformAccessibility> m_accessibility; #endif diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index bda54b4682..4ac60f6077 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -1081,6 +1081,7 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod int qtcode = 0; int count = chars.count(); QString string = translateKeySym(sym, state, qtcode, modifiers, chars, count); + string.truncate(count); bool isAutoRepeat = false; @@ -1102,7 +1103,7 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod bool filtered = false; if (inputContext) { - QKeyEvent event(type, qtcode, modifiers, code, sym, state, string.left(count), isAutoRepeat, count); + QKeyEvent event(type, qtcode, modifiers, code, sym, state, string, isAutoRepeat, count); event.setTimestamp(time); filtered = inputContext->filterEvent(&event); } @@ -1114,7 +1115,7 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod QWindowSystemInterface::handleContextMenuEvent(window, false, pos, globalPos, modifiers); } QWindowSystemInterface::handleExtendedKeyEvent(window, time, type, qtcode, modifiers, - code, sym, state, string.left(count), isAutoRepeat); + code, sym, state, string, isAutoRepeat); } if (isAutoRepeat && type == QEvent::KeyRelease) { @@ -1130,13 +1131,13 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod } if (!filtered && inputContext) { - QKeyEvent event(QEvent::KeyPress, qtcode, modifiers, code, sym, state, string.left(count), isAutoRepeat, count); + QKeyEvent event(QEvent::KeyPress, qtcode, modifiers, code, sym, state, string, isAutoRepeat, count); event.setTimestamp(time); filtered = inputContext->filterEvent(&event); } if (!filtered) QWindowSystemInterface::handleExtendedKeyEvent(window, time, QEvent::KeyPress, qtcode, modifiers, - code, sym, state, string.left(count), isAutoRepeat); + code, sym, state, string, isAutoRepeat); } } diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index c0ddf5c0ae..5af6a9ec9d 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -113,11 +113,45 @@ #endif #define XCOORD_MAX 16383 +enum { + defaultWindowWidth = 160, + defaultWindowHeight = 160 +}; //#ifdef NET_WM_STATE_DEBUG QT_BEGIN_NAMESPACE +#undef FocusIn + +enum QX11EmbedFocusInDetail { + XEMBED_FOCUS_CURRENT = 0, + XEMBED_FOCUS_FIRST = 1, + XEMBED_FOCUS_LAST = 2 +}; + +enum QX11EmbedInfoFlags { + XEMBED_MAPPED = (1 << 0), +}; + +enum QX11EmbedMessageType { + XEMBED_EMBEDDED_NOTIFY = 0, + XEMBED_WINDOW_ACTIVATE = 1, + XEMBED_WINDOW_DEACTIVATE = 2, + XEMBED_REQUEST_FOCUS = 3, + XEMBED_FOCUS_IN = 4, + XEMBED_FOCUS_OUT = 5, + XEMBED_FOCUS_NEXT = 6, + XEMBED_FOCUS_PREV = 7, + XEMBED_MODALITY_ON = 10, + XEMBED_MODALITY_OFF = 11, + XEMBED_REGISTER_ACCELERATOR = 12, + XEMBED_UNREGISTER_ACCELERATOR = 13, + XEMBED_ACTIVATE_ACCELERATOR = 14 +}; + +static unsigned int XEMBED_VERSION = 0; + // Returns true if we should set WM_TRANSIENT_FOR on \a w static inline bool isTransient(const QWindow *w) { @@ -152,7 +186,9 @@ QXcbWindow::QXcbWindow(QWindow *window) , m_gravity(XCB_GRAVITY_STATIC) , m_mapped(false) , m_transparent(false) + , m_usingSyncProtocol(false) , m_deferredActivation(false) + , m_embedded(false) , m_netWmUserTimeWindow(XCB_NONE) , m_dirtyFrameMargins(false) #if defined(XCB_USE_EGL) @@ -164,7 +200,10 @@ QXcbWindow::QXcbWindow(QWindow *window) setConnection(m_screen->connection()); - create(); + if (window->type() != Qt::ForeignWindow) + create(); + else + m_window = window->winId(); } void QXcbWindow::create() @@ -219,12 +258,22 @@ void QXcbWindow::create() QRect rect = window()->geometry(); QPlatformWindow::setGeometry(rect); - rect.setWidth(qBound(1, rect.width(), XCOORD_MAX)); - rect.setHeight(qBound(1, rect.height(), XCOORD_MAX)); + QSize minimumSize = window()->minimumSize(); + if (rect.width() > 0 || rect.height() > 0) { + rect.setWidth(qBound(1, rect.width(), XCOORD_MAX)); + rect.setHeight(qBound(1, rect.height(), XCOORD_MAX)); + } else if (minimumSize.width() > 0 || minimumSize.height() > 0) { + rect.setSize(minimumSize); + } else { + rect.setWidth(defaultWindowWidth); + rect.setHeight(defaultWindowHeight); + } xcb_window_t xcb_parent_id = m_screen->root(); - if (parent()) + if (parent()) { xcb_parent_id = static_cast<QXcbWindow *>(parent())->xcb_window(); + m_embedded = parent()->window()->type() == Qt::ForeignWindow; + } m_format = window()->requestedFormat(); @@ -306,7 +355,9 @@ void QXcbWindow::create() properties[propertyCount++] = atom(QXcbAtom::WM_TAKE_FOCUS); properties[propertyCount++] = atom(QXcbAtom::_NET_WM_PING); - if (m_screen->syncRequestSupported()) + m_usingSyncProtocol = m_screen->syncRequestSupported() && window()->surfaceType() != QSurface::OpenGLSurface; + + if (m_usingSyncProtocol) properties[propertyCount++] = atom(QXcbAtom::_NET_WM_SYNC_REQUEST); if (window()->flags() & Qt::WindowContextHelpButtonHint) @@ -323,7 +374,7 @@ void QXcbWindow::create() m_syncValue.hi = 0; m_syncValue.lo = 0; - if (m_screen->syncRequestSupported()) { + if (m_usingSyncProtocol) { m_syncCounter = xcb_generate_id(xcb_connection()); Q_XCB_CALL(xcb_sync_create_counter(xcb_connection(), m_syncCounter, m_syncValue)); @@ -356,6 +407,14 @@ void QXcbWindow::create() atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32, 1, &leader)); + /* Add XEMBED info; this operation doesn't initiate the embedding. */ + long data[] = { XEMBED_VERSION, XEMBED_MAPPED }; + Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, + atom(QXcbAtom::_XEMBED_INFO), + atom(QXcbAtom::_XEMBED_INFO), + 32, 2, (void *)data)); + + #ifdef XCB_USE_XINPUT2_MAEMO if (connection()->isUsingXInput2Maemo()) { XIEventMask xieventmask; @@ -398,7 +457,8 @@ void QXcbWindow::create() QXcbWindow::~QXcbWindow() { - destroy(); + if (window()->type() != Qt::ForeignWindow) + destroy(); } void QXcbWindow::destroy() @@ -406,7 +466,7 @@ void QXcbWindow::destroy() if (connection()->focusWindow() == this) connection()->setFocusWindow(0); - if (m_syncCounter && m_screen->syncRequestSupported()) + if (m_syncCounter && m_usingSyncProtocol) Q_XCB_CALL(xcb_sync_destroy_counter(xcb_connection(), m_syncCounter)); if (m_window) { if (m_netWmUserTimeWindow) { @@ -436,15 +496,23 @@ void QXcbWindow::setGeometry(const QRect &rect) propagateSizeHints(); const QRect wmGeometry = windowToWmGeometry(rect); - const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; - const qint32 values[] = { - qBound<qint32>(-XCOORD_MAX, wmGeometry.x(), XCOORD_MAX), - qBound<qint32>(-XCOORD_MAX, wmGeometry.y(), XCOORD_MAX), - qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX), - qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX), - }; - - Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values))); + if (qt_window_private(window())->positionAutomatic) { + const quint32 mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; + const qint32 values[] = { + qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX), + qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX), + }; + Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values))); + } else { + const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; + const qint32 values[] = { + qBound<qint32>(-XCOORD_MAX, wmGeometry.x(), XCOORD_MAX), + qBound<qint32>(-XCOORD_MAX, wmGeometry.y(), XCOORD_MAX), + qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX), + qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX), + }; + Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values))); + } xcb_flush(xcb_connection()); } @@ -554,15 +622,17 @@ void QXcbWindow::show() propagateSizeHints(); // update WM_TRANSIENT_FOR - if (isTransient(window())) { + const QWindow *tp = window()->transientParent(); + if (isTransient(window()) || tp != 0) { xcb_window_t transientXcbParent = 0; - if (const QWindow *tp = window()->transientParent()) + if (tp) transientXcbParent = static_cast<const QXcbWindow *>(tp->handle())->winId(); // Default to client leader if there is no transient parent, else modal dialogs can // be hidden by their parents. if (!transientXcbParent) transientXcbParent = static_cast<QXcbScreen *>(screen())->clientLeader(); if (transientXcbParent) { // ICCCM 4.1.2.6 + m_gravity = XCB_GRAVITY_CENTER; Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32, 1, &transientXcbParent)); @@ -1119,7 +1189,15 @@ void QXcbWindow::setParent(const QPlatformWindow *parent) { QPoint topLeft = geometry().topLeft(); - xcb_window_t xcb_parent_id = parent ? static_cast<const QXcbWindow *>(parent)->xcb_window() : m_screen->root(); + xcb_window_t xcb_parent_id; + if (parent) { + const QXcbWindow *qXcbParent = static_cast<const QXcbWindow *>(parent); + xcb_parent_id = qXcbParent->xcb_window(); + m_embedded = qXcbParent->window()->type() == Qt::ForeignWindow; + } else { + xcb_parent_id = m_screen->root(); + m_embedded = false; + } Q_XCB_CALL(xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y())); } @@ -1221,8 +1299,10 @@ void QXcbWindow::propagateSizeHints() QWindow *win = window(); - xcb_size_hints_set_position(&hints, true, rect.x(), rect.y()); - xcb_size_hints_set_size(&hints, true, rect.width(), rect.height()); + if (!qt_window_private(win)->positionAutomatic) + xcb_size_hints_set_position(&hints, true, rect.x(), rect.y()); + if (rect.width() < QWINDOWSIZE_MAX || rect.height() < QWINDOWSIZE_MAX) + xcb_size_hints_set_size(&hints, true, rect.width(), rect.height()); xcb_size_hints_set_win_gravity(&hints, m_gravity); QSize minimumSize = win->minimumSize(); @@ -1248,6 +1328,13 @@ void QXcbWindow::propagateSizeHints() void QXcbWindow::requestActivateWindow() { + /* Never activate embedded windows; doing that would prevent the container + * to re-gain the keyboard focus later. */ + if (m_embedded) { + QPlatformWindow::requestActivateWindow(); + return; + } + if (!m_mapped) { m_deferredActivation = true; return; @@ -1411,7 +1498,8 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even } else if (event->type == atom(QXcbAtom::XdndDrop)) { connection()->drag()->handleDrop(window(), event); #endif - } else if (event->type == atom(QXcbAtom::_XEMBED)) { // QSystemTrayIcon + } else if (event->type == atom(QXcbAtom::_XEMBED)) { + handleXEmbedMessage(event); } else { qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type); } @@ -1453,6 +1541,53 @@ bool QXcbWindow::isExposed() const return m_mapped; } +bool QXcbWindow::isEmbedded(const QPlatformWindow *parentWindow) const +{ + if (!m_embedded) + return false; + + return parentWindow ? (parentWindow == parent()) : true; +} + +QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const +{ + if (!m_embedded) + return pos; + + QPoint ret; + xcb_translate_coordinates_cookie_t cookie = + xcb_translate_coordinates(xcb_connection(), xcb_window(), m_screen->root(), + pos.x(), pos.y()); + xcb_translate_coordinates_reply_t *reply = + xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL); + if (reply) { + ret.setX(reply->dst_x); + ret.setY(reply->dst_y); + free(reply); + } + + return ret; +} + +QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const +{ + if (!m_embedded) + return pos; + QPoint ret; + xcb_translate_coordinates_cookie_t cookie = + xcb_translate_coordinates(xcb_connection(), m_screen->root(), xcb_window(), + pos.x(), pos.y()); + xcb_translate_coordinates_reply_t *reply = + xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL); + if (reply) { + ret.setX(reply->dst_x); + ret.setY(reply->dst_y); + free(reply); + } + + return ret; +} + void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event) { if (event->window == m_window) { @@ -1483,6 +1618,15 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event) updateNetWmUserTime(event->time); + if (m_embedded) { + if (window() != QGuiApplication::focusWindow()) { + const QXcbWindow *container = static_cast<const QXcbWindow *>(parent()); + Q_ASSERT(container != 0); + + sendXEmbedMessage(container->xcb_window(), XEMBED_REQUEST_FOCUS); + } + } + QPoint local(event->event_x, event->event_y); QPoint global(event->root_x, event->root_y); @@ -1638,6 +1782,7 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev QWindowSystemInterface::handleWindowStateChanged(window(), newState); m_lastWindowStateEvent = newState; } + return; } } @@ -1649,7 +1794,7 @@ void QXcbWindow::handleFocusInEvent(const xcb_focus_in_event_t *) QWindowSystemInterface::handleWindowActivated(w); } -static bool focusInPeeker(xcb_generic_event_t *event) +static bool focusInPeeker(QXcbConnection *connection, xcb_generic_event_t *event) { if (!event) { // FocusIn event is not in the queue, proceed with FocusOut normally. @@ -1657,7 +1802,18 @@ static bool focusInPeeker(xcb_generic_event_t *event) return true; } uint response_type = event->response_type & ~0x80; - return response_type == XCB_FOCUS_IN; + if (response_type == XCB_FOCUS_IN) + return true; + + /* We are also interested in XEMBED_FOCUS_IN events */ + if (response_type == XCB_CLIENT_MESSAGE) { + xcb_client_message_event_t *cme = (xcb_client_message_event_t *)event; + if (cme->type == connection->atom(QXcbAtom::_XEMBED) + && cme->data.data32[1] == XEMBED_FOCUS_IN) + return true; + } + + return false; } void QXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *) @@ -1671,7 +1827,7 @@ void QXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *) void QXcbWindow::updateSyncRequestCounter() { - if (m_screen->syncRequestSupported() && (m_syncValue.lo != 0 || m_syncValue.hi != 0)) { + if (m_usingSyncProtocol && (m_syncValue.lo != 0 || m_syncValue.hi != 0)) { Q_XCB_CALL(xcb_sync_set_counter(xcb_connection(), m_syncCounter, m_syncValue)); xcb_flush(xcb_connection()); connection()->sync(); @@ -1721,6 +1877,35 @@ void QXcbWindow::setCursor(xcb_cursor_t cursor) xcb_flush(xcb_connection()); } +void QXcbWindow::windowEvent(QEvent *event) +{ + switch (event->type()) { + case QEvent::FocusIn: + if (m_embedded && !event->spontaneous()) { + QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event); + switch (focusEvent->reason()) { + case Qt::TabFocusReason: + case Qt::BacktabFocusReason: + { + const QXcbWindow *container = + static_cast<const QXcbWindow *>(parent()); + sendXEmbedMessage(container->xcb_window(), + focusEvent->reason() == Qt::TabFocusReason ? + XEMBED_FOCUS_NEXT : XEMBED_FOCUS_PREV); + event->accept(); + } + break; + default: + break; + } + } + break; + default: + break; + } + QPlatformWindow::windowEvent(event); +} + bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner) { const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE); @@ -1749,6 +1934,71 @@ bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner) return true; } +// Sends an XEmbed message. +void QXcbWindow::sendXEmbedMessage(xcb_window_t window, long message, + long detail, long data1, long data2) +{ + xcb_client_message_event_t event; + + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.window = window; + event.type = atom(QXcbAtom::_XEMBED); + event.data.data32[0] = connection()->time(); + event.data.data32[1] = message; + event.data.data32[2] = detail; + event.data.data32[3] = data1; + event.data.data32[4] = data2; + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, window, + XCB_EVENT_MASK_NO_EVENT, (const char *)&event)); +} + +static bool activeWindowChangeQueued(const QWindow *window) +{ + /* Check from window system event queue if the next queued activation + * targets a window other than @window. + */ + QWindowSystemInterfacePrivate::ActivatedWindowEvent *systemEvent = + static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *> + (QWindowSystemInterfacePrivate::peekWindowSystemEvent(QWindowSystemInterfacePrivate::ActivatedWindow)); + return systemEvent && systemEvent->activated != window; +} + +void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event) +{ + connection()->setTime(event->data.data32[0]); + switch (event->data.data32[1]) { + case XEMBED_WINDOW_ACTIVATE: + case XEMBED_WINDOW_DEACTIVATE: + case XEMBED_EMBEDDED_NOTIFY: + break; + case XEMBED_FOCUS_IN: + Qt::FocusReason reason; + switch (event->data.data32[2]) { + case XEMBED_FOCUS_FIRST: + reason = Qt::TabFocusReason; + break; + case XEMBED_FOCUS_LAST: + reason = Qt::BacktabFocusReason; + break; + case XEMBED_FOCUS_CURRENT: + default: + reason = Qt::OtherFocusReason; + break; + } + connection()->setFocusWindow(static_cast<QXcbWindow*>(window()->handle())); + QWindowSystemInterface::handleWindowActivated(window(), reason); + break; + case XEMBED_FOCUS_OUT: + if (window() == QGuiApplication::focusWindow() + && !activeWindowChangeQueued(window())) { + connection()->setFocusWindow(0); + QWindowSystemInterface::handleWindowActivated(0); + } + break; + } +} + #if !defined(QT_NO_SHAPE) static inline xcb_rectangle_t qRectToXCBRectangle(const QRect &r) diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 3b5404684f..f4bd2d96ff 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -87,6 +87,9 @@ public: void setParent(const QPlatformWindow *window); bool isExposed() const; + bool isEmbedded(const QPlatformWindow *parentWindow) const; + QPoint mapToGlobal(const QPoint &pos) const; + QPoint mapFromGlobal(const QPoint &pos) const; void setWindowTitle(const QString &title); void setWindowIcon(const QIcon &icon); @@ -107,6 +110,8 @@ public: QSurfaceFormat format() const; + void windowEvent(QEvent *event); + bool startSystemResize(const QPoint &pos, Qt::Corner corner); void setOpacity(qreal level); @@ -158,6 +163,9 @@ private: void updateDoesNotAcceptFocus(bool doesNotAcceptFocus); QRect windowToWmGeometry(QRect r) const; + void sendXEmbedMessage(xcb_window_t window, long message, + long detail = 0, long data1 = 0, long data2 = 0); + void handleXEmbedMessage(const xcb_client_message_event_t *event); void create(); void destroy(); @@ -181,9 +189,11 @@ private: bool m_mapped; bool m_transparent; + bool m_usingSyncProtocol; bool m_deferredActivation; bool m_deferredExpose; bool m_configureNotifyPending; + bool m_embedded; xcb_window_t m_netWmUserTimeWindow; QSurfaceFormat m_format; |