From 0b8d0804219b7e2e6179112d663b989d5b749d17 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 17 Aug 2011 15:58:39 +0200 Subject: Add Windows to the Lighthouse. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an initial Lighthouse plugin for the Windows operating system. Change-Id: I6934562266e1aa0ac270bf6107df05a9e56ef82c Reviewed-on: http://codereview.qt.nokia.com/3107 Reviewed-by: Oliver Wolff Reviewed-by: Samuel Rødal --- src/plugins/platforms/platforms.pro | 2 + src/plugins/platforms/windows/array.h | 103 ++ src/plugins/platforms/windows/main.cpp | 112 ++ src/plugins/platforms/windows/pixmaputils.cpp | 316 ++++ src/plugins/platforms/windows/pixmaputils.h | 71 + .../platforms/windows/qtwindows_additional.h | 118 ++ src/plugins/platforms/windows/qtwindowsglobal.h | 159 ++ src/plugins/platforms/windows/qwindows.qdocconf | 27 + .../platforms/windows/qwindowsbackingstore.cpp | 143 ++ .../platforms/windows/qwindowsbackingstore.h | 77 + .../platforms/windows/qwindowsclipboard.cpp | 366 +++++ src/plugins/platforms/windows/qwindowsclipboard.h | 94 ++ src/plugins/platforms/windows/qwindowscontext.cpp | 734 +++++++++ src/plugins/platforms/windows/qwindowscontext.h | 171 +++ src/plugins/platforms/windows/qwindowscursor.cpp | 451 ++++++ src/plugins/platforms/windows/qwindowscursor.h | 93 ++ src/plugins/platforms/windows/qwindowsdrag.cpp | 721 +++++++++ src/plugins/platforms/windows/qwindowsdrag.h | 116 ++ .../platforms/windows/qwindowsfontdatabase.cpp | 950 ++++++++++++ .../platforms/windows/qwindowsfontdatabase.h | 107 ++ .../platforms/windows/qwindowsfontengine.cpp | 1223 +++++++++++++++ src/plugins/platforms/windows/qwindowsfontengine.h | 186 +++ .../windows/qwindowsfontenginedirectwrite.cpp | 737 +++++++++ .../windows/qwindowsfontenginedirectwrite.h | 133 ++ .../platforms/windows/qwindowsglcontext.cpp | 974 ++++++++++++ src/plugins/platforms/windows/qwindowsglcontext.h | 165 +++ .../windows/qwindowsguieventdispatcher.cpp | 209 +++ .../platforms/windows/qwindowsguieventdispatcher.h | 71 + .../platforms/windows/qwindowsintegration.cpp | 275 ++++ .../platforms/windows/qwindowsintegration.h | 81 + .../platforms/windows/qwindowsinternalmimedata.h | 70 + .../platforms/windows/qwindowskeymapper.cpp | 1076 ++++++++++++++ src/plugins/platforms/windows/qwindowskeymapper.h | 114 ++ src/plugins/platforms/windows/qwindowsmime.cpp | 1557 ++++++++++++++++++++ src/plugins/platforms/windows/qwindowsmime.h | 102 ++ .../platforms/windows/qwindowsmousehandler.cpp | 288 ++++ .../platforms/windows/qwindowsmousehandler.h | 98 ++ .../platforms/windows/qwindowsnativeimage.cpp | 152 ++ .../platforms/windows/qwindowsnativeimage.h | 82 ++ src/plugins/platforms/windows/qwindowsole.cpp | 476 ++++++ src/plugins/platforms/windows/qwindowsole.h | 127 ++ .../platforms/windows/qwindowsprintersupport.cpp | 134 ++ .../platforms/windows/qwindowsprintersupport.h | 62 + src/plugins/platforms/windows/qwindowsscreen.cpp | 230 +++ src/plugins/platforms/windows/qwindowsscreen.h | 100 ++ src/plugins/platforms/windows/qwindowswindow.cpp | 1317 +++++++++++++++++ src/plugins/platforms/windows/qwindowswindow.h | 284 ++++ src/plugins/platforms/windows/windows.pro | 70 + 48 files changed, 15324 insertions(+) create mode 100644 src/plugins/platforms/windows/array.h create mode 100644 src/plugins/platforms/windows/main.cpp create mode 100644 src/plugins/platforms/windows/pixmaputils.cpp create mode 100644 src/plugins/platforms/windows/pixmaputils.h create mode 100644 src/plugins/platforms/windows/qtwindows_additional.h create mode 100644 src/plugins/platforms/windows/qtwindowsglobal.h create mode 100644 src/plugins/platforms/windows/qwindows.qdocconf create mode 100644 src/plugins/platforms/windows/qwindowsbackingstore.cpp create mode 100644 src/plugins/platforms/windows/qwindowsbackingstore.h create mode 100644 src/plugins/platforms/windows/qwindowsclipboard.cpp create mode 100644 src/plugins/platforms/windows/qwindowsclipboard.h create mode 100644 src/plugins/platforms/windows/qwindowscontext.cpp create mode 100644 src/plugins/platforms/windows/qwindowscontext.h create mode 100644 src/plugins/platforms/windows/qwindowscursor.cpp create mode 100644 src/plugins/platforms/windows/qwindowscursor.h create mode 100644 src/plugins/platforms/windows/qwindowsdrag.cpp create mode 100644 src/plugins/platforms/windows/qwindowsdrag.h create mode 100644 src/plugins/platforms/windows/qwindowsfontdatabase.cpp create mode 100644 src/plugins/platforms/windows/qwindowsfontdatabase.h create mode 100644 src/plugins/platforms/windows/qwindowsfontengine.cpp create mode 100644 src/plugins/platforms/windows/qwindowsfontengine.h create mode 100644 src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp create mode 100644 src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h create mode 100644 src/plugins/platforms/windows/qwindowsglcontext.cpp create mode 100644 src/plugins/platforms/windows/qwindowsglcontext.h create mode 100644 src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp create mode 100644 src/plugins/platforms/windows/qwindowsguieventdispatcher.h create mode 100644 src/plugins/platforms/windows/qwindowsintegration.cpp create mode 100644 src/plugins/platforms/windows/qwindowsintegration.h create mode 100644 src/plugins/platforms/windows/qwindowsinternalmimedata.h create mode 100644 src/plugins/platforms/windows/qwindowskeymapper.cpp create mode 100644 src/plugins/platforms/windows/qwindowskeymapper.h create mode 100644 src/plugins/platforms/windows/qwindowsmime.cpp create mode 100644 src/plugins/platforms/windows/qwindowsmime.h create mode 100644 src/plugins/platforms/windows/qwindowsmousehandler.cpp create mode 100644 src/plugins/platforms/windows/qwindowsmousehandler.h create mode 100644 src/plugins/platforms/windows/qwindowsnativeimage.cpp create mode 100644 src/plugins/platforms/windows/qwindowsnativeimage.h create mode 100644 src/plugins/platforms/windows/qwindowsole.cpp create mode 100644 src/plugins/platforms/windows/qwindowsole.h create mode 100644 src/plugins/platforms/windows/qwindowsprintersupport.cpp create mode 100644 src/plugins/platforms/windows/qwindowsprintersupport.h create mode 100644 src/plugins/platforms/windows/qwindowsscreen.cpp create mode 100644 src/plugins/platforms/windows/qwindowsscreen.h create mode 100644 src/plugins/platforms/windows/qwindowswindow.cpp create mode 100644 src/plugins/platforms/windows/qwindowswindow.h create mode 100644 src/plugins/platforms/windows/windows.pro (limited to 'src/plugins/platforms') diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index 9d7ae296a9..ac001b665b 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -13,3 +13,5 @@ contains(QT_CONFIG, xcb) { mac { SUBDIRS += cocoa } + +win32: SUBDIRS += windows diff --git a/src/plugins/platforms/windows/array.h b/src/plugins/platforms/windows/array.h new file mode 100644 index 0000000000..216f1e8945 --- /dev/null +++ b/src/plugins/platforms/windows/array.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ARRAY_H +#define ARRAY_H + +#include + +QT_BEGIN_NAMESPACE + +/* A simple, non-shared array. */ + +template +class Array +{ + Q_DISABLE_COPY(Array) +public: + enum { initialSize = 5 }; + + typedef T* const_iterator; + + explicit Array(size_t size= 0) : data(0), m_capacity(0), m_size(0) + { if (size) resize(size); } + ~Array() { delete [] data; } + + T *data; + inline size_t size() const { return m_size; } + inline const_iterator begin() const { return data; } + inline const_iterator end() const { return data + m_size; } + + inline void append(const T &value) + { + const size_t oldSize = m_size; + resize(m_size + 1); + data[oldSize] = value; + } + + inline void resize(size_t size) + { + if (size > m_size) + reserve(size > 1 ? size + size / 2 : size_t(initialSize)); + m_size = size; + } + + void reserve(size_t capacity) + { + if (capacity > m_capacity) { + const T *oldData = data; + data = new T[capacity]; + if (oldData) { + qCopy(oldData, oldData + m_size, data); + delete [] oldData; + } + m_capacity = capacity; + } + } + +private: + size_t m_capacity; + size_t m_size; +}; + +QT_END_NAMESPACE + +#endif // ARRAY_H diff --git a/src/plugins/platforms/windows/main.cpp b/src/plugins/platforms/windows/main.cpp new file mode 100644 index 0000000000..933aa76df8 --- /dev/null +++ b/src/plugins/platforms/windows/main.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +#include "qwindowsintegration.h" + +QT_BEGIN_NAMESPACE + +/*! + \group qt-lighthouse-win + \title Qt Lighthouse plugin for Windows + + \brief Class documentation of the Qt Lighthouse plugin for Windows. + + \section1 Tips + + \list + \o The environment variable \c QT_LIGHTHOUSE_WINDOWS_VERBOSE controls + the debug level. It takes the form + \c{:,:}, where + keyword is one of \c integration, \c windows, \c backingstore and + \c fonts. Level is an integer 0..9. + \endlist + */ + +/*! + \class QWindowsIntegrationPlugin + \brief Plugin. + \ingroup qt-lighthouse-win + */ + +/*! + \namespace QtWindows + + \brief Namespace for enumerations, etc. + \ingroup qt-lighthouse-win +*/ + +/*! + \enum QtWindows::WindowsEventType + + \brief Enumerations for WM_XX events. + + With flags that should help to structure the code. + + \ingroup qt-lighthouse-win +*/ + +class QWindowsIntegrationPlugin : public QPlatformIntegrationPlugin +{ +public: + QStringList keys() const; + QPlatformIntegration *create(const QString&, const QStringList&); +}; + +QStringList QWindowsIntegrationPlugin::keys() const +{ + return QStringList(QStringLiteral("windows")); +} + +QPlatformIntegration *QWindowsIntegrationPlugin::create(const QString& system, const QStringList& paramList) +{ + Q_UNUSED(paramList); + if (system.compare(system, QStringLiteral("windows"), Qt::CaseInsensitive) == 0) + return new QWindowsIntegration; + return 0; +} + +Q_EXPORT_PLUGIN2(windows, QWindowsIntegrationPlugin) + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/pixmaputils.cpp b/src/plugins/platforms/windows/pixmaputils.cpp new file mode 100644 index 0000000000..111df5a4b9 --- /dev/null +++ b/src/plugins/platforms/windows/pixmaputils.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pixmaputils.h" + +#include +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +HBITMAP createIconMask(const QBitmap &bitmap) +{ + QImage bm = bitmap.toImage().convertToFormat(QImage::Format_Mono); + const int w = bm.width(); + const int h = bm.height(); + const int bpl = ((w+15)/16)*2; // bpl, 16 bit alignment + QScopedArrayPointer bits(new uchar[bpl * h]); + bm.invertPixels(); + for (int y = 0; y < h; ++y) + memcpy(bits.data() + y * bpl, bm.scanLine(y), bpl); + HBITMAP hbm = CreateBitmap(w, h, 1, 1, bits.data()); + return hbm; +} + +HBITMAP qPixmapToWinHBITMAP(const QPixmap &p, HBitmapFormat format) +{ + if (p.isNull()) + return 0; + + HBITMAP bitmap = 0; + if (p.handle()->classId() != QPlatformPixmap::RasterClass) { + QRasterPlatformPixmap *data = new QRasterPlatformPixmap(p.depth() == 1 ? + QRasterPlatformPixmap::BitmapType : QRasterPlatformPixmap::PixmapType); + data->fromImage(p.toImage(), Qt::AutoColor); + return qPixmapToWinHBITMAP(QPixmap(data), format); + } + + QRasterPlatformPixmap *d = static_cast(p.handle()); + const QImage *rasterImage = d->buffer(); + const int w = rasterImage->width(); + const int h = rasterImage->height(); + + HDC display_dc = GetDC(0); + + // Define the header + BITMAPINFO bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = w; + bmi.bmiHeader.biHeight = -h; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = w * h * 4; + + // Create the pixmap + uchar *pixels = 0; + bitmap = CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, (void **) &pixels, 0, 0); + ReleaseDC(0, display_dc); + if (!bitmap) { + qErrnoWarning("%s, failed to create dibsection", __FUNCTION__); + return 0; + } + if (!pixels) { + qErrnoWarning("%s, did not allocate pixel data", __FUNCTION__); + return 0; + } + + // Copy over the data + QImage::Format imageFormat = QImage::Format_ARGB32; + if (format == HBitmapAlpha) + imageFormat = QImage::Format_RGB32; + else if (format == HBitmapPremultipliedAlpha) + imageFormat = QImage::Format_ARGB32_Premultiplied; + const QImage image = rasterImage->convertToFormat(imageFormat); + const int bytes_per_line = w * 4; + for (int y=0; y < h; ++y) + memcpy(pixels + y * bytes_per_line, image.scanLine(y), bytes_per_line); + + return bitmap; +} + +QPixmap qPixmapFromWinHBITMAP(HBITMAP bitmap, HBitmapFormat format) +{ + // Verify size + BITMAP bitmap_info; + memset(&bitmap_info, 0, sizeof(BITMAP)); + + const int res = GetObject(bitmap, sizeof(BITMAP), &bitmap_info); + if (!res) { + qErrnoWarning("QPixmap::fromWinHBITMAP(), failed to get bitmap info"); + return QPixmap(); + } + const int w = bitmap_info.bmWidth; + const int h = bitmap_info.bmHeight; + + BITMAPINFO bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = w; + bmi.bmiHeader.biHeight = -h; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = w * h * 4; + + // Get bitmap bits + QScopedArrayPointer data(new uchar[bmi.bmiHeader.biSizeImage]); + HDC display_dc = GetDC(0); + if (!GetDIBits(display_dc, bitmap, 0, h, data.data(), &bmi, DIB_RGB_COLORS)) { + ReleaseDC(0, display_dc); + qWarning("%s, failed to get bitmap bits", __FUNCTION__); + return QPixmap(); + } + + QImage::Format imageFormat = QImage::Format_ARGB32_Premultiplied; + uint mask = 0; + if (format == HBitmapNoAlpha) { + imageFormat = QImage::Format_RGB32; + mask = 0xff000000; + } + + // Create image and copy data into image. + QImage image(w, h, imageFormat); + if (image.isNull()) { // failed to alloc? + ReleaseDC(0, display_dc); + qWarning("%s, failed create image of %dx%d", __FUNCTION__, w, h); + return QPixmap(); + } + const int bytes_per_line = w * sizeof(QRgb); + for (int y = 0; y < h; ++y) { + QRgb *dest = (QRgb *) image.scanLine(y); + const QRgb *src = (const QRgb *) (data.data() + y * bytes_per_line); + for (int x = 0; x < w; ++x) { + const uint pixel = src[x]; + if ((pixel & 0xff000000) == 0 && (pixel & 0x00ffffff) != 0) + dest[x] = pixel | 0xff000000; + else + dest[x] = pixel | mask; + } + } + ReleaseDC(0, display_dc); + return QPixmap::fromImage(image); +} + +HICON qPixmapToWinHICON(const QPixmap &p) +{ + QBitmap maskBitmap = p.mask(); + if (maskBitmap.isNull()) { + maskBitmap = QBitmap(p.size()); + maskBitmap.fill(Qt::color1); + } + + ICONINFO ii; + ii.fIcon = true; + ii.hbmMask = createIconMask(maskBitmap); + ii.hbmColor = qPixmapToWinHBITMAP(p, HBitmapAlpha); + ii.xHotspot = 0; + ii.yHotspot = 0; + + HICON hIcon = CreateIconIndirect(&ii); + + DeleteObject(ii.hbmColor); + DeleteObject(ii.hbmMask); + + return hIcon; +} + +static QImage qImageFromWinHBITMAP(HDC hdc, HBITMAP bitmap, int w, int h) +{ + BITMAPINFO bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = w; + bmi.bmiHeader.biHeight = -h; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = w * h * 4; + + QImage image(w, h, QImage::Format_ARGB32_Premultiplied); + if (image.isNull()) + return image; + + // Get bitmap bits + QScopedPointer data(new uchar [bmi.bmiHeader.biSizeImage]); + if (!GetDIBits(hdc, bitmap, 0, h, data.data(), &bmi, DIB_RGB_COLORS)) { + qErrnoWarning("%s: failed to get bitmap bits", __FUNCTION__); + return QImage(); + } + // Create image and copy data into image. + for (int y = 0; y < h; ++y) { + void *dest = (void *) image.scanLine(y); + void *src = data.data() + y * image.bytesPerLine(); + memcpy(dest, src, image.bytesPerLine()); + } + return image; +} + +QPixmap qPixmapFromWinHICON(HICON icon) +{ + bool foundAlpha = false; + HDC screenDevice = GetDC(0); + HDC hdc = CreateCompatibleDC(screenDevice); + ReleaseDC(0, screenDevice); + + ICONINFO iconinfo; + const bool result = GetIconInfo(icon, &iconinfo); //x and y Hotspot describes the icon center + if (!result) { + qErrnoWarning("QPixmap::fromWinHICON(), failed to GetIconInfo()"); + return QPixmap(); + } + + const int w = iconinfo.xHotspot * 2; + const int h = iconinfo.yHotspot * 2; + + BITMAPINFOHEADER bitmapInfo; + bitmapInfo.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.biWidth = w; + bitmapInfo.biHeight = h; + bitmapInfo.biPlanes = 1; + bitmapInfo.biBitCount = 32; + bitmapInfo.biCompression = BI_RGB; + bitmapInfo.biSizeImage = 0; + bitmapInfo.biXPelsPerMeter = 0; + bitmapInfo.biYPelsPerMeter = 0; + bitmapInfo.biClrUsed = 0; + bitmapInfo.biClrImportant = 0; + DWORD* bits; + + HBITMAP winBitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bitmapInfo, DIB_RGB_COLORS, (VOID**)&bits, NULL, 0); + HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap); + DrawIconEx( hdc, 0, 0, icon, iconinfo.xHotspot * 2, iconinfo.yHotspot * 2, 0, 0, DI_NORMAL); + QImage image = qImageFromWinHBITMAP(hdc, winBitmap, w, h); + + for (int y = 0 ; y < h && !foundAlpha ; y++) { + const QRgb *scanLine= reinterpret_cast(image.scanLine(y)); + for (int x = 0; x < w ; x++) { + if (qAlpha(scanLine[x]) != 0) { + foundAlpha = true; + break; + } + } + } + if (!foundAlpha) { + //If no alpha was found, we use the mask to set alpha values + DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK); + const QImage mask = qImageFromWinHBITMAP(hdc, winBitmap, w, h); + + for (int y = 0 ; y < h ; y++){ + QRgb *scanlineImage = reinterpret_cast(image.scanLine(y)); + const QRgb *scanlineMask = mask.isNull() ? 0 : reinterpret_cast(mask.scanLine(y)); + for (int x = 0; x < w ; x++){ + if (scanlineMask && qRed(scanlineMask[x]) != 0) + scanlineImage[x] = 0; //mask out this pixel + else + scanlineImage[x] |= 0xff000000; // set the alpha channel to 255 + } + } + } + //dispose resources created by iconinfo call + DeleteObject(iconinfo.hbmMask); + DeleteObject(iconinfo.hbmColor); + + SelectObject(hdc, oldhdc); //restore state + DeleteObject(winBitmap); + DeleteDC(hdc); + return QPixmap::fromImage(image); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/pixmaputils.h b/src/plugins/platforms/windows/pixmaputils.h new file mode 100644 index 0000000000..bf94a2695c --- /dev/null +++ b/src/plugins/platforms/windows/pixmaputils.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PIXMAPUTILS_H +#define PIXMAPUTILS_H + +#include "qtwindows_additional.h" + +#include + +QT_BEGIN_NAMESPACE + +class QBitmap; +class QPixmap; + +enum HBitmapFormat +{ + HBitmapNoAlpha, + HBitmapPremultipliedAlpha, + HBitmapAlpha +}; + +HBITMAP createIconMask(const QBitmap &bitmap); + +HBITMAP qPixmapToWinHBITMAP(const QPixmap &p, HBitmapFormat format); +HICON qPixmapToWinHICON(const QPixmap &p); + +QPixmap qPixmapFromWinHBITMAP(HBITMAP bitmap, HBitmapFormat format); +QPixmap qPixmapFromWinHICON(HICON icon); + +QT_END_NAMESPACE + +#endif // PIXMAPUTILS_H diff --git a/src/plugins/platforms/windows/qtwindows_additional.h b/src/plugins/platforms/windows/qtwindows_additional.h new file mode 100644 index 0000000000..707d28559a --- /dev/null +++ b/src/plugins/platforms/windows/qtwindows_additional.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTWINDOWS_ADDITIONAL_H +#define QTWINDOWS_ADDITIONAL_H + +#include // get compiler define +#include + +/* Complement the definitions and declarations missing + * when using MinGW or older Windows SDKs. */ + +#if defined(Q_CC_MINGW) +# if !defined(ULW_ALPHA) +# define ULW_ALPHA 0x00000002 +# define LWA_ALPHA 0x00000002 +# endif // !defined(ULW_ALPHA) +# define SPI_GETFONTSMOOTHINGTYPE 0x200A +# define FE_FONTSMOOTHINGCLEARTYPE 0x0002 +# define CLEARTYPE_QUALITY 5 + +# define CF_DIBV5 17 + +#define CO_E_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x80004021L) + +typedef struct tagUPDATELAYEREDWINDOWINFO { + DWORD cbSize; + HDC hdcDst; + const POINT *pptDst; + const SIZE *psize; + HDC hdcSrc; + const POINT *pptSrc; + COLORREF crKey; + const BLENDFUNCTION *pblend; + DWORD dwFlags; + const RECT *prcDirty; +} UPDATELAYEREDWINDOWINFO, *PUPDATELAYEREDWINDOWINFO; + +// OpenGL Pixelformat flags. +#define PFD_SUPPORT_DIRECTDRAW 0x00002000 +#define PFD_DIRECT3D_ACCELERATED 0x00004000 +#define PFD_SUPPORT_COMPOSITION 0x00008000 + +#endif // if defined(Q_CC_MINGW) + +/* Touch is supported from Windows 7 onwards and data structures + * are present in the Windows SDK's, but not in older MSVC Express + * versions. */ + +#if defined(Q_CC_MINGW) || !defined(TOUCHEVENTF_MOVE) + +#define WM_TOUCH 0x0240 + +typedef struct tagTOUCHINPUT { + LONG x; + LONG y; + HANDLE hSource; + DWORD dwID; + DWORD dwFlags; + DWORD dwMask; + DWORD dwTime; + ULONG_PTR dwExtraInfo; + DWORD cxContact; + DWORD cyContact; +} TOUCHINPUT, *PTOUCHINPUT; +typedef TOUCHINPUT const * PCTOUCHINPUT; + +# define TOUCHEVENTF_MOVE 0x0001 +# define TOUCHEVENTF_DOWN 0x0002 +# define TOUCHEVENTF_UP 0x0004 +# define TOUCHEVENTF_INRANGE 0x0008 +# define TOUCHEVENTF_PRIMARY 0x0010 +# define TOUCHEVENTF_NOCOALESCE 0x0020 +# define TOUCHEVENTF_PALM 0x0080 +# define TOUCHINPUTMASKF_CONTACTAREA 0x0004 +# define TOUCHINPUTMASKF_EXTRAINFO 0x0002 + +#endif // if defined(Q_CC_MINGW) || !defined(TOUCHEVENTF_MOVE) + +#endif // QTWINDOWS_ADDITIONAL_H diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h new file mode 100644 index 0000000000..792792a136 --- /dev/null +++ b/src/plugins/platforms/windows/qtwindowsglobal.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTWINDOWSGLOBAL_H +#define QTWINDOWSGLOBAL_H + +#include "qtwindows_additional.h" +#include + +QT_BEGIN_NAMESPACE + +namespace QtWindows +{ + +enum +{ + WindowEventFlag = 0x10000, + MouseEventFlag = 0x20000, + NonClientEventFlag = 0x40000, + InputMethodEventFlag = 0x80000, + KeyEventFlag = 0x100000, + KeyDownEventFlag = 0x200000, + TouchEventFlag = 0x400000, + ClipboardEventFlag = 0x800000, + ApplicationEventFlag = 0x1000000 +}; + +enum WindowsEventType // Simplify event types +{ + ExposeEvent = WindowEventFlag + 1, + ActivateWindowEvent = WindowEventFlag + 2, + DeactivateWindowEvent = WindowEventFlag + 3, + LeaveEvent = WindowEventFlag + 5, + CloseEvent = WindowEventFlag + 6, + ShowEvent = WindowEventFlag + 7, + HideEvent = WindowEventFlag + 8, + DestroyEvent = WindowEventFlag + 9, + MoveEvent = WindowEventFlag + 10, + ResizeEvent = WindowEventFlag + 12, + QuerySizeHints = WindowEventFlag + 15, + CalculateSize = WindowEventFlag + 16, + MouseEvent = MouseEventFlag + 1, + MouseWheelEvent = MouseEventFlag + 2, + TouchEvent = TouchEventFlag + 1, + NonClientMouseEvent = NonClientEventFlag + MouseEventFlag + 1, + KeyEvent = KeyEventFlag + 1, + KeyDownEvent = KeyEventFlag + KeyDownEventFlag + 1, + InputMethodKeyEvent = InputMethodEventFlag + KeyEventFlag + 1, + InputMethodKeyDownEvent = InputMethodEventFlag + KeyEventFlag + KeyDownEventFlag + 1, + ClipboardEvent = ClipboardEventFlag + 1, + ActivateApplicationEvent = ApplicationEventFlag + 1, + DeactivateApplicationEvent = ApplicationEventFlag + 2, + UnknownEvent = 542 +}; + +} // namespace QtWindows + +inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamIn) +{ + switch (message) { + case WM_PAINT: + case WM_ERASEBKGND: + return QtWindows::ExposeEvent; + case WM_CLOSE: + return QtWindows::CloseEvent; + case WM_DESTROY: + return QtWindows::DestroyEvent; + case WM_ACTIVATEAPP: + return (int)wParamIn ? + QtWindows::ActivateApplicationEvent : QtWindows::DeactivateApplicationEvent; + case WM_ACTIVATE: + return LOWORD(wParamIn) == WA_INACTIVE ? + QtWindows::DeactivateWindowEvent : QtWindows::ActivateWindowEvent; + case WM_MOUSELEAVE: + return QtWindows::MouseEvent; + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + return QtWindows::MouseWheelEvent; + case WM_MOVE: + return QtWindows::MoveEvent; + case WM_SHOWWINDOW: + return wParamIn ? QtWindows::ShowEvent : QtWindows::HideEvent; + case WM_SIZE: + return QtWindows::ResizeEvent; + case WM_NCCALCSIZE: + return QtWindows::CalculateSize; + case WM_GETMINMAXINFO: + return QtWindows::QuerySizeHints; + case WM_KEYDOWN: // keyboard event + case WM_SYSKEYDOWN: + return QtWindows::KeyDownEvent; + case WM_KEYUP: + case WM_SYSKEYUP: + case WM_CHAR: + return QtWindows::KeyEvent; + case WM_IME_CHAR: + return QtWindows::InputMethodKeyEvent; + case WM_IME_KEYDOWN: + return QtWindows::InputMethodKeyDownEvent; + case WM_TOUCH: + return QtWindows::TouchEvent; + case WM_CHANGECBCHAIN: + case WM_DRAWCLIPBOARD: + case WM_RENDERFORMAT: + case WM_RENDERALLFORMATS: + case WM_DESTROYCLIPBOARD: + return QtWindows::ClipboardEvent; + default: + break; + } + if (message >= WM_NCMOUSEMOVE && message <= WM_NCMBUTTONDBLCLK) + return QtWindows::NonClientMouseEvent; // + if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) + || (message >= WM_XBUTTONDOWN && message <= WM_XBUTTONDBLCLK)) + return QtWindows::MouseEvent; + return QtWindows::UnknownEvent; +} + +QT_END_NAMESPACE + +#endif // QTWINDOWSGLOBAL_H diff --git a/src/plugins/platforms/windows/qwindows.qdocconf b/src/plugins/platforms/windows/qwindows.qdocconf new file mode 100644 index 0000000000..c5a1ee904a --- /dev/null +++ b/src/plugins/platforms/windows/qwindows.qdocconf @@ -0,0 +1,27 @@ +project = "Qt Windows Lighthouse Plugin" +description = "Documentation of the Qt Windows Lighthouse Plugin" + +language = Cpp + +headerdirs = . + +sourcedirs = . + +showinternal = true + +headers.fileextensions = "*.h" +sources.fileextensions = "*.cpp *.qdoc" + +outputdir = doc + +qhp.projects = QtLighthouseWindows +qhp.QtLighthouseWindowsDev.file = qtlighthousewindows-dev.qhp +qhp.QtLighthouseWindowsDev.namespace = com.nokia.qt.developer.lighthouse +qhp.QtLighthouseWindowsDev.virtualFolder = doc +qhp.QtLighthouseWindowsDev.indexTitle = Qt Windows Lighthouse Plugin +qhp.QtLighthouseWindowsDev.indexRoot = + +# Doxygen compatibility commands + +macro.see = "\\sa" +macro.function = "\\fn" diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp new file mode 100644 index 0000000000..a3698c4a7c --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsbackingstore.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsbackingstore.h" +#include "qwindowswindow.h" +#include "qwindowsnativeimage.h" +#include "qwindowscontext.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QWindowsBackingStore + \brief Backing store for windows. + \ingroup qt-lighthouse-win +*/ + +QWindowsBackingStore::QWindowsBackingStore(QWindow *window) : + QPlatformBackingStore(window) +{ + if (QWindowsContext::verboseBackingStore) + qDebug() << __FUNCTION__ << this << window; +} + +QWindowsBackingStore::~QWindowsBackingStore() +{ + if (QWindowsContext::verboseBackingStore) + qDebug() << __FUNCTION__ << this; +} + +QPaintDevice *QWindowsBackingStore::paintDevice() +{ + Q_ASSERT(!m_image.isNull()); + return &m_image->image(); +} + +void QWindowsBackingStore::flush(QWindow *window, const QRegion ®ion, + const QPoint &offset) +{ + // TODO: Prepare paint for translucent windows. + const QRect br = region.boundingRect(); + if (QWindowsContext::verboseBackingStore > 1) + qDebug() << __FUNCTION__ << window << offset << br; + QWindowsWindow *rw = rasterWindow(); + const HDC dc = rw->getDC(); + if (!dc) { + qErrnoWarning("%s: GetDC failed", __FUNCTION__); + return; + } + + if (!BitBlt(dc, br.x(), br.y(), br.width(), br.height(), + m_image->hdc(), br.x() + offset.x(), br.y() + offset.y(), SRCCOPY)) + qErrnoWarning("%s: BitBlt failed", __FUNCTION__); + rw->releaseDC(); + // Write image for debug purposes. + if (QWindowsContext::verboseBackingStore > 2) { + static int n = 0; + const QString fileName = QString::fromAscii("win%1_%2.png"). + arg(rw->winId()).arg(n++); + m_image->image().save(fileName); + qDebug() << "Wrote " << m_image->image().size() << fileName; + } +} + +void QWindowsBackingStore::resize(const QSize &size, const QRegion ®ion) +{ + if (m_image.isNull() || m_image->image().size() != size) { + if (QWindowsContext::verboseBackingStore) { + QDebug nsp = qDebug().nospace(); + nsp << __FUNCTION__ << ' ' << rasterWindow()->window() + << ' ' << size << ' ' << region; + if (!m_image.isNull()) + nsp << " from: " << m_image->image().size(); + } + m_image.reset(new QWindowsNativeImage(size.width(), size.height(), + QWindowsNativeImage::systemFormat())); + } +} + +void QWindowsBackingStore::beginPaint(const QRegion ®ion) +{ + Q_UNUSED(region); + if (QWindowsContext::verboseBackingStore > 1) + qDebug() << __FUNCTION__; +} + +QWindowsWindow *QWindowsBackingStore::rasterWindow() const +{ + if (const QWindow *w = window()) + if (QPlatformWindow *pw = w->handle()) + return static_cast(pw); + return 0; +} + +HDC QWindowsBackingStore::getDC() const +{ + if (!m_image.isNull()) + return m_image->hdc(); + return 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.h b/src/plugins/platforms/windows/qwindowsbackingstore.h new file mode 100644 index 0000000000..53f033d14b --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsbackingstore.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSBACKINGSTORE_H +#define QWINDOWSBACKINGSTORE_H + +#include "qtwindows_additional.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QWindowsWindow; +class QWindowsNativeImage; + +class QWindowsBackingStore : public QPlatformBackingStore +{ + Q_DISABLE_COPY(QWindowsBackingStore) +public: + QWindowsBackingStore(QWindow *window); + ~QWindowsBackingStore(); + + virtual QPaintDevice *paintDevice(); + virtual void flush(QWindow *window, const QRegion ®ion, const QPoint &offset); + virtual void resize(const QSize &size, const QRegion &r); + virtual void beginPaint(const QRegion &); + + HDC getDC() const; + +private: + QWindowsWindow *rasterWindow() const; + + QScopedPointer m_image; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSBACKINGSTORE_H diff --git a/src/plugins/platforms/windows/qwindowsclipboard.cpp b/src/plugins/platforms/windows/qwindowsclipboard.cpp new file mode 100644 index 0000000000..93063441ba --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsclipboard.cpp @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsclipboard.h" +#include "qwindowscontext.h" +#include "qwindowsole.h" +#include "qwindowsmime.h" +#include "qwindowsguieventdispatcher.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const char formatTextPlainC[] = "text/plain"; +static const char formatTextHtmlC[] = "text/html"; + +/*! + \class QWindowsClipboard + \brief Clipboard implementation. + + Registers a non-visible clipboard viewer window that + receives clipboard events in its own window procedure to be + able to receive clipboard-changed events, which + QPlatformClipboard needs to emit. That requires housekeeping + of the next in the viewer chain. + + \note The OLE-functions used in this class require OleInitialize(). + + \ingroup qt-lighthouse-win +*/ + +QDebug operator<<(QDebug d, const QMimeData &m) +{ + QDebug nospace = d.nospace(); + const QStringList formats = m.formats(); + nospace << "QMimeData: " << formats.join(QStringLiteral(", ")) << '\n' + << " Text=" << m.hasText() << " HTML=" << m.hasHtml() + << " Color=" << m.hasColor() << " Image=" << m.hasImage() + << " URLs=" << m.hasUrls() << '\n'; + if (m.hasText()) + nospace << " Text: '" << m.text() << "'\n"; + if (m.hasHtml()) + nospace << " HTML: '" << m.html() << "'\n"; + if (m.hasColor()) + nospace << " Color: " << qvariant_cast(m.colorData()) << '\n'; + if (m.hasImage()) + nospace << " Image: " << qvariant_cast(m.imageData()).size() << '\n'; + if (m.hasUrls()) + nospace << " URLs: " << m.urls() << '\n'; + return d; +} + +/*! + \class QWindowsInternalMimeDataBase + \brief Base for implementations of QInternalMimeData using a IDataObject COM object. + + In clipboard handling and Drag and drop, static instances + of QInternalMimeData implementations are kept and passed to the client. + + QInternalMimeData provides virtuals that query the formats and retrieve + mime data on demand when the client invokes functions like QMimeData::hasHtml(), + QMimeData::html() on the instance returned. Otherwise, expensive + construction of a new QMimeData object containing all possible + formats would have to be done in each call to mimeData(). + + The base class introduces new virtuals to obtain and release + the instances IDataObject from the clipboard or Drag and Drop and + does conversion using QWindowsMime classes. + + \sa QInternalMimeData, QWindowsMime, QWindowsMimeConverter + \ingroup qt-lighthouse-win +*/ + +bool QWindowsInternalMimeData::hasFormat_sys(const QString &mime) const +{ + IDataObject *pDataObj = retrieveDataObject(); + if (!pDataObj) + return false; + + const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + const bool has = mc.converterToMime(mime, pDataObj) != 0; + releaseDataObject(pDataObj); + if (QWindowsContext::verboseOLE) + qDebug() << __FUNCTION__ << mime << has; + return has; +} + +QStringList QWindowsInternalMimeData::formats_sys() const +{ + IDataObject *pDataObj = retrieveDataObject(); + if (!pDataObj) + return QStringList(); + + const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + const QStringList fmts = mc.allMimesForFormats(pDataObj); + releaseDataObject(pDataObj); + if (QWindowsContext::verboseOLE) + qDebug() << __FUNCTION__ << fmts; + return fmts; +} + +QVariant QWindowsInternalMimeData::retrieveData_sys(const QString &mimeType, + QVariant::Type type) const +{ + IDataObject *pDataObj = retrieveDataObject(); + if (!pDataObj) + return QVariant(); + + QVariant result; + const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + if (const QWindowsMime *converter = mc.converterToMime(mimeType, pDataObj)) + result = converter->convertToMime(mimeType, pDataObj, type); + releaseDataObject(pDataObj); + if (QWindowsContext::verboseOLE) { + QDebug nospace = qDebug().nospace(); + nospace << __FUNCTION__ << ' ' << mimeType << ' ' << type + << " returns " << result.type(); + if (result.type() != QVariant::ByteArray) + nospace << ' ' << result; + } + return result; +} + +/*! + \class QWindowsClipboardRetrievalMimeData + \brief Special mime data class managing delayed retrieval of clipboard data. + + Implementation of QWindowsInternalMimeDataBase that obtains the + IDataObject from the clipboard. + + \sa QWindowsInternalMimeDataBase, QWindowsClipboard + \ingroup qt-lighthouse-win +*/ + +IDataObject *QWindowsClipboardRetrievalMimeData::retrieveDataObject() const +{ + IDataObject * pDataObj = 0; + if (OleGetClipboard(&pDataObj) == S_OK) + return pDataObj; + return 0; +} + +void QWindowsClipboardRetrievalMimeData::releaseDataObject(IDataObject *dataObject) const +{ + dataObject->Release(); +} + +extern "C" LRESULT QT_WIN_CALLBACK qClipboardViewerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + LRESULT result = 0; + if (QWindowsClipboard::instance() + && QWindowsClipboard::instance()->clipboardViewerWndProc(hwnd, message, wParam, lParam, &result)) + return result; + return DefWindowProc(hwnd, message, wParam, lParam); +} + +QWindowsClipboard *QWindowsClipboard::m_instance = 0; + +QWindowsClipboard::QWindowsClipboard() : + m_data(0), m_clipboardViewer(0), m_nextClipboardViewer(0) +{ + QWindowsClipboard::m_instance = this; +} + +QWindowsClipboard::~QWindowsClipboard() +{ + unregisterViewer(); // Should release data if owner. + releaseIData(); + QWindowsClipboard::m_instance = 0; +} + +void QWindowsClipboard::releaseIData() +{ + if (m_data) { + delete m_data->mimeData(); + m_data->releaseQt(); + m_data->Release(); + m_data = 0; + } +} + +void QWindowsClipboard::registerViewer() +{ + m_clipboardViewer = QWindowsContext::instance()-> + createDummyWindow(QStringLiteral("Qt5ClipboardView"), L"Qt5ClipboardView", + qClipboardViewerWndProc, WS_OVERLAPPED); + m_nextClipboardViewer = SetClipboardViewer(m_clipboardViewer); + + if (QWindowsContext::verboseOLE) + qDebug("%s m_clipboardViewer: %p next=%p", __FUNCTION__, + m_clipboardViewer, m_nextClipboardViewer); +} + +void QWindowsClipboard::unregisterViewer() +{ + if (m_clipboardViewer) { + ChangeClipboardChain(m_clipboardViewer, m_nextClipboardViewer); + DestroyWindow(m_clipboardViewer); + m_clipboardViewer = m_nextClipboardViewer = 0; + } +} + +void QWindowsClipboard::propagateClipboardMessage(UINT message, WPARAM wParam, LPARAM lParam) const +{ + if (!m_nextClipboardViewer) + return; + // In rare cases, a clipboard viewer can hang (application crashed, + // suspended by a shell prompt 'Select' or debugger). + if (QWindowsContext::user32dll.isHungAppWindow + && QWindowsContext::user32dll.isHungAppWindow(m_nextClipboardViewer)) { + qWarning("%s: Cowardly refusing to send clipboard message to hung application...", Q_FUNC_INFO); + return; + } + SendMessage(m_nextClipboardViewer, message, wParam, lParam); +} + +/*! + \brief Windows procedure of the clipboard viewer. Emits changed and does + housekeeping of the viewer chain. +*/ + +bool QWindowsClipboard::clipboardViewerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result) +{ + *result = 0; + if (QWindowsContext::verboseOLE) + qDebug("%s HWND=%p 0x%x %s", __FUNCTION__, hwnd, message, + QWindowsGuiEventDispatcher::windowsMessageName(message)); + + switch (message) { + case WM_CHANGECBCHAIN: { + const HWND toBeRemoved = (HWND)wParam; + if (toBeRemoved == m_nextClipboardViewer) { + m_nextClipboardViewer = (HWND)lParam; + } else { + propagateClipboardMessage(message, wParam, lParam); + } + } + return true; + case WM_DRAWCLIPBOARD: + if (QWindowsContext::verboseOLE) + qDebug("Clipboard changed"); + emitChanged(QClipboard::Clipboard); + // clean up the clipboard object if we no longer own the clipboard + if (!ownsClipboard() && m_data) + releaseIData(); + propagateClipboardMessage(message, wParam, lParam); + return true; + case WM_DESTROY: + // Recommended shutdown + if (ownsClipboard()) { + if (QWindowsContext::verboseOLE) + qDebug("Clipboard owner on shutdown, releasing."); + OleFlushClipboard(); + releaseIData(); + } + return true; + } // switch (message) + return false; +} + +QMimeData *QWindowsClipboard::mimeData(QClipboard::Mode mode) +{ + if (QWindowsContext::verboseOLE) + qDebug() << __FUNCTION__ << mode; + if (mode != QClipboard::Clipboard) + return 0; + return &m_retrievalData; +} + +void QWindowsClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) +{ + if (QWindowsContext::verboseOLE) + qDebug() << __FUNCTION__ << mode << *mimeData; + if (mode != QClipboard::Clipboard) + return; + + const bool newData = !m_data || m_data->mimeData() != mimeData; + if (newData) { + releaseIData(); + m_data = new QWindowsOleDataObject(mimeData); + } + + const HRESULT src = OleSetClipboard(m_data); + if (src != S_OK) { + qErrnoWarning("OleSetClipboard: Failed to set data on clipboard: %s", + QWindowsContext::comErrorString(src).constData()); + releaseIData(); + return; + } +} + +void QWindowsClipboard::clear() +{ + const HRESULT src = OleSetClipboard(0); + if (src != S_OK) + qErrnoWarning("OleSetClipboard: Failed to clear the clipboard: 0x%lx", src); +} + +bool QWindowsClipboard::supportsMode(QClipboard::Mode mode) const +{ + return mode == QClipboard::Clipboard; +} + +// Need a non-virtual in destructor. +bool QWindowsClipboard::ownsClipboard() const +{ + return m_data && OleIsCurrentClipboard(m_data) == S_OK; +} + +bool QWindowsClipboard::ownsMode(QClipboard::Mode mode) const +{ + const bool result = mode == QClipboard::Clipboard ? + ownsClipboard() : false; + if (QWindowsContext::verboseOLE) + qDebug("%s %d returns %d", __FUNCTION__, mode, result); + return result; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsclipboard.h b/src/plugins/platforms/windows/qwindowsclipboard.h new file mode 100644 index 0000000000..fab6871012 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsclipboard.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSCLIPBOARD_H +#define QWINDOWSCLIPBOARD_H + +#include "qwindowsinternalmimedata.h" + +#include + +QT_BEGIN_NAMESPACE + +class QWindowsOleDataObject; + +class QWindowsClipboardRetrievalMimeData : public QWindowsInternalMimeData { +public: + +protected: + virtual IDataObject *retrieveDataObject() const; + virtual void releaseDataObject(IDataObject *) const; +}; + +class QWindowsClipboard : public QPlatformClipboard +{ +public: + QWindowsClipboard(); + ~QWindowsClipboard(); + void registerViewer(); // Call in initialization, when context is up. + + 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; + virtual bool ownsMode(QClipboard::Mode mode) const; + + inline bool clipboardViewerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); + + static QWindowsClipboard *instance() { return m_instance; } + +private: + void clear(); + void releaseIData(); + inline void propagateClipboardMessage(UINT message, WPARAM wParam, LPARAM lParam) const; + inline void unregisterViewer(); + inline bool ownsClipboard() const; + + static QWindowsClipboard *m_instance; + + QWindowsClipboardRetrievalMimeData m_retrievalData; + QWindowsOleDataObject *m_data; + HWND m_clipboardViewer; + HWND m_nextClipboardViewer; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSCLIPBOARD_H diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp new file mode 100644 index 0000000000..d62cbfb4c6 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -0,0 +1,734 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowscontext.h" +#include "qwindowswindow.h" +#include "qwindowskeymapper.h" +#include "qwindowsguieventdispatcher.h" +#include "qwindowsmousehandler.h" +#include "qtwindowsglobal.h" +#include "qwindowsmime.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// Verbosity of components +int QWindowsContext::verboseIntegration = 0; +int QWindowsContext::verboseWindows = 0; +int QWindowsContext::verboseEvents = 0; +int QWindowsContext::verboseBackingStore = 0; +int QWindowsContext::verboseFonts = 0; +int QWindowsContext::verboseGL = 0; +int QWindowsContext::verboseOLE = 0; + +// Get verbosity of components from "foo:2,bar:3" +static inline int componentVerbose(const char *v, const char *keyWord) +{ + if (const char *k = strstr(v, keyWord)) { + k += qstrlen(keyWord); + if (*k == ':') { + ++k; + if (isdigit(*k)) + return *k - '0'; + } + } + return 0; +} + +static inline bool hasTouchSupport(QSysInfo::WinVersion wv) +{ + enum { QT_SM_DIGITIZER = 94, QT_NID_INTEGRATED_TOUCH = 0x1, + QT_NID_EXTERNAL_TOUCH = 0x02, QT_NID_MULTI_INPUT = 0x40 }; + + return wv < QSysInfo::WV_WINDOWS7 ? false : + (GetSystemMetrics(QT_SM_DIGITIZER) & (QT_NID_INTEGRATED_TOUCH | QT_NID_EXTERNAL_TOUCH | QT_NID_MULTI_INPUT)) != 0; +} + +#if !defined(LANG_SYRIAC) +# define LANG_SYRIAC 0x5a +#endif + +static inline bool useRTL_Extensions(QSysInfo::WinVersion ver) +{ + if ((ver & QSysInfo::WV_NT_based) && (ver >= QSysInfo::WV_VISTA)) { + // Since the IsValidLanguageGroup/IsValidLocale functions always return true on + // Vista, check the Keyboard Layouts for enabling RTL. + if (const UINT nLayouts = GetKeyboardLayoutList(0, 0)) { + QScopedArrayPointer lpList(new HKL[nLayouts]); + GetKeyboardLayoutList(nLayouts, lpList.data()); + for (UINT i = 0; i < nLayouts; ++i) { + switch (PRIMARYLANGID((quintptr)lpList[i])) { + case LANG_ARABIC: + case LANG_HEBREW: + case LANG_FARSI: + case LANG_SYRIAC: + return true; + default: + break; + } + } + } + return false; + } // NT/Vista + // Pre-NT: figure out whether a RTL language is installed + return IsValidLanguageGroup(LGRPID_ARABIC, LGRPID_INSTALLED) + || IsValidLanguageGroup(LGRPID_HEBREW, LGRPID_INSTALLED) + || IsValidLocale(MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED) + || IsValidLocale(MAKELCID(MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED) + || IsValidLocale(MAKELCID(MAKELANGID(LANG_SYRIAC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED) + || IsValidLocale(MAKELCID(MAKELANGID(LANG_FARSI, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED); +} + +/*! + \class QWindowsUser32DLL + \brief Struct that contains dynamically resolved symbols of User32.dll. + + The stub libraries shipped with the MinGW compiler miss some of the + functions. They need to be retrieved dynamically. + + In addition, touch-related functions are available only from Windows onwards. + These need to resolved dynamically for Q_CC_MSVC as well. + + \ingroup qt-lighthouse-win +*/ + +QWindowsUser32DLL::QWindowsUser32DLL() : + setLayeredWindowAttributes(0), updateLayeredWindow(0), + updateLayeredWindowIndirect(0), + isHungAppWindow(0), + registerTouchWindow(0), getTouchInputInfo(0), closeTouchInputHandle(0) +{ +} + +void QWindowsUser32DLL::init() +{ + QSystemLibrary library(QStringLiteral("user32")); + // MinGW (g++ 3.4.5) accepts only C casts. + setLayeredWindowAttributes = (SetLayeredWindowAttributes)(library.resolve("SetLayeredWindowAttributes")); + updateLayeredWindow = (UpdateLayeredWindow)(library.resolve("UpdateLayeredWindow")); + updateLayeredWindowIndirect = (UpdateLayeredWindowIndirect)(library.resolve("UpdateLayeredWindowIndirect")); + + Q_ASSERT(setLayeredWindowAttributes && updateLayeredWindow + && updateLayeredWindowIndirect); + + isHungAppWindow = (IsHungAppWindow)library.resolve("IsHungAppWindow"); +} + +bool QWindowsUser32DLL::initTouch() +{ + QSystemLibrary library(QStringLiteral("user32")); + registerTouchWindow = (RegisterTouchWindow)(library.resolve("RegisterTouchWindow")); + getTouchInputInfo = (GetTouchInputInfo)(library.resolve("GetTouchInputInfo")); + closeTouchInputHandle = (CloseTouchInputHandle)(library.resolve("CloseTouchInputHandle")); + return registerTouchWindow && getTouchInputInfo && getTouchInputInfo; +} + +QWindowsUser32DLL QWindowsContext::user32dll; + +QWindowsContext *QWindowsContext::m_instance = 0; + +/*! + \class QWindowsContext + \brief Singleton container for all relevant information. + + Holds state information formerly stored in \c qapplication_win.cpp. + \ingroup qt-lighthouse-win +*/ + +typedef QHash HandleBaseWindowHash; + +struct QWindowsContextPrivate { + explicit QWindowsContextPrivate(bool isOpenGL); + + const bool m_isOpenGL; + unsigned m_systemInfo; + QSet m_registeredWindowClassNames; + HandleBaseWindowHash m_windows; + HDC m_displayContext; + const int m_defaultDPI; + QWindowsKeyMapper m_keyMapper; + QWindowsMouseHandler m_mouseHandler; + QWindowsMimeConverter m_mimeConverter; + QSharedPointer m_creationContext; + const HRESULT m_oleInitializeResult; +}; + +QWindowsContextPrivate::QWindowsContextPrivate(bool isOpenGL) : + m_isOpenGL(isOpenGL), + m_systemInfo(0), + m_displayContext(GetDC(0)), + m_defaultDPI(GetDeviceCaps(m_displayContext,LOGPIXELSY)), + m_oleInitializeResult(OleInitialize(NULL)) +{ + QWindowsContext::user32dll.init(); + + const QSysInfo::WinVersion ver = QSysInfo::windowsVersion(); + + if (hasTouchSupport(ver) && QWindowsContext::user32dll.initTouch()) + m_systemInfo |= QWindowsContext::SI_SupportsTouch; + + if (useRTL_Extensions(ver)) { + m_systemInfo |= QWindowsContext::SI_RTL_Extensions; + m_keyMapper.setUseRTLExtensions(true); + } +} + +QWindowsContext::QWindowsContext(bool isOpenGL) : + d(new QWindowsContextPrivate(isOpenGL)) +{ +#ifdef Q_CC_MSVC +# pragma warning( disable : 4996 ) +#endif + m_instance = this; + if (const char *v = getenv("QT_LIGHTHOUSE_WINDOWS_VERBOSE")) { + QWindowsContext::verboseIntegration = componentVerbose(v, "integration"); + QWindowsContext::verboseWindows = componentVerbose(v, "windows"); + QWindowsContext::verboseEvents = componentVerbose(v, "events"); + QWindowsContext::verboseBackingStore = componentVerbose(v, "backingstore"); + QWindowsContext::verboseFonts = componentVerbose(v, "fonts"); + QWindowsContext::verboseGL = componentVerbose(v, "gl"); + QWindowsContext::verboseOLE = componentVerbose(v, "ole"); + } +} + +QWindowsContext::~QWindowsContext() +{ + unregisterWindowClasses(); + if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) + OleUninitialize(); + + m_instance = 0; +} + +QWindowsContext *QWindowsContext::instance() +{ + return m_instance; +} + +unsigned QWindowsContext::systemInfo() const +{ + return d->m_systemInfo; +} + +void QWindowsContext::setWindowCreationContext(const QSharedPointer &ctx) +{ + d->m_creationContext = ctx; +} + +bool QWindowsContext::isOpenGL() const +{ + return d->m_isOpenGL; +} + +int QWindowsContext::defaultDPI() const +{ + return d->m_defaultDPI; +} + +HDC QWindowsContext::displayContext() const +{ + return d->m_displayContext; +} + +QWindow *QWindowsContext::keyGrabber() const +{ + return d->m_keyMapper.keyGrabber(); +} + +void QWindowsContext::setKeyGrabber(QWindow *w) +{ + d->m_keyMapper.setKeyGrabber(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->windowFlags() : (Qt::WindowFlags)0; + const Qt::WindowFlags type = flags & Qt::WindowType_Mask; + + uint style = 0; + bool icon = false; + QString cname = "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; + icon = false; + } else { + cname += QStringLiteral("QWindow"); + style = CS_DBLCLKS; + icon = true; + } + + // force CS_OWNDC when the GL graphics system is + // used as the default renderer + if (d->m_isOpenGL) + style |= CS_OWNDC; + + HBRUSH brush = 0; + if (w && !isGL) + brush = GetSysColorBrush(COLOR_WINDOW); + return registerWindowClass(cname, qWindowsWndProc, style, brush, icon); +} + +QString QWindowsContext::registerWindowClass(QString cname, + WNDPROC proc, + unsigned style, + HBRUSH brush, + bool icon) +{ + // since multiple Qt versions can be used in one process + // each one has to have window class names with a unique name + // The first instance gets the unmodified name; if the class + // has already been registered by another instance of Qt then + // add an instance-specific ID, the address of the window proc. + static int classExists = -1; + + const HINSTANCE appInstance = (HINSTANCE)GetModuleHandle(0); + if (classExists == -1) { + WNDCLASS wcinfo; + classExists = GetClassInfo(appInstance, (wchar_t*)cname.utf16(), &wcinfo); + classExists = classExists && wcinfo.lpfnWndProc != proc; + } + + if (classExists) + cname += QString::number((quintptr)proc); + + if (d->m_registeredWindowClassNames.contains(cname)) // already registered in our list + return cname; + + WNDCLASSEX wc; + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = style; + wc.lpfnWndProc = proc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = appInstance; + if (icon) { + wc.hIcon = (HICON)LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); + if (wc.hIcon) { + int sw = GetSystemMetrics(SM_CXSMICON); + int sh = GetSystemMetrics(SM_CYSMICON); + wc.hIconSm = (HICON)LoadImage(appInstance, L"IDI_ICON1", IMAGE_ICON, sw, sh, 0); + } else { + wc.hIcon = (HICON)LoadImage(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + wc.hIconSm = 0; + } + } else { + wc.hIcon = 0; + wc.hIconSm = 0; + } + wc.hCursor = 0; + wc.hbrBackground = brush; + wc.lpszMenuName = 0; + wc.lpszClassName = (wchar_t*)cname.utf16(); + ATOM atom = RegisterClassEx(&wc); + + if (!atom) + qErrnoWarning("QApplication::regClass: Registering window class '%s' failed.", + qPrintable(cname)); + + d->m_registeredWindowClassNames.insert(cname); + if (QWindowsContext::verboseIntegration || QWindowsContext::verboseWindows) + qDebug().nospace() << __FUNCTION__ << ' ' << cname + << " style=0x" << QString::number(style, 16) + << " brush=" << brush << " icon=" << icon << " atom=" << atom; + return cname; +} + +void QWindowsContext::unregisterWindowClasses() +{ + const HINSTANCE appInstance = (HINSTANCE)GetModuleHandle(0); + + foreach (const QString &name, d->m_registeredWindowClassNames) { + if (QWindowsContext::verboseIntegration) + qDebug() << __FUNCTION__ << name; + UnregisterClass((wchar_t*)name.utf16(), appInstance); + } + d->m_registeredWindowClassNames.clear(); +} + +int QWindowsContext::screenDepth() const +{ + return GetDeviceCaps(d->m_displayContext, BITSPIXEL); +} + +QString QWindowsContext::windowsErrorMessage(unsigned long errorCode) +{ + QString rc = QString::fromLatin1("#%1: ").arg(errorCode); + ushort *lpMsgBuf; + + const int len = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorCode, 0, (LPTSTR)&lpMsgBuf, 0, NULL); + if (len) { + rc = QString::fromUtf16(lpMsgBuf, len); + LocalFree(lpMsgBuf); + } else { + rc += QString::fromLatin1(""); + } + return rc; +} + +void QWindowsContext::addWindow(HWND hwnd, QWindowsWindow *w) +{ + d->m_windows.insert(hwnd, w); +} + +void QWindowsContext::removeWindow(HWND hwnd) +{ + const HandleBaseWindowHash::iterator it = d->m_windows.find(hwnd); + if (it != d->m_windows.end()) { + if (d->m_keyMapper.keyGrabber() == it.value()->window()) + d->m_keyMapper.setKeyGrabber(0); + d->m_windows.erase(it); + } +} + +QWindowsWindow *QWindowsContext::findPlatformWindow(HWND hwnd) const +{ + return d->m_windows.value(hwnd); +} + +QWindow *QWindowsContext::findWindow(HWND hwnd) const +{ + if (const QWindowsWindow *bw = findPlatformWindow(hwnd)) + return bw->window(); + return 0; +} + +QWindow *QWindowsContext::windowUnderMouse() const +{ + return d->m_mouseHandler.windowUnderMouse(); +} + +/*! + \brief Find a child window at a screen point. + + Deep search for a QWindow at global point, skipping non-owned + windows (accessibility?). Implemented using ChildWindowFromPointEx() + instead of (historically used) WindowFromPoint() to get a well-defined + behaviour for hidden/transparent windows. + + \a cwex_flags are flags of ChildWindowFromPointEx(). + \a parent is the parent window, pass GetDesktopWindow() for top levels. +*/ + +QWindowsWindow *QWindowsContext::findPlatformWindowAt(HWND parent, + const QPoint &screenPointIn, + unsigned cwex_flags) const +{ + QWindowsWindow *result = 0; + const POINT screenPoint = { screenPointIn.x(), screenPointIn.y() }; + while (true) { + POINT point = screenPoint; + ScreenToClient(parent, &point); + // Returns parent if inside & none matched. + const HWND child = ChildWindowFromPointEx(parent, point, cwex_flags); + if (child && child != parent) { + if (QWindowsWindow *window = findPlatformWindow(child)) + result = window; + parent = child; + } else { + break; + } + } + return result; +} + +QWindowsMimeConverter &QWindowsContext::mimeConverter() const +{ + return d->m_mimeConverter; +} + +/*! + \brief Convenience to create a non-visible dummy window + for example used as clipboard watcher or for GL. +*/ + +HWND QWindowsContext::createDummyWindow(const QString &classNameIn, + const wchar_t *windowName, + WNDPROC wndProc, DWORD style) +{ + if (!wndProc) + wndProc = DefWindowProc; + QString className = registerWindowClass(classNameIn, wndProc); + return CreateWindowEx(0, (wchar_t*)className.utf16(), + windowName, style, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + 0, NULL, (HINSTANCE)GetModuleHandle(0), NULL); +} + +/*! + \brief Common COM error strings. +*/ + +QByteArray QWindowsContext::comErrorString(HRESULT hr) +{ + switch (hr) { + case S_OK: + return QByteArray("S_OK"); + case S_FALSE: + return QByteArray("S_FALSE"); + case E_UNEXPECTED: + return QByteArray("E_UNEXPECTED"); + case CO_E_ALREADYINITIALIZED: + return QByteArray("CO_E_ALREADYINITIALIZED"); + case CO_E_NOTINITIALIZED: + return QByteArray("CO_E_NOTINITIALIZED"); + case RPC_E_CHANGED_MODE: + return QByteArray("RPC_E_CHANGED_MODE"); + case OLE_E_WRONGCOMPOBJ: + return QByteArray("OLE_E_WRONGCOMPOBJ"); + case CO_E_NOT_SUPPORTED: + return QByteArray("CO_E_NOT_SUPPORTED"); + case E_NOTIMPL: + return QByteArray("E_NOTIMPL"); + case E_INVALIDARG: + return QByteArray(""); + case E_NOINTERFACE: + return QByteArray(""); + case E_POINTER: + return QByteArray(""); + case E_HANDLE: + return QByteArray(""); + case E_ABORT: + return QByteArray(""); + case E_FAIL: + return QByteArray(""); + case E_ACCESSDENIED: + return QByteArray(""); + default: + break; + } + return "Unknown error 0x" + QByteArray::number(quint64(hr), 16); +} + +/*! + \brief Main windows procedure registered for windows. + + \sa QWindowsGuiEventDispatcher +*/ + +bool QWindowsContext::windowsProc(HWND hwnd, UINT message, + QtWindows::WindowsEventType et, + WPARAM wParam, LPARAM lParam, LRESULT *result) +{ + *result = 0; + // Events without an associated QWindow or events we are not interested in. + switch (et) { + case QtWindows::DeactivateApplicationEvent: + case QtWindows::DeactivateWindowEvent: + QWindowSystemInterface::handleWindowActivated(0); + return true; + case QtWindows::ClipboardEvent: + case QtWindows::DestroyEvent: + case QtWindows::UnknownEvent: + return false; + default: + break; + } + + QWindowsWindow *platformWindow = findPlatformWindow(hwnd); + // Before CreateWindowEx() returns, some events are sent, + // for example WM_GETMINMAXINFO asking for size constraints for top levels. + // Pass on to current creation context + if (!platformWindow && !d->m_creationContext.isNull()) { + switch (et) { + case QtWindows::QuerySizeHints: + d->m_creationContext->applyToMinMaxInfo(reinterpret_cast(lParam)); + return true; + case QtWindows::ResizeEvent: + d->m_creationContext->obtainedGeometry.setSize(QSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + return true; + case QtWindows::MoveEvent: + d->m_creationContext->obtainedGeometry.moveTo(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + return true; + case QtWindows::CalculateSize: + return false; + default: + break; + } + } + if (platformWindow) { + if (QWindowsContext::verboseEvents > 1) + qDebug().nospace() << "Event window: " << platformWindow->window(); + } else { + qWarning("%s: No Qt Window found for event 0x%x (%s), hwnd=0x%p.", + __FUNCTION__, message, + QWindowsGuiEventDispatcher::windowsMessageName(message), hwnd); + return false; + } + + MSG msg; + msg.hwnd = hwnd; // re-create MSG structure + msg.message = message; // time and pt fields ignored + msg.wParam = wParam; + msg.lParam = lParam; + msg.pt.x = GET_X_LPARAM(lParam); + msg.pt.y = GET_Y_LPARAM(lParam); + + switch (et) { + case QtWindows::KeyDownEvent: + case QtWindows::KeyEvent: + case QtWindows::InputMethodKeyEvent: + case QtWindows::InputMethodKeyDownEvent: + return d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result); + case QtWindows::MoveEvent: + platformWindow->handleMoved(); + return true; + case QtWindows::ResizeEvent: + platformWindow->handleResized((int)wParam); + return true; + case QtWindows::QuerySizeHints: + platformWindow->getSizeHints(reinterpret_cast(lParam)); + return true; + case QtWindows::CalculateSize: + // NCCALCSIZE_PARAMS structure if wParam==TRUE + if (wParam && QWindowsContext::verboseWindows) { + const NCCALCSIZE_PARAMS *ncp = reinterpret_cast(lParam); + qDebug() << platformWindow->window() << *ncp; + } + break; + case QtWindows::ExposeEvent: + platformWindow->handleWmPaint(hwnd, message, wParam, lParam); + return true; + case QtWindows::MouseWheelEvent: + case QtWindows::MouseEvent: + case QtWindows::NonClientMouseEvent: + case QtWindows::LeaveEvent: + return d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); + case QtWindows::TouchEvent: + return d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result); + case QtWindows::ActivateWindowEvent: + QWindowSystemInterface::handleWindowActivated(platformWindow->window()); + return true; + case QtWindows::ShowEvent: + platformWindow->handleShown(); + return true; + case QtWindows::HideEvent: + platformWindow->handleHidden(); + return true; + case QtWindows::CloseEvent: + QWindowSystemInterface::handleCloseEvent(platformWindow->window()); + return true; + default: + break; + } + return false; +} + +/*! + \brief Windows functions for actual windows. + + There is another one for timers, sockets, etc in + QEventDispatcherWin32. + + \ingroup qt-lighthouse-win +*/ + +extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + LRESULT result; + const QtWindows::WindowsEventType et = windowsEventType(message, wParam); + const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result); + const bool guiEventsQueued = QWindowSystemInterface::windowSystemEventsQueued(); + if (QWindowsContext::verboseEvents > 1) + if (const char *eventName = QWindowsGuiEventDispatcher::windowsMessageName(message)) + qDebug("EVENT: hwd=%p %s msg=0x%x et=0x%x wp=%d at %d,%d handled=%d gui=%d", + hwnd, eventName, message, et, int(wParam), + GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), handled, guiEventsQueued); + if (guiEventsQueued) { + const QWindowsGuiEventDispatcher::DispatchContext dispatchContext = + QWindowsGuiEventDispatcher::currentDispatchContext(); + if (dispatchContext.first) + QWindowSystemInterface::sendWindowSystemEvents(dispatchContext.first, dispatchContext.second); + } + if (!handled) + result = DefWindowProc(hwnd, message, wParam, lParam); + return result; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h new file mode 100644 index 0000000000..8985c7f0f9 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSCONTEXT_H +#define QWINDOWSCONTEXT_H + +#include "qtwindowsglobal.h" +#include "qtwindows_additional.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QWindow; +class QPlatformScreen; +class QWindowsWindow; +class QWindowsMimeConverter; +struct QWindowCreationContext; +struct QWindowsContextPrivate; +class QPoint; + +struct QWindowsUser32DLL +{ + QWindowsUser32DLL(); + inline void init(); + inline bool initTouch(); + + typedef BOOL (WINAPI *RegisterTouchWindow)(HWND, ULONG); + typedef BOOL (WINAPI *GetTouchInputInfo)(HANDLE, UINT, PVOID, int); + typedef BOOL (WINAPI *CloseTouchInputHandle)(HANDLE); + typedef BOOL (WINAPI *SetLayeredWindowAttributes)(HWND, COLORREF, BYTE, DWORD); + typedef BOOL (WINAPI *UpdateLayeredWindow)(HWND, HDC , const POINT *, + const SIZE *, HDC, const POINT *, COLORREF, + const BLENDFUNCTION *, DWORD); + typedef BOOL (WINAPI *UpdateLayeredWindowIndirect)(HWND, const UPDATELAYEREDWINDOWINFO *); + typedef BOOL (WINAPI *IsHungAppWindow)(HWND); + + // Functions missing in Q_CC_GNU stub libraries. + SetLayeredWindowAttributes setLayeredWindowAttributes; + UpdateLayeredWindow updateLayeredWindow; + UpdateLayeredWindowIndirect updateLayeredWindowIndirect; + + // Functions missing in older versions of Windows + IsHungAppWindow isHungAppWindow; + + // Touch functions from Windows 7 onwards (also for use with Q_CC_MSVC). + RegisterTouchWindow registerTouchWindow; + GetTouchInputInfo getTouchInputInfo; + CloseTouchInputHandle closeTouchInputHandle; +}; + +class QWindowsContext +{ + Q_DISABLE_COPY(QWindowsContext) +public: + enum SystemInfoFlags + { + SI_RTL_Extensions = 0x1, + SI_SupportsTouch = 0x2 + }; + + // Verbose flag set by environment variable QT_LIGHTHOUSE_WINDOWS_VERBOSE + static int verboseIntegration; + static int verboseWindows; + static int verboseBackingStore; + static int verboseEvents; + static int verboseFonts; + static int verboseGL; + static int verboseOLE; + + explicit QWindowsContext(bool isOpenGL); + ~QWindowsContext(); + + bool isOpenGL() const; + + int defaultDPI() const; + + QString registerWindowClass(const QWindow *w, bool isGL); + QString registerWindowClass(QString cname, WNDPROC proc, + unsigned style = 0, HBRUSH brush = 0, + bool icon = false); + HWND createDummyWindow(const QString &classNameIn, + const wchar_t *windowName, + WNDPROC wndProc = 0, DWORD style = WS_OVERLAPPED); + + HDC displayContext() const; + int screenDepth() const; + + static QWindowsContext *instance(); + + static QString windowsErrorMessage(unsigned long errorCode); + + void addWindow(HWND, QWindowsWindow *w); + void removeWindow(HWND); + + QWindowsWindow *findPlatformWindow(HWND) const; + QWindow *findWindow(HWND) const; + QWindowsWindow *findPlatformWindowAt(HWND parent, const QPoint &screenPoint, + unsigned cwex_flags) const; + + QWindow *windowUnderMouse() const; + + inline bool windowsProc(HWND hwnd, UINT message, + QtWindows::WindowsEventType et, + WPARAM wParam, LPARAM lParam, LRESULT *result); + + QWindow *keyGrabber() const; + void setKeyGrabber(QWindow *hwnd); + + void setWindowCreationContext(const QSharedPointer &ctx); + + // Returns a combination of SystemInfoFlags + unsigned systemInfo() const; + + QWindowsMimeConverter &mimeConverter() const; + + static QWindowsUser32DLL user32dll; + + static QByteArray comErrorString(HRESULT hr); + +private: + void unregisterWindowClasses(); + + QScopedPointer d; + static QWindowsContext *m_instance; +}; + +extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND, UINT, WPARAM, LPARAM); + +QT_END_NAMESPACE + +#endif // QWINDOWSCONTEXT_H diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp new file mode 100644 index 0000000000..1ad2079962 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowscursor.cpp @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowscursor.h" +#include "qwindowscontext.h" +#include "qwindowswindow.h" +#include "qwindowsscreen.h" +#include "pixmaputils.h" + +#include +#include +#include +#include +#include +#include // getPixmapCursor() + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QWindowsCursor + \brief Platform cursor implementation + + Note that whereas under X11, a cursor can be set as a property of + a window, there is only a global SetCursor() function on Windows. + Each Window sets on the global cursor on receiving a Enter-event + as do the Window manager frames (resize/move handles). + + \ingroup qt-lighthouse-win + \sa QWindowsWindowCursor +*/ + +QWindowsCursor::QWindowsCursor(QPlatformScreen *s) : + QPlatformCursor(s) +{ +} + +HCURSOR QWindowsCursor::createPixmapCursor(const QPixmap &pixmap, int hotX, int hotY) +{ + HCURSOR cur = 0; + QBitmap mask = pixmap.mask(); + if (mask.isNull()) { + mask = QBitmap(pixmap.size()); + mask.fill(Qt::color1); + } + + HBITMAP ic = qPixmapToWinHBITMAP(pixmap, HBitmapAlpha); + const HBITMAP im = createIconMask(mask); + + ICONINFO ii; + ii.fIcon = 0; + ii.xHotspot = hotX; + ii.yHotspot = hotY; + ii.hbmMask = im; + ii.hbmColor = ic; + + cur = CreateIconIndirect(&ii); + + DeleteObject(ic); + DeleteObject(im); + return cur; +} + +HCURSOR QWindowsCursor::createSystemCursor(const QCursor &c) +{ + int hx = c.hotSpot().x(); + int hy = c.hotSpot().y(); + const Qt::CursorShape cshape = c.shape(); + if (cshape == Qt::BitmapCursor) { + const QPixmap pixmap = c.pixmap(); + if (!pixmap.isNull()) + if (const HCURSOR hc = createPixmapCursor(pixmap, hx, hy)) + return hc; + } + + // Non-standard Windows cursors are created from bitmaps + + static const uchar vsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar vsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar hsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03, + 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar hsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00, + 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07, + 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00, + 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static const uchar phand_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, + 0x80, 0x04, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, + 0x80, 0x1c, 0x00, 0x00, 0x80, 0xe4, 0x00, 0x00, 0x80, 0x24, 0x03, 0x00, + 0x80, 0x24, 0x05, 0x00, 0xb8, 0x24, 0x09, 0x00, 0xc8, 0x00, 0x09, 0x00, + 0x88, 0x00, 0x08, 0x00, 0x90, 0x00, 0x08, 0x00, 0xa0, 0x00, 0x08, 0x00, + 0x20, 0x00, 0x08, 0x00, 0x40, 0x00, 0x08, 0x00, 0x40, 0x00, 0x04, 0x00, + 0x80, 0x00, 0x04, 0x00, 0x80, 0x00, 0x04, 0x00, 0x00, 0x01, 0x02, 0x00, + 0x00, 0x01, 0x02, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + static const uchar phandm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, + 0x80, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, + 0x80, 0x1f, 0x00, 0x00, 0x80, 0xff, 0x00, 0x00, 0x80, 0xff, 0x03, 0x00, + 0x80, 0xff, 0x07, 0x00, 0xb8, 0xff, 0x0f, 0x00, 0xf8, 0xff, 0x0f, 0x00, + 0xf8, 0xff, 0x0f, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0xe0, 0xff, 0x0f, 0x00, + 0xe0, 0xff, 0x0f, 0x00, 0xc0, 0xff, 0x0f, 0x00, 0xc0, 0xff, 0x07, 0x00, + 0x80, 0xff, 0x07, 0x00, 0x80, 0xff, 0x07, 0x00, 0x00, 0xff, 0x03, 0x00, + 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + static const uchar openhand_bits[] = { + 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92, + 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20, + 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00}; + static const uchar openhandm_bits[] = { + 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff, + 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f, + 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00}; + static const uchar closedhand_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50, + 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10, + 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00}; + static const uchar closedhandm_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f, + 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f, + 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00}; + + static const uchar * const cursor_bits32[] = { + vsplit_bits, vsplitm_bits, hsplit_bits, hsplitm_bits, + phand_bits, phandm_bits + }; + + wchar_t *sh = 0; + switch (c.shape()) { // map to windows cursor + case Qt::ArrowCursor: + sh = IDC_ARROW; + break; + case Qt::UpArrowCursor: + sh = IDC_UPARROW; + break; + case Qt::CrossCursor: + sh = IDC_CROSS; + break; + case Qt::WaitCursor: + sh = IDC_WAIT; + break; + case Qt::IBeamCursor: + sh = IDC_IBEAM; + break; + case Qt::SizeVerCursor: + sh = IDC_SIZENS; + break; + case Qt::SizeHorCursor: + sh = IDC_SIZEWE; + break; + case Qt::SizeBDiagCursor: + sh = IDC_SIZENESW; + break; + case Qt::SizeFDiagCursor: + sh = IDC_SIZENWSE; + break; + case Qt::SizeAllCursor: + sh = IDC_SIZEALL; + break; + case Qt::ForbiddenCursor: + sh = IDC_NO; + break; + case Qt::WhatsThisCursor: + sh = IDC_HELP; + break; + case Qt::BusyCursor: + sh = IDC_APPSTARTING; + break; + case Qt::PointingHandCursor: + sh = IDC_HAND; + break; + case Qt::BlankCursor: + case Qt::SplitVCursor: + case Qt::SplitHCursor: + case Qt::OpenHandCursor: + case Qt::ClosedHandCursor: + case Qt::BitmapCursor: { + QImage bbits, mbits; + bool invb, invm; + if (cshape == Qt::BlankCursor) { + bbits = QImage(32, 32, QImage::Format_Mono); + bbits.fill(0); // ignore color table + mbits = bbits.copy(); + hx = hy = 16; + invb = invm = false; + } else if (cshape == Qt::OpenHandCursor || cshape == Qt::ClosedHandCursor) { + bool open = cshape == Qt::OpenHandCursor; + QBitmap cb = QBitmap::fromData(QSize(16, 16), open ? openhand_bits : closedhand_bits); + QBitmap cm = QBitmap::fromData(QSize(16, 16), open ? openhandm_bits : closedhandm_bits); + bbits = cb.toImage().convertToFormat(QImage::Format_Mono); + mbits = cm.toImage().convertToFormat(QImage::Format_Mono); + hx = hy = 8; + invb = invm = false; + } else if (cshape != Qt::BitmapCursor) { + int i = cshape - Qt::SplitVCursor; + QBitmap cb = QBitmap::fromData(QSize(32, 32), cursor_bits32[i * 2]); + QBitmap cm = QBitmap::fromData(QSize(32, 32), cursor_bits32[i * 2 + 1]); + bbits = cb.toImage().convertToFormat(QImage::Format_Mono); + mbits = cm.toImage().convertToFormat(QImage::Format_Mono); + if (cshape == Qt::PointingHandCursor) { + hx = 7; + hy = 0; + } else + hx = hy = 16; + invb = invm = false; + } else { + bbits = c.bitmap()->toImage().convertToFormat(QImage::Format_Mono); + mbits = c.mask()->toImage().convertToFormat(QImage::Format_Mono); + invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1)); + invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1)); + } + const int n = qMax(1, bbits.width() / 8); + const int h = bbits.height(); + QScopedArrayPointer xBits(new uchar[h * n]); + QScopedArrayPointer xMask(new uchar[h * n]); + int x = 0; + for (int i = 0; i < h; ++i) { + uchar *bits = bbits.scanLine(i); + uchar *mask = mbits.scanLine(i); + for (int j = 0; j < n; ++j) { + uchar b = bits[j]; + uchar m = mask[j]; + if (invb) + b ^= 0xff; + if (invm) + m ^= 0xff; + xBits[x] = ~m; + xMask[x] = b ^ m; + ++x; + } + } + return CreateCursor(GetModuleHandle(0), hx, hy, bbits.width(), bbits.height(), + xBits.data(), xMask.data()); + } + case Qt::DragCopyCursor: + case Qt::DragMoveCursor: + case Qt::DragLinkCursor: { + const QPixmap pixmap = QGuiApplicationPrivate::instance()->getPixmapCursor(cshape); + return createPixmapCursor(pixmap, hx, hy); + } + default: + qWarning("%s: Invalid cursor shape %d", __FUNCTION__, cshape); + return 0; + } + return (HCURSOR)LoadImage(0, sh, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); +} + +/*! + \brief Return cached standard cursor resources or create new ones. +*/ + +QWindowsWindowCursor QWindowsCursor::standardWindowCursor(Qt::CursorShape shape) +{ + StandardCursorCache::iterator it = m_standardCursorCache.find(shape); + if (it == m_standardCursorCache.end()) + it = m_standardCursorCache.insert(shape, QWindowsWindowCursor(QCursor(shape))); + return it.value(); +} + +/*! + \brief Set a cursor on a window. + + This is called frequently as the mouse moves over widgets in the window + (QLineEdits, etc). +*/ + +void QWindowsCursor::changeCursor(QCursor *cursorIn, QWindow *window) +{ + + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << cursorIn << window; + if (!cursorIn || !window) + return; + const QWindowsWindowCursor wcursor = + cursorIn->shape() == Qt::BitmapCursor ? + QWindowsWindowCursor(*cursorIn) : standardWindowCursor(cursorIn->shape()); + if (wcursor.handle()) { + QWindowsWindow::baseWindowOf(window)->setCursor(wcursor); + } else { + qWarning("%s: Unable to obtain system cursor for %d", + __FUNCTION__, cursorIn->shape()); + } +} + +QPoint QWindowsCursor::mousePosition() +{ + POINT p; + GetCursorPos(&p); + if (QWindowsContext::verboseWindows) + qDebug("%s %ld,%ld", __FUNCTION__, p.x, p.y); + return QPoint(p.x, p.y); +} + +void QWindowsCursor::setPos(const QPoint &pos) +{ + if (QWindowsContext::verboseWindows) + qDebug("%s %d,%d", __FUNCTION__, pos.x(), pos.y()); + SetCursorPos(pos.x(), pos.y()); +} + +/*! + \class QWindowsWindowCursor + \brief Per-Window cursor. Contains a QCursor and manages its associated system + cursor handle resource. + + Based on QSharedDataPointer, so that it can be passed around and + used as a property of QWindowsBaseWindow. + + \ingroup qt-lighthouse-win + \sa QWindowsCursor +*/ + +class QWindowsWindowCursorData : public QSharedData +{ +public: + explicit QWindowsWindowCursorData(const QCursor &c); + ~QWindowsWindowCursorData(); + + const QCursor m_cursor; + const HCURSOR m_handle; +}; + +QWindowsWindowCursorData::QWindowsWindowCursorData(const QCursor &c) : + m_cursor(c), + m_handle(QWindowsCursor::createSystemCursor(c)) +{ +} + +QWindowsWindowCursorData::~QWindowsWindowCursorData() +{ + DestroyCursor(m_handle); +} + +QWindowsWindowCursor::QWindowsWindowCursor(const QCursor &c) : + m_data(new QWindowsWindowCursorData(c)) +{ +} + +QWindowsWindowCursor::~QWindowsWindowCursor() +{ +} + +QWindowsWindowCursor::QWindowsWindowCursor(const QWindowsWindowCursor &rhs) : + m_data(rhs.m_data) +{ +} + +QWindowsWindowCursor & QWindowsWindowCursor::operator =(const QWindowsWindowCursor &rhs) +{ + if (this != &rhs) + m_data.operator =(rhs.m_data); + return *this; +} + +QCursor QWindowsWindowCursor::cursor() const +{ + return m_data->m_cursor; +} + +HCURSOR QWindowsWindowCursor::handle() const +{ + return m_data->m_handle; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowscursor.h b/src/plugins/platforms/windows/qwindowscursor.h new file mode 100644 index 0000000000..bf8cb837d9 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowscursor.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSCURSOR_H +#define QWINDOWSCURSOR_H + +#include "qtwindows_additional.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QWindowsWindowCursorData; + +class QWindowsWindowCursor +{ +public: + explicit QWindowsWindowCursor(const QCursor &c); + ~QWindowsWindowCursor(); + QWindowsWindowCursor(const QWindowsWindowCursor &c); + QWindowsWindowCursor &operator=(const QWindowsWindowCursor &c); + + QCursor cursor() const; + HCURSOR handle() const; + +private: + QSharedDataPointer m_data; +}; + +class QWindowsCursor : public QPlatformCursor +{ +public: + explicit QWindowsCursor(QPlatformScreen *); + + virtual void changeCursor(QCursor * widgetCursor, QWindow * widget); + virtual QPoint pos() const { return mousePosition(); } + virtual void setPos(const QPoint &pos); + + static HCURSOR createPixmapCursor(const QPixmap &pixmap, int hotX, int hotY); + static HCURSOR createSystemCursor(const QCursor &c); + static QPoint mousePosition(); + + QWindowsWindowCursor standardWindowCursor(Qt::CursorShape s = Qt::ArrowCursor); + +private: + typedef QHash StandardCursorCache; + + StandardCursorCache m_standardCursorCache; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSCURSOR_H diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp new file mode 100644 index 0000000000..1535437a32 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -0,0 +1,721 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsdrag.h" +#include "qwindowscontext.h" +#include "qwindowsclipboard.h" +#include "qwindowsintegration.h" +#include "qwindowsole.h" +#include "qtwindows_additional.h" +#include "qwindowswindow.h" +#include "qwindowsmousehandler.h" +#include "qwindowscursor.h" + +#include +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QWindowsDropMimeData + \brief Special mime data class for data retrieval from Drag operations. + + Implementation of QWindowsInternalMimeDataBase which retrieves the + current drop data object from QWindowsDrag. + + \sa QWindowsDrag + \ingroup qt-lighthouse-win +*/ + +IDataObject *QWindowsDropMimeData::retrieveDataObject() const +{ + return QWindowsDrag::instance()->dropDataObject(); +} + +static inline Qt::DropActions translateToQDragDropActions(DWORD pdwEffects) +{ + Qt::DropActions actions = Qt::IgnoreAction; + if (pdwEffects & DROPEFFECT_LINK) + actions |= Qt::LinkAction; + if (pdwEffects & DROPEFFECT_COPY) + actions |= Qt::CopyAction; + if (pdwEffects & DROPEFFECT_MOVE) + actions |= Qt::MoveAction; + return actions; +} + +static inline Qt::DropAction translateToQDragDropAction(DWORD pdwEffect) +{ + if (pdwEffect & DROPEFFECT_LINK) + return Qt::LinkAction; + if (pdwEffect & DROPEFFECT_COPY) + return Qt::CopyAction; + if (pdwEffect & DROPEFFECT_MOVE) + return Qt::MoveAction; + return Qt::IgnoreAction; +} + +static inline DWORD translateToWinDragEffects(Qt::DropActions action) +{ + DWORD effect = DROPEFFECT_NONE; + if (action & Qt::LinkAction) + effect |= DROPEFFECT_LINK; + if (action & Qt::CopyAction) + effect |= DROPEFFECT_COPY; + if (action & Qt::MoveAction) + effect |= DROPEFFECT_MOVE; + return effect; +} + +static inline Qt::KeyboardModifiers toQtKeyboardModifiers(DWORD keyState) +{ + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + if (keyState & MK_SHIFT) + modifiers |= Qt::ShiftModifier; + if (keyState & MK_CONTROL) + modifiers |= Qt::ControlModifier; + if (keyState & MK_ALT) + modifiers |= Qt::AltModifier; + + return modifiers; +} + +/*! + \class QWindowsOleDropSource + \brief Implementation of IDropSource + + Used for drag operations. + + \sa QWindowsDrag + \ingroup qt-lighthouse-win +*/ + +class QWindowsOleDropSource : public IDropSource +{ +public: + QWindowsOleDropSource(); + virtual ~QWindowsOleDropSource(); + + void createCursors(); + + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj); + STDMETHOD_(ULONG,AddRef)(void); + STDMETHOD_(ULONG,Release)(void); + + // IDropSource methods + STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState); + STDMETHOD(GiveFeedback)(DWORD dwEffect); + +private: + typedef QMap ActionCursorMap; + + inline void clearCursors(); + + Qt::MouseButtons m_currentButtons; + Qt::DropAction m_currentAction; + ActionCursorMap m_cursors; + + ULONG m_refs; +}; + +QWindowsOleDropSource::QWindowsOleDropSource() : + m_currentButtons(Qt::NoButton), m_currentAction(Qt::IgnoreAction), + m_refs(1) +{ + if (QWindowsContext::verboseOLE) + qDebug("%s", __FUNCTION__); +} + +QWindowsOleDropSource::~QWindowsOleDropSource() +{ + clearCursors(); + if (QWindowsContext::verboseOLE) + qDebug("%s", __FUNCTION__); +} + +void QWindowsOleDropSource::createCursors() +{ + QDragManager *manager = QDragManager::self(); + if (!manager || !manager->object) + return; + const QPixmap pixmap = manager->object->pixmap(); + const bool hasPixmap = !pixmap.isNull(); + if (!hasPixmap && manager->dragPrivate()->customCursors.isEmpty()) + return; + + QList actions; + actions << Qt::MoveAction << Qt::CopyAction << Qt::LinkAction; + if (hasPixmap) + actions << Qt::IgnoreAction; + const QPoint hotSpot = manager->object->hotSpot(); + for (int cnum = 0; cnum < actions.size(); ++cnum) { + const QPixmap cpm = manager->dragCursor(actions.at(cnum)); + int w = cpm.width(); + int h = cpm.height(); + + if (hasPixmap) { + const int x1 = qMin(-hotSpot.x(), 0); + const int x2 = qMax(pixmap.width() - hotSpot.x(), cpm.width()); + const int y1 = qMin(-hotSpot.y(), 0); + const int y2 = qMax(pixmap.height() - hotSpot.y(), cpm.height()); + + w = x2 - x1 + 1; + h = y2 - y1 + 1; + } + + const QRect srcRect = pixmap.rect(); + const QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y())); + const QPoint newHotSpot = hotSpot; + QPixmap newCursor(w, h); + if (hasPixmap) { + newCursor.fill(QColor(0, 0, 0, 0)); + QPainter p(&newCursor); + p.drawPixmap(pmDest, pixmap, srcRect); + p.drawPixmap(qMax(0,newHotSpot.x()),qMax(0,newHotSpot.y()),cpm); + } else { + newCursor = cpm; + } + + const int hotX = hasPixmap ? qMax(0,newHotSpot.x()) : 0; + const int hotY = hasPixmap ? qMax(0,newHotSpot.y()) : 0; + + if (const HCURSOR sysCursor = QWindowsCursor::createPixmapCursor(newCursor, hotX, hotY)) + m_cursors.insert(actions.at(cnum), sysCursor); + } + if (QWindowsContext::verboseOLE) + qDebug("%s %d cursors", __FUNCTION__, m_cursors.size()); +} + +void QWindowsOleDropSource::clearCursors() +{ + if (!m_cursors.isEmpty()) { + const ActionCursorMap::const_iterator cend = m_cursors.constEnd(); + for (ActionCursorMap::const_iterator it = m_cursors.constBegin(); it != cend; ++it) + DestroyCursor(it.value()); + m_cursors.clear(); + } +} + +//--------------------------------------------------------------------- +// IUnknown Methods +//--------------------------------------------------------------------- + +STDMETHODIMP +QWindowsOleDropSource::QueryInterface(REFIID iid, void FAR* FAR* ppv) +{ + if (iid == IID_IUnknown || iid == IID_IDropSource) { + *ppv = this; + ++m_refs; + return NOERROR; + } + *ppv = NULL; + return ResultFromScode(E_NOINTERFACE); +} + +STDMETHODIMP_(ULONG) +QWindowsOleDropSource::AddRef(void) +{ + return ++m_refs; +} + +STDMETHODIMP_(ULONG) +QWindowsOleDropSource::Release(void) +{ + if (--m_refs == 0) { + delete this; + return 0; + } + return m_refs; +} + +/*! + \brief Check for cancel. +*/ + +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) +{ + HRESULT hr = S_OK; + do { + if (fEscapePressed || QWindowsDrag::instance()->dragBeingCancelled()) { + hr = ResultFromScode(DRAGDROP_S_CANCEL); + break; + } + + // grfKeyState is broken on CE & some Windows XP versions, + // therefore we need to check the state manually + if ((GetAsyncKeyState(VK_LBUTTON) == 0) + && (GetAsyncKeyState(VK_MBUTTON) == 0) + && (GetAsyncKeyState(VK_RBUTTON) == 0)) { + hr = ResultFromScode(DRAGDROP_S_DROP); + break; + } + + const Qt::MouseButtons buttons = QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState); + if (m_currentButtons == Qt::NoButton) { + m_currentButtons = buttons; + } else { + // Button changed: Complete Drop operation. + if (!(m_currentButtons & buttons)) { + hr = ResultFromScode(DRAGDROP_S_DROP); + break; + } + } + + QGuiApplication::processEvents(); + + } while (false); + + QDragManager::self()->willDrop = hr == DRAGDROP_S_DROP; + + if (QWindowsContext::verboseOLE + && (QWindowsContext::verboseOLE > 1 || hr != S_OK)) + qDebug("%s fEscapePressed=%d, grfKeyState=%lu buttons=%d willDrop = %d returns 0x%x", + __FUNCTION__, fEscapePressed,grfKeyState, int(m_currentButtons), + QDragManager::self()->willDrop, int(hr)); + return hr; +} + +/*! + \brief Give feedback: Change cursor accoding to action. +*/ + +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QWindowsOleDropSource::GiveFeedback(DWORD dwEffect) +{ + const Qt::DropAction action = translateToQDragDropAction(dwEffect); + + if (QWindowsContext::verboseOLE > 2) + qDebug("%s dwEffect=%lu, action=%d", __FUNCTION__, dwEffect, action); + + if (m_currentAction != action) { + m_currentAction = action; + QDragManager::self()->emitActionChanged(m_currentAction); + } + + const ActionCursorMap::const_iterator it = m_cursors.constFind(m_currentAction); + if (it != m_cursors.constEnd()) { + SetCursor(it.value()); + return ResultFromScode(S_OK); + } + + return ResultFromScode(DRAGDROP_S_USEDEFAULTCURSORS); +} + +/*! + \class QWindowsOleDropTarget + \brief Implementation of IDropTarget + + To be registered for each window. Currently, drop sites + are enabled for top levels. The child window handling + (sending DragEnter/Leave, etc) is handled in here. + + \sa QWindowsDrag + \ingroup qt-lighthouse-win +*/ + +QWindowsOleDropTarget::QWindowsOleDropTarget(QWindow *w) : + m_refs(1), m_window(w), m_currentWindow(0), m_chosenEffect(0), m_lastKeyState(0) +{ + if (QWindowsContext::verboseOLE) + qDebug() << __FUNCTION__ << this << w; +} + +QWindowsOleDropTarget::~QWindowsOleDropTarget() +{ + if (QWindowsContext::verboseOLE) + qDebug("%s %p", __FUNCTION__, this); +} + +STDMETHODIMP +QWindowsOleDropTarget::QueryInterface(REFIID iid, void FAR* FAR* ppv) +{ + if (iid == IID_IUnknown || iid == IID_IDropTarget) { + *ppv = this; + AddRef(); + return NOERROR; + } + *ppv = NULL; + return ResultFromScode(E_NOINTERFACE); +} + +STDMETHODIMP_(ULONG) +QWindowsOleDropTarget::AddRef(void) +{ + return ++m_refs; +} + +STDMETHODIMP_(ULONG) +QWindowsOleDropTarget::Release(void) +{ + if (--m_refs == 0) { + delete this; + return 0; + } + return m_refs; +} + +QWindow *QWindowsOleDropTarget::findDragOverWindow(const POINTL &pt) const +{ + if (QWindowsWindow *child = + QWindowsWindow::baseWindowOf(m_window)->childAtScreenPoint(QPoint(pt.x, pt.y))) + return child->window(); + return m_window; +} + +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QWindowsOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, + POINTL pt, LPDWORD pdwEffect) +{ + if (QWindowsContext::verboseOLE) + qDebug("%s widget=%p key=%lu, pt=%ld,%ld", __FUNCTION__, m_window, grfKeyState, pt.x, pt.y); + + QWindowsDrag::instance()->setDropDataObject(pDataObj); + pDataObj->AddRef(); + m_currentWindow = m_window; + sendDragEnterEvent(m_window, grfKeyState, pt, pdwEffect); + *pdwEffect = m_chosenEffect; + return NOERROR; +} + +void QWindowsOleDropTarget::sendDragEnterEvent(QWindow *dragEnterWidget, + DWORD grfKeyState, + POINTL pt, LPDWORD pdwEffect) +{ + Q_ASSERT(dragEnterWidget); + + m_lastPoint = QWindowsGeometryHint::mapFromGlobal(dragEnterWidget, QPoint(pt.x,pt.y)); + m_lastKeyState = grfKeyState; + + m_chosenEffect = DROPEFFECT_NONE; + + QDragManager *manager = QDragManager::self(); + QMimeData *md = manager->dropData(); + const Qt::MouseButtons mouseButtons + = QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState); + const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect); + const Qt::KeyboardModifiers keyMods = toQtKeyboardModifiers(grfKeyState); + QDragEnterEvent enterEvent(m_lastPoint, actions, md, mouseButtons, keyMods); + QGuiApplication::sendEvent(m_currentWindow, &enterEvent); + m_answerRect = enterEvent.answerRect(); + if (QWindowsContext::verboseOLE) + qDebug() << __FUNCTION__ << " sent drag enter to " << m_window + << *md << " actions=" << actions + << " mods=" << keyMods << " accepted: " + << enterEvent.isAccepted(); + + if (enterEvent.isAccepted()) + m_chosenEffect = translateToWinDragEffects(enterEvent.dropAction()); + // Documentation states that a drag move event is sent immediately after + // a drag enter event. This will honor widgets overriding dragMoveEvent only: + if (enterEvent.isAccepted()) { + QDragMoveEvent moveEvent(m_lastPoint, actions, md, mouseButtons, keyMods); + m_answerRect = enterEvent.answerRect(); + moveEvent.setDropAction(enterEvent.dropAction()); + moveEvent.accept(); // accept by default, since enter event was accepted. + + QGuiApplication::sendEvent(dragEnterWidget, &moveEvent); + if (moveEvent.isAccepted()) { + m_answerRect = moveEvent.answerRect(); + m_chosenEffect = translateToWinDragEffects(moveEvent.dropAction()); + } else { + m_chosenEffect = DROPEFFECT_NONE; + } + } +} + +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QWindowsOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) +{ + QWindow *dragOverWindow = findDragOverWindow(pt); + + const QPoint tmpPoint = QWindowsGeometryHint::mapFromGlobal(dragOverWindow, QPoint(pt.x,pt.y)); + // see if we should compress this event + if ((tmpPoint == m_lastPoint || m_answerRect.contains(tmpPoint)) + && m_lastKeyState == grfKeyState) { + *pdwEffect = m_chosenEffect; + return NOERROR; + } + + if (QWindowsContext::verboseOLE > 1) + qDebug().nospace() << '>' << __FUNCTION__ << ' ' << m_window << " current " + << dragOverWindow << " key=" << grfKeyState + << " pt=" < dragOverWindowGuard(dragOverWindow); + // Send drag leave event to the previous drag widget. + // Drag-Over widget might be deleted in DragLeave, + // (tasktracker 218353). + QDragLeaveEvent dragLeave; + if (m_currentWindow) + QGuiApplication::sendEvent(m_currentWindow, &dragLeave); + if (!dragOverWindowGuard) { + dragOverWindow = findDragOverWindow(pt); + } + // Send drag enter event to the current drag widget. + m_currentWindow = dragOverWindow; + sendDragEnterEvent(dragOverWindow, grfKeyState, pt, pdwEffect); + } + + QDragManager *manager = QDragManager::self(); + QMimeData *md = manager->dropData(); + + const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect); + + QDragMoveEvent oldEvent(m_lastPoint, actions, md, + QWindowsMouseHandler::keyStateToMouseButtons(m_lastKeyState), + toQtKeyboardModifiers(m_lastKeyState)); + + m_lastPoint = tmpPoint; + m_lastKeyState = grfKeyState; + + QDragMoveEvent e(tmpPoint, actions, md, + QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState), + toQtKeyboardModifiers(grfKeyState)); + if (m_chosenEffect != DROPEFFECT_NONE) { + if (oldEvent.dropAction() == e.dropAction() && + oldEvent.keyboardModifiers() == e.keyboardModifiers()) + e.setDropAction(translateToQDragDropAction(m_chosenEffect)); + e.accept(); + } + QGuiApplication::sendEvent(dragOverWindow, &e); + + m_answerRect = e.answerRect(); + if (e.isAccepted()) + m_chosenEffect = translateToWinDragEffects(e.dropAction()); + else + m_chosenEffect = DROPEFFECT_NONE; + *pdwEffect = m_chosenEffect; + + if (QWindowsContext::verboseOLE > 1) + qDebug("<%s effect=0x%lx", __FUNCTION__, m_chosenEffect); + return NOERROR; +} + +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QWindowsOleDropTarget::DragLeave() +{ + if (QWindowsContext::verboseOLE) + qDebug().nospace() <<__FUNCTION__ << ' ' << m_window; + + m_currentWindow = 0; + QDragLeaveEvent e; + QGuiApplication::sendEvent(m_window, &e); + QWindowsDrag::instance()->releaseDropDataObject(); + + return NOERROR; +} + +#define KEY_STATE_BUTTON_MASK (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) + +QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP +QWindowsOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, DWORD grfKeyState, + POINTL pt, LPDWORD pdwEffect) +{ + QWindow *dropWindow = findDragOverWindow(pt); + + if (QWindowsContext::verboseOLE) + qDebug().nospace() << __FUNCTION__ << ' ' << m_window + << " on " << dropWindow + << " keys=" << grfKeyState << " pt=" + << pt.x << ',' << pt.y; + + m_lastPoint = QWindowsGeometryHint::mapFromGlobal(dropWindow, QPoint(pt.x,pt.y)); + // grfKeyState does not all ways contain button state in the drop so if + // it doesn't then use the last known button state; + if ((grfKeyState & KEY_STATE_BUTTON_MASK) == 0) + grfKeyState |= m_lastKeyState & KEY_STATE_BUTTON_MASK; + m_lastKeyState = grfKeyState; + + QWindowsDrag *windowsDrag = QWindowsDrag::instance(); + QDragManager *manager = QDragManager::self(); + QMimeData *md = manager->dropData(); + QDropEvent e(m_lastPoint, translateToQDragDropActions(*pdwEffect), md, + QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState), + toQtKeyboardModifiers(grfKeyState)); + if (m_chosenEffect != DROPEFFECT_NONE) + e.setDropAction(translateToQDragDropAction(m_chosenEffect)); + + QGuiApplication::sendEvent(dropWindow, &e); + if (m_chosenEffect != DROPEFFECT_NONE) + e.accept(); + + if (e.isAccepted()) { + if (e.dropAction() == Qt::MoveAction || e.dropAction() == Qt::TargetMoveAction) { + if (e.dropAction() == Qt::MoveAction) + m_chosenEffect = DROPEFFECT_MOVE; + else + m_chosenEffect = DROPEFFECT_COPY; + HGLOBAL hData = GlobalAlloc(0, sizeof(DWORD)); + if (hData) { + DWORD *moveEffect = (DWORD *)GlobalLock(hData);; + *moveEffect = DROPEFFECT_MOVE; + GlobalUnlock(hData); + STGMEDIUM medium; + memset(&medium, 0, sizeof(STGMEDIUM)); + medium.tymed = TYMED_HGLOBAL; + medium.hGlobal = hData; + FORMATETC format; + format.cfFormat = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT); + format.tymed = TYMED_HGLOBAL; + format.ptd = 0; + format.dwAspect = 1; + format.lindex = -1; + windowsDrag->dropDataObject()->SetData(&format, &medium, true); + } + } else { + m_chosenEffect = translateToWinDragEffects(e.dropAction()); + } + } else { + m_chosenEffect = DROPEFFECT_NONE; + } + *pdwEffect = m_chosenEffect; + + windowsDrag->releaseDropDataObject(); + return NOERROR; +} + +/*! + \class QWindowsDrag + \brief Windows drag implementation. + + \ingroup qt-lighthouse-win +*/ + +QWindowsDrag::QWindowsDrag() : m_dropDataObject(0), m_dragBeingCancelled(false) +{ +} + +QWindowsDrag::~QWindowsDrag() +{ +} + +void QWindowsDrag::startDrag() +{ + // TODO: Accessibility handling? + QDragManager *dragManager = QDragManager::self(); + QMimeData *dropData = dragManager->dropData(); + m_dragBeingCancelled = false; + + DWORD resultEffect; + QWindowsOleDropSource *windowDropSource = new QWindowsOleDropSource(); + windowDropSource->createCursors(); + QWindowsOleDataObject *dropDataObject = new QWindowsOleDataObject(dropData); + const Qt::DropActions possibleActions = dragManager->possible_actions; + const DWORD allowedEffects = translateToWinDragEffects(possibleActions); + if (QWindowsContext::verboseOLE) + qDebug(">%s possible Actions=%x, effects=0x%lx", __FUNCTION__, + int(possibleActions), allowedEffects); + const HRESULT r = DoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect); + const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect(); + Qt::DropAction ret = Qt::IgnoreAction; + if (r == DRAGDROP_S_DROP) { + if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) { + ret = Qt::TargetMoveAction; + resultEffect = DROPEFFECT_MOVE; + } else { + ret = translateToQDragDropAction(resultEffect); + } + // Force it to be a copy if an unsupported operation occurred. + // This indicates a bug in the drop target. + if (resultEffect != DROPEFFECT_NONE && !(resultEffect & allowedEffects)) + ret = Qt::CopyAction; + } else { + dragManager->setCurrentTarget(0); + } + + // clean up + dropDataObject->releaseQt(); + dropDataObject->Release(); // Will delete obj if refcount becomes 0 + windowDropSource->Release(); // Will delete src if refcount becomes 0 + if (QWindowsContext::verboseOLE) + qDebug("<%s allowedEffects=0x%lx, reportedPerformedEffect=0x%lx, resultEffect=0x%lx, hr=0x%x, dropAction=%d", + __FUNCTION__, allowedEffects, reportedPerformedEffect, resultEffect, int(r), ret); +} + +void QWindowsDrag::move(const QMouseEvent *me) +{ + const QPoint pos = me->pos(); + if (QWindowsContext::verboseOLE) + qDebug("%s %d %d", __FUNCTION__, pos.x(), pos.y()); +} + +void QWindowsDrag::drop(const QMouseEvent *me) +{ + const QPoint pos = me->pos(); + if (QWindowsContext::verboseOLE) + qDebug("%s %d %d", __FUNCTION__, pos.x(), pos.y()); +} + +void QWindowsDrag::cancel() +{ + // TODO: Accessibility handling? + if (QWindowsContext::verboseOLE) + qDebug("%s", __FUNCTION__); + m_dragBeingCancelled = true; +} + +QWindowsDrag *QWindowsDrag::instance() +{ + return static_cast(QWindowsIntegration::instance()->drag()); +} + +void QWindowsDrag::releaseDropDataObject() +{ + if (QWindowsContext::verboseOLE) + qDebug("%s %p", __FUNCTION__, m_dropDataObject); + if (m_dropDataObject) { + m_dropDataObject->Release(); + m_dropDataObject = 0; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsdrag.h b/src/plugins/platforms/windows/qwindowsdrag.h new file mode 100644 index 0000000000..6e6ebe0424 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsdrag.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSDRAG_H +#define QWINDOWSDRAG_H + +#include "qwindowsinternalmimedata.h" + +#include + +QT_BEGIN_NAMESPACE + +class QWindowsDropMimeData : public QWindowsInternalMimeData { +public: + QWindowsDropMimeData() {} + virtual IDataObject *retrieveDataObject() const; +}; + +class QWindowsOleDropTarget : public IDropTarget +{ +public: + explicit QWindowsOleDropTarget(QWindow *w); + virtual ~QWindowsOleDropTarget(); + + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + + // IDropTarget methods + STDMETHOD(DragEnter)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect); + STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect); + STDMETHOD(DragLeave)(); + STDMETHOD(Drop)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect); + +private: + inline QWindow *findDragOverWindow(const POINTL &pt) const; + void sendDragEnterEvent(QWindow *to, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect); + + ULONG m_refs; + QWindow *const m_window; + QWindow *m_currentWindow; + QRect m_answerRect; + QPoint m_lastPoint; + DWORD m_chosenEffect; + DWORD m_lastKeyState; +}; + +class QWindowsDrag : public QPlatformDrag +{ +public: + QWindowsDrag(); + virtual ~QWindowsDrag(); + + virtual QMimeData *platformDropData() { return &m_dropData; } + + virtual void startDrag(); + virtual void move(const QMouseEvent *me); + virtual void drop(const QMouseEvent *me); + virtual void cancel(); + + static QWindowsDrag *instance(); + + IDataObject *dropDataObject() const { return m_dropDataObject; } + void setDropDataObject(IDataObject *dataObject) { m_dropDataObject = dataObject; } + void releaseDropDataObject(); + + bool dragBeingCancelled() const { return m_dragBeingCancelled; } + +private: + QWindowsDropMimeData m_dropData; + IDataObject *m_dropDataObject; + bool m_dragBeingCancelled; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSDRAG_H diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp new file mode 100644 index 0000000000..ea35ab5f9f --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp @@ -0,0 +1,950 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsfontdatabase.h" +#include "qwindowscontext.h" +#include "qwindowsfontengine.h" +#include "qwindowsfontenginedirectwrite.h" +#include "qtwindows_additional.h" + +#include +#include + +#include +#include + +#if !defined(QT_NO_DIRECTWRITE) +# include +# include +#endif + +QT_BEGIN_NAMESPACE + +/*! + \struct QWindowsFontEngineData + \brief Static constant data shared by the font engines. + \ingroup qt-lighthouse-win +*/ + +QWindowsFontEngineData::QWindowsFontEngineData() +#if !defined(QT_NO_DIRECTWRITE) + : directWriteFactory(0) + , directWriteGdiInterop(0) +#endif +{ + // from qapplication_win.cpp + UINT result = 0; + if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0)) + clearTypeEnabled = (result == FE_FONTSMOOTHINGCLEARTYPE); + + int winSmooth; + if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0)) { + fontSmoothingGamma = winSmooth / qreal(1000.0); + } else { + fontSmoothingGamma = 1.0; + } + + // Safeguard ourselves against corrupt registry values... + if (fontSmoothingGamma > 5 || fontSmoothingGamma < 1) + fontSmoothingGamma = qreal(1.4); + + const qreal gray_gamma = 2.31; + for (int i=0; i<256; ++i) + pow_gamma[i] = uint(qRound(qPow(i / qreal(255.), gray_gamma) * 2047)); + + HDC displayDC = GetDC(0); + hdc = CreateCompatibleDC(displayDC); + ReleaseDC(0, displayDC); +} + +QWindowsFontEngineData::~QWindowsFontEngineData() +{ + if (hdc) + ReleaseDC(0, hdc); +#if !defined(QT_NO_DIRECTWRITE) + if (directWriteGdiInterop) + directWriteGdiInterop->Release(); + if (directWriteFactory) + directWriteFactory->Release(); +#endif +} + +#if !defined(QT_NO_DIRECTWRITE) +static inline bool initDirectWrite(QWindowsFontEngineData *d) +{ + if (!d->directWriteFactory) { + const HRESULT hr = DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast(&d->directWriteFactory) + ); + if (FAILED(hr)) { + qErrnoWarning("%s: DWriteCreateFactory failed", __FUNCTION__); + return false; + } + } + if (!d->directWriteGdiInterop) { + const HRESULT hr = d->directWriteFactory->GetGdiInterop(&d->directWriteGdiInterop); + if (FAILED(hr)) { + qErrnoWarning("%s: GetGdiInterop failed", __FUNCTION__); + return false; + } + } + return true; +} + +#endif // !defined(QT_NO_DIRECTWRITE) + +/*! + \class QWindowsFontDatabase + \brief Font database for Windows + + \note The Qt 4.8 WIndows font database employed a mechanism of + delayed population of the database again passing a font name + to EnumFontFamiliesEx(), working around the fact that + EnumFontFamiliesEx() does not list all fonts by default. + This should be introduced to Lighthouse as well? + + \ingroup qt-lighthouse-win +*/ + +QDebug operator<<(QDebug d, const QFontDef &def) +{ + d.nospace() << "Family=" << def.family << " Stylename=" << def.styleName + << " pointsize=" << def.pointSize << " pixelsize=" << def.pixelSize + << " styleHint=" << def.styleHint << " weight=" << def.weight + << " stretch=" << def.stretch << " hintingPreference=" + << def.hintingPreference << ' '; + 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) +{ + if (weight < 400) + return QFont::Light; + if (weight < 600) + return QFont::Normal; + if (weight < 700) + return QFont::DemiBold; + if (weight < 800) + return QFont::Bold; + return QFont::Black; +} + +static inline QFontDatabase::WritingSystem writingSystemFromScript(const QString &scriptName) +{ + if (scriptName == QStringLiteral("Western") + || scriptName == QStringLiteral("Baltic") + || scriptName == QStringLiteral("Central European") + || scriptName == QStringLiteral("Turkish") + || scriptName == QStringLiteral("Vietnamese") + || scriptName == QStringLiteral("OEM/Dos")) + return QFontDatabase::Latin; + if (scriptName == QStringLiteral("Thai")) + return QFontDatabase::Thai; + if (scriptName == QStringLiteral("Symbol") + || scriptName == QStringLiteral("Other")) + return QFontDatabase::Symbol; + if (scriptName == QStringLiteral("CHINESE_GB2312")) + return QFontDatabase::SimplifiedChinese; + if (scriptName == QStringLiteral("CHINESE_BIG5")) + return QFontDatabase::TraditionalChinese; + if (scriptName == QStringLiteral("Cyrillic")) + return QFontDatabase::Cyrillic; + if (scriptName == QStringLiteral("Hangul")) + return QFontDatabase::Korean; + if (scriptName == QStringLiteral("Hebrew")) + return QFontDatabase::Hebrew; + if (scriptName == QStringLiteral("Greek")) + return QFontDatabase::Greek; + if (scriptName == QStringLiteral("Japanese")) + return QFontDatabase::Japanese; + if (scriptName == QStringLiteral("Arabic")) + return QFontDatabase::Arabic; + return QFontDatabase::Any; +} + +static bool addFontToDatabase(QString familyName, const QString &scriptName, + const TEXTMETRIC *textmetric, + const FONTSIGNATURE *signature, + int type) +{ + // the "@family" fonts are just the same as "family". Ignore them. + if (familyName.at(0) == QLatin1Char('@') || familyName.startsWith(QStringLiteral("WST_"))) + return false; + + static const int SMOOTH_SCALABLE = 0xffff; + const QString foundryName; // No such concept. + const NEWTEXTMETRIC *tm = (NEWTEXTMETRIC *)textmetric; + const bool fixed = !(tm->tmPitchAndFamily & TMPF_FIXED_PITCH); + const bool ttf = (tm->tmPitchAndFamily & TMPF_TRUETYPE); + const bool scalable = tm->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE); + const int size = scalable ? SMOOTH_SCALABLE : tm->tmHeight; + const QFont::Style style = tm->tmItalic ? QFont::StyleItalic : QFont::StyleNormal; + const bool antialias = false; + const QFont::Weight weight = weightFromInteger(tm->tmWeight); + const QFont::Stretch stretch = QFont::Unstretched; + + Q_UNUSED(fixed) + + if (QWindowsContext::verboseFonts > 2) { + QDebug nospace = qDebug().nospace(); + nospace << __FUNCTION__ << familyName << scriptName + << "TTF=" << ttf; + if (type & DEVICE_FONTTYPE) + nospace << " DEVICE"; + if (type & RASTER_FONTTYPE) + nospace << " RASTER"; + if (type & TRUETYPE_FONTTYPE) + nospace << " TRUETYPE"; + nospace << " scalable=" << scalable << " Size=" << size + << " Style=" << style << " Weight=" << weight + << " stretch=" << stretch; + } + +/* Fixme: omitted for the moment + if(ttf && localizedName(familyName) && family->english_name.isEmpty()) + family->english_name = getEnglishName(familyName); +*/ + QSupportedWritingSystems writingSystems; + if (type & TRUETYPE_FONTTYPE) { + quint32 unicodeRange[4] = { + signature->fsUsb[0], signature->fsUsb[1], + signature->fsUsb[2], signature->fsUsb[3] + }; + quint32 codePageRange[2] = { + signature->fsCsb[0], signature->fsCsb[1] + }; + writingSystemsFromTrueTypeBits(unicodeRange, codePageRange, &writingSystems); + // ### 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 + // display Thai text by default. As a temporary work around, we special case Segoe UI + // and remove the Thai script from its list of supported writing systems. + if (writingSystems.supported(QFontDatabase::Thai) && + familyName == QStringLiteral("Segoe UI")) + writingSystems.setSupported(QFontDatabase::Thai, false); + } else { + const QFontDatabase::WritingSystem ws = writingSystemFromScript(scriptName); + if (ws != QFontDatabase::Any) + writingSystems.setSupported(ws); + } + + QPlatformFontDatabase::registerFont(familyName, foundryName, weight, + style, stretch, antialias, scalable, size, writingSystems, 0); + // add fonts windows can generate for us: + if (weight <= QFont::DemiBold) + QPlatformFontDatabase::registerFont(familyName, foundryName, QFont::Bold, + style, stretch, antialias, scalable, size, writingSystems, 0); + if (style != QFont::StyleItalic) + QPlatformFontDatabase::registerFont(familyName, foundryName, weight, + QFont::StyleItalic, stretch, antialias, scalable, size, writingSystems, 0); + if (weight <= QFont::DemiBold && style != QFont::StyleItalic) + QPlatformFontDatabase::registerFont(familyName, foundryName, QFont::Bold, + QFont::StyleItalic, stretch, antialias, scalable, size, writingSystems, 0); + return true; +} + +static int CALLBACK storeFont(ENUMLOGFONTEX* f, NEWTEXTMETRICEX *textmetric, + int type, LPARAM namesSetIn) +{ + typedef QSet StringSet; + const QString familyName = QString::fromWCharArray(f->elfLogFont.lfFaceName); + const QString script = QString::fromWCharArray(f->elfScript); + + const FONTSIGNATURE signature = textmetric->ntmFontSig; + + // NEWTEXTMETRICEX is a NEWTEXTMETRIC, which according to the documentation is + // identical to a TEXTMETRIC except for the last four members, which we don't use + // anyway + if (addFontToDatabase(familyName, script, (TEXTMETRIC *)textmetric, &signature, type)) + reinterpret_cast(namesSetIn)->insert(familyName); + + // keep on enumerating + return 1; +} + +void QWindowsFontDatabase::populateFontDatabase() +{ + if (m_families.isEmpty()) { + QPlatformFontDatabase::populateFontDatabase(); + populate(); // Called multiple times. + // Work around EnumFontFamiliesEx() not listing the system font, see below. + const QString sysFontFamily = QGuiApplication::font().family(); + if (!m_families.contains(sysFontFamily)) + populate(sysFontFamily); + } +} + +/*! + \brief Populate font database using EnumFontFamiliesEx(). + + Normally, leaving the name empty should enumerate + all fonts, however, system fonts like "MS Shell Dlg 2" + are only found when specifying the name explicitly. +*/ + +void QWindowsFontDatabase::populate(const QString &family) + { + + if (QWindowsContext::verboseFonts) + qDebug() << __FUNCTION__ << m_families.size() << family; + + HDC dummy = GetDC(0); + LOGFONT lf; + lf.lfCharSet = DEFAULT_CHARSET; + if (family.size() >= LF_FACESIZE) { + qWarning("%s: Unable to enumerate family '%s'.", + __FUNCTION__, qPrintable(family)); + return; + } + wmemcpy(lf.lfFaceName, reinterpret_cast(family.utf16()), + family.size() + 1); + lf.lfPitchAndFamily = 0; + EnumFontFamiliesEx(dummy, &lf, (FONTENUMPROC)storeFont, + (LPARAM)&m_families, 0); + ReleaseDC(0, dummy); +} + +QWindowsFontDatabase::QWindowsFontDatabase() : + m_fontEngineData(new QWindowsFontEngineData) +{ + if (QWindowsContext::verboseFonts) + qDebug() << __FUNCTION__ << "Clear type: " + << m_fontEngineData->clearTypeEnabled << "gamma: " + << m_fontEngineData->fontSmoothingGamma; +} + +QWindowsFontDatabase::~QWindowsFontDatabase() +{ +} + +QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, + QUnicodeTables::Script script, + void *handle) +{ + QFontEngine *fe = QWindowsFontDatabase::createEngine(script, fontDef, + 0, QWindowsContext::instance()->defaultDPI(), false, + QStringList(), m_fontEngineData); + if (QWindowsContext::verboseFonts) + qDebug() << __FUNCTION__ << "FONTDEF" << fontDef << script << fe << handle; + return fe; +} + +QFontEngine *QWindowsFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) +{ + QFontEngine *fe = QPlatformFontDatabase::fontEngine(fontData, pixelSize, hintingPreference); + if (QWindowsContext::verboseFonts) + qDebug() << __FUNCTION__ << "FONTDATA" << fontData << pixelSize << hintingPreference << fe; + return fe; +} + +QStringList QWindowsFontDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const +{ + QStringList result = QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script); + if (!result.isEmpty()) + return result; + if (QWindowsContext::verboseFonts) + qDebug() << __FUNCTION__ << family << style << styleHint + << script << result << m_families.size(); + return result; +} + +QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) +{ + const QStringList result = QPlatformFontDatabase::addApplicationFont(fontData, fileName); + Q_UNIMPLEMENTED(); + return result; +} + +void QWindowsFontDatabase::releaseHandle(void *handle) +{ + if (handle && QWindowsContext::verboseFonts) + qDebug() << __FUNCTION__ << handle; +} + +QString QWindowsFontDatabase::fontDir() const +{ + const QString result = QPlatformFontDatabase::fontDir(); + if (QWindowsContext::verboseFonts) + qDebug() << __FUNCTION__ << result; + return result; +} + +HFONT QWindowsFontDatabase::systemFont() +{ + static const HFONT stock_sysfont = (HFONT)GetStockObject(SYSTEM_FONT); + return stock_sysfont; +} + +// Creation functions + +static inline bool scriptRequiresOpenType(int script) +{ + return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala) + || script == QUnicodeTables::Khmer || script == QUnicodeTables::Nko); +} + +static const char *other_tryFonts[] = { + "Arial", + "MS UI Gothic", + "Gulim", + "SimSun", + "PMingLiU", + "Arial Unicode MS", + 0 +}; + +static const char *jp_tryFonts [] = { + "MS UI Gothic", + "Arial", + "Gulim", + "SimSun", + "PMingLiU", + "Arial Unicode MS", + 0 +}; + +static const char *ch_CN_tryFonts [] = { + "SimSun", + "Arial", + "PMingLiU", + "Gulim", + "MS UI Gothic", + "Arial Unicode MS", + 0 +}; + +static const char *ch_TW_tryFonts [] = { + "PMingLiU", + "Arial", + "SimSun", + "Gulim", + "MS UI Gothic", + "Arial Unicode MS", + 0 +}; + +static const char *kr_tryFonts[] = { + "Gulim", + "Arial", + "PMingLiU", + "SimSun", + "MS UI Gothic", + "Arial Unicode MS", + 0 +}; + +static const char **tryFonts = 0; + +QFontEngine *QWindowsFontDatabase::createEngine(int script, const QFontDef &request, + HDC fontHdc, int dpi, bool rawMode, + const QStringList &family_list, + const QSharedPointer &data) +{ + LOGFONT lf; + memset(&lf, 0, sizeof(LOGFONT)); + + const bool useDevice = (request.styleStrategy & QFont::PreferDevice) && fontHdc; + + const HDC hdc = useDevice ? fontHdc : data->hdc; + + bool stockFont = false; + bool preferClearTypeAA = false; + + HFONT hfont = 0; + +#if !defined(QT_NO_DIRECTWRITE) + bool useDirectWrite = (request.hintingPreference == QFont::PreferNoHinting) + || (request.hintingPreference == QFont::PreferVerticalHinting); + IDWriteFont *directWriteFont = 0; +#else + bool useDirectWrite = false; +#endif + + if (rawMode) { // will choose a stock font + int f = SYSTEM_FONT; + const QString fam = request.family.toLower(); + if (fam == QStringLiteral("default") || fam == QStringLiteral("system")) + f = SYSTEM_FONT; + else if (fam == QStringLiteral("system_fixed")) + f = SYSTEM_FIXED_FONT; + else if (fam == QStringLiteral("ansi_fixed")) + f = ANSI_FIXED_FONT; + else if (fam == QStringLiteral("ansi_var")) + f = ANSI_VAR_FONT; + else if (fam == QStringLiteral("device_default")) + f = DEVICE_DEFAULT_FONT; + else if (fam == QStringLiteral("oem_fixed")) + f = OEM_FIXED_FONT; + else if (fam.at(0) == QLatin1Char('#')) + f = fam.right(fam.length()-1).toInt(); + hfont = (HFONT)GetStockObject(f); + if (!hfont) { + qErrnoWarning("%s: GetStockObject failed", __FUNCTION__); + hfont = QWindowsFontDatabase::systemFont(); + } + stockFont = true; + } else { + int hint = FF_DONTCARE; + switch (request.styleHint) { + case QFont::Helvetica: + hint = FF_SWISS; + break; + case QFont::Times: + hint = FF_ROMAN; + break; + case QFont::Courier: + hint = FF_MODERN; + break; + case QFont::OldEnglish: + hint = FF_DECORATIVE; + break; + case QFont::System: + hint = FF_MODERN; + break; + default: + break; + } + + lf.lfHeight = -qRound(request.pixelSize); + lf.lfWidth = 0; + lf.lfEscapement = 0; + lf.lfOrientation = 0; + if (request.weight == 50) + lf.lfWeight = FW_DONTCARE; + else + lf.lfWeight = (request.weight*900)/99; + lf.lfItalic = request.style != QFont::StyleNormal; + lf.lfCharSet = DEFAULT_CHARSET; + + int strat = OUT_DEFAULT_PRECIS; + if (request.styleStrategy & QFont::PreferBitmap) { + strat = OUT_RASTER_PRECIS; + } else if (request.styleStrategy & QFont::PreferDevice) { + strat = OUT_DEVICE_PRECIS; + } else if (request.styleStrategy & QFont::PreferOutline) { + strat = OUT_OUTLINE_PRECIS; + } else if (request.styleStrategy & QFont::ForceOutline) { + strat = OUT_TT_ONLY_PRECIS; + } + + lf.lfOutPrecision = strat; + + int qual = DEFAULT_QUALITY; + + if (request.styleStrategy & QFont::PreferMatch) + qual = DRAFT_QUALITY; + else if (request.styleStrategy & QFont::PreferQuality) + qual = PROOF_QUALITY; + + if (request.styleStrategy & QFont::PreferAntialias) { + if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP) { + qual = CLEARTYPE_QUALITY; + preferClearTypeAA = true; + } else { + qual = ANTIALIASED_QUALITY; + } + } else if (request.styleStrategy & QFont::NoAntialias) { + qual = NONANTIALIASED_QUALITY; + } + + lf.lfQuality = qual; + + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfPitchAndFamily = DEFAULT_PITCH | hint; + + QString fam = request.family; + + if(fam.isEmpty()) + fam = QStringLiteral("MS Sans Serif"); + + if ((fam == QStringLiteral("MS Sans Serif")) + && (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) { + fam = QStringLiteral("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale + } + if (fam == QStringLiteral("Courier") && !(request.styleStrategy & QFont::PreferBitmap)) + fam = QStringLiteral("Courier New"); + + memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded + + hfont = CreateFontIndirect(&lf); + if (!hfont) + qErrnoWarning("%s: CreateFontIndirect failed", __FUNCTION__); + + stockFont = (hfont == 0); + bool ttf = false; + int avWidth = 0; + BOOL res; + HGDIOBJ oldObj = SelectObject(hdc, hfont); + + TEXTMETRIC tm; + res = GetTextMetrics(hdc, &tm); + avWidth = tm.tmAveCharWidth; + ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE; + SelectObject(hdc, oldObj); + + if (!ttf || !useDirectWrite) { + useDirectWrite = false; + + if (hfont && (!ttf || request.stretch != 100)) { + DeleteObject(hfont); + if (!res) + qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed"); + lf.lfWidth = avWidth * request.stretch/100; + hfont = CreateFontIndirect(&lf); + if (!hfont) + qErrnoWarning("%s: CreateFontIndirect with stretch failed", __FUNCTION__); + } + + if (hfont == 0) { + hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); + stockFont = true; + } + } + +#if !defined(QT_NO_DIRECTWRITE) + else { + // Default to false for DirectWrite (and re-enable once/if everything + // turns out okay) + useDirectWrite = false; + if (initDirectWrite(data.data())) { + const QString nameSubstitute = QWindowsFontEngineDirectWrite::fontNameSubstitute(QString::fromWCharArray(lf.lfFaceName)); + memcpy(lf.lfFaceName, nameSubstitute.utf16(), + sizeof(wchar_t) * qMin(nameSubstitute.length() + 1, LF_FACESIZE)); + + HRESULT hr = data->directWriteGdiInterop->CreateFontFromLOGFONT( + &lf, + &directWriteFont); + if (FAILED(hr)) { + qErrnoWarning("%s: CreateFontFromLOGFONT failed", __FUNCTION__); + } else { + DeleteObject(hfont); + useDirectWrite = true; + } + } + } +#endif + } + + QFontEngine *fe = 0; + if (!useDirectWrite) { + QWindowsFontEngine *few = new QWindowsFontEngine(request.family, hfont, stockFont, lf, data); + few->setObjectName(QStringLiteral("QWindowsFontEngine_") + request.family); + if (preferClearTypeAA) + few->glyphFormat = QFontEngineGlyphCache::Raster_RGBMask; + + // Also check for OpenType tables when using complex scripts + // ### TODO: This only works for scripts that require OpenType. More generally + // 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]) { + qWarning(" OpenType support missing for script\n"); + delete few; + return 0; + } + } + + few->initFontInfo(request, fontHdc, dpi); + fe = few; + } + +#if !defined(QT_NO_DIRECTWRITE) + else { + IDWriteFontFace *directWriteFontFace = NULL; + HRESULT hr = directWriteFont->CreateFontFace(&directWriteFontFace); + if (SUCCEEDED(hr)) { + QWindowsFontEngineDirectWrite *fedw = new QWindowsFontEngineDirectWrite(directWriteFontFace, + request.pixelSize, + data); + fedw->initFontInfo(request, dpi, directWriteFont); + fedw->setObjectName(QStringLiteral("QWindowsFontEngineDirectWrite_") + request.family); + fe = fedw; + } else { + qErrnoWarning("%s: CreateFontFace failed", __FUNCTION__); + } + } + + if (directWriteFont != 0) + directWriteFont->Release(); +#endif + + if(script == QUnicodeTables::Common + && !(request.styleStrategy & QFont::NoFontMerging)) { + QFontDatabase db; + if (!db.writingSystems(request.family).contains(QFontDatabase::Symbol)) { + if(!tryFonts) { + LANGID lid = GetUserDefaultLangID(); + switch( lid&0xff ) { + case LANG_CHINESE: // Chinese (Taiwan) + if ( lid == 0x0804 ) // Taiwan + tryFonts = ch_TW_tryFonts; + else + tryFonts = ch_CN_tryFonts; + break; + case LANG_JAPANESE: + tryFonts = jp_tryFonts; + break; + case LANG_KOREAN: + tryFonts = kr_tryFonts; + break; + default: + tryFonts = other_tryFonts; + break; + } + } + QStringList fm = QFontDatabase().families(); + QStringList list = family_list; + const char **tf = tryFonts; + while(tf && *tf) { + if(fm.contains(QLatin1String(*tf))) + list << QLatin1String(*tf); + ++tf; + } + QFontEngine *mfe = new QWindowsMultiFontEngine(fe, list); + mfe->setObjectName(QStringLiteral("QWindowsMultiFontEngine_") + request.family); + mfe->fontDef = fe->fontDef; + fe = mfe; + } + } + return fe; +} + +static inline int verticalDPI() +{ + return GetDeviceCaps(QWindowsContext::instance()->displayContext(), LOGPIXELSY); +} + +QFont QWindowsFontDatabase::defaultFont() const +{ + LOGFONT lf; + GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(lf), &lf); + QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(lf); + // "MS Shell Dlg 2" is the correct system font >= Win2k + if (systemFont.family() == QStringLiteral("MS Shell Dlg")) + systemFont.setFamily(QStringLiteral("MS Shell Dlg 2")); + if (QWindowsContext::verboseFonts) + qDebug() << __FUNCTION__ << systemFont; + return systemFont; +} + +QHash QWindowsFontDatabase::defaultFonts() const +{ + QHash result; + NONCLIENTMETRICS ncm; + ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0); + + const int verticalRes = verticalDPI(); + + const QFont menuFont = LOGFONT_to_QFont(ncm.lfMenuFont, verticalRes); + const QFont messageFont = LOGFONT_to_QFont(ncm.lfMessageFont, verticalRes); + const QFont statusFont = LOGFONT_to_QFont(ncm.lfStatusFont, verticalRes); + const QFont titleFont = LOGFONT_to_QFont(ncm.lfCaptionFont, verticalRes); + + LOGFONT lfIconTitleFont; + SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0); + const QFont iconTitleFont = LOGFONT_to_QFont(lfIconTitleFont, verticalRes); + + result.insert(QByteArray("QMenu"), menuFont); + result.insert(QByteArray("QMenuBar"), menuFont); + result.insert(QByteArray("QMessageBox"), messageFont); + result.insert(QByteArray("QTipLabel"), statusFont); + result.insert(QByteArray("QStatusBar"), statusFont); + result.insert(QByteArray("Q3TitleBar"), titleFont); + result.insert(QByteArray("QWorkspaceTitleBar"), titleFont); + result.insert(QByteArray("QAbstractItemView"), iconTitleFont); + result.insert(QByteArray("QDockWidgetTitle"), iconTitleFont); + if (QWindowsContext::verboseFonts) { + typedef QHash::const_iterator CIT; + QDebug nsp = qDebug().nospace(); + nsp << __FUNCTION__ << " DPI=" << verticalRes << "\n"; + const CIT cend = result.constEnd(); + for (CIT it = result.constBegin(); it != cend; ++it) + nsp << it.key() << ' ' << it.value() << '\n'; + } + return result; +} + +QFont QWindowsFontDatabase::LOGFONT_to_QFont(const LOGFONT& logFont, int verticalDPI_In) +{ + if (verticalDPI_In <= 0) + verticalDPI_In = verticalDPI(); + QFont qFont(QString::fromWCharArray(logFont.lfFaceName)); + qFont.setItalic(logFont.lfItalic); + if (logFont.lfWeight != FW_DONTCARE) + qFont.setWeight(weightFromInteger(logFont.lfWeight)); + const qreal logFontHeight = qAbs(logFont.lfHeight); + qFont.setPointSizeF(logFontHeight * 72.0 / qreal(verticalDPI_In)); + qFont.setUnderline(false); + qFont.setOverline(false); + qFont.setStrikeOut(false); + return qFont; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.h b/src/plugins/platforms/windows/qwindowsfontdatabase.h new file mode 100644 index 0000000000..a80482f6e2 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSFONTDATABASE_H +#define QWINDOWSFONTDATABASE_H + +#include +#include +#include "qtwindows_additional.h" + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_DIRECTWRITE) + struct IDWriteFactory; + struct IDWriteGdiInterop; +#endif + +class QWindowsFontEngineData +{ + Q_DISABLE_COPY(QWindowsFontEngineData) +public: + QWindowsFontEngineData(); + ~QWindowsFontEngineData(); + + uint pow_gamma[256]; + + bool clearTypeEnabled; + qreal fontSmoothingGamma; + HDC hdc; +#if !defined(QT_NO_DIRECTWRITE) + IDWriteFactory *directWriteFactory; + IDWriteGdiInterop *directWriteGdiInterop; +#endif +}; + +class QWindowsFontDatabase : public QPlatformFontDatabase +{ +public: + QWindowsFontDatabase(); + ~QWindowsFontDatabase(); + + virtual void populateFontDatabase(); + virtual QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::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 addApplicationFont(const QByteArray &fontData, const QString &fileName); + virtual void releaseHandle(void *handle); + virtual QString fontDir() const; + + virtual QFont defaultFont() const; + virtual QHash defaultFonts() const; + + static QFontEngine *createEngine(int script, const QFontDef &request, + HDC fontHdc, int dpi, bool rawMode, + const QStringList &family_list, + const QSharedPointer &data); + + static HFONT systemFont(); + static QFont LOGFONT_to_QFont(const LOGFONT& lf, int verticalDPI = 0); + +private: + void populate(const QString &family = QString()); + QSharedPointer m_fontEngineData; + QSet m_families; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSFONTDATABASE_H diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp new file mode 100644 index 0000000000..80d9ed4686 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -0,0 +1,1223 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if _WIN32_WINNT < 0x0500 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#include "qwindowsfontengine.h" +#include "qwindowsnativeimage.h" +#include "qwindowscontext.h" +#include "qwindowsfontdatabase.h" +#include "qtwindows_additional.h" + +#include // glyph_metrics_t +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +//### mingw needed define +#ifndef TT_PRIM_CSPLINE +#define TT_PRIM_CSPLINE 3 +#endif + +#ifdef MAKE_TAG +#undef MAKE_TAG +#endif +// GetFontData expects the tags in little endian ;( +#define MAKE_TAG(ch1, ch2, ch3, ch4) (\ + (((quint32)(ch4)) << 24) | \ + (((quint32)(ch3)) << 16) | \ + (((quint32)(ch2)) << 8) | \ + ((quint32)(ch1)) \ + ) + +// common DC for all fonts + +QT_BEGIN_NAMESPACE + +typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT); +static PtrGetCharWidthI ptrGetCharWidthI = 0; +static bool resolvedGetCharWidthI = false; + +static void resolveGetCharWidthI() +{ + if (resolvedGetCharWidthI) + return; + resolvedGetCharWidthI = true; + ptrGetCharWidthI = (PtrGetCharWidthI)QSystemLibrary::resolve(QStringLiteral("gdi32"), "GetCharWidthI"); +} + +// defined in qtextengine_win.cpp +typedef void *SCRIPT_CACHE; +typedef HRESULT (WINAPI *fScriptFreeCache)(SCRIPT_CACHE *); +extern fScriptFreeCache ScriptFreeCache; + +static inline quint32 getUInt(unsigned char *p) +{ + quint32 val; + val = *p++ << 24; + val |= *p++ << 16; + val |= *p++ << 8; + val |= *p; + + return val; +} + +static inline quint16 getUShort(unsigned char *p) +{ + quint16 val; + val = *p++ << 8; + val |= *p; + + return val; +} + +// general font engine + +QFixed QWindowsFontEngine::lineThickness() const +{ + if(lineWidth > 0) + return lineWidth; + + return QFontEngine::lineThickness(); +} + +static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc) +{ + int size; + size = GetOutlineTextMetrics(hdc, 0, 0); + OUTLINETEXTMETRIC *otm = (OUTLINETEXTMETRIC *)malloc(size); + GetOutlineTextMetrics(hdc, size, otm); + return otm; +} + +void QWindowsFontEngine::getCMap() +{ + ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE); + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + bool symb = false; + if (ttf) { + cmapTable = getSfntTable(qbswap(MAKE_TAG('c', 'm', 'a', 'p'))); + int size = 0; + cmap = QFontEngine::getCMap(reinterpret_cast(cmapTable.constData()), + cmapTable.size(), &symb, &size); + } + if (!cmap) { + ttf = false; + symb = false; + } + symbol = symb; + designToDevice = 1; + _faceId.index = 0; + if(cmap) { + OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); + designToDevice = QFixed((int)otm->otmEMSquare)/int(otm->otmTextMetrics.tmHeight); + unitsPerEm = otm->otmEMSquare; + x_height = (int)otm->otmsXHeight; + loadKerningPairs(designToDevice); + _faceId.filename = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFullName)).toLatin1(); + lineWidth = otm->otmsUnderscoreSize; + fsType = otm->otmfsType; + free(otm); + } else { + unitsPerEm = tm.tmHeight; + } +} + + +inline unsigned int getChar(const QChar *str, int &i, const int len) +{ + unsigned int uc = str[i].unicode(); + if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) { + uint low = str[i+1].unicode(); + if (low >= 0xdc00 && low < 0xe000) { + uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000; + ++i; + } + } + return uc; +} + +int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs, bool mirrored) const +{ + int i = 0; + int glyph_pos = 0; + if (mirrored) { + if (symbol) { + for (; i < numChars; ++i, ++glyph_pos) { + unsigned int uc = getChar(str, i, numChars); + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc); + if (!glyphs->glyphs[glyph_pos] && uc < 0x100) + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000); + } + } else if (ttf) { + for (; i < numChars; ++i, ++glyph_pos) { + unsigned int uc = getChar(str, i, numChars); + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, QChar::mirroredChar(uc)); + } + } else { + wchar_t first = tm.tmFirstChar; + wchar_t last = tm.tmLastChar; + + for (; i < numChars; ++i, ++glyph_pos) { + uint ucs = QChar::mirroredChar(getChar(str, i, numChars)); + if (ucs >= first && ucs <= last) + glyphs->glyphs[glyph_pos] = ucs; + else + glyphs->glyphs[glyph_pos] = 0; + } + } + } else { + if (symbol) { + for (; i < numChars; ++i, ++glyph_pos) { + unsigned int uc = getChar(str, i, numChars); + glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc); + if(!glyphs->glyphs[glyph_pos] && uc < 0x100) + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000); + } + } else if (ttf) { + for (; i < numChars; ++i, ++glyph_pos) { + unsigned int uc = getChar(str, i, numChars); + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc); + } + } else { + wchar_t first = tm.tmFirstChar; + wchar_t last = tm.tmLastChar; + + for (; i < numChars; ++i, ++glyph_pos) { + uint uc = getChar(str, i, numChars); + if (uc >= first && uc <= last) + glyphs->glyphs[glyph_pos] = uc; + else + glyphs->glyphs[glyph_pos] = 0; + } + } + } + glyphs->numGlyphs = glyph_pos; + return glyph_pos; +} + +/*! + \class QWindowsFontEngine + \brief Standard Windows font engine. + \ingroup qt-lighthouse-win + + Will probably be superseded by a common Free Type font engine in Qt 5.X. +*/ + +QWindowsFontEngine::QWindowsFontEngine(const QString &name, + HFONT _hfont, bool stockFontIn, LOGFONT lf, + QSharedPointer fontEngineData) : + m_fontEngineData(fontEngineData), + _name(name), + hfont(_hfont), + m_logfont(lf), + stockFont(stockFontIn), + ttf(0), + hasOutline(0), + lw(0), + cmap(0), + lbearing(SHRT_MIN), + rbearing(SHRT_MIN), + x_height(-1), + synthesized_flags(-1), + lineWidth(-1), + widthCache(0), + widthCacheSize(0), + designAdvances(0), + designAdvancesSize(0) +{ + if (QWindowsContext::verboseFonts) + qDebug("%s: font='%s', size=%ld", __FUNCTION__, qPrintable(name), lf.lfHeight); + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + fontDef.pixelSize = -lf.lfHeight; + const BOOL res = GetTextMetrics(hdc, &tm); + fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); + if (!res) { + qErrnoWarning("%s: GetTextMetrics failed", __FUNCTION__); + ZeroMemory(&tm, sizeof(TEXTMETRIC)); + } + + cache_cost = tm.tmHeight * tm.tmAveCharWidth * 2000; + getCMap(); + + if (!resolvedGetCharWidthI) + resolveGetCharWidthI(); +} + +QWindowsFontEngine::~QWindowsFontEngine() +{ + if (designAdvances) + free(designAdvances); + + if (widthCache) + free(widthCache); + + // make sure we aren't by accident still selected + SelectObject(m_fontEngineData->hdc, (HFONT)GetStockObject(SYSTEM_FONT)); + + if (!stockFont) { + if (!DeleteObject(hfont)) + qErrnoWarning("%s: QFontEngineWin: failed to delete non-stock font... failed", __FUNCTION__); + } + if (QWindowsContext::verboseFonts) + if (QWindowsContext::verboseFonts) + qDebug("%s: font='%s", __FUNCTION__, qPrintable(_name)); +} + +HGDIOBJ QWindowsFontEngine::selectDesignFont() const +{ + LOGFONT f = m_logfont; + f.lfHeight = unitsPerEm; + HFONT designFont = CreateFontIndirect(&f); + return SelectObject(m_fontEngineData->hdc, designFont); +} + +bool QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const +{ + if (*nglyphs < len) { + *nglyphs = len; + return false; + } + + *nglyphs = getGlyphIndexes(str, len, glyphs, flags & QTextEngine::RightToLeft); + + if (flags & QTextEngine::GlyphIndicesOnly) + return true; + + recalcAdvances(glyphs, flags); + return true; +} + +inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width) +{ + if (ptrGetCharWidthI) + ptrGetCharWidthI(hdc, glyph, 1, 0, &width); +} + +void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const +{ + HGDIOBJ oldFont = 0; + HDC hdc = m_fontEngineData->hdc; + if (ttf && (flags & QTextEngine::DesignMetrics)) { + for(int i = 0; i < glyphs->numGlyphs; i++) { + unsigned int glyph = glyphs->glyphs[i]; + if(int(glyph) >= designAdvancesSize) { + int newSize = (glyph + 256) >> 8 << 8; + designAdvances = q_check_ptr((QFixed *)realloc(designAdvances, + newSize*sizeof(QFixed))); + for(int i = designAdvancesSize; i < newSize; ++i) + designAdvances[i] = -1000000; + designAdvancesSize = newSize; + } + if (designAdvances[glyph] < -999999) { + if (!oldFont) + oldFont = selectDesignFont(); + + int width = 0; + calculateTTFGlyphWidth(hdc, glyph, width); + designAdvances[glyph] = QFixed(width) / designToDevice; + } + glyphs->advances_x[i] = designAdvances[glyph]; + glyphs->advances_y[i] = 0; + } + if(oldFont) + DeleteObject(SelectObject(hdc, oldFont)); + } else { + for(int i = 0; i < glyphs->numGlyphs; i++) { + unsigned int glyph = glyphs->glyphs[i]; + + glyphs->advances_y[i] = 0; + + if (glyph >= widthCacheSize) { + int newSize = (glyph + 256) >> 8 << 8; + widthCache = q_check_ptr((unsigned char *)realloc(widthCache, + newSize*sizeof(QFixed))); + memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize); + widthCacheSize = newSize; + } + glyphs->advances_x[i] = widthCache[glyph]; + // font-width cache failed + if (glyphs->advances_x[i] == 0) { + int width = 0; + if (!oldFont) + oldFont = SelectObject(hdc, hfont); + + if (!ttf) { + QChar ch[2] = { ushort(glyph), 0 }; + int chrLen = 1; + if (glyph > 0xffff) { + ch[0] = QChar::highSurrogate(glyph); + ch[1] = QChar::lowSurrogate(glyph); + ++chrLen; + } + SIZE size = {0, 0}; + GetTextExtentPoint32(hdc, (wchar_t *)ch, chrLen, &size); + width = size.cx; + } else { + calculateTTFGlyphWidth(hdc, glyph, width); + } + glyphs->advances_x[i] = width; + // if glyph's within cache range, store it for later + if (width > 0 && width < 0x100) + widthCache[glyph] = width; + } + } + + if (oldFont) + SelectObject(hdc, oldFont); + } +} + +glyph_metrics_t QWindowsFontEngine::boundingBox(const QGlyphLayout &glyphs) +{ + if (glyphs.numGlyphs == 0) + return glyph_metrics_t(); + + QFixed w = 0; + for (int i = 0; i < glyphs.numGlyphs; ++i) + w += glyphs.effectiveAdvance(i); + + return glyph_metrics_t(0, -tm.tmAscent, w - lastRightBearing(glyphs), tm.tmHeight, w, 0); +} + +bool QWindowsFontEngine::getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const +{ + Q_ASSERT(metrics != 0); + + HDC hdc = m_fontEngineData->hdc; + + GLYPHMETRICS gm; + DWORD res = 0; + MAT2 mat; + mat.eM11.value = mat.eM22.value = 1; + mat.eM11.fract = mat.eM22.fract = 0; + mat.eM21.value = mat.eM12.value = 0; + mat.eM21.fract = mat.eM12.fract = 0; + + if (t.type() > QTransform::TxTranslate) { + // We need to set the transform using the HDC's world + // matrix rather than using the MAT2 above, because the + // results provided when transforming via MAT2 does not + // match the glyphs that are drawn using a WorldTransform + XFORM xform; + xform.eM11 = t.m11(); + xform.eM12 = t.m12(); + xform.eM21 = t.m21(); + xform.eM22 = t.m22(); + xform.eDx = 0; + xform.eDy = 0; + SetGraphicsMode(hdc, GM_ADVANCED); + SetWorldTransform(hdc, &xform); + } + + uint format = GGO_METRICS; + if (ttf) + format |= GGO_GLYPH_INDEX; + res = GetGlyphOutline(hdc, glyph, format, &gm, 0, 0, &mat); + + if (t.type() > QTransform::TxTranslate) { + XFORM xform; + xform.eM11 = xform.eM22 = 1; + xform.eM12 = xform.eM21 = xform.eDx = xform.eDy = 0; + SetWorldTransform(hdc, &xform); + SetGraphicsMode(hdc, GM_COMPATIBLE); + } + + if (res != GDI_ERROR) { + *metrics = glyph_metrics_t(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y, + (int)gm.gmBlackBoxX, (int)gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY); + return true; + } else { + return false; + } +} + +glyph_metrics_t QWindowsFontEngine::boundingBox(glyph_t glyph, const QTransform &t) +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + + glyph_metrics_t glyphMetrics; + bool success = getOutlineMetrics(glyph, t, &glyphMetrics); + + if (!ttf && !success) { + // Bitmap fonts + wchar_t ch = glyph; + ABCFLOAT abc; + GetCharABCWidthsFloat(hdc, ch, ch, &abc); + int width = qRound(abc.abcfB); + + return glyph_metrics_t(QFixed::fromReal(abc.abcfA), -tm.tmAscent, width, tm.tmHeight, width, 0).transformed(t); + } + + return glyphMetrics; +} + +QFixed QWindowsFontEngine::ascent() const +{ + return tm.tmAscent; +} + +QFixed QWindowsFontEngine::descent() const +{ + // ### we subtract 1 to even out the historical +1 in QFontMetrics' + // ### height=asc+desc+1 equation. Fix in Qt5. + return tm.tmDescent - 1; +} + +QFixed QWindowsFontEngine::leading() const +{ + return tm.tmExternalLeading; +} + + +QFixed QWindowsFontEngine::xHeight() const +{ + if(x_height >= 0) + return x_height; + return QFontEngine::xHeight(); +} + +QFixed QWindowsFontEngine::averageCharWidth() const +{ + return tm.tmAveCharWidth; +} + +qreal QWindowsFontEngine::maxCharWidth() const +{ + return tm.tmMaxCharWidth; +} + +enum { max_font_count = 256 }; +static const ushort char_table[] = { + 40, + 67, + 70, + 75, + 86, + 88, + 89, + 91, + 102, + 114, + 124, + 127, + 205, + 645, + 884, + 922, + 1070, + 12386, + 0 +}; + +static const int char_table_entries = sizeof(char_table)/sizeof(ushort); + +#ifndef Q_CC_MINGW +void QWindowsFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing) +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + + if (ttf) + { + ABC abcWidths; + GetCharABCWidthsI(hdc, glyph, 1, 0, &abcWidths); + if (leftBearing) + *leftBearing = abcWidths.abcA; + if (rightBearing) + *rightBearing = abcWidths.abcC; + } else { + QFontEngine::getGlyphBearings(glyph, leftBearing, rightBearing); + } +} +#endif // Q_CC_MINGW + +qreal QWindowsFontEngine::minLeftBearing() const +{ + if (lbearing == SHRT_MIN) + minRightBearing(); // calculates both + + return lbearing; +} + +qreal QWindowsFontEngine::minRightBearing() const +{ + if (rbearing == SHRT_MIN) { + int ml = 0; + int mr = 0; + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + if (ttf) { + ABC *abc = 0; + int n = tm.tmLastChar - tm.tmFirstChar; + if (n <= max_font_count) { + abc = new ABC[n+1]; + GetCharABCWidths(hdc, tm.tmFirstChar, tm.tmLastChar, abc); + } else { + abc = new ABC[char_table_entries+1]; + for(int i = 0; i < char_table_entries; i++) + GetCharABCWidths(hdc, char_table[i], char_table[i], abc + i); + n = char_table_entries; + } + ml = abc[0].abcA; + mr = abc[0].abcC; + for (int i = 1; i < n; i++) { + if (abc[i].abcA + abc[i].abcB + abc[i].abcC != 0) { + ml = qMin(ml,abc[i].abcA); + mr = qMin(mr,abc[i].abcC); + } + } + delete [] abc; + } else { + ABCFLOAT *abc = 0; + int n = tm.tmLastChar - tm.tmFirstChar+1; + if (n <= max_font_count) { + abc = new ABCFLOAT[n]; + GetCharABCWidthsFloat(hdc, tm.tmFirstChar, tm.tmLastChar, abc); + } else { + abc = new ABCFLOAT[char_table_entries]; + for(int i = 0; i < char_table_entries; i++) + GetCharABCWidthsFloat(hdc, char_table[i], char_table[i], abc+i); + n = char_table_entries; + } + float fml = abc[0].abcfA; + float fmr = abc[0].abcfC; + for (int i=1; i string->unicode() || tm.tmLastChar < string->unicode()) + return false; + } + } + return true; +} + +QFontEngine::Type QWindowsFontEngine::type() const +{ + return QFontEngine::Win; +} + +static inline double qt_fixed_to_double(const FIXED &p) { + return ((p.value << 16) + p.fract) / 65536.0; +} + +static inline QPointF qt_to_qpointf(const POINTFX &pt, qreal scale) { + return QPointF(qt_fixed_to_double(pt.x) * scale, -qt_fixed_to_double(pt.y) * scale); +} + +#ifndef GGO_UNHINTED +#define GGO_UNHINTED 0x0100 +#endif + +static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc, + QPainterPath *path, bool ttf, glyph_metrics_t *metric = 0, qreal scale = 1) +{ + MAT2 mat; + mat.eM11.value = mat.eM22.value = 1; + mat.eM11.fract = mat.eM22.fract = 0; + mat.eM21.value = mat.eM12.value = 0; + mat.eM21.fract = mat.eM12.fract = 0; + uint glyphFormat = GGO_NATIVE; + + if (ttf) + glyphFormat |= GGO_GLYPH_INDEX; + + GLYPHMETRICS gMetric; + memset(&gMetric, 0, sizeof(GLYPHMETRICS)); + int bufferSize = GDI_ERROR; + bufferSize = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat); + if ((DWORD)bufferSize == GDI_ERROR) { + return false; + } + + void *dataBuffer = new char[bufferSize]; + DWORD ret = GDI_ERROR; + ret = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, bufferSize, dataBuffer, &mat); + if (ret == GDI_ERROR) { + delete [](char *)dataBuffer; + return false; + } + + if(metric) { + // #### obey scale + *metric = glyph_metrics_t(gMetric.gmptGlyphOrigin.x, -gMetric.gmptGlyphOrigin.y, + (int)gMetric.gmBlackBoxX, (int)gMetric.gmBlackBoxY, + gMetric.gmCellIncX, gMetric.gmCellIncY); + } + + int offset = 0; + int headerOffset = 0; + TTPOLYGONHEADER *ttph = 0; + + QPointF oset = position.toPointF(); + while (headerOffset < bufferSize) { + ttph = (TTPOLYGONHEADER*)((char *)dataBuffer + headerOffset); + + QPointF lastPoint(qt_to_qpointf(ttph->pfxStart, scale)); + path->moveTo(lastPoint + oset); + offset += sizeof(TTPOLYGONHEADER); + TTPOLYCURVE *curve; + while (offsetcb)) { + curve = (TTPOLYCURVE*)((char*)(dataBuffer) + offset); + switch (curve->wType) { + case TT_PRIM_LINE: { + for (int i=0; icpfx; ++i) { + QPointF p = qt_to_qpointf(curve->apfx[i], scale) + oset; + path->lineTo(p); + } + break; + } + case TT_PRIM_QSPLINE: { + const QPainterPath::Element &elm = path->elementAt(path->elementCount()-1); + QPointF prev(elm.x, elm.y); + QPointF endPoint; + for (int i=0; icpfx - 1; ++i) { + QPointF p1 = qt_to_qpointf(curve->apfx[i], scale) + oset; + QPointF p2 = qt_to_qpointf(curve->apfx[i+1], scale) + oset; + if (i < curve->cpfx - 2) { + endPoint = QPointF((p1.x() + p2.x()) / 2, (p1.y() + p2.y()) / 2); + } else { + endPoint = p2; + } + + path->quadTo(p1, endPoint); + prev = endPoint; + } + + break; + } + case TT_PRIM_CSPLINE: { + for (int i=0; icpfx; ) { + QPointF p2 = qt_to_qpointf(curve->apfx[i++], scale) + oset; + QPointF p3 = qt_to_qpointf(curve->apfx[i++], scale) + oset; + QPointF p4 = qt_to_qpointf(curve->apfx[i++], scale) + oset; + path->cubicTo(p2, p3, p4); + } + break; + } + default: + qWarning("QFontEngineWin::addOutlineToPath, unhandled switch case"); + } + offset += sizeof(TTPOLYCURVE) + (curve->cpfx-1) * sizeof(POINTFX); + } + path->closeSubpath(); + headerOffset += ttph->cb; + } + delete [] (char*)dataBuffer; + + return true; +} + +void QWindowsFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags) +{ + LOGFONT lf = m_logfont; + // The sign must be negative here to make sure we match against character height instead of + // hinted cell height. This ensures that we get linear matching, and we need this for + // paths since we later on apply a scaling transform to the glyph outline to get the + // font at the correct pixel size. + lf.lfHeight = -unitsPerEm; + lf.lfWidth = 0; + HFONT hf = CreateFontIndirect(&lf); + HDC hdc = m_fontEngineData->hdc; + HGDIOBJ oldfont = SelectObject(hdc, hf); + + for(int i = 0; i < nglyphs; ++i) { + if (!addGlyphToPath(glyphs[i], positions[i], hdc, path, ttf, /*metric*/0, + qreal(fontDef.pixelSize) / unitsPerEm)) { + // Some windows fonts, like "Modern", are vector stroke + // fonts, which are reported as TMPF_VECTOR but do not + // support GetGlyphOutline, and thus we set this bit so + // that addOutLineToPath can check it and return safely... + hasOutline = false; + break; + } + } + DeleteObject(SelectObject(hdc, oldfont)); +} + +void QWindowsFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, + QPainterPath *path, QTextItem::RenderFlags flags) +{ + if(tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) { + hasOutline = true; + QFontEngine::addOutlineToPath(x, y, glyphs, path, flags); + if (hasOutline) { + // has_outline is set to false if addGlyphToPath gets + // false from GetGlyphOutline, meaning its not an outline + // font. + return; + } + } + QFontEngine::addBitmapFontToPath(x, y, glyphs, path, flags); +} + +QFontEngine::FaceId QWindowsFontEngine::faceId() const +{ + return _faceId; +} + +QT_BEGIN_INCLUDE_NAMESPACE +#include +QT_END_INCLUDE_NAMESPACE + +int QWindowsFontEngine::synthesized() const +{ + if(synthesized_flags == -1) { + synthesized_flags = 0; + if(ttf) { + const DWORD HEAD = MAKE_TAG('h', 'e', 'a', 'd'); + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + uchar data[4]; + GetFontData(hdc, HEAD, 44, &data, 4); + USHORT macStyle = getUShort(data); + if (tm.tmItalic && !(macStyle & 2)) + synthesized_flags = SynthesizedItalic; + if (fontDef.stretch != 100 && ttf) + synthesized_flags |= SynthesizedStretch; + if (tm.tmWeight >= 500 && !(macStyle & 1)) + synthesized_flags |= SynthesizedBold; + //qDebug() << "font is" << _name << + // "it=" << (macStyle & 2) << fontDef.style << "flags=" << synthesized_flags; + } + } + return synthesized_flags; +} + +QFixed QWindowsFontEngine::emSquareSize() const +{ + return unitsPerEm; +} + +QFontEngine::Properties QWindowsFontEngine::properties() const +{ + LOGFONT lf = m_logfont; + lf.lfHeight = unitsPerEm; + HFONT hf = CreateFontIndirect(&lf); + HDC hdc = m_fontEngineData->hdc; + HGDIOBJ oldfont = SelectObject(hdc, hf); + OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); + Properties p; + p.emSquare = unitsPerEm; + p.italicAngle = otm->otmItalicAngle; + p.postscriptName = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFamilyName)).toLatin1(); + p.postscriptName += QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpStyleName)).toLatin1(); + p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(p.postscriptName); + p.boundingBox = QRectF(otm->otmrcFontBox.left, -otm->otmrcFontBox.top, + otm->otmrcFontBox.right - otm->otmrcFontBox.left, + otm->otmrcFontBox.top - otm->otmrcFontBox.bottom); + p.ascent = otm->otmAscent; + p.descent = -otm->otmDescent; + p.leading = (int)otm->otmLineGap; + p.capHeight = 0; + p.lineWidth = otm->otmsUnderscoreSize; + free(otm); + DeleteObject(SelectObject(hdc, oldfont)); + return p; +} + +void QWindowsFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) +{ + LOGFONT lf = m_logfont; + lf.lfHeight = unitsPerEm; + int flags = synthesized(); + if(flags & SynthesizedItalic) + lf.lfItalic = false; + lf.lfWidth = 0; + HFONT hf = CreateFontIndirect(&lf); + HDC hdc = m_fontEngineData->hdc; + HGDIOBJ oldfont = SelectObject(hdc, hf); + QFixedPoint p; + p.x = 0; + p.y = 0; + addGlyphToPath(glyph, p, hdc, path, ttf, metrics); + DeleteObject(SelectObject(hdc, oldfont)); +} + +bool QWindowsFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + if (!ttf) + return false; + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + DWORD t = qbswap(tag); + *length = GetFontData(hdc, t, 0, buffer, *length); + return *length != GDI_ERROR; +} + +#if !defined(CLEARTYPE_QUALITY) +# define CLEARTYPE_QUALITY 5 +#endif + +QWindowsNativeImage *QWindowsFontEngine::drawGDIGlyph(HFONT font, glyph_t glyph, int margin, + const QTransform &t, + QImage::Format mask_format) +{ + Q_UNUSED(mask_format) + glyph_metrics_t gm = boundingBox(glyph); + +// printf(" -> for glyph %4x\n", glyph); + + int gx = gm.x.toInt(); + int gy = gm.y.toInt(); + int iw = gm.width.toInt(); + int ih = gm.height.toInt(); + + if (iw <= 0 || iw <= 0) + return 0; + + bool has_transformation = t.type() > QTransform::TxTranslate; + + unsigned int options = ttf ? ETO_GLYPH_INDEX : 0; + XFORM xform; + + if (has_transformation) { + xform.eM11 = t.m11(); + xform.eM12 = t.m12(); + xform.eM21 = t.m21(); + xform.eM22 = t.m22(); + xform.eDx = margin; + xform.eDy = margin; + + HDC hdc = CreateCompatibleDC(QWindowsContext::instance()->displayContext()); + + SetGraphicsMode(hdc, GM_ADVANCED); + SetWorldTransform(hdc, &xform); + HGDIOBJ old_font = SelectObject(hdc, font); + + int ggo_options = GGO_METRICS | (ttf ? GGO_GLYPH_INDEX : 0); + GLYPHMETRICS tgm; + MAT2 mat; + memset(&mat, 0, sizeof(mat)); + mat.eM11.value = mat.eM22.value = 1; + + if (GetGlyphOutline(hdc, glyph, ggo_options, &tgm, 0, 0, &mat) == GDI_ERROR) { + qWarning("QWinFontEngine: unable to query transformed glyph metrics..."); + return 0; + } + + iw = tgm.gmBlackBoxX; + ih = tgm.gmBlackBoxY; + + xform.eDx -= tgm.gmptGlyphOrigin.x; + xform.eDy += tgm.gmptGlyphOrigin.y; + + SetGraphicsMode(hdc, GM_COMPATIBLE); + SelectObject(hdc, old_font); + ReleaseDC(0, hdc); + } + QWindowsNativeImage *ni = new QWindowsNativeImage(iw + 2 * margin + 4, + ih + 2 * margin + 4, + QWindowsNativeImage::systemFormat()); + + /*If cleartype is enabled we use the standard system format even on Windows CE + and not the special textbuffer format we have to use if cleartype is disabled*/ + + ni->image().fill(0xffffffff); + + HDC hdc = ni->hdc(); + + SelectObject(hdc, GetStockObject(NULL_BRUSH)); + SelectObject(hdc, GetStockObject(BLACK_PEN)); + SetTextColor(hdc, RGB(0,0,0)); + SetBkMode(hdc, TRANSPARENT); + SetTextAlign(hdc, TA_BASELINE); + + HGDIOBJ old_font = SelectObject(hdc, font); + + if (has_transformation) { + SetGraphicsMode(hdc, GM_ADVANCED); + SetWorldTransform(hdc, &xform); + ExtTextOut(hdc, 0, 0, options, 0, (LPCWSTR) &glyph, 1, 0); + } else + { + ExtTextOut(hdc, -gx + margin, -gy + margin, options, 0, (LPCWSTR) &glyph, 1, 0); + } + + SelectObject(hdc, old_font); + return ni; +} + +QImage QWindowsFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &xform) +{ + HFONT font = hfont; + if (m_fontEngineData->clearTypeEnabled) { + LOGFONT lf = m_logfont; + lf.lfQuality = ANTIALIASED_QUALITY; + font = CreateFontIndirect(&lf); + } + QImage::Format mask_format = QWindowsNativeImage::systemFormat(); + mask_format = QImage::Format_RGB32; + + QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, 0, xform, mask_format); + if (mask == 0) + return QImage(); + + QImage indexed(mask->width(), mask->height(), QImage::Format_Indexed8); + + // ### This part is kinda pointless, but we'll crash later if we don't because some + // code paths expects there to be colortables for index8-bit... + QVector colors(256); + for (int i=0; i<256; ++i) + colors[i] = qRgba(0, 0, 0, i); + indexed.setColorTable(colors); + + // Copy data... Cannot use QPainter here as GDI has messed up the + // Alpha channel of the ni.image pixels... + for (int y=0; yheight(); ++y) { + uchar *dest = indexed.scanLine(y); + if (mask->image().format() == QImage::Format_RGB16) { + const qint16 *src = (qint16 *) ((const QImage &) mask->image()).scanLine(y); + for (int x=0; xwidth(); ++x) + dest[x] = 255 - qGray(src[x]); + } else { + const uint *src = (uint *) ((const QImage &) mask->image()).scanLine(y); + for (int x=0; xwidth(); ++x) { + if (QWindowsNativeImage::systemFormat() == QImage::Format_RGB16) + dest[x] = 255 - qGray(src[x]); + else + dest[x] = 255 - (m_fontEngineData->pow_gamma[qGray(src[x])] * 255. / 2047.); + } + } + } + + // Cleanup... + delete mask; + if (m_fontEngineData->clearTypeEnabled) { + DeleteObject(font); + } + + return indexed; +} + +#define SPI_GETFONTSMOOTHINGCONTRAST 0x200C +#define SPI_SETFONTSMOOTHINGCONTRAST 0x200D + +QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, const QTransform &t) +{ + HFONT font = hfont; + + int contrast; + SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0); + SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) 1000, 0); + + QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, margin, t, QImage::Format_RGB32); + SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) contrast, 0); + + if (mask == 0) + return QImage(); + + // Gracefully handle the odd case when the display is 16-bit + const QImage source = mask->image().depth() == 32 + ? mask->image() + : mask->image().convertToFormat(QImage::Format_RGB32); + + QImage rgbMask(mask->width(), mask->height(), QImage::Format_RGB32); + for (int y=0; yheight(); ++y) { + uint *dest = (uint *) rgbMask.scanLine(y); + const uint *src = (uint *) source.scanLine(y); + for (int x=0; xwidth(); ++x) { + dest[x] = 0xffffffff - (0x00ffffff & src[x]); + } + } + + delete mask; + + return rgbMask; +} + +QFontEngine *QWindowsFontEngine::cloneWithSize(qreal pixelSize) const +{ + QFontDef request = fontDef; + QString actualFontName = request.family; + if (!uniqueFamilyName.isEmpty()) + request.family = uniqueFamilyName; + request.pixelSize = pixelSize; + + QFontEngine *fontEngine = + QWindowsFontDatabase::createEngine(QUnicodeTables::Common, request, 0, + QWindowsContext::instance()->defaultDPI(), + false, + QStringList(), m_fontEngineData); + if (fontEngine) + fontEngine->fontDef.family = actualFontName; + return fontEngine; +} + +void QWindowsFontEngine::initFontInfo(const QFontDef &request, + HDC fontHdc, + int dpi) +{ + fontDef = request; // most settings are equal + HDC dc = ((request.styleStrategy & QFont::PreferDevice) && fontHdc) ? fontHdc : m_fontEngineData->hdc; + SelectObject(dc, hfont); + wchar_t n[64]; + GetTextFace(dc, 64, n); + fontDef.family = QString::fromWCharArray(n); + fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); + if (fontDef.pointSize < 0) { + fontDef.pointSize = fontDef.pixelSize * 72. / dpi; + } else if (fontDef.pixelSize == -1) { + fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.); + } +} + +/*! + \class QWindowsMultiFontEngine + \brief Standard Windows Multi font engine. + \ingroup qt-lighthouse-win + + "Merges" several font engines that have gaps in the + supported writing systems. + + Will probably be superseded by a common Free Type font engine in Qt 5.X. +*/ + +QWindowsMultiFontEngine::QWindowsMultiFontEngine(QFontEngine *first, const QStringList &fallbacks) + : QFontEngineMulti(fallbacks.size()+1), + fallbacks(fallbacks) +{ + if (QWindowsContext::verboseFonts) + qDebug() << __FUNCTION__ << engines.size() << first << first->fontDef.family << fallbacks; + engines[0] = first; + first->ref.ref(); + fontDef = engines[0]->fontDef; + cache_cost = first->cache_cost; +} + +QWindowsMultiFontEngine::~QWindowsMultiFontEngine() +{ + if (QWindowsContext::verboseFonts) + qDebug("%s", __FUNCTION__); +} + +void QWindowsMultiFontEngine::loadEngine(int at) +{ + Q_ASSERT(at < engines.size()); + Q_ASSERT(engines.at(at) == 0); + + const QString fam = fallbacks.at(at-1); + QWindowsFontEngine *fe = static_cast(engines.at(0)); + LOGFONT lf = fe->logfont(); + memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded + HFONT hfont = CreateFontIndirect(&lf); + + bool stockFont = false; + if (hfont == 0) { + hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); + stockFont = true; + } + engines[at] = new QWindowsFontEngine(fam, hfont, stockFont, lf, fe->fontEngineData()); + engines[at]->ref.ref(); + engines[at]->fontDef = fontDef; + if (QWindowsContext::verboseFonts) + qDebug("%s %d %s", __FUNCTION__, at, qPrintable(fam)); + + + // TODO: increase cost in QFontCache for the font engine loaded here +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsfontengine.h b/src/plugins/platforms/windows/qwindowsfontengine.h new file mode 100644 index 0000000000..bed5ecffbd --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsfontengine.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSFONTENGINE_H +#define QWINDOWSFONTENGINE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// Enable access to HB_Face in harfbuzz includes included by qfontengine_p.h. +#define QT_BUILD_GUI_LIB +#include +#undef QT_BUILD_GUI_LIB + +#include +#include + +#include "qtwindows_additional.h" + +QT_BEGIN_NAMESPACE + +class QWindowsNativeImage; +class QWindowsFontEngineData; + +class QWindowsFontEngine : public QFontEngine +{ + Q_DISABLE_COPY(QWindowsFontEngine) +public: + QWindowsFontEngine(const QString &name, HFONT, bool, LOGFONT, + QSharedPointer fontEngineData); + + ~QWindowsFontEngine(); + void initFontInfo(const QFontDef &request, + HDC fontHdc, int dpi); + + virtual QFixed lineThickness() const; + virtual Properties properties() const; + virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics); + virtual FaceId faceId() const; + virtual bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; + virtual int synthesized() const; + virtual QFixed emSquareSize() const; + + virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const; + virtual void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const; + + virtual void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags); + virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags); + + HGDIOBJ selectDesignFont() const; + + virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs); + virtual glyph_metrics_t boundingBox(glyph_t g) { return boundingBox(g, QTransform()); } + virtual glyph_metrics_t boundingBox(glyph_t g, const QTransform &t); + + + virtual QFixed ascent() const; + virtual QFixed descent() const; + virtual QFixed leading() const; + virtual QFixed xHeight() const; + virtual QFixed averageCharWidth() const; + virtual qreal maxCharWidth() const; + virtual qreal minLeftBearing() const; + virtual qreal minRightBearing() const; + + virtual const char *name() const; + + bool canRender(const QChar *string, int len); + + Type type() const; + + virtual QImage alphaMapForGlyph(glyph_t t) { return alphaMapForGlyph(t, QTransform()); } + virtual QImage alphaMapForGlyph(glyph_t, const QTransform &xform); + virtual QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, int margin, const QTransform &xform); + + virtual QFontEngine *cloneWithSize(qreal pixelSize) const; + +#ifndef Q_CC_MINGW + virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0); +#endif + + int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs, bool mirrored) const; + void getCMap(); + + bool getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const; + + static QFontEngine *createEngine(int script, const QFontDef &request, + HDC fontHdc, int dpi, bool rawMode, + const QStringList &family_list, + const QSharedPointer &data); + + QSharedPointer fontEngineData() const { return m_fontEngineData; } + LOGFONT logfont() const { return m_logfont; } + +private: + QWindowsNativeImage *drawGDIGlyph(HFONT font, glyph_t, int margin, const QTransform &xform, + QImage::Format mask_format); + + const QSharedPointer m_fontEngineData; + + const QString _name; + QString uniqueFamilyName; + const HFONT hfont; + const LOGFONT m_logfont; + uint stockFont : 1; + uint ttf : 1; + uint hasOutline : 1; + TEXTMETRIC tm; + int lw; + const unsigned char *cmap; + QByteArray cmapTable; + mutable qreal lbearing; + mutable qreal rbearing; + QFixed designToDevice; + int unitsPerEm; + QFixed x_height; + FaceId _faceId; + + mutable int synthesized_flags; + mutable QFixed lineWidth; + mutable unsigned char *widthCache; + mutable uint widthCacheSize; + mutable QFixed *designAdvances; + mutable int designAdvancesSize; +}; + +class QWindowsMultiFontEngine : public QFontEngineMulti +{ +public: + QWindowsMultiFontEngine(QFontEngine *first, const QStringList &fallbacks); + virtual ~QWindowsMultiFontEngine(); + void loadEngine(int at); + + QStringList fallbacks; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSFONTENGINE_H diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp new file mode 100644 index 0000000000..46f0b0c336 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp @@ -0,0 +1,737 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_DIRECTWRITE + +#include "qwindowsfontenginedirectwrite.h" +#include "qwindowsfontdatabase.h" +#include "qwindowscontext.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +// Convert from design units to logical pixels +#define DESIGN_TO_LOGICAL(DESIGN_UNIT_VALUE) \ + QFixed::fromReal((qreal(DESIGN_UNIT_VALUE) / qreal(m_unitsPerEm)) * fontDef.pixelSize) + +namespace { + + class GeometrySink: public IDWriteGeometrySink + { + public: + GeometrySink(QPainterPath *path) : m_path(path), m_refCount(0) + { + Q_ASSERT(m_path != 0); + } + + IFACEMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *beziers, UINT bezierCount); + IFACEMETHOD_(void, AddLines)(const D2D1_POINT_2F *points, UINT pointCount); + IFACEMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin); + IFACEMETHOD(Close)(); + IFACEMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd); + IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode); + IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags); + + IFACEMETHOD_(unsigned long, AddRef)(); + IFACEMETHOD_(unsigned long, Release)(); + IFACEMETHOD(QueryInterface)(IID const &riid, void **ppvObject); + + private: + inline static QPointF fromD2D1_POINT_2F(const D2D1_POINT_2F &inp) + { + return QPointF(inp.x, inp.y); + } + + unsigned long m_refCount; + QPointF m_startPoint; + QPainterPath *m_path; + }; + + void GeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, + UINT bezierCount) + { + for (uint i=0; icubicTo(c1, c2, p2); + } + } + + void GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) + { + for (uint i=0; ilineTo(fromD2D1_POINT_2F(points[i])); + } + + void GeometrySink::BeginFigure(D2D1_POINT_2F startPoint, + D2D1_FIGURE_BEGIN /*figureBegin*/) + { + m_startPoint = fromD2D1_POINT_2F(startPoint); + m_path->moveTo(m_startPoint); + } + + IFACEMETHODIMP GeometrySink::Close() + { + return E_NOTIMPL; + } + + void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) + { + if (figureEnd == D2D1_FIGURE_END_CLOSED) + m_path->closeSubpath(); + } + + void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) + { + m_path->setFillRule(fillMode == D2D1_FILL_MODE_ALTERNATE + ? Qt::OddEvenFill + : Qt::WindingFill); + } + + void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT /*vertexFlags*/) + { + /* Not implemented */ + } + + IFACEMETHODIMP_(unsigned long) GeometrySink::AddRef() + { + return InterlockedIncrement(&m_refCount); + } + + IFACEMETHODIMP_(unsigned long) GeometrySink::Release() + { + unsigned long newCount = InterlockedDecrement(&m_refCount); + if (newCount == 0) + { + delete this; + return 0; + } + + return newCount; + } + + IFACEMETHODIMP GeometrySink::QueryInterface(IID const &riid, void **ppvObject) + { + if (__uuidof(IDWriteGeometrySink) == riid) { + *ppvObject = this; + } else if (__uuidof(IUnknown) == riid) { + *ppvObject = this; + } else { + *ppvObject = NULL; + return E_FAIL; + } + + AddRef(); + return S_OK; + } + +} + +/*! + \class QWindowsFontEngineDirectWrite + \brief Windows font engine using Direct Write. + \ingroup qt-lighthouse-win + + Font engine for subpixel positioned text on Windows Vista + (with platform update) and Windows 7. If selected during + configuration, the engine will be selected only when the hinting + preference of a font is set to None or Vertical hinting. The font + database uses most of the same logic but creates a direct write + font based on the LOGFONT rather than a GDI handle. + + The engine is currently regarded as experimental, meaning that code + using it should do substantial testing to make sure it covers their + use cases. + + Will probably be superseded by a common Free Type font engine in Qt 5.X. +*/ + +QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *directWriteFontFace, + qreal pixelSize, + const QSharedPointer &d) + + : m_fontEngineData(d) + , m_directWriteFontFace(directWriteFontFace) + , m_directWriteBitmapRenderTarget(0) + , m_lineThickness(-1) + , m_unitsPerEm(-1) + , m_ascent(-1) + , m_descent(-1) + , m_xHeight(-1) + , m_lineGap(-1) +{ + if (QWindowsContext::verboseFonts) + qDebug("%s %g", __FUNCTION__, pixelSize); + + d->directWriteFactory->AddRef(); + m_directWriteFontFace->AddRef(); + + fontDef.pixelSize = pixelSize; + collectMetrics(); +} + +QWindowsFontEngineDirectWrite::~QWindowsFontEngineDirectWrite() +{ + if (QWindowsContext::verboseFonts) + qDebug("%s", __FUNCTION__); + + m_fontEngineData->directWriteFactory->Release(); + m_directWriteFontFace->Release(); + + if (m_directWriteBitmapRenderTarget != 0) + m_directWriteBitmapRenderTarget->Release(); +} + +void QWindowsFontEngineDirectWrite::collectMetrics() +{ + if (m_directWriteFontFace != 0) { + DWRITE_FONT_METRICS metrics; + + m_directWriteFontFace->GetMetrics(&metrics); + m_unitsPerEm = metrics.designUnitsPerEm; + + m_lineThickness = DESIGN_TO_LOGICAL(metrics.underlineThickness); + m_ascent = DESIGN_TO_LOGICAL(metrics.ascent); + m_descent = DESIGN_TO_LOGICAL(metrics.descent); + m_xHeight = DESIGN_TO_LOGICAL(metrics.xHeight); + m_lineGap = DESIGN_TO_LOGICAL(metrics.lineGap); + } +} + +QFixed QWindowsFontEngineDirectWrite::lineThickness() const +{ + if (m_lineThickness > 0) + return m_lineThickness; + else + return QFontEngine::lineThickness(); +} + +bool QWindowsFontEngineDirectWrite::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + if (m_directWriteFontFace) { + DWORD t = qbswap(tag); + + const void *tableData = 0; + void *tableContext = 0; + UINT32 tableSize; + BOOL exists; + HRESULT hr = m_directWriteFontFace->TryGetFontTable( + t, &tableData, &tableSize, &tableContext, &exists + ); + + if (SUCCEEDED(hr)) { + if (!exists) + return false; + + if (buffer == 0) { + *length = tableSize; + return true; + } else if (*length < tableSize) { + return false; + } + + qMemCopy(buffer, tableData, tableSize); + m_directWriteFontFace->ReleaseFontTable(tableContext); + + return true; + } else { + qErrnoWarning("%s: TryGetFontTable failed", __FUNCTION__); + } + } + + return false; +} + +QFixed QWindowsFontEngineDirectWrite::emSquareSize() const +{ + if (m_unitsPerEm > 0) + return m_unitsPerEm; + else + return QFontEngine::emSquareSize(); +} + +inline unsigned int getChar(const QChar *str, int &i, const int len) +{ + unsigned int uc = str[i].unicode(); + if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) { + uint low = str[i+1].unicode(); + if (low >= 0xdc00 && low < 0xe000) { + uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000; + ++i; + } + } + return uc; +} + +bool QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, + int *nglyphs, QTextEngine::ShaperFlags flags) const +{ + if (m_directWriteFontFace != 0) { + QVarLengthArray codePoints(len); + for (int i=0; i glyphIndices(len); + HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(codePoints.data(), + len, + glyphIndices.data()); + + if (SUCCEEDED(hr)) { + for (int i=0; iglyphs[i] = glyphIndices[i]; + + *nglyphs = len; + + if (!(flags & QTextEngine::GlyphIndicesOnly)) + recalcAdvances(glyphs, 0); + + return true; + } else { + qErrnoWarning("%s: GetGlyphIndicesW failed", __FUNCTION__); + } + } + + return false; +} + +void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const +{ + if (m_directWriteFontFace == 0) + return; + + QVarLengthArray glyphIndices(glyphs->numGlyphs); + + // ### Caching? + for(int i=0; inumGlyphs; i++) + glyphIndices[i] = UINT16(glyphs->glyphs[i]); + + QVarLengthArray glyphMetrics(glyphIndices.size()); + HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(), + glyphIndices.size(), + glyphMetrics.data()); + if (SUCCEEDED(hr)) { + for (int i=0; inumGlyphs; ++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; + } + } else { + qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__); + } +} + +void QWindowsFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags) +{ + if (m_directWriteFontFace == 0) + return; + + QVarLengthArray glyphIndices(nglyphs); + QVarLengthArray glyphOffsets(nglyphs); + QVarLengthArray glyphAdvances(nglyphs); + + for (int i=0; iGetGlyphRunOutline( + fontDef.pixelSize, + glyphIndices.data(), + glyphAdvances.data(), + glyphOffsets.data(), + nglyphs, + false, + flags & QTextItem::RightToLeft, + &geometrySink + ); + + if (FAILED(hr)) + qErrnoWarning("%s: GetGlyphRunOutline failed", __FUNCTION__); +} + +glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(const QGlyphLayout &glyphs) +{ + if (glyphs.numGlyphs == 0) + return glyph_metrics_t(); + + bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics; + + QFixed w = 0; + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += round ? glyphs.effectiveAdvance(i).round() : glyphs.effectiveAdvance(i); + + } + + return glyph_metrics_t(0, -m_ascent, w - lastRightBearing(glyphs), m_ascent + m_descent, w, 0); +} + +glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(glyph_t g) +{ + if (m_directWriteFontFace == 0) + return glyph_metrics_t(); + + UINT16 glyphIndex = g; + + DWRITE_GLYPH_METRICS glyphMetrics; + HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics); + if (SUCCEEDED(hr)) { + QFixed advanceWidth = DESIGN_TO_LOGICAL(glyphMetrics.advanceWidth); + QFixed leftSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.leftSideBearing); + QFixed rightSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.rightSideBearing); + QFixed advanceHeight = DESIGN_TO_LOGICAL(glyphMetrics.advanceHeight); + QFixed verticalOriginY = DESIGN_TO_LOGICAL(glyphMetrics.verticalOriginY); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + advanceWidth = advanceWidth.round(); + advanceHeight = advanceHeight.round(); + } + + QFixed width = advanceWidth - leftSideBearing - rightSideBearing; + + return glyph_metrics_t(-leftSideBearing, -verticalOriginY, + width, m_ascent + m_descent, + advanceWidth, advanceHeight); + } else { + qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__); + } + + return glyph_metrics_t(); +} + +QFixed QWindowsFontEngineDirectWrite::ascent() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_ascent.round() + : m_ascent; +} + +QFixed QWindowsFontEngineDirectWrite::descent() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? (m_descent - 1).round() + : (m_descent - 1); +} + +QFixed QWindowsFontEngineDirectWrite::leading() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_lineGap.round() + : m_lineGap; +} + +QFixed QWindowsFontEngineDirectWrite::xHeight() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_xHeight.round() + : m_xHeight; +} + +qreal QWindowsFontEngineDirectWrite::maxCharWidth() const +{ + // ### + return 0; +} + +QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) +{ + QImage im = imageForGlyph(glyph, subPixelPosition, 0, QTransform()); + + QImage indexed(im.width(), im.height(), QImage::Format_Indexed8); + QVector colors(256); + for (int i=0; i<256; ++i) + colors[i] = qRgba(0, 0, 0, i); + indexed.setColorTable(colors); + + for (int y=0; ypow_gamma[qGray(0xffffffff - *src)] * 255. / 2047.); + ++dst; + ++src; + } + } + + return indexed; +} + +bool QWindowsFontEngineDirectWrite::supportsSubPixelPositions() const +{ + return true; +} + +QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t, + QFixed subPixelPosition, + int margin, + const QTransform &xform) +{ + glyph_metrics_t metrics = QFontEngine::boundingBox(t, xform); + int width = (metrics.width + margin * 2 + 4).ceil().toInt() ; + int height = (metrics.height + margin * 2 + 4).ceil().toInt(); + + UINT16 glyphIndex = t; + FLOAT glyphAdvance = metrics.xoff.toReal(); + + DWRITE_GLYPH_OFFSET glyphOffset; + glyphOffset.advanceOffset = 0; + glyphOffset.ascenderOffset = 0; + + DWRITE_GLYPH_RUN glyphRun; + glyphRun.fontFace = m_directWriteFontFace; + glyphRun.fontEmSize = fontDef.pixelSize; + glyphRun.glyphCount = 1; + glyphRun.glyphIndices = &glyphIndex; + glyphRun.glyphAdvances = &glyphAdvance; + glyphRun.isSideways = false; + glyphRun.bidiLevel = 0; + glyphRun.glyphOffsets = &glyphOffset; + + QFixed x = margin - metrics.x.round() + subPixelPosition; + QFixed y = margin - metrics.y.floor(); + + DWRITE_MATRIX transform; + transform.dx = x.toReal(); + transform.dy = y.toReal(); + transform.m11 = xform.m11(); + transform.m12 = xform.m12(); + transform.m21 = xform.m21(); + transform.m22 = xform.m22(); + + IDWriteGlyphRunAnalysis *glyphAnalysis = NULL; + HRESULT hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis( + &glyphRun, + 1.0f, + &transform, + DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, + DWRITE_MEASURING_MODE_NATURAL, + 0.0, 0.0, + &glyphAnalysis + ); + + if (SUCCEEDED(hr)) { + RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = width; + rect.bottom = height; + + int size = width * height * 3; + BYTE *alphaValues = new BYTE[size]; + qMemSet(alphaValues, size, 0); + + hr = glyphAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, + &rect, + alphaValues, + size); + + if (SUCCEEDED(hr)) { + QImage img(width, height, QImage::Format_RGB32); + img.fill(0xffffffff); + + for (int y=0; y(img.scanLine(y)); + BYTE *src = alphaValues + width * 3 * y; + + for (int x=0; xRelease(); + + return img; + } else { + delete[] alphaValues; + glyphAnalysis->Release(); + + qErrnoWarning("%s: CreateAlphaTexture failed", __FUNCTION__); + } + + } else { + qErrnoWarning("%s: CreateGlyphRunAnalysis failed", __FUNCTION__); + } + + return QImage(); +} + +QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t, + QFixed subPixelPosition, + int margin, + const QTransform &xform) +{ + QImage mask = imageForGlyph(t, subPixelPosition, margin, xform); + return mask.depth() == 32 + ? mask + : mask.convertToFormat(QImage::Format_RGB32); +} + +const char *QWindowsFontEngineDirectWrite::name() const +{ + return 0; +} + +bool QWindowsFontEngineDirectWrite::canRender(const QChar *string, int len) +{ + QVarLengthArray codePoints(len); + int actualLength = 0; + for (int i=0; i glyphIndices(actualLength); + HRESULT hr = m_directWriteFontFace->GetGlyphIndices(codePoints.data(), actualLength, + glyphIndices.data()); + if (FAILED(hr)) { + qErrnoWarning("%s: GetGlyphIndices failed", __FUNCTION__); + return false; + } else { + for (int i=0; ifontDef = fontDef; + fontEngine->fontDef.pixelSize = pixelSize; + + return fontEngine; +} + +void QWindowsFontEngineDirectWrite::initFontInfo(const QFontDef &request, + int dpi, IDWriteFont *font) +{ + fontDef = request; + + IDWriteFontFamily *fontFamily = NULL; + HRESULT hr = font->GetFontFamily(&fontFamily); + + IDWriteLocalizedStrings *familyNames = NULL; + if (SUCCEEDED(hr)) + hr = fontFamily->GetFamilyNames(&familyNames); + + UINT32 index = 0; + BOOL exists = false; + + wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; + + if (SUCCEEDED(hr)) { + int defaultLocaleSuccess = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH); + + if (defaultLocaleSuccess) + hr = familyNames->FindLocaleName(localeName, &index, &exists); + + if (SUCCEEDED(hr) && !exists) + hr = familyNames->FindLocaleName(L"en-us", &index, &exists); + } + + if (!exists) + index = 0; + + UINT32 length = 0; + if (SUCCEEDED(hr)) + hr = familyNames->GetStringLength(index, &length); + + wchar_t *name = new (std::nothrow) wchar_t[length+1]; + if (name == NULL) + hr = E_OUTOFMEMORY; + + // Get the family name. + if (SUCCEEDED(hr)) + hr = familyNames->GetString(index, name, length + 1); + + if (SUCCEEDED(hr)) + fontDef.family = QString::fromWCharArray(name); + + delete[] name; + if (familyNames != NULL) + familyNames->Release(); + + if (FAILED(hr)) + qErrnoWarning(hr, "initFontInfo: Failed to get family name"); + + if (fontDef.pointSize < 0) + fontDef.pointSize = fontDef.pixelSize * 72. / dpi; + else if (fontDef.pixelSize == -1) + fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.); +} + +QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyName) +{ + static const char keyC[] = "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\" + "FontSubstitutes"; + return QSettings(QLatin1String(keyC), QSettings::NativeFormat).value(familyName, familyName).toString(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_DIRECTWRITE diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h new file mode 100644 index 0000000000..1333720481 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSFONTENGINEDIRECTWRITE_H +#define QWINDOWSFONTENGINEDIRECTWRITE_H + +#ifndef QT_NO_DIRECTWRITE + +// Enable access to HB_Face in harfbuzz includes included by qfontengine_p.h. +#define QT_BUILD_GUI_LIB +#include +#undef QT_BUILD_GUI_LIB + +#include + +class QWindowsFontEngineData; + +struct IDWriteFont ; +struct IDWriteFontFace ; +struct IDWriteFactory ; +struct IDWriteBitmapRenderTarget ; +struct IDWriteGdiInterop ; + +QT_BEGIN_NAMESPACE + +class QWindowsFontEngineDirectWrite : public QFontEngine +{ + Q_OBJECT +public: + explicit QWindowsFontEngineDirectWrite(IDWriteFontFace *directWriteFontFace, + qreal pixelSize, + const QSharedPointer &d); + ~QWindowsFontEngineDirectWrite(); + + void initFontInfo(const QFontDef &request, int dpi, IDWriteFont *font); + + QFixed lineThickness() const; + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; + QFixed emSquareSize() const; + + bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const; + void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const; + + void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags); + + glyph_metrics_t boundingBox(const QGlyphLayout &glyphs); + glyph_metrics_t boundingBox(glyph_t g); + + QFixed ascent() const; + QFixed descent() const; + QFixed leading() const; + QFixed xHeight() const; + qreal maxCharWidth() const; + + const char *name() const; + + bool supportsSubPixelPositions() const; + + QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition); + QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, int margin, + const QTransform &xform); + + QFontEngine *cloneWithSize(qreal pixelSize) const; + + bool canRender(const QChar *string, int len); + Type type() const; + + static QString fontNameSubstitute(const QString &familyName); + +private: + friend class QRawFontPrivate; + + QImage imageForGlyph(glyph_t t, QFixed subPixelPosition, int margin, const QTransform &xform); + void collectMetrics(); + + const QSharedPointer m_fontEngineData; + + IDWriteFontFace *m_directWriteFontFace; + IDWriteBitmapRenderTarget *m_directWriteBitmapRenderTarget; + + QFixed m_lineThickness; + int m_unitsPerEm; + QFixed m_ascent; + QFixed m_descent; + QFixed m_xHeight; + QFixed m_lineGap; + FaceId m_faceId; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_DIRECTWRITE + +#endif // QWINDOWSFONTENGINEDIRECTWRITE_H diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp new file mode 100644 index 0000000000..e67f61a792 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -0,0 +1,974 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsglcontext.h" +#include "qwindowscontext.h" +#include "qwindowswindow.h" + +#include +#include + +#include +#if defined(Q_CC_MINGW) +# include +#else +# include +#endif + +// #define DEBUG_GL + +// ARB extension API +#ifndef WGL_ARB_multisample +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#endif + +#ifndef WGL_ARB_pixel_format +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_DRAW_TO_BITMAP_ARB 0x2002 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NEED_PALETTE_ARB 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 +#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 +#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 +#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A +#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B +#define WGL_SHARE_DEPTH_ARB 0x200C +#define WGL_SHARE_STENCIL_ARB 0x200D +#define WGL_SHARE_ACCUM_ARB 0x200E +#define WGL_SUPPORT_GDI_ARB 0x200F +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201A +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ALPHA_SHIFT_ARB 0x201C +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_SWAP_EXCHANGE_ARB 0x2028 +#define WGL_SWAP_COPY_ARB 0x2029 +#define WGL_SWAP_UNDEFINED_ARB 0x202A +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C +#endif + +#ifndef WGL_ARB_create_context +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x0001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x0002 +// Error codes returned by GetLastError(). +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#endif + +#ifndef GL_VERSION_3_2 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001 +#endif + +QT_BEGIN_NAMESPACE + +template inline bool testFlag(MaskType mask, FlagType flag) +{ + return (mask & MaskType(flag)) != 0; +} + +static inline bool hasGLOverlay(const PIXELFORMATDESCRIPTOR &pd) +{ return (pd.bReserved & 0x0f) != 0; } + +static inline bool isDirectRendering(const PIXELFORMATDESCRIPTOR &pfd) +{ return (pfd.dwFlags & PFD_GENERIC_ACCELERATED) || !(pfd.dwFlags & PFD_GENERIC_FORMAT); } + +static inline void initPixelFormatDescriptor(PIXELFORMATDESCRIPTOR *d) +{ + memset(d, 0, sizeof(PIXELFORMATDESCRIPTOR)); + d->nSize = sizeof(PIXELFORMATDESCRIPTOR); + d->nVersion = 1; +} + +QDebug operator<<(QDebug d, const PIXELFORMATDESCRIPTOR &pd) +{ + QDebug nsp = d.nospace(); + nsp << "PIXELFORMATDESCRIPTOR " + << "dwFlags=" << hex << showbase << pd.dwFlags << dec << noshowbase; + if (pd.dwFlags & PFD_DRAW_TO_WINDOW) nsp << " PFD_DRAW_TO_WINDOW"; + if (pd.dwFlags & PFD_DRAW_TO_BITMAP) nsp << " PFD_DRAW_TO_BITMAP"; + if (pd.dwFlags & PFD_SUPPORT_GDI) nsp << " PFD_SUPPORT_GDI"; + if (pd.dwFlags & PFD_SUPPORT_OPENGL) nsp << " PFD_SUPPORT_OPENGL"; + if (pd.dwFlags & PFD_GENERIC_ACCELERATED) nsp << " PFD_GENERIC_ACCELERATED"; + if (pd.dwFlags & PFD_SUPPORT_DIRECTDRAW) nsp << " PFD_SUPPORT_DIRECTDRAW"; + if (pd.dwFlags & PFD_DIRECT3D_ACCELERATED) nsp << " PFD_DIRECT3D_ACCELERATED"; + if (pd.dwFlags & PFD_SUPPORT_COMPOSITION) nsp << " PFD_SUPPORT_COMPOSITION"; + if (pd.dwFlags & PFD_GENERIC_FORMAT) nsp << " PFD_GENERIC_FORMAT"; + if (pd.dwFlags & PFD_NEED_PALETTE) nsp << " PFD_NEED_PALETTE"; + if (pd.dwFlags & PFD_NEED_SYSTEM_PALETTE) nsp << " PFD_NEED_SYSTEM_PALETTE"; + if (pd.dwFlags & PFD_DOUBLEBUFFER) nsp << " PFD_DOUBLEBUFFER"; + if (pd.dwFlags & PFD_STEREO) nsp << " PFD_STEREO"; + if (pd.dwFlags & PFD_SWAP_LAYER_BUFFERS) nsp << " PFD_SWAP_LAYER_BUFFERS"; + if (hasGLOverlay(pd)) nsp << " overlay"; + nsp << " iPixelType=" << pd.iPixelType << " cColorBits=" << pd.cColorBits + << " cRedBits=" << pd.cRedBits << " cRedShift=" << pd.cRedShift + << " cGreenBits=" << pd.cGreenBits << " cGreenShift=" << pd.cGreenShift + << " cBlueBits=" << pd.cBlueBits << " cBlueShift=" << pd.cBlueShift; + nsp << " cDepthBits=" << pd.cDepthBits; + if (pd.cStencilBits) + nsp << " cStencilBits=" << pd.cStencilBits; + if (pd.cAuxBuffers) + nsp << " cAuxBuffers=" << pd.cAuxBuffers; + nsp << " iLayerType=" << pd.iLayerType; + if (pd.dwVisibleMask) + nsp << " dwVisibleMask=" << pd.dwVisibleMask; + if (pd.cAlphaBits) + nsp << " cAlphaBits=" << pd.cAlphaBits << " cAlphaShift=" << pd.cAlphaShift; + if (pd.cAccumBits) + nsp << " cAccumBits=" << pd.cAccumBits << " cAccumRedBits=" << pd.cAccumRedBits + << " cAccumGreenBits=" << pd.cAccumGreenBits << " cAccumBlueBits=" << pd.cAccumBlueBits + << " cAccumAlphaBits=" << pd.cAccumAlphaBits; + return d; +} + +// Check whether an obtained PIXELFORMATDESCRIPTOR matches the request. +static inline bool + isAcceptableFormat(const QWindowsOpenGLAdditionalFormat &additional, + const PIXELFORMATDESCRIPTOR &pfd, + bool ignoreGLSupport = false) // ARB format may not contain it. +{ + const bool pixmapRequested = testFlag(additional.formatFlags, QWindowsGLRenderToPixmap); + return (ignoreGLSupport || testFlag(pfd.dwFlags, PFD_SUPPORT_OPENGL)) + && testFlag(pfd.dwFlags, PFD_DRAW_TO_BITMAP) == pixmapRequested + && hasGLOverlay(pfd) == testFlag(additional.formatFlags, QWindowsGLOverlay) + && (!pixmapRequested || pfd.cColorBits == additional.pixmapDepth); +} + +static void describeFormats(HDC hdc) +{ + const int pfiMax = DescribePixelFormat(hdc, 0, 0, NULL); + for (int i = 0; i < pfiMax; i++) { + PIXELFORMATDESCRIPTOR pfd; + initPixelFormatDescriptor(&pfd); + DescribePixelFormat(hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd); + qDebug() << '#' << i << '/' << pfiMax << ':' << pfd; + } +} + +// Classic GDI API +namespace GDI { +static QSurfaceFormat + qSurfaceFormatFromPixelFormat(const PIXELFORMATDESCRIPTOR &pfd, + QWindowsOpenGLAdditionalFormat *additionalIn = 0) +{ + QSurfaceFormat format; + if (pfd.dwFlags & PFD_DOUBLEBUFFER) + format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + format.setDepthBufferSize(pfd.cDepthBits); + + if (pfd.iPixelType == PFD_TYPE_RGBA) + format.setAlphaBufferSize(pfd.cAlphaBits); + format.setRedBufferSize(pfd.cRedBits); + format.setGreenBufferSize(pfd.cGreenBits); + format.setBlueBufferSize(pfd.cBlueBits); + format.setStencilBufferSize(pfd.cStencilBits); + format.setStereo(pfd.dwFlags & PFD_STEREO); + if (additionalIn) { + QWindowsOpenGLAdditionalFormat additional; + if (isDirectRendering(pfd)) + additional.formatFlags |= QWindowsGLDirectRendering; + if (hasGLOverlay(pfd)) + additional.formatFlags |= QWindowsGLOverlay; + if (pfd.cAccumRedBits) + additional.formatFlags |= QWindowsGLAccumBuffer; + if (testFlag(pfd.dwFlags, PFD_DRAW_TO_BITMAP)) { + additional.formatFlags |= QWindowsGLRenderToPixmap; + additional.pixmapDepth = pfd.cColorBits; + } + *additionalIn = additional; + } + return format; +} + +static PIXELFORMATDESCRIPTOR + qPixelFormatFromSurfaceFormat(const QSurfaceFormat &format, + const QWindowsOpenGLAdditionalFormat &additional) +{ + PIXELFORMATDESCRIPTOR pfd; + initPixelFormatDescriptor(&pfd); + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.dwFlags = PFD_SUPPORT_OPENGL; + if (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA) + pfd.dwFlags = PFD_SUPPORT_COMPOSITION; + const bool isPixmap = (additional.formatFlags & QWindowsGLRenderToPixmap) != 0; + pfd.dwFlags |= isPixmap ? PFD_DRAW_TO_BITMAP : PFD_DRAW_TO_WINDOW; + if (!(additional.formatFlags & QWindowsGLDirectRendering)) + pfd.dwFlags |= PFD_GENERIC_FORMAT; + + if (format.stereo()) + pfd.dwFlags |= PFD_STEREO; + if (format.swapBehavior() == QSurfaceFormat::DoubleBuffer && !isPixmap) + pfd.dwFlags |= PFD_DOUBLEBUFFER; + pfd.cDepthBits = + format.depthBufferSize() >= 0 ? format.depthBufferSize() : 32; + pfd.cAlphaBits = format.alphaBufferSize() > 0 ? format.alphaBufferSize() : 8; + pfd.cStencilBits = format.stencilBufferSize() > 0 ? format.stencilBufferSize() : 8; + if (additional.formatFlags & QWindowsGLAccumBuffer) + pfd.cAccumRedBits = pfd.cAccumGreenBits = pfd.cAccumBlueBits = pfd.cAccumAlphaBits = 16; + return pfd; +} + +// Choose a suitable pixelformat using GDI WinAPI in case ARB +// functions cannot be found. First tries to find a suitable +// format using GDI function ChoosePixelFormat(). Since that +// does not handle overlay and direct-rendering requests, manually loop +// over the available formats to find the best one. +// Note: As of Windows 7, it seems direct-rendering is handled, so, +// the code might be obsolete? +static int choosePixelFormat(HDC hdc, const QSurfaceFormat &format, + const QWindowsOpenGLAdditionalFormat &additional, + PIXELFORMATDESCRIPTOR *obtainedPfd) +{ + // 1) Try ChoosePixelFormat(). + PIXELFORMATDESCRIPTOR requestedPfd = qPixelFormatFromSurfaceFormat(format, QWindowsGLDirectRendering); + initPixelFormatDescriptor(obtainedPfd); + int pixelFormat = ChoosePixelFormat(hdc, &requestedPfd); + if (pixelFormat >= 0) { + DescribePixelFormat(hdc, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), obtainedPfd); + if (isAcceptableFormat(additional, *obtainedPfd)) + return pixelFormat; + } + // 2) No matching format found, manual search loop. + const int pfiMax = DescribePixelFormat(hdc, 0, 0, NULL); + int bestScore = -1; + int bestPfi = -1; + const bool stereoRequested = format.stereo(); + const bool accumBufferRequested = testFlag(additional.formatFlags, QWindowsGLAccumBuffer); + const bool doubleBufferRequested = format.swapBehavior() == QSurfaceFormat::DoubleBuffer; + const bool directRenderingRequested = testFlag(additional.formatFlags, QWindowsGLDirectRendering); + for (int pfi = 1; pfi <= pfiMax; pfi++) { + PIXELFORMATDESCRIPTOR checkPfd; + initPixelFormatDescriptor(&checkPfd); + DescribePixelFormat(hdc, pfi, sizeof(PIXELFORMATDESCRIPTOR), &checkPfd); + if (isAcceptableFormat(additional, checkPfd)) { + int score = checkPfd.cColorBits + checkPfd.cAlphaBits + checkPfd.cStencilBits; + if (accumBufferRequested) + score += checkPfd.cAccumBits; + if (doubleBufferRequested == testFlag(checkPfd.dwFlags, PFD_DOUBLEBUFFER)) + score += 1000; + if (stereoRequested == testFlag(checkPfd.dwFlags, PFD_STEREO)) + score += 2000; + if (directRenderingRequested == isDirectRendering(checkPfd)) + score += 4000; + if (checkPfd.iPixelType == PFD_TYPE_RGBA) + score += 8000; + if (score > bestScore) { + bestScore = score; + bestPfi = pfi; + *obtainedPfd = checkPfd; + } + if (QWindowsContext::verboseGL) + qDebug() << __FUNCTION__ << " checking " << pfi << '/' << pfiMax + << " score=" << score << " (best " << bestPfi << '/' << bestScore + << ") " << checkPfd; + } + } // for + if (bestPfi > 0) + pixelFormat = bestPfi; + return pixelFormat; +} + +static inline HGLRC createContext(HDC hdc, HGLRC shared) +{ + HGLRC result = wglCreateContext(hdc); + if (!result) { + qErrnoWarning("%s: wglCreateContext failed.", __FUNCTION__); + return 0; + } + if (shared && !wglShareLists(shared, result)) + qErrnoWarning("%s: wglShareLists() failed.", __FUNCTION__); + return result; +} +} // namespace GDI + +// ARB OpenGL extension API +namespace ARB { +// Choose a suitable pixelformat using ARB extension functions. +static int choosePixelFormat(HDC hdc, + const QOpenGLStaticContext &staticContext, + const QSurfaceFormat &format, + const QWindowsOpenGLAdditionalFormat &additional, + PIXELFORMATDESCRIPTOR *obtainedPfd) +{ + enum { attribSize =40 }; + if ((additional.formatFlags & QWindowsGLRenderToPixmap) || !staticContext.hasExtensions()) + return 0; + + int iAttributes[attribSize]; + qFill(iAttributes, iAttributes + attribSize, int(0)); + int i = 0; + iAttributes[i++] = WGL_ACCELERATION_ARB; + iAttributes[i++] = testFlag(additional.formatFlags, QWindowsGLDirectRendering) ? + WGL_FULL_ACCELERATION_ARB : WGL_NO_ACCELERATION_ARB; + iAttributes[i++] = WGL_SUPPORT_OPENGL_ARB; + iAttributes[i++] = TRUE; + iAttributes[i++] = WGL_DRAW_TO_WINDOW_ARB; + iAttributes[i++] = TRUE; + iAttributes[i++] = WGL_COLOR_BITS_ARB; + iAttributes[i++] = 24; + switch (format.swapBehavior()) { + case QSurfaceFormat::DefaultSwapBehavior: + case QSurfaceFormat::TripleBuffer: + break; + case QSurfaceFormat::SingleBuffer: + iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; + iAttributes[i++] = FALSE; + break; + case QSurfaceFormat::DoubleBuffer: + iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; + iAttributes[i++] = TRUE; + break; + } + if (format.stereo()) { + iAttributes[i++] = WGL_STEREO_ARB; + iAttributes[i++] = TRUE; + } + if (format.depthBufferSize() >= 0) { + iAttributes[i++] = WGL_DEPTH_BITS_ARB; + iAttributes[i++] = format.depthBufferSize(); + } + iAttributes[i++] = WGL_PIXEL_TYPE_ARB; + iAttributes[i++] = WGL_TYPE_RGBA_ARB; + if (format.redBufferSize() >= 0) { + iAttributes[i++] = WGL_RED_BITS_ARB; + iAttributes[i++] = format.redBufferSize(); + } + if (format.greenBufferSize() >= 0) { + iAttributes[i++] = WGL_GREEN_BITS_ARB; + iAttributes[i++] = format.greenBufferSize(); + } + if (format.blueBufferSize() >= 0) { + iAttributes[i++] = WGL_BLUE_BITS_ARB; + iAttributes[i++] = format.blueBufferSize(); + } + iAttributes[i++] = WGL_ALPHA_BITS_ARB; + iAttributes[i++] = format.alphaBufferSize() >= 0 ? format.alphaBufferSize() : 8; + if (additional.formatFlags & QWindowsGLAccumBuffer) { + iAttributes[i++] = WGL_ACCUM_BITS_ARB; + iAttributes[i++] = 16; + } + iAttributes[i++] = WGL_STENCIL_BITS_ARB; + iAttributes[i++] = 8; + if (additional.formatFlags & QWindowsGLOverlay) { + iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB; + iAttributes[i++] = 1; + } + const bool sampleBuffersRequested = format.samples() > 1 + && testFlag(staticContext.extensions, QOpenGLStaticContext::SampleBuffers); + int samplesValuePosition = 0; + int samplesEnabledPosition = 0; + if (sampleBuffersRequested) { + iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; + samplesEnabledPosition = i; + iAttributes[i++] = TRUE; + iAttributes[i++] = WGL_SAMPLES_ARB; + samplesValuePosition = i; + iAttributes[i++] = format.samples(); + } + // If sample buffer request cannot be satisfied, reduce request. + int pixelFormat = 0; + uint numFormats = 0; + while (true) { + const bool valid = + staticContext.wglChoosePixelFormatARB(hdc, iAttributes, 0, 1, + &pixelFormat, &numFormats) + && numFormats >= 1; + if (valid || !sampleBuffersRequested) + break; + if (iAttributes[samplesValuePosition] > 1) { + iAttributes[samplesValuePosition] /= 2; + } else { + break; + } + } + // Verify if format is acceptable. Note that the returned + // formats have been observed to not contain PFD_SUPPORT_OPENGL, ignore. + initPixelFormatDescriptor(obtainedPfd); + DescribePixelFormat(hdc, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), obtainedPfd); + if (!isAcceptableFormat(additional, *obtainedPfd, true)) { + if (QWindowsContext::verboseGL) + qDebug() << __FUNCTION__ << " obtained px #" << pixelFormat + << " not acceptable=" << *obtainedPfd; + pixelFormat = 0; + } + + if (QWindowsContext::verboseGL) { + QDebug nsp = qDebug().nospace(); + nsp << __FUNCTION__; + if (sampleBuffersRequested) + nsp << " samples=" << iAttributes[samplesValuePosition]; + nsp << " Attributes: " << hex << showbase; + for (int ii = 0; ii < i; ++ii) + nsp << iAttributes[ii] << ','; + nsp << noshowbase << dec << "\n obtained px #" << pixelFormat + << " of " << numFormats << "\n " << *obtainedPfd; + } // Debug + + return pixelFormat; +} + +static QSurfaceFormat + qSurfaceFormatFromHDC(const QOpenGLStaticContext &staticContext, + HDC hdc, int pixelFormat, + QWindowsOpenGLAdditionalFormat *additionalIn = 0) +{ + enum { attribSize =40 }; + + QSurfaceFormat result; + if (!staticContext.hasExtensions()) + return result; + int iAttributes[attribSize]; + int iValues[attribSize]; + qFill(iAttributes, iAttributes + attribSize, int(0)); + qFill(iValues, iValues + attribSize, int(0)); + + int i = 0; + const bool hasSampleBuffers = testFlag(staticContext.extensions, QOpenGLStaticContext::SampleBuffers); + + iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; // 0 + iAttributes[i++] = WGL_DEPTH_BITS_ARB; // 1 + iAttributes[i++] = WGL_PIXEL_TYPE_ARB; // 2 + iAttributes[i++] = WGL_RED_BITS_ARB; // 3 + iAttributes[i++] = WGL_GREEN_BITS_ARB; // 4 + iAttributes[i++] = WGL_BLUE_BITS_ARB; // 5 + iAttributes[i++] = WGL_ALPHA_BITS_ARB; // 6 + iAttributes[i++] = WGL_ACCUM_BITS_ARB; // 7 + iAttributes[i++] = WGL_STENCIL_BITS_ARB; // 8 + iAttributes[i++] = WGL_STEREO_ARB; // 9 + iAttributes[i++] = WGL_ACCELERATION_ARB; // 10 + iAttributes[i++] = WGL_NUMBER_OVERLAYS_ARB; // 11 + if (hasSampleBuffers) { + iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; // 12 + iAttributes[i++] = WGL_SAMPLES_ARB; // 13 + } + if (!staticContext.wglGetPixelFormatAttribIVARB(hdc, pixelFormat, 0, i, + iAttributes, iValues)) + return result; + if (iValues[0]) + result.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + result.setDepthBufferSize(iValues[1]); + result.setRedBufferSize(iValues[3]); + result.setGreenBufferSize(iValues[4]); + result.setBlueBufferSize(iValues[5]); + result.setAlphaBufferSize(iValues[6]); + result.setStencilBufferSize(iValues[8]); + result.setStereo(iValues[9]); + if (hasSampleBuffers) + result.setSamples(iValues[13]); + if (additionalIn) { + if (iValues[7]) + additionalIn->formatFlags |= QWindowsGLAccumBuffer; + if (iValues[10] == WGL_FULL_ACCELERATION_ARB) + additionalIn->formatFlags |= QWindowsGLDirectRendering; + if (iValues[11]) + additionalIn->formatFlags |= QWindowsGLOverlay; + } + return result; +} + +static HGLRC createContext(const QOpenGLStaticContext &staticContext, + HDC hdc, + const QSurfaceFormat &format, + const QWindowsOpenGLAdditionalFormat &additional, + int majorVersion = 0, + int minorVersion = 0, + HGLRC shared = 0) +{ + enum { attribSize = 11 }; + + if (!staticContext.hasExtensions()) + return 0; + int attributes[attribSize]; + int attribIndex = 0; + qFill(attributes, attributes + attribSize, int(0)); + + if (majorVersion) { + attributes[attribIndex++] = WGL_CONTEXT_MAJOR_VERSION_ARB; + attributes[attribIndex++] = majorVersion; + attributes[attribIndex++] = WGL_CONTEXT_MINOR_VERSION_ARB; + attributes[attribIndex++] = minorVersion; + } + if (majorVersion >= 3 && additional.formatFlags & QWindowsGLDeprecatedFunctions) { + attributes[attribIndex++] = WGL_CONTEXT_FLAGS_ARB; + attributes[attribIndex++] = WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + } + if ((staticContext.majorVersion == 3 && staticContext.minorVersion >= 2) + || staticContext.majorVersion > 3) { + const QSurfaceFormat::OpenGLContextProfile profile = QSurfaceFormat::NoProfile; + // format.profile(): TODO: Not implemented yet. + Q_UNUSED(format); + switch (profile) { + case QSurfaceFormat::NoProfile: + break; + case QSurfaceFormat::CoreProfile: + attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB; + attributes[attribIndex++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + break; + case QSurfaceFormat::CompatibilityProfile: + attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB; + attributes[attribIndex++] = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + break; + } + } + const HGLRC result = + staticContext.wglCreateContextAttribsARB(hdc, shared, attributes); + if (!result) + qErrnoWarning("%s: wglCreateContextAttribsARB() failed.", __FUNCTION__); + return result; +} + +} // namespace ARB + +// Helpers for temporary contexts +static inline HWND createDummyGLWindow() +{ + return QWindowsContext::instance()-> + createDummyWindow(QStringLiteral("QtOpenGLDummyWindow"), + L"OpenGLDummyWindow", 0, WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); +} + +// Create a dummy GL context (see QOpenGLTemporaryContext). +static inline HGLRC createDummyGLContext(HDC dc) +{ + if (!dc) + return 0; + PIXELFORMATDESCRIPTOR pixelFormDescriptor; + initPixelFormatDescriptor(&pixelFormDescriptor); + pixelFormDescriptor.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_GENERIC_FORMAT; + pixelFormDescriptor.iPixelType = PFD_TYPE_RGBA; + const int pixelFormat = ChoosePixelFormat(dc, &pixelFormDescriptor); + if (!pixelFormat) { + qErrnoWarning("%s: ChoosePixelFormat failed.", __FUNCTION__); + return 0; + } + if (!SetPixelFormat(dc, pixelFormat, &pixelFormDescriptor)) { + qErrnoWarning("%s: SetPixelFormat failed.", __FUNCTION__); + return 0; + } + HGLRC rc = wglCreateContext(dc); + if (!rc) { + qErrnoWarning("%s: wglCreateContext failed.", __FUNCTION__); + return 0; + } + return rc; +} + +static inline QOpenGLContextData currentOpenGLContextData() +{ + QOpenGLContextData result; + result.hdc = wglGetCurrentDC(); + result.renderingContext = wglGetCurrentContext(); + return result; +} + +static inline QOpenGLContextData createDummyWindowOpenGLContextData() +{ + QOpenGLContextData result; + result.hwnd = createDummyGLWindow(); + result.hdc = GetDC(result.hwnd); + result.renderingContext = createDummyGLContext(result.hdc); + return result; +} + +/*! + \class QOpenGLTemporaryContext + \brief A temporary context that can be instantiated on the stack. + + Functions like wglGetProcAddress() or glGetString() only work if there + is a current GL context. + + \ingroup qt-lighthouse-win +*/ + +class QOpenGLTemporaryContext +{ + Q_DISABLE_COPY(QOpenGLTemporaryContext) +public: + QOpenGLTemporaryContext(); + ~QOpenGLTemporaryContext(); + +private: + const QOpenGLContextData m_previous; + const QOpenGLContextData m_current; +}; + +QOpenGLTemporaryContext::QOpenGLTemporaryContext() : + m_previous(currentOpenGLContextData()), + m_current(createDummyWindowOpenGLContextData()) +{ + wglMakeCurrent(m_current.hdc, m_current.renderingContext); +} + +QOpenGLTemporaryContext::~QOpenGLTemporaryContext() +{ + wglMakeCurrent(m_previous.hdc, m_previous.renderingContext); + ReleaseDC(m_current.hwnd, m_current.hdc); + DestroyWindow(m_current.hwnd); + wglDeleteContext(m_current.renderingContext); +} + +/*! + \class QWindowsOpenGLAdditionalFormat + \brief Additional format information that is not in QSurfaceFormat + \ingroup qt-lighthouse-win +*/ + +/*! + \class QOpenGLStaticContext + \brief Static Open GL context containing version information, extension function pointers, etc. + + Functions pending integration in the next version of OpenGL are post-fixed ARB. + + \note Initialization requires an active context (see create()). + + \sa QWindowsGLContext + \ingroup qt-lighthouse-win +*/ + +#define SAMPLE_BUFFER_EXTENSION "GL_ARB_multisample" + +QOpenGLStaticContext::QOpenGLStaticContext() : + vendor(QOpenGLStaticContext::getGlString(GL_VENDOR)), + renderer(QOpenGLStaticContext::getGlString(GL_RENDERER)), + extensionNames(QOpenGLStaticContext::getGlString(GL_EXTENSIONS)), + majorVersion(0), minorVersion(0), + extensions(0), + wglGetPixelFormatAttribIVARB((WglGetPixelFormatAttribIVARB)wglGetProcAddress("wglGetPixelFormatAttribivARB")), + wglChoosePixelFormatARB((WglChoosePixelFormatARB)wglGetProcAddress("wglChoosePixelFormatARB")), + wglCreateContextAttribsARB((WglCreateContextAttribsARB)wglGetProcAddress("wglCreateContextAttribsARB")) +{ + if (extensionNames.startsWith(SAMPLE_BUFFER_EXTENSION" ") + || extensionNames.indexOf(" "SAMPLE_BUFFER_EXTENSION" ") != -1) + extensions |= SampleBuffers; + // Get version + do { + const QByteArray version = QOpenGLStaticContext::getGlString(GL_VERSION); + if (version.isEmpty()) + break; + const int majorDot = version.indexOf('.'); + if (majorDot == -1) + break; + int minorDot = version.indexOf('.', majorDot + 1); + if (minorDot == -1) + minorDot = version.size(); + majorVersion = version.mid(0, majorDot).toInt(); + minorVersion = version.mid(majorDot + 1, minorDot - majorDot - 1).toInt(); + } while (false); +} + +QByteArray QOpenGLStaticContext::getGlString(unsigned int which) +{ + if (const GLubyte *s = glGetString(which)) + return QByteArray((const char*)s); + return QByteArray(); +} + +QOpenGLStaticContext *QOpenGLStaticContext::create() +{ + // We need a current context for wglGetProcAdress()/getGLString() to work. + QScopedPointer temporaryContext; + if (!wglGetCurrentContext()) + temporaryContext.reset(new QOpenGLTemporaryContext); + QOpenGLStaticContext *result = new QOpenGLStaticContext; + if (QWindowsContext::verboseGL) + qDebug() << __FUNCTION__ << *result; + return result; +} + +QDebug operator<<(QDebug d, const QOpenGLStaticContext &s) +{ + QDebug nsp = d.nospace(); + nsp << "OpenGL: " << s.vendor << ',' << s.renderer << ",v" + << s.majorVersion << '.' << s.minorVersion; + if (s.extensions & QOpenGLStaticContext::SampleBuffers) + nsp << ",SampleBuffers"; + if (s.hasExtensions()) + nsp << ", Extension-API present"; + nsp << "\nExtensions: " << s.extensionNames; + return d; +} + +/*! + \class QWindowsGLContext + \brief Open GL context. + + An Open GL context for use with several windows. + As opposed to other implementations, activating a GL context for + a window requires a HDC allocated for it. The first time this + HDC is created for the window, the pixel format must be applied, + which will affect the window as well. The HDCs are stored in a list of + QOpenGLContextData and are released in doneCurrent(). + + \ingroup qt-lighthouse-win +*/ + +QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContext, + QGuiGLContext *context) : + m_staticContext(staticContext), + m_context(context), + m_pixelFormat(0), m_extensionsUsed(false) +{ + // workaround for matrox driver: + // make a cheap call to opengl to force loading of DLL + static bool opengl32dll = false; + if (!opengl32dll) { + GLint params; + glGetIntegerv(GL_DEPTH_BITS, ¶ms); + opengl32dll = true; + } + + // SetPixelFormat (as of Windows 7) requires a real window. + // Create a dummy one as we are not associated with a window yet. + // Try to find a suitable pixel format using preferably ARB extensions + // (default to GDI) and store that. + HWND dummyWindow = 0; + HDC hdc = 0; + do { + dummyWindow = createDummyGLWindow(); + if (!dummyWindow) + break; + hdc = GetDC(dummyWindow); + if (!hdc) + break; + + if (QWindowsContext::verboseGL > 1) + describeFormats(hdc); + // Preferably use direct rendering and ARB extensions (unless pixmap) + const QWindowsOpenGLAdditionalFormat + requestedAdditional(QWindowsGLDirectRendering|QWindowsGLDeprecatedFunctions); + const bool tryExtensions = m_staticContext->hasExtensions() + && !testFlag(requestedAdditional.formatFlags, QWindowsGLRenderToPixmap); + QWindowsOpenGLAdditionalFormat obtainedAdditional; + if (tryExtensions) { + m_pixelFormat = + ARB::choosePixelFormat(hdc, *m_staticContext, context->format(), + requestedAdditional, &m_obtainedPixelFormatDescriptor); + if (m_pixelFormat > 0) { + m_obtainedFormat = + ARB::qSurfaceFormatFromHDC(*m_staticContext, hdc, m_pixelFormat, + &obtainedAdditional); + m_extensionsUsed = true; + } + } // tryExtensions + if (!m_pixelFormat) { // Failed, try GDI + m_pixelFormat = GDI::choosePixelFormat(hdc, context->format(), requestedAdditional, + &m_obtainedPixelFormatDescriptor); + if (m_pixelFormat) + m_obtainedFormat = + GDI::qSurfaceFormatFromPixelFormat(m_obtainedPixelFormatDescriptor, + &obtainedAdditional); + } // try GDI + if (!m_pixelFormat) { + qWarning("%s: Unable find a suitable pixel format.", __FUNCTION__); + break; + } + if (!SetPixelFormat(hdc, m_pixelFormat, &m_obtainedPixelFormatDescriptor)) { + qErrnoWarning("SetPixelFormat failed."); + break; + } + // Create context with sharing, again preferably using ARB. + HGLRC sharingRenderingContext = 0; + if (const QPlatformGLContext *sc = context->shareHandle()) + sharingRenderingContext = static_cast(sc)->renderingContext(); + + if (m_extensionsUsed) + m_renderingContext = + ARB::createContext(*m_staticContext, hdc, context->format(), + requestedAdditional, 0, 0, sharingRenderingContext); + if (!m_renderingContext) + m_renderingContext = GDI::createContext(hdc, sharingRenderingContext); + + if (!m_renderingContext) { + qWarning("Unable to create a GL Context."); + break; + } + } while (false); + if (hdc) + ReleaseDC(dummyWindow, hdc); + if (dummyWindow) + DestroyWindow(dummyWindow); + + if (QWindowsContext::verboseGL) + qDebug() + << __FUNCTION__ << this << " requested: " << context->format() + << "\n obtained #" << m_pixelFormat << (m_extensionsUsed ? "ARB" : "GDI") + << m_obtainedFormat << "\n " << m_obtainedPixelFormatDescriptor + << "\n HGLRC=" << m_renderingContext; +} + +QWindowsGLContext::~QWindowsGLContext() +{ + if (m_renderingContext) + wglDeleteContext(m_renderingContext); + releaseDCs(); +} + +void QWindowsGLContext::releaseDCs() +{ + const QOpenGLContextData *end = m_windowContexts.end(); + for (const QOpenGLContextData *p = m_windowContexts.begin(); p < end; ++p) + ReleaseDC(p->hwnd, p->hdc); + m_windowContexts.resize(0); +} + +static inline QWindowsWindow *glWindowOf(QPlatformSurface *s) +{ + return static_cast(s); +} + +static inline HWND handleOf(QPlatformSurface *s) +{ + return glWindowOf(s)->handle(); +} + +// Find a window in a context list. +static inline const QOpenGLContextData * + findByHWND(const Array &data, HWND hwnd) +{ + const QOpenGLContextData *end = data.end(); + for (const QOpenGLContextData *p = data.begin(); p < end; ++p) + if (p->hwnd == hwnd) + return p; + return 0; +} + +void QWindowsGLContext::swapBuffers(QPlatformSurface *surface) +{ + if (QWindowsContext::verboseGL > 1) + qDebug() << __FUNCTION__ << surface; + if (const QOpenGLContextData *contextData = findByHWND(m_windowContexts, handleOf(surface))) { + SwapBuffers(contextData->hdc); + } else { + qWarning("%s: Cannot find window %p", __FUNCTION__, handleOf(surface)); + } +} + +bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface) +{ +#ifdef DEBUG_GL + if (QWindowsContext::verboseGL > 1) + qDebug("%s context=%p contexts=%d", __FUNCTION__, this, m_windowContexts.size()); +#endif // DEBUG_GL + // Do we already have a DC entry for that window? + QWindowsWindow *window = static_cast(surface); + const HWND hwnd = window->handle(); + if (const QOpenGLContextData *contextData = findByHWND(m_windowContexts, hwnd)) + return wglMakeCurrent(contextData->hdc, contextData->renderingContext); + // Create a new entry. + const QOpenGLContextData newContext(m_renderingContext, hwnd, GetDC(hwnd)); + if (!newContext.hdc) + return false; + // Initialize pixel format first time. This will apply to + // the HWND as well and must be done only once. + if (!window->testFlag(QWindowsWindow::PixelFormatInitialized)) { + if (!SetPixelFormat(newContext.hdc, m_pixelFormat, &m_obtainedPixelFormatDescriptor)) { + qErrnoWarning("%s: SetPixelFormat() failed", __FUNCTION__); + ReleaseDC(newContext.hwnd, newContext.hdc); + return false; + } + window->setFlag(QWindowsWindow::PixelFormatInitialized); + } + m_windowContexts.append(newContext); + return wglMakeCurrent(newContext.hdc, newContext.renderingContext); +} + +void QWindowsGLContext::doneCurrent() +{ +#ifdef DEBUG_GL + if (QWindowsContext::verboseGL > 1) + qDebug("%s context=%p %d contexts", __FUNCTION__, this, m_windowContexts.size()); +#endif // DEBUG_GL + wglMakeCurrent(0, 0); + releaseDCs(); +} + +QWindowsGLContext::GL_Proc QWindowsGLContext::getProcAddress(const QByteArray &procName) +{ + // TODO: Will that work with the calling conventions? + GL_Proc procAddress = reinterpret_cast(wglGetProcAddress(procName.constData())); + if (QWindowsContext::verboseGL) + qDebug("%s('%s') with current_hglrc=%p returns %p", + __FUNCTION__, procName.constData(), + wglGetCurrentContext(), procAddress); + return procAddress; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsglcontext.h b/src/plugins/platforms/windows/qwindowsglcontext.h new file mode 100644 index 0000000000..c5edd8978f --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsglcontext.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSGLCONTEXT_H +#define QWINDOWSGLCONTEXT_H + +#include "array.h" +#include "qtwindows_additional.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDebug; + +enum QWindowsGLFormatFlags +{ + QWindowsGLDirectRendering = 0x1, + QWindowsGLOverlay = 0x2, + QWindowsGLRenderToPixmap = 0x4, + QWindowsGLAccumBuffer = 0x8, + QWindowsGLDeprecatedFunctions = 0x10 +}; + +// Additional format information for Windows. +struct QWindowsOpenGLAdditionalFormat +{ + QWindowsOpenGLAdditionalFormat(unsigned formatFlagsIn = 0, unsigned pixmapDepthIn = 0) : + formatFlags(formatFlagsIn), pixmapDepth(pixmapDepthIn) {} + unsigned formatFlags; // QWindowsGLFormatFlags. + unsigned pixmapDepth; // for QWindowsGLRenderToPixmap +}; + +// Per-window data for active OpenGL contexts. +struct QOpenGLContextData +{ + QOpenGLContextData(HGLRC r, HWND h, HDC d) : renderingContext(r), hwnd(h), hdc(d) {} + QOpenGLContextData() : renderingContext(0), hwnd(0), hdc(0) {} + + HGLRC renderingContext; + HWND hwnd; + HDC hdc; +}; + +class QOpenGLStaticContext +{ + Q_DISABLE_COPY(QOpenGLStaticContext) + QOpenGLStaticContext(); +public: + enum Extensions + { + SampleBuffers = 0x1 + }; + + typedef bool + (APIENTRY *WglGetPixelFormatAttribIVARB) + (HDC hdc, int iPixelFormat, int iLayerPlane, + uint nAttributes, const int *piAttributes, int *piValues); + + typedef bool + (APIENTRY *WglChoosePixelFormatARB)(HDC hdc, const int *piAttribList, + const float *pfAttribFList, uint nMaxFormats, int *piFormats, + UINT *nNumFormats); + + typedef HGLRC + (APIENTRY *WglCreateContextAttribsARB)(HDC, HGLRC, const int *); + + bool hasExtensions() const + { return wglGetPixelFormatAttribIVARB && wglChoosePixelFormatARB && wglCreateContextAttribsARB; } + + static QOpenGLStaticContext *create(); + static QByteArray getGlString(unsigned int which); + + const QByteArray vendor; + const QByteArray renderer; + const QByteArray extensionNames; + int majorVersion; + int minorVersion; + unsigned extensions; + + WglGetPixelFormatAttribIVARB wglGetPixelFormatAttribIVARB; + WglChoosePixelFormatARB wglChoosePixelFormatARB; + WglCreateContextAttribsARB wglCreateContextAttribsARB; +}; + +QDebug operator<<(QDebug d, const QOpenGLStaticContext &); + +class QWindowsGLContext : public QPlatformGLContext +{ +public: + typedef QSharedPointer QOpenGLStaticContextPtr; + + explicit QWindowsGLContext(const QOpenGLStaticContextPtr &staticContext, + QGuiGLContext *context); + virtual ~QWindowsGLContext(); + bool isValid() const { return m_renderingContext; } + virtual QSurfaceFormat format() const { return m_obtainedFormat; } + + virtual void swapBuffers(QPlatformSurface *surface); + + virtual bool makeCurrent(QPlatformSurface *surface); + virtual void doneCurrent(); + + typedef void (*GL_Proc) (); + + virtual GL_Proc getProcAddress(const QByteArray &procName); + + HGLRC renderingContext() const { return m_renderingContext; } + +private: + inline void releaseDCs(); + + const QOpenGLStaticContextPtr m_staticContext; + QGuiGLContext *m_context; + QSurfaceFormat m_obtainedFormat; + HGLRC m_renderingContext; + Array m_windowContexts; + PIXELFORMATDESCRIPTOR m_obtainedPixelFormatDescriptor; + int m_pixelFormat; + bool m_extensionsUsed; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSGLCONTEXT_H diff --git a/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp b/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp new file mode 100644 index 0000000000..fcfdd4fbdd --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsguieventdispatcher.h" +#include "qwindowscontext.h" + +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QWindowsGuiEventDispatcher + \brief Event dispatcher for Windows + + Maintains a global stack storing the current event dispatcher and + its processing flags for access from the Windows procedure + qWindowsWndProc. Handling the Lighthouse gui events should be done + from within the qWindowsWndProc to ensure correct processing of messages. + + \ingroup qt-lighthouse-win +*/ + +typedef QStack DispatchContextStack; + +Q_GLOBAL_STATIC(DispatchContextStack, dispatchContextStack) + +QWindowsGuiEventDispatcher::QWindowsGuiEventDispatcher(QObject *parent) : + QEventDispatcherWin32(parent) +{ + setObjectName(QStringLiteral("QWindowsGuiEventDispatcher_0x") + QString::number((quintptr)this, 16)); + if (QWindowsContext::verboseEvents) + qDebug("%s %s", __FUNCTION__, qPrintable(objectName())); + dispatchContextStack()->push(DispatchContext(this, QEventLoop::AllEvents)); +} + +QWindowsGuiEventDispatcher::~QWindowsGuiEventDispatcher() +{ + if (QWindowsContext::verboseEvents) + qDebug("%s %s", __FUNCTION__, qPrintable(objectName())); + if (!dispatchContextStack()->isEmpty()) + dispatchContextStack()->pop(); +} + +bool QWindowsGuiEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + DispatchContextStack &stack = *dispatchContextStack(); + if (QWindowsContext::verboseEvents > 2) + qDebug(">%s %s %d", __FUNCTION__, qPrintable(objectName()), stack.size()); + stack.push(DispatchContext(this, flags)); + const bool rc = QEventDispatcherWin32::processEvents(flags); + stack.pop(); + if (QWindowsContext::verboseEvents > 2) + qDebug("<%s %s returns %d", __FUNCTION__, qPrintable(objectName()), rc); + return rc; +} + +QWindowsGuiEventDispatcher::DispatchContext QWindowsGuiEventDispatcher::currentDispatchContext() +{ + const DispatchContextStack &stack = *dispatchContextStack(); + if (stack.isEmpty()) { + qWarning("%s: No dispatch context", __FUNCTION__); + return DispatchContext(0, 0); + } + return stack.top(); +} + +// Helpers for printing debug output for WM_* messages. +struct MessageDebugEntry +{ + UINT message; + const char *description; + bool interesting; +}; + +static const MessageDebugEntry +messageDebugEntries[] = { + {WM_CREATE, "WM_CREATE", true}, + {WM_PAINT, "WM_PAINT", true}, + {WM_CLOSE, "WM_CLOSE", true}, + {WM_DESTROY, "WM_DESTROY", true}, + {WM_MOVE, "WM_MOVE", true}, + {WM_SIZE, "WM_SIZE", true}, + {WM_MOUSEACTIVATE,"WM_MOUSEACTIVATE", true}, + {WM_CHILDACTIVATE, "WM_CHILDACTIVATE", true}, + {WM_PARENTNOTIFY, "WM_PARENTNOTIFY", true}, + {WM_GETICON, "WM_GETICON", false}, + {WM_KEYDOWN, "WM_KEYDOWN", true}, + {WM_SYSKEYDOWN, "WM_SYSKEYDOWN", true}, + {WM_SYSCOMMAND, "WM_SYSCOMMAND", true}, + {WM_KEYUP, "WM_KEYUP", true}, + {WM_SYSKEYUP, "WM_SYSKEYUP", true}, + {WM_IME_CHAR, "WM_IMECHAR", true}, + {WM_IME_KEYDOWN, "WM_IMECHAR", true}, + {WM_CANCELMODE, "WM_CANCELMODE", true}, + {WM_CHAR, "WM_CHAR", true}, + {WM_DEADCHAR, "WM_DEADCHAR", true}, + {WM_ACTIVATE, "WM_ACTIVATE", true}, + {WM_GETMINMAXINFO, "WM_GETMINMAXINFO", true}, + {WM_SETFOCUS, "WM_SETFOCUS", true}, + {WM_KILLFOCUS, "WM_KILLFOCUS", true}, + {WM_ENABLE, "WM_ENABLE", true}, + {WM_SHOWWINDOW, "WM_SHOWWINDOW", true}, + {WM_GETMINMAXINFO, "WM_GETMINMAXINFO"}, + {WM_WINDOWPOSCHANGING, "WM_WINDOWPOSCHANGING", true}, + {WM_WINDOWPOSCHANGED, "WM_WINDOWPOSCHANGED", true}, + {WM_SETCURSOR, "WM_SETCURSOR", false}, + {WM_GETFONT, "WM_GETFONT", true}, + {WM_NCMOUSEMOVE, "WM_NCMOUSEMOVE", true}, + {WM_LBUTTONDOWN, "WM_LBUTTONDOWN", true}, + {WM_LBUTTONUP, "WM_LBUTTONUP", true}, + {WM_LBUTTONDBLCLK, "WM_LBUTTONDBLCLK", true}, + {WM_RBUTTONDOWN, "WM_RBUTTONDOWN", true}, + {WM_RBUTTONUP, "WM_RBUTTONUP", true}, + {WM_RBUTTONDBLCLK, "WM_RBUTTONDBLCLK", true}, + {WM_MBUTTONDOWN, "WM_MBUTTONDOWN", true}, + {WM_MBUTTONUP, "WM_MBUTTONUP", true}, + {WM_MBUTTONDBLCLK, "WM_MBUTTONDBLCLK", true}, + {WM_MOUSEWHEEL, "WM_MOUSEWHEEL", true}, + {WM_XBUTTONDOWN, "WM_XBUTTONDOWN", true}, + {WM_XBUTTONUP, "WM_XBUTTONUP", true}, + {WM_XBUTTONDBLCLK, "WM_XBUTTONDBLCLK", true}, + {WM_MOUSEHWHEEL, "WM_MOUSEHWHEEL", true}, + {WM_NCCREATE, "WM_NCCREATE", true}, + {WM_NCCALCSIZE, "WM_NCCALCSIZE", true}, + {WM_NCACTIVATE, "WM_NCACTIVATE", true}, + {WM_NCMOUSELEAVE, "WM_NCMOUSELEAVE", true}, + {WM_NCLBUTTONDOWN, "WM_NCLBUTTONDOWN", true}, + {WM_NCLBUTTONUP, "WM_NCLBUTTONUP", true}, + {WM_ACTIVATEAPP, "WM_ACTIVATEAPP", true}, + {WM_NCPAINT, "WM_NCPAINT", true}, + {WM_ERASEBKGND, "WM_ERASEBKGND", true}, + {WM_MOUSEMOVE, "WM_MOUSEMOVE", true}, + {WM_MOUSELEAVE, "WM_MOUSELEAVE", true}, + {WM_NCHITTEST, "WM_NCHITTEST", false}, + {WM_IME_SETCONTEXT, "WM_IME_SETCONTEXT", true}, + {WM_IME_NOTIFY, "WM_IME_NOTIFY", true}, +#if defined(WM_DWMNCRENDERINGCHANGED) + {WM_DWMNCRENDERINGCHANGED, "WM_DWMNCRENDERINGCHANGED", true}, +#endif + {WM_IME_SETCONTEXT, "WM_IME_SETCONTEXT", true}, + {WM_IME_NOTIFY, "WM_IME_NOTIFY", true}, + {WM_TOUCH, "WM_TOUCH", true}, + {WM_CHANGECBCHAIN, "WM_CHANGECBCHAIN", true}, + {WM_DRAWCLIPBOARD, "WM_DRAWCLIPBOARD", true}, + {WM_RENDERFORMAT, "WM_RENDERFORMAT", true}, + {WM_RENDERALLFORMATS, "WM_RENDERALLFORMATS", true}, + {WM_DESTROYCLIPBOARD, "WM_DESTROYCLIPBOARD", true}, + {WM_CAPTURECHANGED, "WM_CAPTURECHANGED", true} +}; + +static inline const MessageDebugEntry *messageDebugEntry(UINT msg) +{ + for (size_t i = 0; i < sizeof(messageDebugEntries)/sizeof(MessageDebugEntry); i++) + if (messageDebugEntries[i].message == msg) + return messageDebugEntries + i; + return 0; +} + +const char *QWindowsGuiEventDispatcher::windowsMessageName(UINT msg) +{ + if (const MessageDebugEntry *e = messageDebugEntry(msg)) + return e->description; + return "Unknown"; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsguieventdispatcher.h b/src/plugins/platforms/windows/qwindowsguieventdispatcher.h new file mode 100644 index 0000000000..00fd234eff --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsguieventdispatcher.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSGUIEVENTDISPATCHER_H +#define QWINDOWSGUIEVENTDISPATCHER_H + +#include "qtwindowsglobal.h" +#include "qtwindows_additional.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QWindowsGuiEventDispatcher : public QEventDispatcherWin32 +{ + Q_OBJECT +public: + explicit QWindowsGuiEventDispatcher(QObject *parent = 0); + ~QWindowsGuiEventDispatcher(); + + typedef QPair DispatchContext; + + static DispatchContext currentDispatchContext(); + + static const char *windowsMessageName(UINT msg); + + virtual bool QT_ENSURE_STACK_ALIGNED_FOR_SSE processEvents(QEventLoop::ProcessEventsFlags flags); +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSGUIEVENTDISPATCHER_H diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp new file mode 100644 index 0000000000..a0fada2ca5 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsintegration.h" +#include "qwindowsbackingstore.h" +#include "qwindowswindow.h" +#include "qwindowscontext.h" +#include "qwindowsglcontext.h" +#include "qwindowsscreen.h" +#include "qwindowsfontdatabase.h" +#include "qwindowsprintersupport.h" +#include "qwindowsguieventdispatcher.h" +#include "qwindowsclipboard.h" +#include "qwindowsdrag.h" + +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QWindowsNativeInterface + \brief Provides access to native handles. + + Currently implemented keys + \list + \o handle (HWND) + \o getDC (DC) + \o releaseDC Releases the previously acquired DC and returns 0. + \endlist + + \ingroup qt-lighthouse-win +*/ + +class QWindowsNativeInterface : public QPlatformNativeInterface +{ +public: + virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window); + virtual void *nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *bs); +}; + +void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resource, QWindow *window) +{ + if (!window || !window->handle()) { + qWarning("%s: '%s' requested for null window or window without handle.", __FUNCTION__, resource.constData()); + return 0; + } + QWindowsWindow *bw = static_cast(window->handle()); + if (resource == "handle") + return bw->handle(); + if (window->surfaceType() == QWindow::RasterSurface) { + if (resource == "getDC") + return bw->getDC(); + if (resource == "releaseDC") { + bw->releaseDC(); + return 0; + } + } + qWarning("%s: Invalid key '%s' requested.", __FUNCTION__, resource.constData()); + return 0; +} + +void *QWindowsNativeInterface::nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *bs) +{ + if (!bs || !bs->handle()) { + qWarning("%s: '%s' requested for null backingstore or backingstore without handle.", __FUNCTION__, resource.constData()); + return 0; + } + QWindowsBackingStore *wbs = static_cast(bs->handle()); + if (resource == "getDC") + return wbs->getDC(); + qWarning("%s: Invalid key '%s' requested.", __FUNCTION__, resource.constData()); + return 0; +} + +/*! + \class QWindowsIntegration + \brief QPlatformIntegration implementation for Windows. + \ingroup qt-lighthouse-win +*/ + +struct QWindowsIntegrationPrivate +{ + typedef QSharedPointer QOpenGLStaticContextPtr; + + explicit QWindowsIntegrationPrivate(bool openGL); + + const bool m_openGL; + QWindowsContext m_context; + QWindowsPrinterSupport m_printerSupport; + QWindowsFontDatabase m_fontDatabase; + QWindowsNativeInterface m_nativeInterface; + QWindowsClipboard m_clipboard; + QWindowsDrag m_drag; + QWindowsGuiEventDispatcher *m_eventDispatcher; + QOpenGLStaticContextPtr m_staticOpenGLContext; +}; + +QWindowsIntegrationPrivate::QWindowsIntegrationPrivate(bool openGL) + : m_openGL(openGL) + , m_context(openGL) + , m_eventDispatcher(new QWindowsGuiEventDispatcher) +{ +} + +QWindowsIntegration::QWindowsIntegration(bool openGL) : + d(new QWindowsIntegrationPrivate(openGL)) +{ + QGuiApplicationPrivate::instance()->setEventDispatcher(d->m_eventDispatcher); + d->m_clipboard.registerViewer(); + foreach (QPlatformScreen *pscr, QWindowsScreen::screens()) + screenAdded(pscr); +} + +QWindowsIntegration::~QWindowsIntegration() +{ + if (QWindowsContext::verboseIntegration) + qDebug("%s", __FUNCTION__); +} + +bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case ThreadedPixmaps: + return true; + default: + return QPlatformIntegration::hasCapability(cap); + } + return false; +} + +QPlatformPixmap *QWindowsIntegration::createPlatformPixmap(QPlatformPixmap::PixelType type) const +{ + if (QWindowsContext::verboseIntegration) + qDebug() << __FUNCTION__ << type; + return new QRasterPlatformPixmap(type); +} + +QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) const +{ + const bool isGL = window->surfaceType() == QWindow::OpenGLSurface; + QWindowsWindow::WindowData requested; + requested.flags = window->windowFlags(); + requested.geometry = window->geometry(); + const QWindowsWindow::WindowData obtained + = QWindowsWindow::WindowData::create(window, requested, window->windowTitle(), isGL); + if (QWindowsContext::verboseIntegration || QWindowsContext::verboseWindows) + qDebug().nospace() + << __FUNCTION__ << ' ' << window << '\n' + << " Requested: " << requested.geometry << " Flags=" + << QWindowsWindow::debugWindowFlags(requested.flags) << '\n' + << " Obtained : " << obtained.geometry << " Margins " + << obtained.frame << " Flags=" + << QWindowsWindow::debugWindowFlags(obtained.flags) + << " Handle=" << obtained.hwnd << '\n'; + if (!obtained.hwnd) + return 0; + if (requested.flags != obtained.flags) + window->setWindowFlags(obtained.flags); + if (requested.geometry != obtained.geometry) + QWindowSystemInterface::handleGeometryChange(window, obtained.geometry); + return new QWindowsWindow(window, obtained); +} + +QPlatformBackingStore *QWindowsIntegration::createPlatformBackingStore(QWindow *window) const +{ + if (QWindowsContext::verboseIntegration) + qDebug() << __FUNCTION__ << window; + return new QWindowsBackingStore(window); +} + +QPlatformGLContext + *QWindowsIntegration::createPlatformGLContext(QGuiGLContext *context) const +{ + if (QWindowsContext::verboseIntegration) + qDebug() << __FUNCTION__ << context->format(); + if (d->m_staticOpenGLContext.isNull()) + d->m_staticOpenGLContext = + QSharedPointer(QOpenGLStaticContext::create()); + QScopedPointer result(new QWindowsGLContext(d->m_staticOpenGLContext, context)); + if (result->isValid()) + return result.take(); + return 0; +} + +QPlatformFontDatabase *QWindowsIntegration::fontDatabase() const +{ + return &d->m_fontDatabase; +} + +QPlatformPrinterSupport *QWindowsIntegration::printerSupport() const +{ + if (QWindowsContext::verboseIntegration) + qDebug() << __FUNCTION__; + return &d->m_printerSupport; +} + +QPlatformNativeInterface *QWindowsIntegration::nativeInterface() const +{ + return &d->m_nativeInterface; +} + +QPlatformClipboard * QWindowsIntegration::clipboard() const +{ + return &d->m_clipboard; +} + +QPlatformDrag *QWindowsIntegration::drag() const +{ + if (QWindowsContext::verboseIntegration) + qDebug("%s", __FUNCTION__ ); + return &d->m_drag; +} + +QPlatformInputContext * QWindowsIntegration::inputContext() const +{ + Q_UNIMPLEMENTED(); + return QPlatformIntegration::inputContext(); +} + +QWindowsIntegration *QWindowsIntegration::instance() +{ + return static_cast(QGuiApplicationPrivate::platformIntegration()); +} + +QAbstractEventDispatcher * QWindowsIntegration::guiThreadEventDispatcher() const +{ + return d->m_eventDispatcher; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h new file mode 100644 index 0000000000..a2d3d3acf0 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSINTEGRATION_H +#define QWINDOWSINTEGRATION_H + +#include +#include + +QT_BEGIN_NAMESPACE + +struct QWindowsIntegrationPrivate; + +class QWindowsIntegration : public QPlatformIntegration +{ +public: + QWindowsIntegration(bool openGL = false); + virtual ~QWindowsIntegration(); + + bool hasCapability(QPlatformIntegration::Capability cap) const; + + virtual QPlatformPixmap *createPlatformPixmap(QPlatformPixmap::PixelType type) const; + QPlatformWindow *createPlatformWindow(QWindow *window) const; + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + virtual QPlatformGLContext *createPlatformGLContext(QGuiGLContext *context) const; + virtual QAbstractEventDispatcher *guiThreadEventDispatcher() const; + + virtual QPlatformClipboard *clipboard() const; + virtual QPlatformDrag *drag() const; + virtual QPlatformInputContext *inputContext() const; + virtual QPlatformNativeInterface *nativeInterface() const; + virtual QPlatformPrinterSupport *printerSupport() const; + virtual QPlatformFontDatabase *fontDatabase() const; + + static QWindowsIntegration *instance(); + +private: + QScopedPointer d; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/windows/qwindowsinternalmimedata.h b/src/plugins/platforms/windows/qwindowsinternalmimedata.h new file mode 100644 index 0000000000..49fc69059b --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsinternalmimedata.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSINTERNALMIME_H +#define QWINDOWSINTERNALMIME_H + +#include "qtwindows_additional.h" + +#include // QInternalMime +#include + +QT_BEGIN_NAMESPACE + +class QDebug; + +// Implementation in qwindowsclipboard.cpp. +class QWindowsInternalMimeData : public QInternalMimeData { +public: + virtual bool hasFormat_sys(const QString &mimetype) const; + virtual QStringList formats_sys() const; + virtual QVariant retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const; + +protected: + virtual IDataObject *retrieveDataObject() const = 0; + virtual void releaseDataObject(IDataObject *) const {} +}; + +QDebug operator<<(QDebug d, const QMimeData &m); + +QT_END_NAMESPACE + +#endif // QWINDOWSINTERNALMIME_H diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp new file mode 100644 index 0000000000..3ec32b2325 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -0,0 +1,1076 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowskeymapper.h" +#include "qwindowscontext.h" +#include "qwindowswindow.h" +#include "qwindowsguieventdispatcher.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QWindowsKeyMapper + \brief Translates Windows keys to QWindowSystemInterface events. + \ingroup qt-lighthouse-win + + In addition, handles some special keys to display system menus, etc. + The code originates from \c qkeymapper_win.cpp. +*/ + +QWindowsKeyMapper::QWindowsKeyMapper() + : m_useRTLExtensions(false), m_keyGrabber(0) +{ + memset(keyLayout, 0, sizeof(keyLayout)); +} + +QWindowsKeyMapper::~QWindowsKeyMapper() +{ + deleteLayouts(); +} + +#ifndef LANG_PASHTO +#define LANG_PASHTO 0x63 +#endif +#ifndef LANG_SYRIAC +#define LANG_SYRIAC 0x5a +#endif +#ifndef LANG_DIVEHI +#define LANG_DIVEHI 0x65 +#endif +#ifndef VK_OEM_PLUS +#define VK_OEM_PLUS 0xBB +#endif +#ifndef VK_OEM_3 +#define VK_OEM_3 0xC0 +#endif + +// Key recorder ------------------------------------------------------------------------[ start ] -- +struct KeyRecord { + KeyRecord(int c, int a, int s, const QString &t) : code(c), ascii(a), state(s), text(t) {} + KeyRecord() {} + + int code; + int ascii; + int state; + QString text; +}; + +static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers... +struct KeyRecorder +{ + KeyRecorder() : nrecs(0) {} + + inline KeyRecord *findKey(int code, bool remove); + inline void storeKey(int code, int ascii, int state, const QString& text); + inline void clearKeys(); + + int nrecs; + KeyRecord deleted_record; // A copy of last entry removed from records[] + KeyRecord records[QT_MAX_KEY_RECORDINGS]; +}; +static KeyRecorder key_recorder; + +KeyRecord *KeyRecorder::findKey(int code, bool remove) +{ + KeyRecord *result = 0; + for (int i = 0; i < nrecs; ++i) { + if (records[i].code == code) { + if (remove) { + deleted_record = records[i]; + // Move rest down, and decrease count + while (i + 1 < nrecs) { + records[i] = records[i + 1]; + ++i; + } + --nrecs; + result = &deleted_record; + } else { + result = &records[i]; + } + break; + } + } + return result; +} + +void KeyRecorder::storeKey(int code, int ascii, int state, const QString& text) +{ + Q_ASSERT_X(nrecs != QT_MAX_KEY_RECORDINGS, + "Internal KeyRecorder", + "Keyboard recorder buffer overflow, consider increasing QT_MAX_KEY_RECORDINGS"); + + if (nrecs == QT_MAX_KEY_RECORDINGS) { + qWarning("Qt: Internal keyboard buffer overflow"); + return; + } + records[nrecs++] = KeyRecord(code,ascii,state,text); +} + +void KeyRecorder::clearKeys() +{ + nrecs = 0; +} +// Key recorder --------------------------------------------------------------------------[ end ] -- + + +// Key translation ---------------------------------------------------------------------[ start ] -- +// Meaning of values: +// 0 = Character output key, needs keyboard driver mapping +// Key_unknown = Unknown Virtual Key, no translation possible, ignore +static const uint KeyTbl[] = { // Keyboard mapping table + // Dec | Hex | Windows Virtual key + Qt::Key_unknown, // 0 0x00 + Qt::Key_unknown, // 1 0x01 VK_LBUTTON | Left mouse button + Qt::Key_unknown, // 2 0x02 VK_RBUTTON | Right mouse button + Qt::Key_Cancel, // 3 0x03 VK_CANCEL | Control-Break processing + Qt::Key_unknown, // 4 0x04 VK_MBUTTON | Middle mouse button + Qt::Key_unknown, // 5 0x05 VK_XBUTTON1 | X1 mouse button + Qt::Key_unknown, // 6 0x06 VK_XBUTTON2 | X2 mouse button + Qt::Key_unknown, // 7 0x07 -- unassigned -- + Qt::Key_Backspace, // 8 0x08 VK_BACK | BackSpace key + Qt::Key_Tab, // 9 0x09 VK_TAB | Tab key + Qt::Key_unknown, // 10 0x0A -- reserved -- + Qt::Key_unknown, // 11 0x0B -- reserved -- + Qt::Key_Clear, // 12 0x0C VK_CLEAR | Clear key + Qt::Key_Return, // 13 0x0D VK_RETURN | Enter key + Qt::Key_unknown, // 14 0x0E -- unassigned -- + Qt::Key_unknown, // 15 0x0F -- unassigned -- + Qt::Key_Shift, // 16 0x10 VK_SHIFT | Shift key + Qt::Key_Control, // 17 0x11 VK_CONTROL | Ctrl key + Qt::Key_Alt, // 18 0x12 VK_MENU | Alt key + Qt::Key_Pause, // 19 0x13 VK_PAUSE | Pause key + Qt::Key_CapsLock, // 20 0x14 VK_CAPITAL | Caps-Lock + Qt::Key_unknown, // 21 0x15 VK_KANA / VK_HANGUL | IME Kana or Hangul mode + Qt::Key_unknown, // 22 0x16 -- unassigned -- + Qt::Key_unknown, // 23 0x17 VK_JUNJA | IME Junja mode + Qt::Key_unknown, // 24 0x18 VK_FINAL | IME final mode + Qt::Key_unknown, // 25 0x19 VK_HANJA / VK_KANJI | IME Hanja or Kanji mode + Qt::Key_unknown, // 26 0x1A -- unassigned -- + Qt::Key_Escape, // 27 0x1B VK_ESCAPE | Esc key + Qt::Key_unknown, // 28 0x1C VK_CONVERT | IME convert + Qt::Key_unknown, // 29 0x1D VK_NONCONVERT | IME non-convert + Qt::Key_unknown, // 30 0x1E VK_ACCEPT | IME accept + Qt::Key_Mode_switch,// 31 0x1F VK_MODECHANGE | IME mode change request + Qt::Key_Space, // 32 0x20 VK_SPACE | Spacebar + Qt::Key_PageUp, // 33 0x21 VK_PRIOR | Page Up key + Qt::Key_PageDown, // 34 0x22 VK_NEXT | Page Down key + Qt::Key_End, // 35 0x23 VK_END | End key + Qt::Key_Home, // 36 0x24 VK_HOME | Home key + Qt::Key_Left, // 37 0x25 VK_LEFT | Left arrow key + Qt::Key_Up, // 38 0x26 VK_UP | Up arrow key + Qt::Key_Right, // 39 0x27 VK_RIGHT | Right arrow key + Qt::Key_Down, // 40 0x28 VK_DOWN | Down arrow key + Qt::Key_Select, // 41 0x29 VK_SELECT | Select key + Qt::Key_Printer, // 42 0x2A VK_PRINT | Print key + Qt::Key_Execute, // 43 0x2B VK_EXECUTE | Execute key + Qt::Key_Print, // 44 0x2C VK_SNAPSHOT | Print Screen key + Qt::Key_Insert, // 45 0x2D VK_INSERT | Ins key + Qt::Key_Delete, // 46 0x2E VK_DELETE | Del key + Qt::Key_Help, // 47 0x2F VK_HELP | Help key + 0, // 48 0x30 (VK_0) | 0 key + 0, // 49 0x31 (VK_1) | 1 key + 0, // 50 0x32 (VK_2) | 2 key + 0, // 51 0x33 (VK_3) | 3 key + 0, // 52 0x34 (VK_4) | 4 key + 0, // 53 0x35 (VK_5) | 5 key + 0, // 54 0x36 (VK_6) | 6 key + 0, // 55 0x37 (VK_7) | 7 key + 0, // 56 0x38 (VK_8) | 8 key + 0, // 57 0x39 (VK_9) | 9 key + Qt::Key_unknown, // 58 0x3A -- unassigned -- + Qt::Key_unknown, // 59 0x3B -- unassigned -- + Qt::Key_unknown, // 60 0x3C -- unassigned -- + Qt::Key_unknown, // 61 0x3D -- unassigned -- + Qt::Key_unknown, // 62 0x3E -- unassigned -- + Qt::Key_unknown, // 63 0x3F -- unassigned -- + Qt::Key_unknown, // 64 0x40 -- unassigned -- + 0, // 65 0x41 (VK_A) | A key + 0, // 66 0x42 (VK_B) | B key + 0, // 67 0x43 (VK_C) | C key + 0, // 68 0x44 (VK_D) | D key + 0, // 69 0x45 (VK_E) | E key + 0, // 70 0x46 (VK_F) | F key + 0, // 71 0x47 (VK_G) | G key + 0, // 72 0x48 (VK_H) | H key + 0, // 73 0x49 (VK_I) | I key + 0, // 74 0x4A (VK_J) | J key + 0, // 75 0x4B (VK_K) | K key + 0, // 76 0x4C (VK_L) | L key + 0, // 77 0x4D (VK_M) | M key + 0, // 78 0x4E (VK_N) | N key + 0, // 79 0x4F (VK_O) | O key + 0, // 80 0x50 (VK_P) | P key + 0, // 81 0x51 (VK_Q) | Q key + 0, // 82 0x52 (VK_R) | R key + 0, // 83 0x53 (VK_S) | S key + 0, // 84 0x54 (VK_T) | T key + 0, // 85 0x55 (VK_U) | U key + 0, // 86 0x56 (VK_V) | V key + 0, // 87 0x57 (VK_W) | W key + 0, // 88 0x58 (VK_X) | X key + 0, // 89 0x59 (VK_Y) | Y key + 0, // 90 0x5A (VK_Z) | Z key + Qt::Key_Meta, // 91 0x5B VK_LWIN | Left Windows - MS Natural kbd + Qt::Key_Meta, // 92 0x5C VK_RWIN | Right Windows - MS Natural kbd + Qt::Key_Menu, // 93 0x5D VK_APPS | Application key-MS Natural kbd + Qt::Key_unknown, // 94 0x5E -- reserved -- + Qt::Key_Sleep, // 95 0x5F VK_SLEEP + Qt::Key_0, // 96 0x60 VK_NUMPAD0 | Numeric keypad 0 key + Qt::Key_1, // 97 0x61 VK_NUMPAD1 | Numeric keypad 1 key + Qt::Key_2, // 98 0x62 VK_NUMPAD2 | Numeric keypad 2 key + Qt::Key_3, // 99 0x63 VK_NUMPAD3 | Numeric keypad 3 key + Qt::Key_4, // 100 0x64 VK_NUMPAD4 | Numeric keypad 4 key + Qt::Key_5, // 101 0x65 VK_NUMPAD5 | Numeric keypad 5 key + Qt::Key_6, // 102 0x66 VK_NUMPAD6 | Numeric keypad 6 key + Qt::Key_7, // 103 0x67 VK_NUMPAD7 | Numeric keypad 7 key + Qt::Key_8, // 104 0x68 VK_NUMPAD8 | Numeric keypad 8 key + Qt::Key_9, // 105 0x69 VK_NUMPAD9 | Numeric keypad 9 key + Qt::Key_Asterisk, // 106 0x6A VK_MULTIPLY | Multiply key + Qt::Key_Plus, // 107 0x6B VK_ADD | Add key + Qt::Key_Comma, // 108 0x6C VK_SEPARATOR | Separator key + Qt::Key_Minus, // 109 0x6D VK_SUBTRACT | Subtract key + Qt::Key_Period, // 110 0x6E VK_DECIMAL | Decimal key + Qt::Key_Slash, // 111 0x6F VK_DIVIDE | Divide key + Qt::Key_F1, // 112 0x70 VK_F1 | F1 key + Qt::Key_F2, // 113 0x71 VK_F2 | F2 key + Qt::Key_F3, // 114 0x72 VK_F3 | F3 key + Qt::Key_F4, // 115 0x73 VK_F4 | F4 key + Qt::Key_F5, // 116 0x74 VK_F5 | F5 key + Qt::Key_F6, // 117 0x75 VK_F6 | F6 key + Qt::Key_F7, // 118 0x76 VK_F7 | F7 key + Qt::Key_F8, // 119 0x77 VK_F8 | F8 key + Qt::Key_F9, // 120 0x78 VK_F9 | F9 key + Qt::Key_F10, // 121 0x79 VK_F10 | F10 key + Qt::Key_F11, // 122 0x7A VK_F11 | F11 key + Qt::Key_F12, // 123 0x7B VK_F12 | F12 key + Qt::Key_F13, // 124 0x7C VK_F13 | F13 key + Qt::Key_F14, // 125 0x7D VK_F14 | F14 key + Qt::Key_F15, // 126 0x7E VK_F15 | F15 key + Qt::Key_F16, // 127 0x7F VK_F16 | F16 key + Qt::Key_F17, // 128 0x80 VK_F17 | F17 key + Qt::Key_F18, // 129 0x81 VK_F18 | F18 key + Qt::Key_F19, // 130 0x82 VK_F19 | F19 key + Qt::Key_F20, // 131 0x83 VK_F20 | F20 key + Qt::Key_F21, // 132 0x84 VK_F21 | F21 key + Qt::Key_F22, // 133 0x85 VK_F22 | F22 key + Qt::Key_F23, // 134 0x86 VK_F23 | F23 key + Qt::Key_F24, // 135 0x87 VK_F24 | F24 key + Qt::Key_unknown, // 136 0x88 -- unassigned -- + Qt::Key_unknown, // 137 0x89 -- unassigned -- + Qt::Key_unknown, // 138 0x8A -- unassigned -- + Qt::Key_unknown, // 139 0x8B -- unassigned -- + Qt::Key_unknown, // 140 0x8C -- unassigned -- + Qt::Key_unknown, // 141 0x8D -- unassigned -- + Qt::Key_unknown, // 142 0x8E -- unassigned -- + Qt::Key_unknown, // 143 0x8F -- unassigned -- + Qt::Key_NumLock, // 144 0x90 VK_NUMLOCK | Num Lock key + Qt::Key_ScrollLock, // 145 0x91 VK_SCROLL | Scroll Lock key + // Fujitsu/OASYS kbd -------------------- + 0, //Qt::Key_Jisho, // 146 0x92 VK_OEM_FJ_JISHO | 'Dictionary' key / + // VK_OEM_NEC_EQUAL = key on numpad on NEC PC-9800 kbd + Qt::Key_Massyo, // 147 0x93 VK_OEM_FJ_MASSHOU | 'Unregister word' key + Qt::Key_Touroku, // 148 0x94 VK_OEM_FJ_TOUROKU | 'Register word' key + 0, //Qt::Key_Oyayubi_Left,//149 0x95 VK_OEM_FJ_LOYA | 'Left OYAYUBI' key + 0, //Qt::Key_Oyayubi_Right,//150 0x96 VK_OEM_FJ_ROYA | 'Right OYAYUBI' key + Qt::Key_unknown, // 151 0x97 -- unassigned -- + Qt::Key_unknown, // 152 0x98 -- unassigned -- + Qt::Key_unknown, // 153 0x99 -- unassigned -- + Qt::Key_unknown, // 154 0x9A -- unassigned -- + Qt::Key_unknown, // 155 0x9B -- unassigned -- + Qt::Key_unknown, // 156 0x9C -- unassigned -- + Qt::Key_unknown, // 157 0x9D -- unassigned -- + Qt::Key_unknown, // 158 0x9E -- unassigned -- + Qt::Key_unknown, // 159 0x9F -- unassigned -- + Qt::Key_Shift, // 160 0xA0 VK_LSHIFT | Left Shift key + Qt::Key_Shift, // 161 0xA1 VK_RSHIFT | Right Shift key + Qt::Key_Control, // 162 0xA2 VK_LCONTROL | Left Ctrl key + Qt::Key_Control, // 163 0xA3 VK_RCONTROL | Right Ctrl key + Qt::Key_Alt, // 164 0xA4 VK_LMENU | Left Menu key + Qt::Key_Alt, // 165 0xA5 VK_RMENU | Right Menu key + Qt::Key_Back, // 166 0xA6 VK_BROWSER_BACK | Browser Back key + Qt::Key_Forward, // 167 0xA7 VK_BROWSER_FORWARD | Browser Forward key + Qt::Key_Refresh, // 168 0xA8 VK_BROWSER_REFRESH | Browser Refresh key + Qt::Key_Stop, // 169 0xA9 VK_BROWSER_STOP | Browser Stop key + Qt::Key_Search, // 170 0xAA VK_BROWSER_SEARCH | Browser Search key + Qt::Key_Favorites, // 171 0xAB VK_BROWSER_FAVORITES| Browser Favorites key + Qt::Key_HomePage, // 172 0xAC VK_BROWSER_HOME | Browser Start and Home key + Qt::Key_VolumeMute, // 173 0xAD VK_VOLUME_MUTE | Volume Mute key + Qt::Key_VolumeDown, // 174 0xAE VK_VOLUME_DOWN | Volume Down key + Qt::Key_VolumeUp, // 175 0xAF VK_VOLUME_UP | Volume Up key + Qt::Key_MediaNext, // 176 0xB0 VK_MEDIA_NEXT_TRACK | Next Track key + Qt::Key_MediaPrevious, //177 0xB1 VK_MEDIA_PREV_TRACK | Previous Track key + Qt::Key_MediaStop, // 178 0xB2 VK_MEDIA_STOP | Stop Media key + Qt::Key_MediaPlay, // 179 0xB3 VK_MEDIA_PLAY_PAUSE | Play/Pause Media key + Qt::Key_LaunchMail, // 180 0xB4 VK_LAUNCH_MAIL | Start Mail key + Qt::Key_LaunchMedia,// 181 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key + Qt::Key_Launch0, // 182 0xB6 VK_LAUNCH_APP1 | Start Application 1 key + Qt::Key_Launch1, // 183 0xB7 VK_LAUNCH_APP2 | Start Application 2 key + Qt::Key_unknown, // 184 0xB8 -- reserved -- + Qt::Key_unknown, // 185 0xB9 -- reserved -- + 0, // 186 0xBA VK_OEM_1 | ';:' for US + 0, // 187 0xBB VK_OEM_PLUS | '+' any country + 0, // 188 0xBC VK_OEM_COMMA | ',' any country + 0, // 189 0xBD VK_OEM_MINUS | '-' any country + 0, // 190 0xBE VK_OEM_PERIOD | '.' any country + 0, // 191 0xBF VK_OEM_2 | '/?' for US + 0, // 192 0xC0 VK_OEM_3 | '`~' for US + Qt::Key_unknown, // 193 0xC1 -- reserved -- + Qt::Key_unknown, // 194 0xC2 -- reserved -- + Qt::Key_unknown, // 195 0xC3 -- reserved -- + Qt::Key_unknown, // 196 0xC4 -- reserved -- + Qt::Key_unknown, // 197 0xC5 -- reserved -- + Qt::Key_unknown, // 198 0xC6 -- reserved -- + Qt::Key_unknown, // 199 0xC7 -- reserved -- + Qt::Key_unknown, // 200 0xC8 -- reserved -- + Qt::Key_unknown, // 201 0xC9 -- reserved -- + Qt::Key_unknown, // 202 0xCA -- reserved -- + Qt::Key_unknown, // 203 0xCB -- reserved -- + Qt::Key_unknown, // 204 0xCC -- reserved -- + Qt::Key_unknown, // 205 0xCD -- reserved -- + Qt::Key_unknown, // 206 0xCE -- reserved -- + Qt::Key_unknown, // 207 0xCF -- reserved -- + Qt::Key_unknown, // 208 0xD0 -- reserved -- + Qt::Key_unknown, // 209 0xD1 -- reserved -- + Qt::Key_unknown, // 210 0xD2 -- reserved -- + Qt::Key_unknown, // 211 0xD3 -- reserved -- + Qt::Key_unknown, // 212 0xD4 -- reserved -- + Qt::Key_unknown, // 213 0xD5 -- reserved -- + Qt::Key_unknown, // 214 0xD6 -- reserved -- + Qt::Key_unknown, // 215 0xD7 -- reserved -- + Qt::Key_unknown, // 216 0xD8 -- unassigned -- + Qt::Key_unknown, // 217 0xD9 -- unassigned -- + Qt::Key_unknown, // 218 0xDA -- unassigned -- + 0, // 219 0xDB VK_OEM_4 | '[{' for US + 0, // 220 0xDC VK_OEM_5 | '\|' for US + 0, // 221 0xDD VK_OEM_6 | ']}' for US + 0, // 222 0xDE VK_OEM_7 | ''"' for US + 0, // 223 0xDF VK_OEM_8 + Qt::Key_unknown, // 224 0xE0 -- reserved -- + Qt::Key_unknown, // 225 0xE1 VK_OEM_AX | 'AX' key on Japanese AX kbd + Qt::Key_unknown, // 226 0xE2 VK_OEM_102 | "<>" or "\|" on RT 102-key kbd + Qt::Key_unknown, // 227 0xE3 VK_ICO_HELP | Help key on ICO + Qt::Key_unknown, // 228 0xE4 VK_ICO_00 | 00 key on ICO + Qt::Key_unknown, // 229 0xE5 VK_PROCESSKEY | IME Process key + Qt::Key_unknown, // 230 0xE6 VK_ICO_CLEAR | + Qt::Key_unknown, // 231 0xE7 VK_PACKET | Unicode char as keystrokes + Qt::Key_unknown, // 232 0xE8 -- unassigned -- + // Nokia/Ericsson definitions --------------- + Qt::Key_unknown, // 233 0xE9 VK_OEM_RESET + Qt::Key_unknown, // 234 0xEA VK_OEM_JUMP + Qt::Key_unknown, // 235 0xEB VK_OEM_PA1 + Qt::Key_unknown, // 236 0xEC VK_OEM_PA2 + Qt::Key_unknown, // 237 0xED VK_OEM_PA3 + Qt::Key_unknown, // 238 0xEE VK_OEM_WSCTRL + Qt::Key_unknown, // 239 0xEF VK_OEM_CUSEL + Qt::Key_unknown, // 240 0xF0 VK_OEM_ATTN + Qt::Key_unknown, // 241 0xF1 VK_OEM_FINISH + Qt::Key_unknown, // 242 0xF2 VK_OEM_COPY + Qt::Key_unknown, // 243 0xF3 VK_OEM_AUTO + Qt::Key_unknown, // 244 0xF4 VK_OEM_ENLW + Qt::Key_unknown, // 245 0xF5 VK_OEM_BACKTAB + Qt::Key_unknown, // 246 0xF6 VK_ATTN | Attn key + Qt::Key_unknown, // 247 0xF7 VK_CRSEL | CrSel key + Qt::Key_unknown, // 248 0xF8 VK_EXSEL | ExSel key + Qt::Key_unknown, // 249 0xF9 VK_EREOF | Erase EOF key + Qt::Key_Play, // 250 0xFA VK_PLAY | Play key + Qt::Key_Zoom, // 251 0xFB VK_ZOOM | Zoom key + Qt::Key_unknown, // 252 0xFC VK_NONAME | Reserved + Qt::Key_unknown, // 253 0xFD VK_PA1 | PA1 key + Qt::Key_Clear, // 254 0xFE VK_OEM_CLEAR | Clear key + 0 +}; + +// Possible modifier states. +// NOTE: The order of these states match the order in QWindowsKeyMapper::updatePossibleKeyCodes()! +static const Qt::KeyboardModifiers ModsTbl[] = { + Qt::NoModifier, // 0 + Qt::ShiftModifier, // 1 + Qt::ControlModifier, // 2 + Qt::ControlModifier | Qt::ShiftModifier, // 3 + Qt::AltModifier, // 4 + Qt::AltModifier | Qt::ShiftModifier, // 5 + Qt::AltModifier | Qt::ControlModifier, // 6 + Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7 + Qt::NoModifier, // Fall-back to raw Key_* +}; + +/** + Remap return or action key to select key for windows mobile. +*/ +inline int winceKeyBend(int keyCode) +{ + return KeyTbl[keyCode]; +} + +// Translate a VK into a Qt key code, or unicode character +static inline int toKeyOrUnicode(int vk, int scancode, unsigned char *kbdBuffer, bool *isDeadkey = 0) +{ + Q_ASSERT(vk > 0 && vk < 256); + int code = 0; + QChar unicodeBuffer[5]; + int res = ToUnicode(vk, scancode, kbdBuffer, reinterpret_cast(unicodeBuffer), 5, 0); + if (res) + code = unicodeBuffer[0].toUpper().unicode(); + + // Qt::Key_*'s are not encoded below 0x20, so try again, and DEL keys (0x7f) is encoded with a + // proper Qt::Key_ code + if (code < 0x20 || code == 0x7f) // Handles res==0 too + code = winceKeyBend(vk); + + if (isDeadkey) + *isDeadkey = (res == -1); + + return code == Qt::Key_unknown ? 0 : code; +} + +int qt_translateKeyCode(int vk) +{ + int code = winceKeyBend((vk < 0 || vk > 255) ? 0 : vk); + return code == Qt::Key_unknown ? 0 : code; +} + +static inline int asciiToKeycode(char a, int state) +{ + if (a >= 'a' && a <= 'z') + a = toupper(a); + if ((state & Qt::ControlModifier) != 0) { + if (a >= 0 && a <= 31) // Ctrl+@..Ctrl+A..CTRL+Z..Ctrl+_ + a += '@'; // to @..A..Z.._ + } + return a & 0xff; +} + +static inline bool isModifierKey(int code) +{ + return (code >= Qt::Key_Shift) && (code <= Qt::Key_ScrollLock); +} +// Key translation -----------------------------------------------------------------------[ end ]--- + + +// Keyboard map private ----------------------------------------------------------------[ start ]--- + +/* + \internal + A Windows KeyboardLayoutItem has 8 possible states: + 1. Unmodified + 2. Shift + 3. Control + 4. Control + Shift + 5. Alt + 6. Alt + Shift + 7. Alt + Control + 8. Alt + Control + Shift +*/ +struct KeyboardLayoutItem { + bool dirty; + quint8 deadkeys; + quint32 qtKey[9]; // Can by any Qt::Key_, or unicode character +}; + +void QWindowsKeyMapper::deleteLayouts() +{ + for (int i = 0; i < 255; ++i) { + if (keyLayout[i]) { + delete keyLayout[i]; + keyLayout[i] = 0; + } + } +} + +void QWindowsKeyMapper::changeKeyboard() +{ + deleteLayouts(); + + /* MAKELCID()'s first argument is a WORD, and GetKeyboardLayout() + * returns a DWORD. */ + + LCID newLCID = MAKELCID((quintptr)GetKeyboardLayout(0), SORT_DEFAULT); +// keyboardInputLocale = qt_localeFromLCID(newLCID); + + bool bidi = false; + wchar_t LCIDFontSig[16]; + if (GetLocaleInfo(newLCID, LOCALE_FONTSIGNATURE, LCIDFontSig, sizeof(LCIDFontSig) / sizeof(wchar_t)) + && (LCIDFontSig[7] & (wchar_t)0x0800)) + bidi = true; + + keyboardInputDirection = bidi ? Qt::RightToLeft : Qt::LeftToRight; +} + +void QWindowsKeyMapper::clearRecordedKeys() +{ + key_recorder.clearKeys(); +} + + +inline void setKbdState(unsigned char *kbd, bool shift, bool ctrl, bool alt) +{ + kbd[VK_LSHIFT ] = (shift ? 0x80 : 0); + kbd[VK_SHIFT ] = (shift ? 0x80 : 0); + kbd[VK_LCONTROL] = (ctrl ? 0x80 : 0); + kbd[VK_CONTROL ] = (ctrl ? 0x80 : 0); + kbd[VK_RMENU ] = (alt ? 0x80 : 0); + kbd[VK_MENU ] = (alt ? 0x80 : 0); +} + +void QWindowsKeyMapper::updateKeyMap(const MSG &msg) +{ + unsigned char kbdBuffer[256]; // Will hold the complete keyboard state + GetKeyboardState(kbdBuffer); + quint32 scancode = (msg.lParam >> 16) & 0xfff; + updatePossibleKeyCodes(kbdBuffer, scancode, msg.wParam); +} + +void QWindowsKeyMapper::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode, + quint32 vk_key) +{ + if (!vk_key || (keyLayout[vk_key] && !keyLayout[vk_key]->dirty)) + return; + + if (!keyLayout[vk_key]) + keyLayout[vk_key] = new KeyboardLayoutItem; + + // Copy keyboard state, so we can modify and query output for each possible permutation + unsigned char buffer[256]; + memcpy(buffer, kbdBuffer, sizeof(buffer)); + // Always 0, as Windows doesn't treat these as modifiers; + buffer[VK_LWIN ] = 0; + buffer[VK_RWIN ] = 0; + buffer[VK_CAPITAL ] = 0; + buffer[VK_NUMLOCK ] = 0; + buffer[VK_SCROLL ] = 0; + // Always 0, since we'll only change the other versions + buffer[VK_RSHIFT ] = 0; + buffer[VK_RCONTROL] = 0; + buffer[VK_LMENU ] = 0; // Use right Alt, since left Ctrl + right Alt is considered AltGraph + + bool isDeadKey = false; + keyLayout[vk_key]->deadkeys = 0; + keyLayout[vk_key]->dirty = false; + setKbdState(buffer, false, false, false); + keyLayout[vk_key]->qtKey[0] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x01 : 0; + setKbdState(buffer, true, false, false); + keyLayout[vk_key]->qtKey[1] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x02 : 0; + setKbdState(buffer, false, true, false); + keyLayout[vk_key]->qtKey[2] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x04 : 0; + setKbdState(buffer, true, true, false); + keyLayout[vk_key]->qtKey[3] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x08 : 0; + setKbdState(buffer, false, false, true); + keyLayout[vk_key]->qtKey[4] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x10 : 0; + setKbdState(buffer, true, false, true); + keyLayout[vk_key]->qtKey[5] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x20 : 0; + setKbdState(buffer, false, true, true); + keyLayout[vk_key]->qtKey[6] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x40 : 0; + setKbdState(buffer, true, true, true); + keyLayout[vk_key]->qtKey[7] = toKeyOrUnicode(vk_key, scancode, buffer, &isDeadKey); + keyLayout[vk_key]->deadkeys |= isDeadKey ? 0x80 : 0; + // Add a fall back key for layouts which don't do composition and show non-latin1 characters + int fallbackKey = winceKeyBend(vk_key); + if (!fallbackKey || fallbackKey == Qt::Key_unknown) { + fallbackKey = 0; + if (vk_key != keyLayout[vk_key]->qtKey[0] && vk_key < 0x5B && vk_key > 0x2F) + fallbackKey = vk_key; + } + keyLayout[vk_key]->qtKey[8] = fallbackKey; + + // If this vk_key a Dead Key + if (MapVirtualKey(vk_key, 2) & 0x80000000) { + // Push a Space, then the original key through the low-level ToAscii functions. + // We do this because these functions (ToAscii / ToUnicode) will alter the internal state of + // the keyboard driver By doing the following, we set the keyboard driver state back to what + // it was before we wrecked it with the code above. + // We need to push the space with an empty keystate map, since the driver checks the map for + // transitions in modifiers, so this helps us capture all possible deadkeys. + unsigned char emptyBuffer[256]; + memset(emptyBuffer, 0, sizeof(emptyBuffer)); + ::ToAscii(VK_SPACE, 0, emptyBuffer, reinterpret_cast(&buffer), 0); + ::ToAscii(vk_key, scancode, kbdBuffer, reinterpret_cast(&buffer), 0); + } + + if (QWindowsContext::verboseEvents > 1) { + qDebug("updatePossibleKeyCodes for virtual key = 0x%02x!", vk_key); + for (int i = 0; i < 9; ++i) { + qDebug(" [%d] (%d,0x%02x,'%c') %s", i, + keyLayout[vk_key]->qtKey[i], + keyLayout[vk_key]->qtKey[i], + keyLayout[vk_key]->qtKey[i] ? keyLayout[vk_key]->qtKey[i] : 0x03, + keyLayout[vk_key]->deadkeys & (1<deadkeys & 1<windowFlags() & Qt::WindowMinimizeButtonHint)?enabled:disabled); + bool maximized = IsZoomed(topLevelHwnd); + + EnableMenuItem(menu, SC_MAXIMIZE, ! (topLevel->windowFlags() & Qt::WindowMaximizeButtonHint) || maximized?disabled:enabled); + EnableMenuItem(menu, SC_RESTORE, maximized?enabled:disabled); + + // We should _not_ check with the setFixedSize(x,y) case here, since Windows is not able to check + // this and our menu here would be out-of-sync with the menu produced by mouse-click on the + // System Menu, or right-click on the title bar. + EnableMenuItem(menu, SC_SIZE, (topLevel->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) || maximized?disabled:enabled); + EnableMenuItem(menu, SC_MOVE, maximized?disabled:enabled); + EnableMenuItem(menu, SC_CLOSE, enabled); + // Set bold on close menu item + MENUITEMINFO closeItem; + closeItem.cbSize = sizeof(MENUITEMINFO); + closeItem.fMask = MIIM_STATE; + closeItem.fState = MFS_DEFAULT; + SetMenuItemInfo(menu, SC_CLOSE, FALSE, &closeItem); + +#undef enabled +#undef disabled + const int ret = TrackPopupMenuEx(menu, + TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD, + topLevel->geometry().x(), topLevel->geometry().y(), + topLevelHwnd, + 0); + if (ret) + qWindowsWndProc(topLevelHwnd, WM_SYSCOMMAND, ret, 0); +} + +static inline void sendExtendedPressRelease(QWindow *w, int k, + Qt::KeyboardModifiers mods, + quint32 nativeScanCode, + quint32 nativeVirtualKey, + quint32 nativeModifiers, + const QString & text = QString(), + bool autorep = false, + ushort count = 1) +{ + QWindowSystemInterface::handleExtendedKeyEvent(w, QEvent::KeyPress, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); + QWindowSystemInterface::handleExtendedKeyEvent(w, QEvent::KeyRelease, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); +} + +/*! + \brief To be called from the window procedure. +*/ + +bool QWindowsKeyMapper::translateKeyEvent(QWindow *widget, HWND hwnd, + const MSG &msg, LRESULT *result) +{ + *result = 0; + MSG peekedMsg; + // consume dead chars?(for example, typing '`','a' resulting in a-accent). + if (PeekMessage(&peekedMsg, hwnd, 0, 0, PM_NOREMOVE) && peekedMsg.message == WM_DEADCHAR) + return true; + if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) + updateKeyMap(msg); + translateKeyEventInternal(widget, msg, false); + return true; +} + +bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &msg, bool /* grab */) +{ + const int msgType = msg.message; + + const quint32 scancode = (msg.lParam >> 16) & 0xfff; + const quint32 vk_key = MapVirtualKey(scancode, 1); + const bool isNumpad = (msg.wParam >= VK_NUMPAD0 && msg.wParam <= VK_NUMPAD9); + quint32 nModifiers = 0; + + QWindow *receiver = m_keyGrabber ? m_keyGrabber : window; + + // Map native modifiers to some bit representation + nModifiers |= (GetKeyState(VK_LSHIFT ) & 0x80 ? ShiftLeft : 0); + nModifiers |= (GetKeyState(VK_RSHIFT ) & 0x80 ? ShiftRight : 0); + nModifiers |= (GetKeyState(VK_LCONTROL) & 0x80 ? ControlLeft : 0); + nModifiers |= (GetKeyState(VK_RCONTROL) & 0x80 ? ControlRight : 0); + nModifiers |= (GetKeyState(VK_LMENU ) & 0x80 ? AltLeft : 0); + nModifiers |= (GetKeyState(VK_RMENU ) & 0x80 ? AltRight : 0); + nModifiers |= (GetKeyState(VK_LWIN ) & 0x80 ? MetaLeft : 0); + nModifiers |= (GetKeyState(VK_RWIN ) & 0x80 ? MetaRight : 0); + // Add Lock keys to the same bits + nModifiers |= (GetKeyState(VK_CAPITAL ) & 0x01 ? CapsLock : 0); + nModifiers |= (GetKeyState(VK_NUMLOCK ) & 0x01 ? NumLock : 0); + nModifiers |= (GetKeyState(VK_SCROLL ) & 0x01 ? ScrollLock : 0); + + if (msg.lParam & ExtendedKey) + nModifiers |= msg.lParam & ExtendedKey; + + // Get the modifier states (may be altered later, depending on key code) + int state = 0; + state |= (nModifiers & ShiftAny ? Qt::ShiftModifier : 0); + state |= (nModifiers & ControlAny ? Qt::ControlModifier : 0); + state |= (nModifiers & AltAny ? Qt::AltModifier : 0); + state |= (nModifiers & MetaAny ? Qt::MetaModifier : 0); + + // Now we know enough to either have MapVirtualKey or our own keymap tell us if it's a deadkey + const bool isDeadKey = isADeadKey(msg.wParam, state) + || MapVirtualKey(msg.wParam, 2) & 0x80000000; + + // A multi-character key or a Input method character + // not found by our look-ahead + if (msgType == WM_CHAR || msgType == WM_IME_CHAR) { + sendExtendedPressRelease(receiver, 0, Qt::KeyboardModifier(state), scancode, vk_key, nModifiers, messageKeyText(msg), false, 0); + return true; + } + + bool result = false; + // handle Directionality changes (BiDi) with RTL extensions + if (m_useRTLExtensions) { + static int dirStatus = 0; + if (!dirStatus && state == Qt::ControlModifier + && msg.wParam == VK_CONTROL + && msgType == WM_KEYDOWN) { + if (GetKeyState(VK_LCONTROL) < 0) + dirStatus = VK_LCONTROL; + else if (GetKeyState(VK_RCONTROL) < 0) + dirStatus = VK_RCONTROL; + } else if (dirStatus) { + if (msgType == WM_KEYDOWN) { + if (msg.wParam == VK_SHIFT) { + if (dirStatus == VK_LCONTROL && GetKeyState(VK_LSHIFT) < 0) + dirStatus = VK_LSHIFT; + else if (dirStatus == VK_RCONTROL && GetKeyState(VK_RSHIFT) < 0) + dirStatus = VK_RSHIFT; + } else { + dirStatus = 0; + } + } else if (msgType == WM_KEYUP) { + if (dirStatus == VK_LSHIFT + && ((msg.wParam == VK_SHIFT && GetKeyState(VK_LCONTROL)) + || (msg.wParam == VK_CONTROL && GetKeyState(VK_LSHIFT)))) { + sendExtendedPressRelease(receiver, Qt::Key_Direction_L, 0, scancode, msg.wParam, nModifiers, QString(), false, 0); + result = true; + dirStatus = 0; + } else if (dirStatus == VK_RSHIFT + && ( (msg.wParam == VK_SHIFT && GetKeyState(VK_RCONTROL)) + || (msg.wParam == VK_CONTROL && GetKeyState(VK_RSHIFT)))) { + sendExtendedPressRelease(receiver, Qt::Key_Direction_R, 0, scancode, msg.wParam, nModifiers, QString(), false, 0); + result = true; + dirStatus = 0; + } else { + dirStatus = 0; + } + } else { + dirStatus = 0; + } + } + } // RTL + + // IME will process these keys, so simply return + if (msg.wParam == VK_PROCESSKEY) + return true; + + // Ignore invalid virtual keycodes (see bugs 127424, QTBUG-3630) + if (msg.wParam == 0 || msg.wParam == 0xFF) + return true; + + // Translate VK_* (native) -> Key_* (Qt) keys + // If it's a dead key, we cannot use the toKeyOrUnicode() function, since that will change + // the internal state of the keyboard driver, resulting in that dead keys no longer works. + // ..also if we're typing numbers on the keypad, while holding down the Alt modifier. + int code = 0; + 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); + } + + // Invert state logic: + // If the key actually pressed is a modifier key, then we remove its modifier key from the + // state, since a modifier-key can't have itself as a modifier + if (code == Qt::Key_Control) + state = state ^ Qt::ControlModifier; + else if (code == Qt::Key_Shift) + state = state ^ Qt::ShiftModifier; + else if (code == Qt::Key_Alt) + state = state ^ Qt::AltModifier; + + // If the bit 24 of lParm is set you received a enter, + // otherwise a Return. (This is the extended key bit) + if ((code == Qt::Key_Return) && (msg.lParam & 0x1000000)) + code = Qt::Key_Enter; + + // All cursor keys without extended bit + if (!(msg.lParam & 0x1000000)) { + switch (code) { + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_Insert: + case Qt::Key_Delete: + case Qt::Key_Asterisk: + case Qt::Key_Plus: + case Qt::Key_Minus: + case Qt::Key_Period: + case Qt::Key_0: + case Qt::Key_1: + case Qt::Key_2: + case Qt::Key_3: + case Qt::Key_4: + case Qt::Key_5: + case Qt::Key_6: + case Qt::Key_7: + case Qt::Key_8: + case Qt::Key_9: + state |= ((msg.wParam >= '0' && msg.wParam <= '9') + || (msg.wParam >= VK_OEM_PLUS && msg.wParam <= VK_OEM_3)) + ? 0 : Qt::KeypadModifier; + default: + if ((uint)msg.lParam == 0x004c0001 || (uint)msg.lParam == 0xc04c0001) + state |= Qt::KeypadModifier; + break; + } + } + // Other keys with with extended bit + else { + switch (code) { + case Qt::Key_Enter: + case Qt::Key_Slash: + case Qt::Key_NumLock: + state |= Qt::KeypadModifier; + default: + break; + } + } + + // KEYDOWN --------------------------------------------------------------------------------- + if (msgType == WM_KEYDOWN || msgType == WM_IME_KEYDOWN || msgType == WM_SYSKEYDOWN) { + // Get the last record of this key press, so we can validate the current state + // The record is not removed from the list + KeyRecord *rec = key_recorder.findKey(msg.wParam, false); + + // If rec's state doesn't match the current state, something has changed behind our back + // (Consumed by modal widget is one possibility) So, remove the record from the list + // This will stop the auto-repeat of the key, should a modifier change, for example + if (rec && rec->state != state) { + key_recorder.findKey(msg.wParam, true); + rec = 0; + } + + // Find unicode character from Windows Message Queue + MSG wm_char; + UINT charType = (msgType == WM_KEYDOWN + ? WM_CHAR + : msgType == WM_IME_KEYDOWN ? WM_IME_CHAR : WM_SYSCHAR); + + QChar uch; + if (PeekMessage(&wm_char, 0, charType, charType, PM_REMOVE)) { + // Found a ?_CHAR + uch = QChar((ushort)wm_char.wParam); + if (msgType == WM_SYSKEYDOWN && uch.isLetter() && (msg.lParam & KF_ALTDOWN)) + uch = uch.toLower(); // (See doc of WM_SYSCHAR) Alt-letter + if (!code && !uch.row()) + code = asciiToKeycode(uch.cell(), state); + } + + // Special handling for the WM_IME_KEYDOWN message. Microsoft IME (Korean) will not + // generate a WM_IME_CHAR message corresponding to this message. We might get wrong + // results, if we map this virtual key-code directly (for eg '?' US layouts). So try + // to find the correct key using the current message parameters & keyboard state. + if (uch.isNull() && msgType == WM_IME_KEYDOWN) { + BYTE keyState[256]; + wchar_t newKey[3] = {0}; + GetKeyboardState(keyState); + int val = ToUnicode(vk_key, scancode, keyState, newKey, 2, 0); + if (val == 1) { + uch = QChar(newKey[0]); + } else { + // If we are still not able to find a unicode key, pass the WM_IME_KEYDOWN + // message to DefWindowProc() for generating a proper WM_KEYDOWN. + return false; + } + } + + // If no ?_CHAR was found in the queue; deduct character from the ?_KEYDOWN parameters + if (uch.isNull()) { + if (msg.wParam == VK_DELETE) { + uch = QChar(QLatin1Char(0x7f)); // Windows doesn't know this one. + } else { + if (msgType != WM_SYSKEYDOWN || !code) { + UINT map = MapVirtualKey(msg.wParam, 2); + // If the high bit of the return value is set, it's a deadkey + if (!(map & 0x80000000)) + uch = QChar((ushort)map); + } + } + if (!code && !uch.row()) + code = asciiToKeycode(uch.cell(), state); + } + + // Special handling of global Windows hotkeys + if (state == Qt::AltModifier) { + switch (code) { + case Qt::Key_Escape: + case Qt::Key_Tab: + case Qt::Key_Enter: + case Qt::Key_F4: + return false; // Send the event on to Windows + case Qt::Key_Space: + // do not pass this key to windows, we will process it ourselves + showSystemMenu(receiver); + return true; + default: + break; + } + } + + // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation + if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier) + code = Qt::Key_Backtab; + + // If we have a record, it means that the key is already pressed, the state is the same + // so, we have an auto-repeating key + if (rec) { + if (code < Qt::Key_Shift || code > Qt::Key_ScrollLock) { + QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code, + Qt::KeyboardModifier(state), scancode, msg.wParam, nModifiers, rec->text, true, 0); + QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code, + Qt::KeyboardModifier(state), scancode, msg.wParam, nModifiers, rec->text, true, 0); + result = true; + } + } + // No record of the key being previous pressed, so we now send a QEvent::KeyPress event, + // and store the key data into our records. + else { + const QString text = uch.isNull() ? QString() : QString(uch); + const char a = uch.row() ? 0 : uch.cell(); + key_recorder.storeKey(msg.wParam, a, state, text); + QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code, + Qt::KeyboardModifier(state), scancode, msg.wParam, nModifiers, text, false, 0); + result =true; + bool store = true; + // Alt+ go to the Win32 menu system if unhandled by Qt + if (msgType == WM_SYSKEYDOWN && !result && a) { + HWND parent = GetParent(QWindowsWindow::handleOf(receiver)); + while (parent) { + if (GetMenu(parent)) { + SendMessage(parent, WM_SYSCOMMAND, SC_KEYMENU, a); + store = false; + result = true; + break; + } + parent = GetParent(parent); + } + } + if (!store) + key_recorder.findKey(msg.wParam, true); + } + } + + // KEYUP ----------------------------------------------------------------------------------- + else { + // Try to locate the key in our records, and remove it if it exists. + // The key may not be in our records if, for example, the down event was handled by + // win32 natively, or our window gets focus while a key is already press, but now gets + // the key release event. + KeyRecord* rec = key_recorder.findKey(msg.wParam, true); + if (!rec && !(code == Qt::Key_Shift + || code == Qt::Key_Control + || code == Qt::Key_Meta + || code == Qt::Key_Alt)) { + // Someone ate the key down event + } else { + if (!code) + code = asciiToKeycode(rec->ascii ? rec->ascii : msg.wParam, state); + + // Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation + if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier) + code = Qt::Key_Backtab; + QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code, + Qt::KeyboardModifier(state), scancode, msg.wParam, nModifiers, + (rec ? rec->text : QString()), false, 0); + result = true; + // don't pass Alt to Windows unless we are embedded in a non-Qt window + if (code == Qt::Key_Alt) { + const QWindowsContext *context = QWindowsContext::instance(); + HWND parent = GetParent(QWindowsWindow::handleOf(receiver)); + while (parent) { + if (!context->findPlatformWindow(parent) && GetMenu(parent)) { + result = false; + break; + } + parent = GetParent(parent); + } + } + } + } + return result; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowskeymapper.h b/src/plugins/platforms/windows/qwindowskeymapper.h new file mode 100644 index 0000000000..0d50193730 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowskeymapper.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSKEYMAPPER_H +#define QWINDOWSKEYMAPPER_H + +#include "qtwindows_additional.h" + +#include + +QT_BEGIN_NAMESPACE + +class QWindow; + +struct KeyboardLayoutItem; + +class QWindowsKeyMapper +{ + Q_DISABLE_COPY(QWindowsKeyMapper) +public: + explicit QWindowsKeyMapper(); + ~QWindowsKeyMapper(); + + void changeKeyboard(); + + void setUseRTLExtensions(bool e) { m_useRTLExtensions = e; } + bool useRTLExtensions() const { return m_useRTLExtensions; } + + bool translateKeyEvent(QWindow *widget, HWND hwnd, const MSG &msg, LRESULT *result); + + QWindow *keyGrabber() const { return m_keyGrabber; } + void setKeyGrabber(QWindow *w) { m_keyGrabber = w; } + +private: + bool translateKeyEventInternal(QWindow *receiver, const MSG &msg, bool grab); + void updateKeyMap(const MSG &msg); + + bool m_useRTLExtensions; + + QLocale keyboardInputLocale; + Qt::LayoutDirection keyboardInputDirection; + + void clearRecordedKeys(); + void updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode, quint32 vk_key); + bool isADeadKey(unsigned int vk_key, unsigned int modifiers); + void deleteLayouts(); + + KeyboardLayoutItem *keyLayout[256]; + QWindow *m_keyGrabber; +}; + +enum WindowsNativeModifiers { + ShiftLeft = 0x00000001, + ControlLeft = 0x00000002, + AltLeft = 0x00000004, + MetaLeft = 0x00000008, + ShiftRight = 0x00000010, + ControlRight = 0x00000020, + AltRight = 0x00000040, + MetaRight = 0x00000080, + CapsLock = 0x00000100, + NumLock = 0x00000200, + ScrollLock = 0x00000400, + ExtendedKey = 0x01000000, + + // Convenience mappings + ShiftAny = 0x00000011, + ControlAny = 0x00000022, + AltAny = 0x00000044, + MetaAny = 0x00000088, + LockAny = 0x00000700 +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSKEYMAPPER_H diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp new file mode 100644 index 0000000000..09104a43cf --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsmime.cpp @@ -0,0 +1,1557 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsmime.h" +#include "qwindowscontext.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/* The MSVC compilers allows multi-byte characters, that has the behavior of + * that each character gets shifted into position. 0x73524742 below is for MSVC + * equivalent to doing 'sRGB', but this does of course not work + * on conformant C++ compilers. */ +#define BMP_LCS_sRGB 0x73524742 +#define BMP_LCS_GM_IMAGES 0x00000004L + +struct _CIEXYZ { + long ciexyzX, ciexyzY, ciexyzZ; +}; + +struct _CIEXYZTRIPLE { + _CIEXYZ ciexyzRed, ciexyzGreen, ciexyzBlue; +}; + +struct BMP_BITMAPV5HEADER { + DWORD bV5Size; + LONG bV5Width; + LONG bV5Height; + WORD bV5Planes; + WORD bV5BitCount; + DWORD bV5Compression; + DWORD bV5SizeImage; + LONG bV5XPelsPerMeter; + LONG bV5YPelsPerMeter; + DWORD bV5ClrUsed; + DWORD bV5ClrImportant; + DWORD bV5RedMask; + DWORD bV5GreenMask; + DWORD bV5BlueMask; + DWORD bV5AlphaMask; + DWORD bV5CSType; + _CIEXYZTRIPLE bV5Endpoints; + DWORD bV5GammaRed; + DWORD bV5GammaGreen; + DWORD bV5GammaBlue; + DWORD bV5Intent; + DWORD bV5ProfileData; + DWORD bV5ProfileSize; + DWORD bV5Reserved; +}; +static const int BMP_BITFIELDS = 3; + +static const char dibFormatC[] = "dib"; + +static inline QByteArray msgConversionError(const char *func, const char *format) +{ + QByteArray msg = func; + msg += ": Unable to convert DIB image. The image converter plugin for '"; + msg += format; + msg += "' is not available. Available formats: "; + foreach (const QByteArray &af, QImageReader::supportedImageFormats()) { + msg += af; + msg += ' '; + } + return msg; +} + +static inline QImage readDib(QByteArray data) +{ + QBuffer buffer(&data); + buffer.open(QIODevice::ReadOnly); + QImageReader reader(&buffer, dibFormatC); + if (!reader.canRead()) { + qWarning("%s", msgConversionError(__FUNCTION__, dibFormatC).constData()); + return QImage(); + } + return reader.read(); +} + +static QByteArray writeDib(const QImage &img) +{ + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::ReadWrite); + QImageWriter writer(&buffer, dibFormatC); + if (!writer.canWrite()) { + qWarning("%s", msgConversionError(__FUNCTION__, dibFormatC).constData()); + return ba; + } + if (!writer.write(img)) + ba.clear(); + return ba; +} + +static bool qt_write_dibv5(QDataStream &s, QImage image) +{ + QIODevice* d = s.device(); + if (!d->isWritable()) + return false; + + //depth will be always 32 + int bpl_bmp = image.width()*4; + + BMP_BITMAPV5HEADER bi ={0}; + bi.bV5Size = sizeof(BMP_BITMAPV5HEADER); + bi.bV5Width = image.width(); + bi.bV5Height = image.height(); + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5SizeImage = bpl_bmp*image.height(); + bi.bV5XPelsPerMeter = 0; + bi.bV5YPelsPerMeter = 0; + bi.bV5ClrUsed = 0; + bi.bV5ClrImportant = 0; + bi.bV5BlueMask = 0x000000ff; + bi.bV5GreenMask = 0x0000ff00; + bi.bV5RedMask = 0x00ff0000; + bi.bV5AlphaMask = 0xff000000; + bi.bV5CSType = BMP_LCS_sRGB; //LCS_sRGB + bi.bV5Intent = BMP_LCS_GM_IMAGES; //LCS_GM_IMAGES + + d->write(reinterpret_cast(&bi), bi.bV5Size); + if (s.status() != QDataStream::Ok) + return false; + + DWORD colorSpace[3] = {0x00ff0000,0x0000ff00,0x000000ff}; + d->write(reinterpret_cast(colorSpace), sizeof(colorSpace)); + if (s.status() != QDataStream::Ok) + return false; + + if (image.format() != QImage::Format_ARGB32) + image = image.convertToFormat(QImage::Format_ARGB32); + + uchar *buf = new uchar[bpl_bmp]; + uchar *b; + + memset(buf, 0, bpl_bmp); + for (int y=image.height()-1; y>=0; y--) { + // write the image bits + QRgb *p = (QRgb *)image.scanLine(y); + QRgb *end = p + image.width(); + b = buf; + while (p < end) { + int alpha = qAlpha(*p); + if (alpha) { + *b++ = qBlue(*p); + *b++ = qGreen(*p); + *b++ = qRed(*p); + } else { + //white for fully transparent pixels. + *b++ = 0xff; + *b++ = 0xff; + *b++ = 0xff; + } + *b++ = alpha; + p++; + } + d->write((char*)buf, bpl_bmp); + if (s.status() != QDataStream::Ok) { + delete[] buf; + return false; + } + } + delete[] buf; + return true; +} + +static int calc_shift(int mask) +{ + int result = 0; + while (!(mask & 1)) { + result++; + mask >>= 1; + } + return result; +} + +//Supports only 32 bit DIBV5 +static bool qt_read_dibv5(QDataStream &s, QImage &image) +{ + BMP_BITMAPV5HEADER bi; + QIODevice* d = s.device(); + if (d->atEnd()) + return false; + + d->read((char *)&bi, sizeof(bi)); // read BITMAPV5HEADER header + if (s.status() != QDataStream::Ok) + return false; + + int nbits = bi.bV5BitCount; + int comp = bi.bV5Compression; + if (nbits != 32 || bi.bV5Planes != 1 || comp != BMP_BITFIELDS) + return false; //Unsupported DIBV5 format + + int w = bi.bV5Width, h = bi.bV5Height; + int red_mask = bi.bV5RedMask; + int green_mask = bi.bV5GreenMask; + int blue_mask = bi.bV5BlueMask; + int alpha_mask = bi.bV5AlphaMask; + int red_shift = 0; + int green_shift = 0; + int blue_shift = 0; + int alpha_shift = 0; + QImage::Format format = QImage::Format_ARGB32; + + if (bi.bV5Height < 0) + h = -h; // support images with negative height + if (image.size() != QSize(w, h) || image.format() != format) { + image = QImage(w, h, format); + if (image.isNull()) // could not create image + return false; + } + image.setDotsPerMeterX(bi.bV5XPelsPerMeter); + image.setDotsPerMeterY(bi.bV5YPelsPerMeter); + // read color table + DWORD colorSpace[3]; + if (d->read((char *)colorSpace, sizeof(colorSpace)) != sizeof(colorSpace)) + return false; + + red_shift = calc_shift(red_mask); + green_shift = calc_shift(green_mask); + blue_shift = calc_shift(blue_mask); + if (alpha_mask) { + alpha_shift = calc_shift(alpha_mask); + } + + int bpl = image.bytesPerLine(); + uchar *data = image.bits(); + register QRgb *p; + QRgb *end; + uchar *buf24 = new uchar[bpl]; + int bpl24 = ((w*nbits+31)/32)*4; + uchar *b; + unsigned int c; + + while (--h >= 0) { + p = (QRgb *)(data + h*bpl); + end = p + w; + if (d->read((char *)buf24,bpl24) != bpl24) + break; + b = buf24; + while (p < end) { + c = *b | (*(b+1))<<8 | (*(b+2))<<16 | (*(b+3))<<24; + *p++ = qRgba(((c & red_mask) >> red_shift) , + ((c & green_mask) >> green_shift), + ((c & blue_mask) >> blue_shift), + ((c & alpha_mask) >> alpha_shift)); + b += 4; + } + } + delete[] buf24; + + if (bi.bV5Height < 0) { + // Flip the image + uchar *buf = new uchar[bpl]; + h = -bi.bV5Height; + for (int y = 0; y < h/2; ++y) { + memcpy(buf, data + y*bpl, bpl); + memcpy(data + y*bpl, data + (h-y-1)*bpl, bpl); + memcpy(data + (h-y-1)*bpl, buf, bpl); + } + delete [] buf; + } + + return true; +} + +//#define QMIME_DEBUG + +// helpers for using global memory + +static int getCf(const FORMATETC &formatetc) +{ + return formatetc.cfFormat; +} + +static FORMATETC setCf(int cf) +{ + FORMATETC formatetc; + formatetc.cfFormat = cf; + formatetc.dwAspect = DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.ptd = NULL; + formatetc.tymed = TYMED_HGLOBAL; + return formatetc; +} + +static bool setData(const QByteArray &data, STGMEDIUM *pmedium) +{ + HGLOBAL hData = GlobalAlloc(0, data.size()); + if (!hData) + return false; + + void *out = GlobalLock(hData); + memcpy(out, data.data(), data.size()); + GlobalUnlock(hData); + pmedium->tymed = TYMED_HGLOBAL; + pmedium->hGlobal = hData; + pmedium->pUnkForRelease = 0; + return true; +} + +static QByteArray getData(int cf, IDataObject *pDataObj) +{ + QByteArray data; + FORMATETC formatetc = setCf(cf); + STGMEDIUM s; + if (pDataObj->GetData(&formatetc, &s) == S_OK) { + DWORD * val = (DWORD*)GlobalLock(s.hGlobal); + data = QByteArray::fromRawData((char*)val, GlobalSize(s.hGlobal)); + data.detach(); + GlobalUnlock(s.hGlobal); + ReleaseStgMedium(&s); + } else { + //Try reading IStream data + formatetc.tymed = TYMED_ISTREAM; + if (pDataObj->GetData(&formatetc, &s) == S_OK) { + char szBuffer[4096]; + ULONG actualRead = 0; + LARGE_INTEGER pos = {{0, 0}}; + //Move to front (can fail depending on the data model implemented) + HRESULT hr = s.pstm->Seek(pos, STREAM_SEEK_SET, NULL); + while(SUCCEEDED(hr)){ + hr = s.pstm->Read(szBuffer, sizeof(szBuffer), &actualRead); + if (SUCCEEDED(hr) && actualRead > 0) { + data += QByteArray::fromRawData(szBuffer, actualRead); + } + if (actualRead != sizeof(szBuffer)) + break; + } + data.detach(); + ReleaseStgMedium(&s); + } + } + return data; +} + +static bool canGetData(int cf, IDataObject * pDataObj) +{ + FORMATETC formatetc = setCf(cf); + if (pDataObj->QueryGetData(&formatetc) != S_OK){ + formatetc.tymed = TYMED_ISTREAM; + return pDataObj->QueryGetData(&formatetc) == S_OK; + } + return true; +} + +/*! + \class QWindowsMime + \brief The QWindowsMime class maps open-standard MIME to Window Clipboard formats. + \ingroup qt-lighthouse-win + + Qt's drag-and-drop and clipboard facilities use the MIME standard. + On X11, this maps trivially to the Xdnd protocol, but on Windows + although some applications use MIME types to describe clipboard + formats, others use arbitrary non-standardized naming conventions, + or unnamed built-in formats of Windows. + + By instantiating subclasses of QWindowsMime that provide conversions + between Windows Clipboard and MIME formats, you can convert + proprietary clipboard formats to MIME formats. + + Qt has predefined support for the following Windows Clipboard formats: + + \table + \header \o Windows Format \o Equivalent MIME type + \row \o \c CF_UNICODETEXT \o \c text/plain + \row \o \c CF_TEXT \o \c text/plain + \row \o \c CF_DIB \o \c{image/xyz}, where \c xyz is + a \l{QImageWriter::supportedImageFormats()}{Qt image format} + \row \o \c CF_HDROP \o \c text/uri-list + \row \o \c CF_INETURL \o \c text/uri-list + \row \o \c CF_HTML \o \c text/html + \endtable + + An example use of this class would be to map the Windows Metafile + clipboard format (\c CF_METAFILEPICT) to and from the MIME type + \c{image/x-wmf}. This conversion might simply be adding or removing + a header, or even just passing on the data. See \l{Drag and Drop} + for more information on choosing and definition MIME types. + + You can check if a MIME type is convertible using canConvertFromMime() and + can perform conversions with convertToMime() and convertFromMime(). + + \sa QWindowsMimeConverter +*/ + +/*! +Constructs a new conversion object, adding it to the globally accessed +list of available converters. +*/ +QWindowsMime::QWindowsMime() +{ +} + +/*! +Destroys a conversion object, removing it from the global +list of available converters. +*/ +QWindowsMime::~QWindowsMime() +{ +} + +/*! + Registers the MIME type \a mime, and returns an ID number + identifying the format on Windows. +*/ +int QWindowsMime::registerMimeType(const QString &mime) +{ + int f = RegisterClipboardFormat(reinterpret_cast (mime.utf16())); + if (!f) + qErrnoWarning("QWindowsMime::registerMimeType: Failed to register clipboard format"); + + return f; +} + +/*! +\fn bool QWindowsMime::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const + + Returns true if the converter can convert from the \a mimeData to + the format specified in \a formatetc. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn bool QWindowsMime::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const + + Returns true if the converter can convert to the \a mimeType from + the available formats in \a pDataObj. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! +\fn QString QWindowsMime::mimeForFormat(const FORMATETC &formatetc) const + + Returns the mime type that will be created form the format specified + in \a formatetc, or an empty string if this converter does not support + \a formatetc. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! +\fn QVector QWindowsMime::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const + + Returns a QVector of FORMATETC structures representing the different windows clipboard + formats that can be provided for the \a mimeType from the \a mimeData. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QVariant QWindowsMime::convertToMime(const QString &mimeType, IDataObject *pDataObj, + QVariant::Type preferredType) const + + Returns a QVariant containing the converted data for \a mimeType from \a pDataObj. + If possible the QVariant should be of the \a preferredType to avoid needless conversions. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! +\fn bool QWindowsMime::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const + + Convert the \a mimeData to the format specified in \a formatetc. + The converted data should then be placed in \a pmedium structure. + + Return true if the conversion was successful. + + All subclasses must reimplement this pure virtual function. +*/ + +class QWindowsMimeText : public QWindowsMime +{ +public: + bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; + QVariant convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const; + QString mimeForFormat(const FORMATETC &formatetc) const; + bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const; + QVector formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; +}; + +bool QWindowsMimeText::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + int cf = getCf(formatetc); + return (cf == CF_UNICODETEXT || cf == CF_TEXT) && mimeData->hasText(); +} + +/* +text/plain is defined as using CRLF, but so many programs don't, +and programmers just look for '\n' in strings. +Windows really needs CRLF, so we ensure it here. +*/ +bool QWindowsMimeText::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const +{ + if (canConvertFromMime(formatetc, mimeData)) { + QByteArray data; + int cf = getCf(formatetc); + if (cf == CF_TEXT) { + data = mimeData->text().toLocal8Bit(); + // Anticipate required space for CRLFs at 1/40 + int maxsize=data.size()+data.size()/40+3; + QByteArray r(maxsize, '\0'); + char* o = r.data(); + const char* d = data.data(); + const int s = data.size(); + bool cr=false; + int j=0; + for (int i=0; i= maxsize) { + maxsize += maxsize/4; + r.resize(maxsize); + o = r.data(); + } + } + o[j]=0; + return setData(r, pmedium); + } else if (cf == CF_UNICODETEXT) { + QString str = mimeData->text(); + const QChar *u = str.unicode(); + QString res; + const int s = str.length(); + int maxsize = s + s/40 + 3; + res.resize(maxsize); + int ri = 0; + bool cr = false; + for (int i=0; i < s; ++i) { + if (*u == QLatin1Char('\r')) + cr = true; + else { + if (*u == QLatin1Char('\n') && !cr) + res[ri++] = QLatin1Char('\r'); + cr = false; + } + res[ri++] = *u; + if (ri+3 >= maxsize) { + maxsize += maxsize/4; + res.resize(maxsize); + } + ++u; + } + res.truncate(ri); + const int byteLength = res.length() * sizeof(ushort); + QByteArray r(byteLength + 2, '\0'); + memcpy(r.data(), res.unicode(), byteLength); + r[byteLength] = 0; + r[byteLength+1] = 0; + return setData(r, pmedium); + } + } + return false; +} + +bool QWindowsMimeText::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + return mimeType.startsWith(QStringLiteral("text/plain")) + && (canGetData(CF_UNICODETEXT, pDataObj) + || canGetData(CF_TEXT, pDataObj)); +} + +QString QWindowsMimeText::mimeForFormat(const FORMATETC &formatetc) const +{ + int cf = getCf(formatetc); + if (cf == CF_UNICODETEXT || cf == CF_TEXT) + return QStringLiteral("text/plain"); + return QString(); +} + + +QVector QWindowsMimeText::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +{ + QVector formatics; + if (mimeType.startsWith(QStringLiteral("text/plain")) && mimeData->hasText()) { + formatics += setCf(CF_UNICODETEXT); + formatics += setCf(CF_TEXT); + } + return formatics; +} + +QVariant QWindowsMimeText::convertToMime(const QString &mime, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const +{ + QVariant ret; + + if (canConvertToMime(mime, pDataObj)) { + QString str; + QByteArray data = getData(CF_UNICODETEXT, pDataObj); + if (!data.isEmpty()) { + str = QString::fromWCharArray((const wchar_t *)data.data()); + str.replace(QStringLiteral("\r\n"), QStringLiteral("\n")); + } else { + data = getData(CF_TEXT, pDataObj); + if (!data.isEmpty()) { + const char* d = data.data(); + const int s = qstrlen(d); + QByteArray r(data.size()+1, '\0'); + char* o = r.data(); + int j=0; + for (int i=0; i formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; +private: + int CF_INETURL_W; // wide char version + int CF_INETURL; +}; + +QWindowsMimeURI::QWindowsMimeURI() +{ + CF_INETURL_W = QWindowsMime::registerMimeType(QStringLiteral("UniformResourceLocatorW")); + CF_INETURL = QWindowsMime::registerMimeType(QStringLiteral("UniformResourceLocator")); +} + +bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + if (getCf(formatetc) == CF_HDROP) { + QList urls = mimeData->urls(); + for (int i=0; ihasFormat(QStringLiteral("text/uri-list")); +} + +bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const +{ + if (canConvertFromMime(formatetc, mimeData)) { + if (getCf(formatetc) == CF_HDROP) { + QList urls = mimeData->urls(); + QStringList fileNames; + int size = sizeof(DROPFILES)+2; + for (int i=0; ipFiles = sizeof(DROPFILES); + GetCursorPos(&d->pt); // try + d->fNC = true; + char* files = ((char*)d) + d->pFiles; + + d->fWide = true; + wchar_t* f = (wchar_t*)files; + for (int i=0; i urls = mimeData->urls(); + QByteArray result; + if (!urls.isEmpty()) { + QString url = urls.at(0).toString(); + result = QByteArray((const char *)url.utf16(), url.length() * sizeof(ushort)); + } + result.append('\0'); + result.append('\0'); + return setData(result, pmedium); + } else if (getCf(formatetc) == CF_INETURL) { + QList urls = mimeData->urls(); + QByteArray result; + if (!urls.isEmpty()) + result = urls.at(0).toString().toLocal8Bit(); + return setData(result, pmedium); + } + } + + return false; +} + +bool QWindowsMimeURI::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + return mimeType == QStringLiteral("text/uri-list") + && (canGetData(CF_HDROP, pDataObj) || canGetData(CF_INETURL_W, pDataObj) || canGetData(CF_INETURL, pDataObj)); +} + +QString QWindowsMimeURI::mimeForFormat(const FORMATETC &formatetc) const +{ + QString format; + if (getCf(formatetc) == CF_HDROP || getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) + format = QStringLiteral("text/uri-list"); + return format; +} + +QVector QWindowsMimeURI::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +{ + QVector formatics; + if (mimeType == QStringLiteral("text/uri-list")) { + if (canConvertFromMime(setCf(CF_HDROP), mimeData)) + formatics += setCf(CF_HDROP); + if (canConvertFromMime(setCf(CF_INETURL_W), mimeData)) + formatics += setCf(CF_INETURL_W); + if (canConvertFromMime(setCf(CF_INETURL), mimeData)) + formatics += setCf(CF_INETURL); + } + return formatics; +} + +QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pDataObj, QVariant::Type preferredType) const +{ + if (mimeType == QStringLiteral("text/uri-list")) { + if (canGetData(CF_HDROP, pDataObj)) { + QByteArray texturi; + QList urls; + + QByteArray data = getData(CF_HDROP, pDataObj); + if (data.isEmpty()) + return QVariant(); + + LPDROPFILES hdrop = (LPDROPFILES)data.data(); + if (hdrop->fWide) { + const wchar_t* filesw = (const wchar_t *)(data.data() + hdrop->pFiles); + int i = 0; + while (filesw[i]) { + QString fileurl = QString::fromWCharArray(filesw + i); + urls += QUrl::fromLocalFile(fileurl); + i += fileurl.length()+1; + } + } else { + const char* files = (const char *)data.data() + hdrop->pFiles; + int i=0; + while (files[i]) { + urls += QUrl::fromLocalFile(QString::fromLocal8Bit(files+i)); + i += int(strlen(files+i))+1; + } + } + + if (preferredType == QVariant::Url && urls.size() == 1) + return urls.at(0); + else if (!urls.isEmpty()) + return urls; + } else if (canGetData(CF_INETURL_W, pDataObj)) { + QByteArray data = getData(CF_INETURL_W, pDataObj); + if (data.isEmpty()) + return QVariant(); + return QUrl(QString::fromWCharArray((const wchar_t *)data.constData())); + } else if (canGetData(CF_INETURL, pDataObj)) { + QByteArray data = getData(CF_INETURL, pDataObj); + if (data.isEmpty()) + return QVariant(); + return QUrl(QString::fromLocal8Bit(data.constData())); + } + } + return QVariant(); +} + +class QWindowsMimeHtml : public QWindowsMime +{ +public: + QWindowsMimeHtml(); + + // for converting from Qt + bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; + QVector formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; + + // for converting to Qt + bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; + QString mimeForFormat(const FORMATETC &formatetc) const; + +private: + int CF_HTML; +}; + +QWindowsMimeHtml::QWindowsMimeHtml() +{ + CF_HTML = QWindowsMime::registerMimeType(QStringLiteral("HTML Format")); +} + +QVector QWindowsMimeHtml::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +{ + QVector formatetcs; + if (mimeType == QStringLiteral("text/html") && (!mimeData->html().isEmpty())) + formatetcs += setCf(CF_HTML); + return formatetcs; +} + +QString QWindowsMimeHtml::mimeForFormat(const FORMATETC &formatetc) const +{ + if (getCf(formatetc) == CF_HTML) + return QStringLiteral("text/html"); + return QString(); +} + +bool QWindowsMimeHtml::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + return mimeType == QStringLiteral("text/html") && canGetData(CF_HTML, pDataObj); +} + + +bool QWindowsMimeHtml::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + return getCf(formatetc) == CF_HTML && (!mimeData->html().isEmpty()); +} + +/* +The windows HTML clipboard format is as follows (xxxxxxxxxx is a 10 integer number giving the positions +in bytes). Charset used is mostly utf8, but can be different, ie. we have to look for the charset tag + + Version: 1.0 + StartHTML:xxxxxxxxxx + EndHTML:xxxxxxxxxx + StartFragment:xxxxxxxxxx + EndFragment:xxxxxxxxxx + ...html... + +*/ +QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const +{ + Q_UNUSED(preferredType); + QVariant result; + if (canConvertToMime(mime, pDataObj)) { + QByteArray html = getData(CF_HTML, pDataObj); +#ifdef QMIME_DEBUG + qDebug("QWindowsMimeHtml::convertToMime"); + qDebug("raw :"); + qDebug(html); +#endif + int start = html.indexOf("StartFragment:"); + int end = html.indexOf("EndFragment:"); + + if (start != -1) { + int startOffset = start + 14; + int i = startOffset; + while (html.at(i) != '\r' && html.at(i) != '\n') + ++i; + QByteArray bytecount = html.mid(startOffset, i - startOffset); + start = bytecount.toInt(); + } + + if (end != -1) { + int endOffset = end + 12; + int i = endOffset ; + while (html.at(i) != '\r' && html.at(i) != '\n') + ++i; + QByteArray bytecount = html.mid(endOffset , i - endOffset); + end = bytecount.toInt(); + } + + if (end > start && start > 0) { + html = "" + html.mid(start, end - start); + html += ""; + html.replace('\r', ""); + result = QString::fromUtf8(html); + } + } + return result; +} + +bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const +{ + if (canConvertFromMime(formatetc, mimeData)) { + QByteArray data = mimeData->html().toUtf8(); + QByteArray result = + "Version:1.0\r\n" // 0-12 + "StartHTML:0000000105\r\n" // 13-35 + "EndHTML:0000000000\r\n" // 36-55 + "StartFragment:0000000000\r\n" // 58-86 + "EndFragment:0000000000\r\n\r\n"; // 87-105 + + if (data.indexOf("") == -1) + result += ""; + result += data; + if (data.indexOf("") == -1) + result += ""; + + // set the correct number for EndHTML + QByteArray pos = QString::number(result.size()).toLatin1(); + memcpy((char *)(result.data() + 53 - pos.length()), pos.constData(), pos.length()); + + // set correct numbers for StartFragment and EndFragment + pos = QString::number(result.indexOf("") + 20).toLatin1(); + memcpy((char *)(result.data() + 79 - pos.length()), pos.constData(), pos.length()); + pos = QString::number(result.indexOf("")).toLatin1(); + memcpy((char *)(result.data() + 103 - pos.length()), pos.constData(), pos.length()); + + return setData(result, pmedium); + } + return false; +} + + +#ifndef QT_NO_IMAGEFORMAT_BMP +class QWindowsMimeImage : public QWindowsMime +{ +public: + QWindowsMimeImage(); + // for converting from Qt + bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; + QVector formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; + + // for converting to Qt + bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; + QString mimeForFormat(const FORMATETC &formatetc) const; +private: + bool hasOriginalDIBV5(IDataObject *pDataObj) const; + UINT CF_PNG; +}; + +QWindowsMimeImage::QWindowsMimeImage() +{ + CF_PNG = RegisterClipboardFormat(L"PNG"); +} + +QVector QWindowsMimeImage::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +{ + QVector formatetcs; + if (mimeData->hasImage() && mimeType == QStringLiteral("application/x-qt-image")) { + //add DIBV5 if image has alpha channel + QImage image = qvariant_cast(mimeData->imageData()); + if (!image.isNull() && image.hasAlphaChannel()) + formatetcs += setCf(CF_DIBV5); + formatetcs += setCf(CF_DIB); + } + return formatetcs; +} + +QString QWindowsMimeImage::mimeForFormat(const FORMATETC &formatetc) const +{ + int cf = getCf(formatetc); + if (cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG)) + return QStringLiteral("application/x-qt-image"); + return QString(); +} + +bool QWindowsMimeImage::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + if ((mimeType == QStringLiteral("application/x-qt-image")) && + (canGetData(CF_DIB, pDataObj) || canGetData(CF_PNG, pDataObj))) + return true; + return false; +} + +bool QWindowsMimeImage::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + int cf = getCf(formatetc); + if (mimeData->hasImage()) { + if (cf == CF_DIB) + return true; + else if (cf == CF_DIBV5) { + //support DIBV5 conversion only if the image has alpha channel + QImage image = qvariant_cast(mimeData->imageData()); + if (!image.isNull() && image.hasAlphaChannel()) + return true; + } + } + return false; +} + +bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const +{ + int cf = getCf(formatetc); + if ((cf == CF_DIB || cf == CF_DIBV5) && mimeData->hasImage()) { + QImage img = qvariant_cast(mimeData->imageData()); + if (img.isNull()) + return false; + QByteArray ba; + if (cf == CF_DIB) { + if (img.format() > QImage::Format_ARGB32) + img = img.convertToFormat(QImage::Format_RGB32); + const QByteArray ba = writeDib(img); + if (!ba.isEmpty()) + return setData(ba, pmedium); + } else { + QDataStream s(&ba, QIODevice::WriteOnly); + s.setByteOrder(QDataStream::LittleEndian);// Intel byte order #### + if (qt_write_dibv5(s, img)) + return setData(ba, pmedium); + } + } + return false; +} + +bool QWindowsMimeImage::hasOriginalDIBV5(IDataObject *pDataObj) const +{ + bool isSynthesized = true; + IEnumFORMATETC *pEnum =NULL; + HRESULT res = pDataObj->EnumFormatEtc(1, &pEnum); + if (res == S_OK && pEnum) { + FORMATETC fc; + while ((res = pEnum->Next(1, &fc, 0)) == S_OK) { + if (fc.ptd) + CoTaskMemFree(fc.ptd); + if (fc.cfFormat == CF_DIB) + break; + else if (fc.cfFormat == CF_DIBV5) { + isSynthesized = false; + break; + } + } + pEnum->Release(); + } + return !isSynthesized; +} + +QVariant QWindowsMimeImage::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const +{ + Q_UNUSED(preferredType); + QVariant result; + if (mimeType != QStringLiteral("application/x-qt-image")) + return result; + //Try to convert from a format which has more data + //DIBV5, use only if its is not synthesized + if (canGetData(CF_DIBV5, pDataObj) && hasOriginalDIBV5(pDataObj)) { + QImage img; + QByteArray data = getData(CF_DIBV5, pDataObj); + QDataStream s(&data, QIODevice::ReadOnly); + s.setByteOrder(QDataStream::LittleEndian); + if (qt_read_dibv5(s, img)) { // #### supports only 32bit DIBV5 + return img; + } + } + //PNG, MS Office place this (undocumented) + if (canGetData(CF_PNG, pDataObj)) { + QImage img; + QByteArray data = getData(CF_PNG, pDataObj); + if (img.loadFromData(data, "PNG")) { + return img; + } + } + //Fallback to DIB + if (canGetData(CF_DIB, pDataObj)) { + const QImage img = readDib(getData(CF_DIB, pDataObj)); + if (!img.isNull()) + return img; + } + // Failed + return result; +} +#endif + +class QBuiltInMimes : public QWindowsMime +{ +public: + QBuiltInMimes(); + + // for converting from Qt + bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; + QVector formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; + + // for converting to Qt + bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; + QString mimeForFormat(const FORMATETC &formatetc) const; + +private: + QMap outFormats; + QMap inFormats; +}; + +QBuiltInMimes::QBuiltInMimes() +: QWindowsMime() +{ + outFormats.insert(QWindowsMime::registerMimeType(QStringLiteral("application/x-color")), QStringLiteral("application/x-color")); + inFormats.insert(QWindowsMime::registerMimeType(QStringLiteral("application/x-color")), QStringLiteral("application/x-color")); +} + +bool QBuiltInMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + // really check + return formatetc.tymed & TYMED_HGLOBAL + && outFormats.contains(formatetc.cfFormat) + && mimeData->formats().contains(outFormats.value(formatetc.cfFormat)); +} + +bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const +{ + if (canConvertFromMime(formatetc, mimeData)) { + QByteArray data; + if (outFormats.value(getCf(formatetc)) == QStringLiteral("text/html")) { + // text/html is in wide chars on windows (compatible with mozillia) + QString html = mimeData->html(); + // same code as in the text converter up above + const QChar *u = html.unicode(); + QString res; + const int s = html.length(); + int maxsize = s + s/40 + 3; + res.resize(maxsize); + int ri = 0; + bool cr = false; + for (int i=0; i < s; ++i) { + if (*u == QLatin1Char('\r')) + cr = true; + else { + if (*u == QLatin1Char('\n') && !cr) + res[ri++] = QLatin1Char('\r'); + cr = false; + } + res[ri++] = *u; + if (ri+3 >= maxsize) { + maxsize += maxsize/4; + res.resize(maxsize); + } + ++u; + } + res.truncate(ri); + const int byteLength = res.length() * sizeof(ushort); + QByteArray r(byteLength + 2, '\0'); + memcpy(r.data(), res.unicode(), byteLength); + r[byteLength] = 0; + r[byteLength+1] = 0; + data = r; + } else { +#ifndef QT_NO_DRAGANDDROP + data = QInternalMimeData::renderDataHelper(outFormats.value(getCf(formatetc)), mimeData); +#endif //QT_NO_DRAGANDDROP + } + return setData(data, pmedium); + } + return false; +} + +QVector QBuiltInMimes::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const +{ + QVector formatetcs; + if (!outFormats.keys(mimeType).isEmpty() && mimeData->formats().contains(mimeType)) + formatetcs += setCf(outFormats.key(mimeType)); + return formatetcs; +} + +bool QBuiltInMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + return (!inFormats.keys(mimeType).isEmpty()) + && canGetData(inFormats.key(mimeType), pDataObj); +} + +QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const +{ + QVariant val; + if (canConvertToMime(mimeType, pDataObj)) { + QByteArray data = getData(inFormats.key(mimeType), pDataObj); + if (!data.isEmpty()) { +#ifdef QMIME_DEBUG + qDebug("QBuiltInMimes::convertToMime()"); +#endif + if (mimeType == QStringLiteral("text/html") && preferredType == QVariant::String) { + // text/html is in wide chars on windows (compatible with Mozilla) + val = QString::fromWCharArray((const wchar_t *)data.data()); + } else { + val = data; // it should be enough to return the data and let QMimeData do the rest. + } + } + } + return val; +} + +QString QBuiltInMimes::mimeForFormat(const FORMATETC &formatetc) const +{ + return inFormats.value(getCf(formatetc)); +} + + +class QLastResortMimes : public QWindowsMime +{ +public: + + QLastResortMimes(); + // for converting from Qt + bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const; + QVector formatsForMime(const QString &mimeType, const QMimeData *mimeData) const; + + // for converting to Qt + bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const; + QVariant convertToMime(const QString &mime, IDataObject *pDataObj, QVariant::Type preferredType) const; + QString mimeForFormat(const FORMATETC &formatetc) const; + +private: + QMap formats; + static QStringList ianaTypes; + static QStringList excludeList; +}; + +QStringList QLastResortMimes::ianaTypes; +QStringList QLastResortMimes::excludeList; + +QLastResortMimes::QLastResortMimes() +{ + //MIME Media-Types + if (!ianaTypes.size()) { + ianaTypes.append(QStringLiteral("application/")); + ianaTypes.append(QStringLiteral("audio/")); + ianaTypes.append(QStringLiteral("example/")); + ianaTypes.append(QStringLiteral("image/")); + ianaTypes.append(QStringLiteral("message/")); + ianaTypes.append(QStringLiteral("model/")); + ianaTypes.append(QStringLiteral("multipart/")); + ianaTypes.append(QStringLiteral("text/")); + ianaTypes.append(QStringLiteral("video/")); + } + //Types handled by other classes + if (!excludeList.size()) { + excludeList.append(QStringLiteral("HTML Format")); + excludeList.append(QStringLiteral("UniformResourceLocator")); + excludeList.append(QStringLiteral("text/html")); + excludeList.append(QStringLiteral("text/plain")); + excludeList.append(QStringLiteral("text/uri-list")); + excludeList.append(QStringLiteral("application/x-qt-image")); + excludeList.append(QStringLiteral("application/x-color")); + } +} + +bool QLastResortMimes::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + // really check +#ifndef QT_NO_DRAGANDDROP + return formatetc.tymed & TYMED_HGLOBAL + && (formats.contains(formatetc.cfFormat) + && QInternalMimeData::hasFormatHelper(formats.value(formatetc.cfFormat), mimeData)); +#else + Q_UNUSED(mimeData); + Q_UNUSED(formatetc); + return formatetc.tymed & TYMED_HGLOBAL + && formats.contains(formatetc.cfFormat); +#endif //QT_NO_DRAGANDDROP +} + +bool QLastResortMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const +{ +#ifndef QT_NO_DRAGANDDROP + return canConvertFromMime(formatetc, mimeData) + && setData(QInternalMimeData::renderDataHelper(formats.value(getCf(formatetc)), mimeData), pmedium); +#else + Q_UNUSED(mimeData); + Q_UNUSED(formatetc); + Q_UNUSED(pmedium); + return false; +#endif //QT_NO_DRAGANDDROP +} + +QVector QLastResortMimes::formatsForMime(const QString &mimeType, const QMimeData * /*mimeData*/) const +{ + QVector formatetcs; + if (!formats.keys(mimeType).isEmpty()) { + formatetcs += setCf(formats.key(mimeType)); + } else if (!excludeList.contains(mimeType, Qt::CaseInsensitive)){ + // register any other available formats + int cf = QWindowsMime::registerMimeType(mimeType); + QLastResortMimes *that = const_cast(this); + that->formats.insert(cf, mimeType); + formatetcs += setCf(cf); + } + return formatetcs; +} +static const char x_qt_windows_mime[] = "application/x-qt-windows-mime;value=\""; + +static bool isCustomMimeType(const QString &mimeType) +{ + return mimeType.startsWith(QLatin1String(x_qt_windows_mime), Qt::CaseInsensitive); +} + +static QString customMimeType(const QString &mimeType) +{ + int len = sizeof(x_qt_windows_mime) - 1; + int n = mimeType.lastIndexOf(QLatin1Char('\"'))-len; + return mimeType.mid(len, n); +} + +bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + if (isCustomMimeType(mimeType)) { + QString clipFormat = customMimeType(mimeType); + int cf = RegisterClipboardFormat(reinterpret_cast (clipFormat.utf16())); + return canGetData(cf, pDataObj); + } else if (formats.keys(mimeType).isEmpty()) { + // if it is not in there then register it an see if we can get it + int cf = QWindowsMime::registerMimeType(mimeType); + return canGetData(cf, pDataObj); + } else { + return canGetData(formats.key(mimeType), pDataObj); + } + return false; +} + +QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const +{ + Q_UNUSED(preferredType); + QVariant val; + if (canConvertToMime(mimeType, pDataObj)) { + QByteArray data; + if (isCustomMimeType(mimeType)) { + QString clipFormat = customMimeType(mimeType); + int cf = RegisterClipboardFormat(reinterpret_cast (clipFormat.utf16())); + data = getData(cf, pDataObj); + } else if (formats.keys(mimeType).isEmpty()) { + int cf = QWindowsMime::registerMimeType(mimeType); + data = getData(cf, pDataObj); + } else { + data = getData(formats.key(mimeType), pDataObj); + } + if (!data.isEmpty()) + val = data; // it should be enough to return the data and let QMimeData do the rest. + } + return val; +} + +QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const +{ + QString format = formats.value(getCf(formatetc)); + if (!format.isEmpty()) + return format; + + wchar_t buffer[256]; + int len = GetClipboardFormatName(getCf(formatetc), buffer, 256); + + if (len) { + QString clipFormat = QString::fromWCharArray(buffer, len); +#ifndef QT_NO_DRAGANDDROP + if (QInternalMimeData::canReadData(clipFormat)) + format = clipFormat; + else if((formatetc.cfFormat >= 0xC000)){ + //create the mime as custom. not registered. + if (!excludeList.contains(clipFormat, Qt::CaseInsensitive)) { + //check if this is a mime type + bool ianaType = false; + int sz = ianaTypes.size(); + for (int i = 0; i < sz; i++) { + if (clipFormat.startsWith(ianaTypes[i], Qt::CaseInsensitive)) { + ianaType = true; + break; + } + } + if (!ianaType) + format = QLatin1String(x_qt_windows_mime) + clipFormat + QLatin1Char('\"'); + else + format = clipFormat; + } + } +#endif //QT_NO_DRAGANDDROP + } + + return format; +} + +/*! + \class QWindowsMimeConverter + \brief Manages the list of QWindowsMime instances. + \ingroup qt-lighthouse-win + \sa QWindowsMime +*/ + +QWindowsMimeConverter::QWindowsMimeConverter() +{ +} + +QWindowsMimeConverter::~QWindowsMimeConverter() +{ + qDeleteAll(m_mimes); +} + +QWindowsMime * QWindowsMimeConverter::converterToMime(const QString &mimeType, IDataObject *pDataObj) const +{ + ensureInitialized(); + for (int i = m_mimes.size()-1; i >= 0; --i) { + if (m_mimes.at(i)->canConvertToMime(mimeType, pDataObj)) + return m_mimes.at(i); + } + return 0; +} + +QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) const +{ + ensureInitialized(); + QStringList formats; + LPENUMFORMATETC FAR fmtenum; + HRESULT hr = pDataObj->EnumFormatEtc(DATADIR_GET, &fmtenum); + + if (hr == NOERROR) { + FORMATETC fmtetc; + while (S_OK == fmtenum->Next(1, &fmtetc, 0)) { +#if defined(QMIME_DEBUG) + qDebug("QWindowsMime::allMimesForFormats()"); + wchar_t buf[256] = {0}; + GetClipboardFormatName(fmtetc.cfFormat, buf, 255); + qDebug("CF = %d : %s", fmtetc.cfFormat, QString::fromWCharArray(buf)); +#endif + for (int i= m_mimes.size() - 1; i >= 0; --i) { + QString format = m_mimes.at(i)->mimeForFormat(fmtetc); + if (!format.isEmpty() && !formats.contains(format)) { + formats += format; + } + } + // as documented in MSDN to avoid possible memleak + if (fmtetc.ptd) + CoTaskMemFree(fmtetc.ptd); + } + fmtenum->Release(); + } + if (QWindowsContext::verboseOLE) + qDebug() << __FUNCTION__ << pDataObj << formats; + return formats; +} + +QWindowsMime * QWindowsMimeConverter::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const +{ + ensureInitialized(); + for (int i = m_mimes.size()-1; i >= 0; --i) { + if (m_mimes.at(i)->canConvertFromMime(formatetc, mimeData)) + return m_mimes.at(i); + } + return 0; +} + +QVector QWindowsMimeConverter::allFormatsForMime(const QMimeData *mimeData) const +{ + ensureInitialized(); + QVector formatics; +#ifdef QT_NO_DRAGANDDROP + Q_UNUSED(mimeData); +#else + formatics.reserve(20); + const QStringList formats = QInternalMimeData::formatsHelper(mimeData); + for (int f = 0; f < formats.size(); ++f) { + for (int i = m_mimes.size() - 1; i >= 0; --i) + formatics += m_mimes.at(i)->formatsForMime(formats.at(f), mimeData); + } +#endif //QT_NO_DRAGANDDROP + return formatics; +} + +void QWindowsMimeConverter::ensureInitialized() const +{ + if (m_mimes.isEmpty()) { + m_mimes << new QWindowsMimeImage << new QLastResortMimes + << new QWindowsMimeText << new QWindowsMimeURI + << new QWindowsMimeHtml << new QBuiltInMimes; + } +} + +QVariant QWindowsMimeConverter::convertToMime(const QStringList &mimeTypes, + IDataObject *pDataObj, + QVariant::Type preferredType, + QString *formatIn /* = 0 */) const +{ + foreach (const QString &format, mimeTypes) { + if (const QWindowsMime *converter = converterToMime(format, pDataObj)) { + if (converter->canConvertToMime(format, pDataObj)) { + const QVariant dataV = converter->convertToMime(format, pDataObj, preferredType); + if (dataV.isValid()) { + if (QWindowsContext::verboseOLE) + qDebug() << __FUNCTION__ << mimeTypes << "\nFormat: " + << format << pDataObj << " returns " << dataV; + if (formatIn) + *formatIn = format; + return dataV; + } + } + } + } + if (QWindowsContext::verboseOLE) + qDebug() << __FUNCTION__ << "fails" << mimeTypes << pDataObj << preferredType; + return QVariant(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsmime.h b/src/plugins/platforms/windows/qwindowsmime.h new file mode 100644 index 0000000000..85f61a91e2 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsmime.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSMIME_H +#define QWINDOWSMIME_H + +#include "qtwindows_additional.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QMimeData; + +class QWindowsMime +{ + Q_DISABLE_COPY(QWindowsMime) +public: + QWindowsMime(); + virtual ~QWindowsMime(); + + // for converting from Qt + virtual bool canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const = 0; + virtual bool convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM * pmedium) const = 0; + virtual QVector formatsForMime(const QString &mimeType, const QMimeData *mimeData) const = 0; + + // for converting to Qt + virtual bool canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const = 0; + virtual QVariant convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const = 0; + virtual QString mimeForFormat(const FORMATETC &formatetc) const = 0; + + static int registerMimeType(const QString &mime); +}; + +class QWindowsMimeConverter +{ + Q_DISABLE_COPY(QWindowsMimeConverter) +public: + QWindowsMimeConverter(); + ~QWindowsMimeConverter(); + + QWindowsMime *converterToMime(const QString &mimeType, IDataObject *pDataObj) const; + QStringList allMimesForFormats(IDataObject *pDataObj) const; + QWindowsMime *converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const; + QVector allFormatsForMime(const QMimeData *mimeData) const; + + // Convenience. + QVariant convertToMime(const QStringList &mimeTypes, IDataObject *pDataObj, QVariant::Type preferredType, + QString *format = 0) const; + +private: + typedef QSharedPointer MimePtr; + + void ensureInitialized() const; + + mutable QList m_mimes; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSMIME_H diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp new file mode 100644 index 0000000000..dea965b439 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsmousehandler.h" +#include "qwindowscontext.h" +#include "qwindowswindow.h" +#include "qwindowsintegration.h" + +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +static inline void compressMouseMove(MSG *msg) +{ + // Compress mouse move events + if (msg->message == WM_MOUSEMOVE) { + MSG mouseMsg; + while (PeekMessage(&mouseMsg, msg->hwnd, WM_MOUSEFIRST, + WM_MOUSELAST, PM_NOREMOVE)) { + if (mouseMsg.message == WM_MOUSEMOVE) { +#define PEEKMESSAGE_IS_BROKEN 1 +#ifdef PEEKMESSAGE_IS_BROKEN + // Since the Windows PeekMessage() function doesn't + // correctly return the wParam for WM_MOUSEMOVE events + // if there is a key release event in the queue + // _before_ the mouse event, we have to also consider + // key release events (kls 2003-05-13): + MSG keyMsg; + bool done = false; + while (PeekMessage(&keyMsg, 0, WM_KEYFIRST, WM_KEYLAST, + PM_NOREMOVE)) { + if (keyMsg.time < mouseMsg.time) { + if ((keyMsg.lParam & 0xC0000000) == 0x40000000) { + PeekMessage(&keyMsg, 0, keyMsg.message, + keyMsg.message, PM_REMOVE); + } else { + done = true; + break; + } + } else { + break; // no key event before the WM_MOUSEMOVE event + } + } + if (done) + break; +#else + // Actually the following 'if' should work instead of + // the above key event checking, but apparently + // PeekMessage() is broken :-( + if (mouseMsg.wParam != msg.wParam) + break; // leave the message in the queue because + // the key state has changed +#endif + // Update the passed in MSG structure with the + // most recent one. + msg->lParam = mouseMsg.lParam; + msg->wParam = mouseMsg.wParam; + // Extract the x,y coordinates from the lParam as we do in the WndProc + msg->pt.x = GET_X_LPARAM(mouseMsg.lParam); + msg->pt.y = GET_Y_LPARAM(mouseMsg.lParam); + ClientToScreen(msg->hwnd, &(msg->pt)); + // Remove the mouse move message + PeekMessage(&mouseMsg, msg->hwnd, WM_MOUSEMOVE, + WM_MOUSEMOVE, PM_REMOVE); + } else { + break; // there was no more WM_MOUSEMOVE event + } + } + } +} + +/*! + \class QWindowsMouseHandler + \brief Windows mouse handler + + Dispatches mouse and touch events. Separate for code cleanliness. + + \ingroup qt-lighthouse-win +*/ + +QWindowsMouseHandler::QWindowsMouseHandler() : + m_windowUnderMouse(0) +{ +} + +bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, + QtWindows::WindowsEventType et, + MSG msg, LRESULT *result) +{ + if (et & QtWindows::NonClientEventFlag) + return false; + if (et == QtWindows::MouseWheelEvent) + return translateMouseWheelEvent(window, hwnd, msg, result); + *result = 0; + if (msg.message == WM_MOUSELEAVE) { + // When moving out of a child, MouseMove within parent is received first + // (see below) + if (QWindowsContext::verboseEvents) + qDebug() << "WM_MOUSELEAVE for " << window << " current= " << m_windowUnderMouse; + if (window == m_windowUnderMouse) { + QWindowSystemInterface::handleLeaveEvent(window); + m_windowUnderMouse = 0; + } + return true; + } + compressMouseMove(&msg); + const QPoint client(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); + // Enter new window: track to generate leave event. + if (m_windowUnderMouse != window) { + // The tracking on m_windowUnderMouse might still be active and + // trigger later on. + if (m_windowUnderMouse) { + if (QWindowsContext::verboseEvents) + qDebug() << "Synthetic leave for " << m_windowUnderMouse; + QWindowSystemInterface::handleLeaveEvent(m_windowUnderMouse); + } + m_windowUnderMouse = window; + if (QWindowsContext::verboseEvents) + qDebug() << "Entering " << window; + QWindowsWindow::baseWindowOf(window)->applyCursor(); + QWindowSystemInterface::handleEnterEvent(window); + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hwnd; + tme.dwHoverTime = HOVER_DEFAULT; // + if (!TrackMouseEvent(&tme)) + qWarning("TrackMouseEvent failed."); + } + QWindowSystemInterface::handleMouseEvent(window, client, + QWindowsGeometryHint::mapToGlobal(hwnd, client), + keyStateToMouseButtons((int)msg.wParam)); + return true; +} + +bool QWindowsMouseHandler::translateMouseWheelEvent(QWindow *window, HWND, + MSG msg, LRESULT *) +{ + const Qt::MouseButtons buttons = keyStateToMouseButtons((int)msg.wParam); + int delta; + if (msg.message == WM_MOUSEWHEEL || msg.message == WM_MOUSEHWHEEL) + delta = (short) HIWORD (msg.wParam); + else + delta = (int) msg.wParam; + + Qt::Orientation orientation = (msg.message == WM_MOUSEHWHEEL + || (buttons & Qt::AltModifier)) ? + Qt::Horizontal : Qt::Vertical; + + // according to the MSDN documentation on WM_MOUSEHWHEEL: + // a positive value indicates that the wheel was rotated to the right; + // a negative value indicates that the wheel was rotated to the left. + // Qt defines this value as the exact opposite, so we have to flip the value! + if (msg.message == WM_MOUSEHWHEEL) + delta = -delta; + + const QPoint globalPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); + // TODO: if there is a widget under the mouse and it is not shadowed + // QWindow *receiver = windowAt(pos); + // by modality, we send the event to it first. + //synaptics touchpad shows its own widget at this position + //so widgetAt() will fail with that HWND, try child of this widget + // if (!receiver) receiver = window->childAt(pos); + QWindow *receiver = window; + QWindowSystemInterface::handleWheelEvent(receiver, + QWindowsGeometryHint::mapFromGlobal(receiver, globalPos), + globalPos, + delta, orientation); + return true; +} + +// from bool QApplicationPrivate::translateTouchEvent() +bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, + QtWindows::WindowsEventType, + MSG msg, LRESULT *) +{ + typedef QWindowSystemInterface::TouchPoint QTouchPoint; + typedef QList QTouchPointList; + + const QRect screenGeometry = window->screen()->geometry(); + + const int winTouchPointCount = msg.wParam; + QScopedArrayPointer winTouchInputs(new TOUCHINPUT[winTouchPointCount]); + memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchPointCount); + + QTouchPointList touchPoints; + touchPoints.reserve(winTouchPointCount); + Qt::TouchPointStates allStates = 0; + + Q_ASSERT(QWindowsContext::user32dll.getTouchInputInfo); + + QWindowsContext::user32dll.getTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT)); + for (int i = 0; i < winTouchPointCount; ++i) { + const TOUCHINPUT &winTouchInput = winTouchInputs[i]; + QTouchPoint touchPoint; + touchPoint.pressure = 1.0; + touchPoint.isPrimary = (winTouchInput.dwFlags & TOUCHEVENTF_PRIMARY) != 0; + touchPoint.id = m_touchInputIDToTouchPointID.value(winTouchInput.dwID, -1); + if (touchPoint.id == -1) { + touchPoint.id = m_touchInputIDToTouchPointID.size(); + m_touchInputIDToTouchPointID.insert(winTouchInput.dwID, touchPoint.id); + } + + QPointF screenPos = QPointF(qreal(winTouchInput.x) / qreal(100.), qreal(winTouchInput.y) / qreal(100.)); + if (winTouchInput.dwMask & TOUCHINPUTMASKF_CONTACTAREA) + touchPoint.area.setSize(QSizeF(qreal(winTouchInput.cxContact) / qreal(100.), + qreal(winTouchInput.cyContact) / qreal(100.))); + touchPoint.area.moveCenter(screenPos); + + if (winTouchInput.dwFlags & TOUCHEVENTF_DOWN) { + touchPoint.state = Qt::TouchPointPressed; + } else if (winTouchInput.dwFlags & TOUCHEVENTF_UP) { + touchPoint.state = Qt::TouchPointReleased; + } else { + // TODO: Previous code checked" + // screenPos == touchPoint.normalPosition -> Qt::TouchPointStationary, but + // but touchPoint.normalPosition was never initialized? + touchPoint.state = touchPoint.state; + } + + touchPoint.normalPosition = QPointF(screenPos.x() / screenGeometry.width(), + screenPos.y() / screenGeometry.height()); + + allStates |= touchPoint.state; + + touchPoints.append(touchPoint); + } + + QWindowsContext::user32dll.closeTouchInputHandle((HANDLE) msg.lParam); + + // all touch points released, forget the ids we've seen, they may not be reused + if ((allStates & Qt::TouchPointStateMask) == Qt::TouchPointReleased) + m_touchInputIDToTouchPointID.clear(); + + // TODO: Device used to be hardcoded to screen in previous code. + // What is the correct event type? Which parts of translateRawTouchEvent() are required? + QWindowSystemInterface::handleTouchEvent(window, QEvent::TouchBegin, + QTouchEvent::TouchScreen, + touchPoints); + return true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.h b/src/plugins/platforms/windows/qwindowsmousehandler.h new file mode 100644 index 0000000000..227a66babf --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsmousehandler.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSMOUSEHANDLER_H +#define QWINDOWSMOUSEHANDLER_H + +#include "qtwindowsglobal.h" +#include "qtwindows_additional.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QWindow; + +class QWindowsMouseHandler +{ + Q_DISABLE_COPY(QWindowsMouseHandler) +public: + QWindowsMouseHandler(); + + bool translateMouseEvent(QWindow *widget, HWND hwnd, + QtWindows::WindowsEventType t, MSG msg, + LRESULT *result); + bool translateTouchEvent(QWindow *widget, HWND hwnd, + QtWindows::WindowsEventType t, MSG msg, + LRESULT *result); + + static inline Qt::MouseButtons keyStateToMouseButtons(int); + + QWindow *windowUnderMouse() const { return m_windowUnderMouse.data(); } + +private: + inline bool translateMouseWheelEvent(QWindow *window, HWND hwnd, + MSG msg, LRESULT *result); + + QPointer m_windowUnderMouse; + QHash m_touchInputIDToTouchPointID; +}; + +Qt::MouseButtons QWindowsMouseHandler::keyStateToMouseButtons(int wParam) +{ + Qt::MouseButtons mb(Qt::NoButton); + if (wParam & MK_LBUTTON) + mb |= Qt::LeftButton; + if (wParam & MK_MBUTTON) + mb |= Qt::MiddleButton; + if (wParam & MK_RBUTTON) + mb |= Qt::RightButton; + if (wParam & MK_XBUTTON1) + mb |= Qt::XButton1; + if (wParam & MK_XBUTTON2) + mb |= Qt::XButton2; + return mb; +} + +QT_END_NAMESPACE + +#endif // QWINDOWSMOUSEHANDLER_H diff --git a/src/plugins/platforms/windows/qwindowsnativeimage.cpp b/src/plugins/platforms/windows/qwindowsnativeimage.cpp new file mode 100644 index 0000000000..53311c5fd7 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsnativeimage.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsnativeimage.h" +#include "qwindowscontext.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +typedef struct { + BITMAPINFOHEADER bmiHeader; + DWORD redMask; + DWORD greenMask; + DWORD blueMask; +} BITMAPINFO_MASK; + +/*! + \class QWindowsNativeImage + \brief Windows Native image + + Note that size can be 0 (widget autotests with zero size), which + causes CreateDIBSection() to fail. + + \sa QWindowsBackingStore + \ingroup qt-lighthouse-win +*/ + +static inline HDC createDC() +{ + HDC display_dc = GetDC(0); + HDC hdc = CreateCompatibleDC(display_dc); + ReleaseDC(0, display_dc); + Q_ASSERT(hdc); + return hdc; +} + +static inline HBITMAP createDIB(HDC hdc, int width, int height, + QImage::Format format, + uchar **bitsIn) +{ + BITMAPINFO_MASK bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -height; // top-down. + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biSizeImage = 0; + + if (format == QImage::Format_RGB16) { + bmi.bmiHeader.biBitCount = 16; + bmi.bmiHeader.biCompression = BI_BITFIELDS; + bmi.redMask = 0xF800; + bmi.greenMask = 0x07E0; + bmi.blueMask = 0x001F; + } else { + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.redMask = 0; + bmi.greenMask = 0; + bmi.blueMask = 0; + } + + void *bits = 0; + HBITMAP bitmap = CreateDIBSection(hdc, reinterpret_cast(&bmi), + DIB_RGB_COLORS, &bits, 0, 0); + if (!bitmap || !bits) + qFatal("%s: CreateDIBSection failed.", __FUNCTION__); + + *bitsIn = (uchar*)bits; + return bitmap; +} + +QWindowsNativeImage::QWindowsNativeImage(int width, int height, + QImage::Format format) : + m_hdc(createDC()), + m_bitmap(0), + m_null_bitmap(0) +{ + if (width != 0 && height != 0) { + uchar *bits; + m_bitmap = createDIB(m_hdc, width, height, format, &bits); + m_null_bitmap = (HBITMAP)SelectObject(m_hdc, m_bitmap); + m_image = QImage(bits, width, height, format); + Q_ASSERT(m_image.paintEngine()->type() == QPaintEngine::Raster); + static_cast(m_image.paintEngine())->setDC(m_hdc); + } else { + m_image = QImage(width, height, format); + } + + GdiFlush(); +} + +QWindowsNativeImage::~QWindowsNativeImage() +{ + if (m_hdc) { + if (m_bitmap) { + if (m_null_bitmap) + SelectObject(m_hdc, m_null_bitmap); + DeleteObject(m_bitmap); + } + DeleteDC(m_hdc); + } +} + +QImage::Format QWindowsNativeImage::systemFormat() +{ + static const int depth = QWindowsContext::instance()->screenDepth(); + return depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsnativeimage.h b/src/plugins/platforms/windows/qwindowsnativeimage.h new file mode 100644 index 0000000000..c77805a10a --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsnativeimage.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSNATIVEIMAGE_H +#define QWINDOWSNATIVEIMAGE_H + +#include "qtwindows_additional.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QWindowsNativeImage +{ + Q_DISABLE_COPY(QWindowsNativeImage) +public: + QWindowsNativeImage(int width, int height, + QImage::Format format); + + ~QWindowsNativeImage(); + + inline int width() const { return m_image.width(); } + inline int height() const { return m_image.height(); } + + QImage &image() { return m_image; } + const QImage &image() const { return m_image; } + + HDC hdc() const { return m_hdc; } + + static QImage::Format systemFormat(); + +private: + const HDC m_hdc; + QImage m_image; + + HBITMAP m_bitmap; + HBITMAP m_null_bitmap; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSNATIVEIMAGE_H diff --git a/src/plugins/platforms/windows/qwindowsole.cpp b/src/plugins/platforms/windows/qwindowsole.cpp new file mode 100644 index 0000000000..864dc3dbb7 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsole.cpp @@ -0,0 +1,476 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsole.h" +#include "qwindowsmime.h" +#include "qwindowscontext.h" +\ +#include +#include +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QWindowsOleDataObject + \brief OLE data container + + The following methods are NOT supported for data transfer using the + clipboard or drag-drop: + \list + \o IDataObject::SetData -- return E_NOTIMPL + \o IDataObject::DAdvise -- return OLE_E_ADVISENOTSUPPORTED + \o ::DUnadvise + \o ::EnumDAdvise + \o IDataObject::GetCanonicalFormatEtc -- return E_NOTIMPL + (NOTE: must set pformatetcOut->ptd = NULL) + \endlist + + \ingroup qt-lighthouse-win +*/ + +QWindowsOleDataObject::QWindowsOleDataObject(QMimeData *mimeData) : + m_refs(1), data(mimeData), + CF_PERFORMEDDROPEFFECT(RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT)), + performedEffect(DROPEFFECT_NONE) +{ + if (QWindowsContext::verboseOLE) + qDebug("%s '%s'", __FUNCTION__, qPrintable(mimeData->formats().join(QStringLiteral(", ")))); +} + +QWindowsOleDataObject::~QWindowsOleDataObject() +{ + if (QWindowsContext::verboseOLE) + qDebug("%s", __FUNCTION__); +} + +void QWindowsOleDataObject::releaseQt() +{ + data = 0; +} + +QMimeData *QWindowsOleDataObject::mimeData() const +{ + return data.data(); +} + +DWORD QWindowsOleDataObject::reportedPerformedEffect() const +{ + return performedEffect; +} + +//--------------------------------------------------------------------- +// IUnknown Methods +//--------------------------------------------------------------------- + +STDMETHODIMP +QWindowsOleDataObject::QueryInterface(REFIID iid, void FAR* FAR* ppv) +{ + if (iid == IID_IUnknown || iid == IID_IDataObject) { + *ppv = this; + AddRef(); + return NOERROR; + } + *ppv = NULL; + return ResultFromScode(E_NOINTERFACE); +} + +STDMETHODIMP_(ULONG) +QWindowsOleDataObject::AddRef(void) +{ + return ++m_refs; +} + +STDMETHODIMP_(ULONG) +QWindowsOleDataObject::Release(void) +{ + if (--m_refs == 0) { + releaseQt(); + delete this; + return 0; + } + return m_refs; +} + +STDMETHODIMP +QWindowsOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium) +{ + HRESULT hr = ResultFromScode(DATA_E_FORMATETC); + + if (QWindowsContext::verboseOLE) { + wchar_t buf[256] = {0}; + GetClipboardFormatName(pformatetc->cfFormat, buf, 255); + qDebug("%s CF = %d : %s", __FUNCTION__, pformatetc->cfFormat, qPrintable(QString::fromWCharArray(buf))); + } + + if (data) { + const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + if (QWindowsMime *converter = mc.converterFromMime(*pformatetc, data)) + if (converter->convertFromMime(*pformatetc, data, pmedium)) + hr = ResultFromScode(S_OK); + } + + if (QWindowsContext::verboseOLE) { + wchar_t buf[256] = {0}; + GetClipboardFormatName(pformatetc->cfFormat, buf, 255); + qDebug("%s CF = %d : %s returns 0x%x", __FUNCTION__, pformatetc->cfFormat, + qPrintable(QString::fromWCharArray(buf)), int(hr)); + } + + return hr; +} + +STDMETHODIMP +QWindowsOleDataObject::GetDataHere(LPFORMATETC, LPSTGMEDIUM) +{ + return ResultFromScode(DATA_E_FORMATETC); +} + +STDMETHODIMP +QWindowsOleDataObject::QueryGetData(LPFORMATETC pformatetc) +{ + HRESULT hr = ResultFromScode(DATA_E_FORMATETC); + + if (QWindowsContext::verboseOLE > 1) + qDebug("%s", __FUNCTION__); + + if (data) { + const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + hr = mc.converterFromMime(*pformatetc, data) ? + ResultFromScode(S_OK) : ResultFromScode(S_FALSE); + } + if (QWindowsContext::verboseOLE > 1) + qDebug("%s returns 0x%x", __FUNCTION__, int(hr)); + return hr; +} + +STDMETHODIMP +QWindowsOleDataObject::GetCanonicalFormatEtc(LPFORMATETC, LPFORMATETC pformatetcOut) +{ + pformatetcOut->ptd = NULL; + return ResultFromScode(E_NOTIMPL); +} + +STDMETHODIMP +QWindowsOleDataObject::SetData(LPFORMATETC pFormatetc, STGMEDIUM *pMedium, BOOL fRelease) +{ + if (QWindowsContext::verboseOLE > 1) + qDebug("%s", __FUNCTION__); + + HRESULT hr = ResultFromScode(E_NOTIMPL); + + if (pFormatetc->cfFormat == CF_PERFORMEDDROPEFFECT && pMedium->tymed == TYMED_HGLOBAL) { + DWORD * val = (DWORD*)GlobalLock(pMedium->hGlobal); + performedEffect = *val; + GlobalUnlock(pMedium->hGlobal); + if (fRelease) + ReleaseStgMedium(pMedium); + hr = ResultFromScode(S_OK); + } + if (QWindowsContext::verboseOLE > 1) + qDebug("%s returns 0x%x", __FUNCTION__, int(hr)); + return hr; +} + + +STDMETHODIMP +QWindowsOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc) +{ + if (QWindowsContext::verboseOLE > 1) + qDebug("%s", __FUNCTION__); + + if (!data) + return ResultFromScode(DATA_E_FORMATETC); + + SCODE sc = S_OK; + + QVector fmtetcs; + if (dwDirection == DATADIR_GET) { + QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); + fmtetcs = mc.allFormatsForMime(data); + } else { + FORMATETC formatetc; + formatetc.cfFormat = CF_PERFORMEDDROPEFFECT; + formatetc.dwAspect = DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.ptd = NULL; + formatetc.tymed = TYMED_HGLOBAL; + fmtetcs.append(formatetc); + } + + QWindowsOleEnumFmtEtc *enumFmtEtc = new QWindowsOleEnumFmtEtc(fmtetcs); + *ppenumFormatEtc = enumFmtEtc; + if (enumFmtEtc->isNull()) { + delete enumFmtEtc; + *ppenumFormatEtc = NULL; + sc = E_OUTOFMEMORY; + } + + return ResultFromScode(sc); +} + +STDMETHODIMP +QWindowsOleDataObject::DAdvise(FORMATETC FAR*, DWORD, + LPADVISESINK, DWORD FAR*) +{ + return ResultFromScode(OLE_E_ADVISENOTSUPPORTED); +} + + +STDMETHODIMP +QWindowsOleDataObject::DUnadvise(DWORD) +{ + return ResultFromScode(OLE_E_ADVISENOTSUPPORTED); +} + +STDMETHODIMP +QWindowsOleDataObject::EnumDAdvise(LPENUMSTATDATA FAR*) +{ + return ResultFromScode(OLE_E_ADVISENOTSUPPORTED); +} + +/*! + \class QWindowsOleEnumFmtEtc + \brief Enumerates the FORMATETC structures supported by QWindowsOleDataObject. + \ingroup qt-lighthouse-win +*/ + +QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QVector &fmtetcs) : + m_dwRefs(1), m_nIndex(0), m_isNull(false) +{ + if (QWindowsContext::verboseOLE > 1) + qDebug("%s", __FUNCTION__); + m_lpfmtetcs.reserve(fmtetcs.count()); + for (int idx = 0; idx < fmtetcs.count(); ++idx) { + LPFORMATETC destetc = new FORMATETC(); + if (copyFormatEtc(destetc, (LPFORMATETC)&(fmtetcs.at(idx)))) { + m_lpfmtetcs.append(destetc); + } else { + m_isNull = true; + delete destetc; + break; + } + } +} + +QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QVector &lpfmtetcs) : + m_dwRefs(1), m_nIndex(0), m_isNull(false) +{ + if (QWindowsContext::verboseOLE > 1) + qDebug("%s", __FUNCTION__); + m_lpfmtetcs.reserve(lpfmtetcs.count()); + for (int idx = 0; idx < lpfmtetcs.count(); ++idx) { + LPFORMATETC srcetc = lpfmtetcs.at(idx); + LPFORMATETC destetc = new FORMATETC(); + if (copyFormatEtc(destetc, srcetc)) { + m_lpfmtetcs.append(destetc); + } else { + m_isNull = true; + delete destetc; + break; + } + } +} + +QWindowsOleEnumFmtEtc::~QWindowsOleEnumFmtEtc() +{ + LPMALLOC pmalloc; + + if (CoGetMalloc(MEMCTX_TASK, &pmalloc) == NOERROR) { + for (int idx = 0; idx < m_lpfmtetcs.count(); ++idx) { + LPFORMATETC tmpetc = m_lpfmtetcs.at(idx); + if (tmpetc->ptd) + pmalloc->Free(tmpetc->ptd); + delete tmpetc; + } + + pmalloc->Release(); + } + m_lpfmtetcs.clear(); +} + +bool QWindowsOleEnumFmtEtc::isNull() const +{ + return m_isNull; +} + +// IUnknown methods +STDMETHODIMP +QWindowsOleEnumFmtEtc::QueryInterface(REFIID riid, void FAR* FAR* ppvObj) +{ + if (riid == IID_IUnknown || riid == IID_IEnumFORMATETC) { + *ppvObj = this; + AddRef(); + return NOERROR; + } + *ppvObj = NULL; + return ResultFromScode(E_NOINTERFACE); +} + +STDMETHODIMP_(ULONG) +QWindowsOleEnumFmtEtc::AddRef(void) +{ + return ++m_dwRefs; +} + +STDMETHODIMP_(ULONG) +QWindowsOleEnumFmtEtc::Release(void) +{ + if (--m_dwRefs == 0) { + delete this; + return 0; + } + return m_dwRefs; +} + +// IEnumFORMATETC methods +STDMETHODIMP +QWindowsOleEnumFmtEtc::Next(ULONG celt, LPFORMATETC rgelt, ULONG FAR* pceltFetched) +{ + ULONG i=0; + ULONG nOffset; + + if (rgelt == NULL) + return ResultFromScode(E_INVALIDARG); + + while (i < celt) { + nOffset = m_nIndex + i; + + if (nOffset < ULONG(m_lpfmtetcs.count())) { + copyFormatEtc((LPFORMATETC)&(rgelt[i]), m_lpfmtetcs.at(nOffset)); + i++; + } else { + break; + } + } + + m_nIndex += (WORD)i; + + if (pceltFetched != NULL) + *pceltFetched = i; + + if (i != celt) + return ResultFromScode(S_FALSE); + + return NOERROR; +} + +STDMETHODIMP +QWindowsOleEnumFmtEtc::Skip(ULONG celt) +{ + ULONG i=0; + ULONG nOffset; + + while (i < celt) { + nOffset = m_nIndex + i; + + if (nOffset < ULONG(m_lpfmtetcs.count())) { + i++; + } else { + break; + } + } + + m_nIndex += (WORD)i; + + if (i != celt) + return ResultFromScode(S_FALSE); + + return NOERROR; +} + +STDMETHODIMP +QWindowsOleEnumFmtEtc::Reset() +{ + m_nIndex = 0; + return NOERROR; +} + +STDMETHODIMP +QWindowsOleEnumFmtEtc::Clone(LPENUMFORMATETC FAR* newEnum) +{ + if (newEnum == NULL) + return ResultFromScode(E_INVALIDARG); + + QWindowsOleEnumFmtEtc *result = new QWindowsOleEnumFmtEtc(m_lpfmtetcs); + result->m_nIndex = m_nIndex; + + if (result->isNull()) { + delete result; + return ResultFromScode(E_OUTOFMEMORY); + } else { + *newEnum = result; + } + + return NOERROR; +} + +bool QWindowsOleEnumFmtEtc::copyFormatEtc(LPFORMATETC dest, LPFORMATETC src) const +{ + if (dest == NULL || src == NULL) + return false; + + *dest = *src; + + if (src->ptd) { + LPVOID pout; + LPMALLOC pmalloc; + + if (CoGetMalloc(MEMCTX_TASK, &pmalloc) != NOERROR) + return false; + + pout = (LPVOID)pmalloc->Alloc(src->ptd->tdSize); + memcpy(dest->ptd, src->ptd, size_t(src->ptd->tdSize)); + + pmalloc->Release(); + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsole.h b/src/plugins/platforms/windows/qwindowsole.h new file mode 100644 index 0000000000..d979af3b31 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsole.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSOLE_H +#define QWINDOWSOLE_H + +#include "qtwindows_additional.h" + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QMimeData; +class QWindow; + +class QWindowsOleDataObject : public IDataObject +{ +public: + explicit QWindowsOleDataObject(QMimeData *mimeData); + virtual ~QWindowsOleDataObject(); + + void releaseQt(); + QMimeData *mimeData() const; + DWORD reportedPerformedEffect() const; + + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj); + STDMETHOD_(ULONG,AddRef)(void); + STDMETHOD_(ULONG,Release)(void); + + // IDataObject methods + STDMETHOD(GetData)(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium); + STDMETHOD(GetDataHere)(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium); + STDMETHOD(QueryGetData)(LPFORMATETC pformatetc); + STDMETHOD(GetCanonicalFormatEtc)(LPFORMATETC pformatetc, LPFORMATETC pformatetcOut); + STDMETHOD(SetData)(LPFORMATETC pformatetc, STGMEDIUM FAR * pmedium, + BOOL fRelease); + STDMETHOD(EnumFormatEtc)(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc); + STDMETHOD(DAdvise)(FORMATETC FAR* pFormatetc, DWORD advf, + LPADVISESINK pAdvSink, DWORD FAR* pdwConnection); + STDMETHOD(DUnadvise)(DWORD dwConnection); + STDMETHOD(EnumDAdvise)(LPENUMSTATDATA FAR* ppenumAdvise); + +private: + ULONG m_refs; + QPointer data; + int CF_PERFORMEDDROPEFFECT; + DWORD performedEffect; +}; + +class QWindowsOleEnumFmtEtc : public IEnumFORMATETC +{ +public: + explicit QWindowsOleEnumFmtEtc(const QVector &fmtetcs); + explicit QWindowsOleEnumFmtEtc(const QVector &lpfmtetcs); + virtual ~QWindowsOleEnumFmtEtc(); + + bool isNull() const; + + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj); + STDMETHOD_(ULONG,AddRef)(void); + STDMETHOD_(ULONG,Release)(void); + + // IEnumFORMATETC methods + STDMETHOD(Next)(ULONG celt, LPFORMATETC rgelt, ULONG FAR* pceltFetched); + STDMETHOD(Skip)(ULONG celt); + STDMETHOD(Reset)(void); + STDMETHOD(Clone)(LPENUMFORMATETC FAR* newEnum); + +private: + bool copyFormatEtc(LPFORMATETC dest, LPFORMATETC src) const; + + ULONG m_dwRefs; + ULONG m_nIndex; + QVector m_lpfmtetcs; + bool m_isNull; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSOLE_H diff --git a/src/plugins/platforms/windows/qwindowsprintersupport.cpp b/src/plugins/platforms/windows/qwindowsprintersupport.cpp new file mode 100644 index 0000000000..3d43c61e10 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsprintersupport.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsprintersupport.h" + +#ifdef HAS_PRINTENGINE +# include +#endif + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +QPrintEngine *QWindowsPrinterSupport::createNativePrintEngine(QPrinter::PrinterMode mode) +{ +#ifdef HAS_PRINTENGINE + return new QWin32PrintEngine(mode); +#else + Q_UNUSED(mode); + Q_UNIMPLEMENTED(); + return 0; +#endif +} + +QPaintEngine *QWindowsPrinterSupport::createPaintEngine(QPrintEngine *engine, QPrinter::PrinterMode) +{ +#ifdef HAS_PRINTENGINE + return static_cast(engine); +#else + Q_UNIMPLEMENTED(); + Q_UNUSED(engine); + return 0; +#endif +} + +QList QWindowsPrinterSupport::supportedPaperSizes(const QPrinterInfo &printerInfo) const +{ + QList paperSizes; + const QString printerName = printerInfo.printerName(); + const wchar_t *nameUtf16 = reinterpret_cast(printerName.utf16()); + DWORD size = DeviceCapabilities(nameUtf16, NULL, DC_PAPERS, NULL, NULL); + if ((int)size != -1) { + wchar_t *papers = new wchar_t[size]; + size = DeviceCapabilities(nameUtf16, NULL, DC_PAPERS, papers, NULL); +#ifdef HAS_PRINTENGINE + for (int c = 0; c < (int)size; ++c) + paperSizes.append(mapDevmodePaperSize(papers[c])); +#endif + delete [] papers; + } + return paperSizes; +} + +QList QWindowsPrinterSupport::availablePrinters() +{ + QList printers; + + DWORD needed = 0; + DWORD returned = 0; + if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, 0, 0, &needed, &returned)) { + LPBYTE buffer = new BYTE[needed]; + if (EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, buffer, needed, &needed, &returned)) { + PPRINTER_INFO_4 infoList = reinterpret_cast(buffer); + QPrinterInfo defPrn = defaultPrinter(); + for (uint i = 0; i < returned; ++i) { + const QString printerName(QString::fromWCharArray(infoList[i].pPrinterName)); + const bool isDefault = printerName == defPrn.printerName(); + printers.append(QPlatformPrinterSupport::printerInfo(printerName, + isDefault)); + } + } + delete [] buffer; + } + + return printers; +} + +QPrinterInfo QWindowsPrinterSupport::defaultPrinter() +{ + QString noPrinters(QStringLiteral("qt_no_printers")); + wchar_t buffer[256]; + GetProfileString(L"windows", L"device", (wchar_t*)noPrinters.utf16(), buffer, 256); + QString output = QString::fromWCharArray(buffer); + if (output != noPrinters) { + // Filter out the name of the printer, which should be everything before a comma. + const QString printerName = output.split(QLatin1Char(',')).value(0); + return QPlatformPrinterSupport::printerInfo(printerName, true); + } + + return QPrinterInfo(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsprintersupport.h b/src/plugins/platforms/windows/qwindowsprintersupport.h new file mode 100644 index 0000000000..c0a8190599 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsprintersupport.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSPRINTERSUPPORT_H +#define QWINDOWSPRINTERSUPPORT_H + +#include + +QT_BEGIN_NAMESPACE + +class QWindowsPrinterSupport : public QPlatformPrinterSupport +{ +public: + virtual QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode); + virtual QPaintEngine *createPaintEngine(QPrintEngine *, QPrinter::PrinterMode); + + virtual QList supportedPaperSizes(const QPrinterInfo &) const; + virtual QPrinterInfo defaultPrinter(); + virtual QList availablePrinters(); +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSPRINTERSUPPORT_H diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp new file mode 100644 index 0000000000..3de508a1c7 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowsscreen.h" +#include "qwindowscontext.h" +#include "qwindowswindow.h" +#include "pixmaputils.h" +#include "qwindowscursor.h" + +#include "qtwindows_additional.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +typedef QPair DPI; + +QWindowsScreenData::QWindowsScreenData() : + dpi(96, 96), + depth(32), + format(QImage::Format_ARGB32_Premultiplied), primary(false) +{ +} + +static inline DPI deviceDPI(HDC hdc) +{ + return DPI(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY)); +} + +static inline QSize deviceSizeMM(const QSize &pixels, const DPI &dpi) +{ + const qreal inchToMM = 25.4; + const qreal h = qreal(pixels.width()) / qreal(dpi.first) * inchToMM; + const qreal v = qreal(pixels.height()) / qreal(dpi.second) * inchToMM; + return QSize(qRound(h), qRound(v)); +} + +static inline DPI deviceDPI(const QSize &pixels, const QSize &physicalSizeMM) +{ + const qreal inchToMM = 25.4; + const qreal h = qreal(pixels.width()) / (qreal(physicalSizeMM.width()) / inchToMM); + const qreal v = qreal(pixels.height()) / (qreal(physicalSizeMM.height()) / inchToMM); + return DPI(qRound(v), qRound(h)); +} + +typedef QList WindowsScreenDataList; + +// from QDesktopWidget, taking WindowsScreenDataList as LPARAM +BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p) +{ + MONITORINFOEX info; + memset(&info, 0, sizeof(MONITORINFOEX)); + info.cbSize = sizeof(MONITORINFOEX); + if (GetMonitorInfo(hMonitor, &info) == FALSE) + return TRUE; + + WindowsScreenDataList *result = reinterpret_cast(p); + QWindowsScreenData data; + data.geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1)); + if (HDC hdc = CreateDC(info.szDevice, NULL, NULL, NULL)) { + data.dpi = deviceDPI(hdc); + DeleteDC(hdc); + } else { + qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %d DPI.", + __FUNCTION__, qPrintable(QString::fromWCharArray(info.szDevice)), + data.dpi.first); + } + data.physicalSizeMM = deviceSizeMM(data.geometry.size(), data.dpi); + data.geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1)); + data.availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1)); + data.primary = (info.dwFlags & MONITORINFOF_PRIMARY) != 0; + result->append(data); + return TRUE; +} + +/*! + \class QWindowsScreen + \brief Windows screen. + \ingroup qt-lighthouse-win +*/ + +QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) : + m_data(data), m_cursor(this) +{ +} + +QList QWindowsScreen::screens() +{ + // Retrieve monitors and add static depth information to each. + WindowsScreenDataList data; + EnumDisplayMonitors(0, 0, monitorEnumCallback, (LPARAM)&data); + + const int depth = QWindowsContext::instance()->screenDepth(); + const QImage::Format format = depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32; + QList result; + + const WindowsScreenDataList::const_iterator scend = data.constEnd(); + for (WindowsScreenDataList::const_iterator it = data.constBegin(); it != scend; ++it) { + QWindowsScreenData d = *it; + d.depth = depth; + d.format = format; + if (QWindowsContext::verboseIntegration) + qDebug() << "Screen" << d.geometry << d.availableGeometry << d.primary + << " physical " << d.physicalSizeMM << " DPI" << d.dpi + << "Depth: " << d.depth << " Format: " << d.format; + result.append(new QWindowsScreen(d)); + } + return result; +} + +QPixmap QWindowsScreen::grabWindow(WId window, int x, int y, int width, int height) const +{ + if (QWindowsContext::verboseIntegration) + qDebug() << __FUNCTION__ << window << x << y << width << height; + RECT r; + HWND hwnd = (HWND)window; + GetClientRect(hwnd, &r); + + if (width < 0) width = r.right - r.left; + if (height < 0) height = r.bottom - r.top; + + // Create and setup bitmap + HDC display_dc = GetDC(0); + HDC bitmap_dc = CreateCompatibleDC(display_dc); + HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height); + HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap); + + // copy data + HDC window_dc = GetDC(hwnd); + BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY | CAPTUREBLT); + + // clean up all but bitmap + ReleaseDC(hwnd, window_dc); + SelectObject(bitmap_dc, null_bitmap); + DeleteDC(bitmap_dc); + + const QPixmap pixmap = qPixmapFromWinHBITMAP(bitmap, HBitmapNoAlpha); + + DeleteObject(bitmap); + ReleaseDC(0, display_dc); + + return pixmap; +} + +/*! + \brief Find a top level window taking the flags of ChildWindowFromPointEx. +*/ + +QWindow *QWindowsScreen::findTopLevelAt(const QPoint &point, unsigned flags) +{ + QWindow* result = 0; + if (QPlatformWindow *bw = QWindowsContext::instance()-> + findPlatformWindowAt(GetDesktopWindow(), point, flags)) + result = QWindowsWindow::topLevelOf(bw->window()); + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << point << flags << result; + return result; +} + +QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags) +{ + QWindow* result = 0; + if (QPlatformWindow *bw = QWindowsContext::instance()-> + findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags)) + result = bw->window(); + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << screenPoint << " returns " << result; + return result; +} + +QWindow *QWindowsScreen::windowUnderMouse(unsigned flags) +{ + return QWindowsScreen::windowAt(QWindowsCursor::mousePosition(), flags); +} + +QWindowsScreen *QWindowsScreen::screenOf(const QWindow *w) +{ + if (w) + if (const QScreen *s = w->screen()) + if (QPlatformScreen *pscr = s->handle()) + return static_cast(pscr); + if (const QScreen *ps = QGuiApplication::primaryScreen()) + if (QPlatformScreen *ppscr = ps->handle()) + return static_cast(ppscr); + return 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h new file mode 100644 index 0000000000..e24af7af09 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSSCREEN_H +#define QWINDOWSSCREEN_H + +#include "qwindowscursor.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct QWindowsScreenData +{ + QWindowsScreenData(); + + QRect geometry; + QRect availableGeometry; + QPair dpi; + QSize physicalSizeMM; + int depth; + QImage::Format format; + bool primary; +}; + +class QWindowsScreen : public QPlatformScreen +{ +public: + explicit QWindowsScreen(const QWindowsScreenData &data); + + static QWindowsScreen *screenOf(const QWindow *w = 0); + + virtual QRect geometry() const { return m_data.geometry; } + virtual QRect availableGeometry() const { return m_data.availableGeometry; } + virtual int depth() const { return m_data.depth; } + virtual QImage::Format format() const { return m_data.format; } + virtual QSize physicalSize() const { return m_data.physicalSizeMM; } + + virtual QWindow *topLevelAt(const QPoint &point) const + { return QWindowsScreen::findTopLevelAt(point, CWP_SKIPINVISIBLE); } + + static QWindow *findTopLevelAt(const QPoint &point, unsigned flags); + static QWindow *windowAt(const QPoint &point, unsigned flags = CWP_SKIPINVISIBLE); + static QWindow *windowUnderMouse(unsigned flags = CWP_SKIPINVISIBLE); + + static QList screens(); + + virtual QPixmap grabWindow(WId window, int x, int y, int width, int height) const; + + const QWindowsCursor &cursor() const { return m_cursor; } + QWindowsCursor &cursor() { return m_cursor; } + +private: + const QWindowsScreenData m_data; + QWindowsCursor m_cursor; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSSCREEN_H diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp new file mode 100644 index 0000000000..95b770f043 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -0,0 +1,1317 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwindowswindow.h" +#include "qwindowsnativeimage.h" +#include "qwindowscontext.h" +#include "qwindowsdrag.h" +#include "qwindowsscreen.h" +#include "qwindowscursor.h" + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +static QByteArray debugWinStyle(DWORD style) +{ + + QByteArray rc = "0x"; + rc += QByteArray::number(qulonglong(style), 16); + if (style & WS_POPUP) + rc += " WS_POPUP"; + if (style & WS_CHILD) + rc += " WS_CHILD"; + if (style & WS_OVERLAPPED) + rc += " WS_OVERLAPPED"; + if (style & WS_CLIPSIBLINGS) + rc += " WS_CLIPSIBLINGS"; + if (style & WS_CLIPCHILDREN) + rc += " WS_CLIPCHILDREN"; + if (style & WS_THICKFRAME) + rc += " WS_THICKFRAME"; + if (style & WS_DLGFRAME) + rc += " WS_DLGFRAME"; + if (style & WS_SYSMENU) + rc += " WS_SYSMENU"; + if (style & WS_MINIMIZEBOX) + rc += " WS_MINIMIZEBOX"; + if (style & WS_MAXIMIZEBOX) + rc += " WS_MAXIMIZEBOX"; + return rc; +} + +static QByteArray debugWindowStates(Qt::WindowStates s) +{ + + QByteArray rc = "0x"; + rc += QByteArray::number(int(s), 16); + if (s & Qt::WindowMinimized) + rc += " WindowMinimized"; + if (s & Qt::WindowMaximized) + rc += " WindowMaximized"; + if (s & Qt::WindowFullScreen) + rc += " WindowFullScreen"; + if (s & Qt::WindowActive) + rc += " WindowActive"; + return rc; +} + +QDebug operator<<(QDebug d, const MINMAXINFO &i) +{ + d.nospace() << "MINMAXINFO maxSize=" << i.ptMaxSize.x << ',' + << i.ptMaxSize.y << " maxpos=" << i.ptMaxPosition.x + << ',' << i.ptMaxPosition.y << " mintrack=" + << i.ptMinTrackSize.x << ',' << i.ptMinTrackSize.y + << " maxtrack=" << i.ptMaxTrackSize.x << ',' + << i.ptMaxTrackSize.y; + return d; +} + +static inline QSize qSizeOfRect(const RECT &rect) +{ + return QSize(rect.right -rect.left, rect.bottom - rect.top); +} + +static inline QRect qrectFromRECT(const RECT &rect) +{ + return QRect(QPoint(rect.left, rect.top), qSizeOfRect(rect)); +} + +QDebug operator<<(QDebug d, const RECT &r) +{ + d.nospace() << "RECT: left/top=" << r.left << ',' << r.top + << " right/bottom=" << r.right << ',' << r.bottom; + return d; +} + +QDebug operator<<(QDebug d, const NCCALCSIZE_PARAMS &p) +{ + qDebug().nospace() << "NCCALCSIZE_PARAMS " + << qrectFromRECT(p.rgrc[0]) + << ' ' << qrectFromRECT(p.rgrc[1]) << ' ' + << qrectFromRECT(p.rgrc[2]); + return d; +} + +static inline QRect frameGeometry(HWND hwnd) +{ + RECT rect = { 0, 0, 0, 0 }; + GetWindowRect(hwnd, &rect); + return qrectFromRECT(rect); +} + +QSize clientSize(HWND hwnd) +{ + RECT rect = { 0, 0, 0, 0 }; + GetClientRect(hwnd, &rect); // Always returns point 0,0, thus unusable for geometry. + return qSizeOfRect(rect); +} + +// from qwidget_win.cpp/maximum layout size check removed. +static bool shouldShowMaximizeButton(Qt::WindowFlags flags) +{ + if (flags & Qt::MSWindowsFixedSizeDialogHint) + 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; +} + +/*! + \class WindowCreationData + \brief Window creation code. + + This struct gathers all information required to create a window. + Window creation is split in 3 steps: + + \list + \o fromWindow() Gather all required information + \o create() Create the system handle. + \o initialize() Post creation initialization steps. + \endlist + + The reason for this split is to also enable changing the QWindowFlags + by calling: + + \list + \o fromWindow() Gather information and determine new system styles + \o applyWindowFlags() to apply the new window system styles. + \o initialize() Post creation initialization steps. + \endlist + + Contains the window creation code formerly in qwidget_win.cpp. + + \sa QWindowCreationContext + \ingroup qt-lighthouse-win +*/ + +struct WindowCreationData +{ + typedef QWindowsWindow::WindowData WindowData; + + WindowCreationData() : parentHandle(0), type(Qt::Widget), style(0), exStyle(0), + topLevel(false), popup(false), dialog(false), desktop(false), + tool(false) {} + + void fromWindow(const QWindow *w, const Qt::WindowFlags flags, bool isGL); + inline WindowData create(const QWindow *w, const QRect &geometry, QString title) const; + inline void applyWindowFlags(HWND hwnd) const; + void initialize(HWND h, bool frameChange) const; + + Qt::WindowFlags flags; + HWND parentHandle; + Qt::WindowType type; + unsigned style; + unsigned exStyle; + bool isGL; + bool topLevel; + bool popup; + bool dialog; + bool desktop; + bool tool; +}; + +QDebug operator<<(QDebug debug, const WindowCreationData &d) +{ + debug.nospace() << QWindowsWindow::debugWindowFlags(d.flags) + << " gs=" << d.isGL << " topLevel=" << d.topLevel << " popup=" + << d.popup << " dialog=" << d.dialog << " desktop=" << d.desktop + << " tool=" << d.tool << " style=" << debugWinStyle(d.style) + << " exStyle=0x" << QString::number(d.exStyle, 16) + << " parent=" << d.parentHandle; + return debug; +} + +void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flagsIn, + bool isGLin) +{ + isGL = isGLin; + flags = flagsIn; + topLevel = w->isTopLevel(); + + if (topLevel && flags == 1) { + qWarning("Remove me: fixing toplevel window flags"); + flags |= Qt::WindowTitleHint|Qt::WindowSystemMenuHint|Qt::WindowMinimizeButtonHint + |Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint; + } + + type = static_cast(int(flags) & Qt::WindowType_Mask); + switch (type) { + case Qt::Dialog: + case Qt::Sheet: + dialog = true; + break; + case Qt::Drawer: + case Qt::Tool: + tool = true; + break; + case Qt::Popup: + popup = true; + break; + case Qt::Desktop: + desktop = true; + break; + default: + break; + } + if ((flags & Qt::MSWindowsFixedSizeDialogHint)) + dialog = true; + + // Parent: Use transient parent for top levels. + if (popup) { + flags |= Qt::WindowStaysOnTopHint; // a popup stays on top, no parent. + } else { + if (const QWindow *parentWindow = topLevel ? w->transientParent() : w->parent()) + parentHandle = QWindowsWindow::handleOf(parentWindow); + } + + if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) { + style = WS_POPUP; + } else if (topLevel && !desktop) { + if (flags & Qt::FramelessWindowHint) + style = WS_POPUP; // no border + else if (flags & Qt::WindowTitleHint) + style = WS_OVERLAPPED; + else + style = 0; + } else { + style = WS_CHILD; + } + + if (!desktop) { + // if (!testAttribute(Qt::WA_PaintUnclipped)) + // ### Commented out for now as it causes some problems, but + // this should be correct anyway, so dig some more into this +#ifdef Q_FLATTEN_EXPOSE + if (isGL) + style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; // see SetPixelFormat +#else + style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ; +#endif + if (topLevel) { + if ((type == Qt::Window || dialog || tool)) { + if (!(flags & Qt::FramelessWindowHint)) { + style |= WS_POPUP; + if (flags & Qt::MSWindowsFixedSizeDialogHint) { + style |= WS_DLGFRAME; + } else { + style |= WS_THICKFRAME; + } + } + if (flags & Qt::WindowTitleHint) + style |= WS_CAPTION; + if (flags & Qt::WindowSystemMenuHint) + style |= WS_SYSMENU; + if (flags & Qt::WindowMinimizeButtonHint) + style |= WS_MINIMIZEBOX; + if (shouldShowMaximizeButton(flags)) + style |= WS_MAXIMIZEBOX; + if (tool) + exStyle |= WS_EX_TOOLWINDOW; + if (flags & Qt::WindowContextHelpButtonHint) + exStyle |= WS_EX_CONTEXTHELP; + } else { + exStyle |= WS_EX_TOOLWINDOW; + } + } + } +} + +QWindowsWindow::WindowData + WindowCreationData::create(const QWindow *w, const QRect &geometry, QString title) const +{ + typedef QSharedPointer QWindowCreationContextPtr; + + WindowData result; + result.flags = flags; + + if (desktop) { // desktop widget. No frame, hopefully? + result.hwnd = GetDesktopWindow(); + result.geometry = frameGeometry(result.hwnd); + if (QWindowsContext::verboseWindows) + qDebug().nospace() << "Created desktop window " << w << result.hwnd; + return result; + } + + const HINSTANCE appinst = (HINSTANCE)GetModuleHandle(0); + + const QString windowClassName = QWindowsContext::instance()->registerWindowClass(w, isGL); + + if (title.isEmpty() && (result.flags & Qt::WindowTitleHint)) + title = topLevel ? qAppName() : w->objectName(); + + const wchar_t *titleUtf16 = reinterpret_cast(title.utf16()); + const wchar_t *classNameUtf16 = reinterpret_cast(windowClassName.utf16()); + + // Capture events before CreateWindowEx() returns. + const QWindowCreationContextPtr context(new QWindowCreationContext(w, geometry, style, exStyle)); + QWindowsContext::instance()->setWindowCreationContext(context); + + if (QWindowsContext::verboseWindows) + qDebug().nospace() + << "CreateWindowEx: " << w << *this + << " class=" <frameWidth << 'x' << context->frameHeight + << '+' << context->frameX << '+' << context->frameY; + + 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: " + << context->obtainedGeometry << context->margins; + + if (!result.hwnd) { + qErrnoWarning("%s: CreateWindowEx failed", __FUNCTION__); + return result; + } + + result.geometry = context->obtainedGeometry; + result.frame = context->margins; + return result; +} + +void WindowCreationData::applyWindowFlags(HWND hwnd) const +{ + // Keep enabled and visible from the current style. + const LONG_PTR oldStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + const LONG_PTR oldExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + + const LONG_PTR newStyle = style | (oldStyle & (WS_DISABLED|WS_VISIBLE)); + if (oldStyle != newStyle) + SetWindowLongPtr(hwnd, GWL_STYLE, newStyle); + const LONG_PTR newExStyle = exStyle; + if (newExStyle != oldExStyle) + SetWindowLongPtr(hwnd, GWL_EXSTYLE, newExStyle); + if (QWindowsContext::verboseWindows) + qDebug().nospace() << __FUNCTION__ << hwnd << *this + << "\n Style from " << debugWinStyle(oldStyle) << "\n to " + << debugWinStyle(newStyle) << "\n ExStyle from 0x" + << QByteArray::number(qulonglong(oldExStyle), 16) << " to 0x" + << QByteArray::number(qulonglong(newExStyle), 16); +} + +void WindowCreationData::initialize(HWND hwnd, bool frameChange) const +{ + if (desktop || !hwnd) + return; + UINT flags = SWP_NOMOVE | SWP_NOSIZE; + if (frameChange) + flags |= SWP_FRAMECHANGED; + if (topLevel) { + flags |= SWP_NOACTIVATE; + if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip)) { + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, flags); + if (flags & Qt::WindowStaysOnBottomHint) + 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, flags); + } + if (flags & (Qt::CustomizeWindowHint|Qt::WindowTitleHint)) { + HMENU systemMenu = GetSystemMenu(hwnd, FALSE); + if (flags & Qt::WindowCloseButtonHint) + EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED); + else + EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED); + } + } else { // child. + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, flags); + } +} + +/*! + \class QWindowsGeometryHint + \brief Stores geometry constraints and provides utility functions. + + Geometry constraints ready to apply to a MINMAXINFO taking frame + into account. + + \ingroup qt-lighthouse-win +*/ + +#define QWINDOWSIZE_MAX ((1<<24)-1) + +QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w) : + minimumSize(w->minimumSize()), + maximumSize(w->maximumSize()) +{ +} + +bool QWindowsGeometryHint::validSize(const QSize &s) const +{ + const int width = s.width(); + const int height = s.height(); + return width >= minimumSize.width() && width <= maximumSize.width() + && height >= minimumSize.height() && height <= maximumSize.height(); +} + +QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle) +{ + RECT rect = {0,0,0,0}; + style &= ~(WS_OVERLAPPED); // Not permitted, see docs. + if (!AdjustWindowRectEx(&rect, style, FALSE, exStyle)) + qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__); + const QMargins result(qAbs(rect.left), qAbs(rect.top), + qAbs(rect.right), qAbs(rect.bottom)); + if (QWindowsContext::verboseWindows) + qDebug().nospace() << __FUNCTION__ << " style= 0x" + << QString::number(style, 16) + << " exStyle=0x" << QString::number(exStyle, 16) << ' ' << rect << ' ' << result; + + return result; +} + +void QWindowsGeometryHint::applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const +{ + return applyToMinMaxInfo(GetWindowLong(hwnd, GWL_STYLE), + GetWindowLong(hwnd, GWL_EXSTYLE), mmi); +} + +void QWindowsGeometryHint::applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const +{ + if (QWindowsContext::verboseWindows) + qDebug().nospace() << '>' << __FUNCTION__ << '<' << " min=" + << minimumSize.width() << ',' << minimumSize.height() + << " max=" << maximumSize.width() << ',' << maximumSize.height() + << " in " << *mmi; + + const QMargins margins = QWindowsGeometryHint::frame(style, exStyle); + const int frameWidth = margins.left() + margins.right(); + const int frameHeight = margins.top() + margins.bottom(); + if (minimumSize.width() > 0) + mmi->ptMinTrackSize.x = minimumSize.width() + frameWidth; + if (minimumSize.height() > 0) + mmi->ptMinTrackSize.y = minimumSize.height() + frameHeight; + + const int maximumWidth = qMax(maximumSize.width(), minimumSize.width()); + const int maximumHeight = qMax(maximumSize.height(), minimumSize.height()); + if (maximumWidth < QWINDOWSIZE_MAX) + mmi->ptMaxTrackSize.x = maximumWidth + frameWidth; + // windows with title bar have an implicit size limit of 112 pixels + if (maximumHeight < QWINDOWSIZE_MAX) + mmi->ptMaxTrackSize.y = qMax(maximumHeight + frameHeight, 112); + if (QWindowsContext::verboseWindows) + qDebug().nospace() << '<' << __FUNCTION__ + << " frame=" << margins << ' ' << frameWidth << ',' << frameHeight + << " out " << *mmi; +} + +/*! + \class QWindowCreationContext + \brief Active Context for creating windows. + + There is a phase in window creation (WindowCreationData::create()) + in which events are sent before the system API CreateWindowEx() returns + the handle. These cannot be handled by the platform window as the association + of the unknown handle value to the window does not exist yet and as not + to trigger recursive handle creation, etc. + + In that phase, an instance of QWindowCreationContext is set on + QWindowsContext. + + QWindowCreationContext stores the information to answer the initial + WM_GETMINMAXINFO and obtains the corrected size/position. + + \sa WindowCreationData, QWindowsContext + \ingroup qt-lighthouse-win +*/ + +QWindowCreationContext::QWindowCreationContext(const QWindow *w, + const QRect &geometry, + DWORD style_, DWORD exStyle_) : + geometryHint(w), style(style_), exStyle(exStyle_), + requestedGeometry(geometry), obtainedGeometry(geometry), + margins(QWindowsGeometryHint::frame(style, exStyle)), + frameX(CW_USEDEFAULT), frameY(CW_USEDEFAULT), + frameWidth(CW_USEDEFAULT), frameHeight(CW_USEDEFAULT) +{ + // Geometry of toplevels does not consider window frames. + // TODO: No concept of WA_wasMoved yet that would indicate a + // CW_USEDEFAULT unless set. For now, assume that 0,0 means 'default' + // for toplevels. + if (geometry.isValid()) { + if (!w->isTopLevel() || geometry.y() >= margins.top()) { + frameX = geometry.x() - margins.left(); + frameY = geometry.y() - margins.top(); + } + frameWidth = geometry.width() + margins.left() + margins.right(); + frameHeight = geometry.height() + margins.top() + margins.bottom(); + } + if (QWindowsContext::verboseWindows) + qDebug().nospace() + << __FUNCTION__ << ' ' << w << " min" << geometryHint.minimumSize + << " min" << geometryHint.maximumSize; +} + +/*! + \class QWindowsBaseWindow + \brief Raster or OpenGL Window. + + \list + \o Raster type: handleWmPaint() is implemented to + to bitblt the image. The DC can be accessed + via getDC/Relase DC, which has a special handling + when within a paint event (in that case, the DC obtained + from BeginPaint() is returned). + + \o Open GL: The first time QWindowsGLContext accesses + the handle, it sets up the pixelformat on the DC + which in turn sets it on the window (see flag + PixelFormatInitialized). + handleWmPaint() is empty (although required). + \endlist + + \ingroup qt-lighthouse-win +*/ + +QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : + QPlatformWindow(aWindow), + m_data(data), + m_flags(0), + m_hdc(0), + m_windowState(aWindow->windowState()), + m_opacity(1.0), + m_mouseGrab(false), + m_cursor(QWindowsScreen::screenOf(aWindow)->cursor().standardWindowCursor()), + m_dropTarget(0) +{ + if (aWindow->surfaceType() == QWindow::OpenGLSurface) + setFlag(OpenGL_Surface); + QWindowsContext::instance()->addWindow(m_data.hwnd, this); + if (aWindow->isTopLevel()) { + switch (aWindow->windowType()) { + case Qt::Window: + case Qt::Dialog: + case Qt::Sheet: + case Qt::Drawer: + case Qt::Popup: + case Qt::Tool: + registerDropSite(); + break; + default: + break; + } + } +} + +QWindowsWindow::~QWindowsWindow() +{ + destroyWindow(); +} + +void QWindowsWindow::destroyWindow() +{ + if (QWindowsContext::verboseIntegration || QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << this << window() << m_data.hwnd; + if (m_data.hwnd) { + unregisterDropSite(); + if (m_data.hwnd != GetDesktopWindow()) + DestroyWindow(m_data.hwnd); + QWindowsContext::instance()->removeWindow(m_data.hwnd); + m_data.hwnd = 0; + } +} + +void QWindowsWindow::registerDropSite() +{ + if (m_data.hwnd && !m_dropTarget) { + m_dropTarget = new QWindowsOleDropTarget(window()); + RegisterDragDrop(m_data.hwnd, m_dropTarget); + CoLockObjectExternal(m_dropTarget, true, true); + } +} + +void QWindowsWindow::unregisterDropSite() +{ + if (m_data.hwnd && m_dropTarget) { + m_dropTarget->Release(); + CoLockObjectExternal(m_dropTarget, false, true); + RevokeDragDrop(m_data.hwnd); + m_dropTarget = 0; + } +} + +QWindow *QWindowsWindow::topLevelOf(QWindow *w) +{ + while (QWindow *parent = w->parent()) + w = parent; + return w; +} + +QWindowsWindow::WindowData + QWindowsWindow::WindowData::create(const QWindow *w, + const WindowData ¶meters, + const QString &title, + bool isGL) +{ + WindowCreationData creationData; + creationData.fromWindow(w, parameters.flags, isGL); + WindowData result = creationData.create(w, parameters.geometry, title); + creationData.initialize(result.hwnd, false); + return result; +} + +void QWindowsWindow::setVisible(bool visible) +{ + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << this << window() << m_data.hwnd << visible; + if (m_data.hwnd) { + if (visible) { + show_sys(); + } else { + hide_sys(); + } + } +} + +bool QWindowsWindow::isVisible() const +{ + return m_data.hwnd && IsWindowVisible(m_data.hwnd); +} + +// partially from QWidgetPrivate::show_sys() +void QWindowsWindow::show_sys() const +{ + int sm = SW_SHOWNORMAL; + bool fakedMaximize = false; + const QWindow *w = window(); + const Qt::WindowFlags flags = w->windowFlags(); + const Qt::WindowType type = w->windowType(); + if (w->isTopLevel()) { + const Qt::WindowState state = w->windowState(); + if (state & Qt::WindowMinimized) { + sm = SW_SHOWMINIMIZED; + if (!isVisible()) + sm = SW_SHOWMINNOACTIVE; + } else if (state & Qt::WindowMaximized) { + sm = SW_SHOWMAXIMIZED; + // Windows will not behave correctly when we try to maximize a window which does not + // have minimize nor maximize buttons in the window frame. Windows would then ignore + // non-available geometry, and rather maximize the widget to the full screen, minus the + // window frame (caption). So, we do a trick here, by adding a maximize button before + // maximizing the widget, and then remove the maximize button afterwards. + if (flags & Qt::WindowTitleHint && + !(flags & (Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint))) { + fakedMaximize = TRUE; + setStyle(style() | WS_MAXIMIZEBOX); + } + } + } + if (type == Qt::Popup || type == Qt::ToolTip || type == Qt::Tool) + sm = SW_SHOWNOACTIVATE; + + ShowWindow(m_data.hwnd, sm); + + if (fakedMaximize) { + setStyle(style() & ~WS_MAXIMIZEBOX); + SetWindowPos(m_data.hwnd, 0, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER + | SWP_FRAMECHANGED); + } +} + +// partially from QWidgetPrivate::hide_sys() +void QWindowsWindow::hide_sys() const +{ + const Qt::WindowFlags flags = window()->windowFlags(); + if (flags != Qt::Desktop) { + if (flags & Qt::Popup) + ShowWindow(m_data.hwnd, SW_HIDE); + else + SetWindowPos(m_data.hwnd,0, 0,0,0,0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER); + } +} + +void QWindowsWindow::setParent(const QPlatformWindow *newParent) +{ + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << window() << newParent; + + if (newParent != parent() && m_data.hwnd) + setParent_sys(newParent); +} + +void QWindowsWindow::setParent_sys(const QPlatformWindow *parent) const +{ + HWND parentHWND = 0; + if (parent) { + const QWindowsWindow *parentW = static_cast(parent); + parentHWND = parentW->handle(); + } + SetParent(m_data.hwnd, parentHWND); +} + +void QWindowsWindow::handleShown() +{ + QWindowSystemInterface::handleMapEvent(window()); +} + +void QWindowsWindow::handleHidden() +{ + QWindowSystemInterface::handleUnmapEvent(window()); +} + +void QWindowsWindow::setGeometry(const QRect &rect) +{ + const QSize oldSize = m_data.geometry.size(); + m_data.geometry = rect; + const QSize newSize = rect.size(); + // Check on hint. + if (newSize != oldSize) { + const QWindowsGeometryHint hint(window()); + if (!hint.validSize(newSize)) { + qWarning("%s: Attempt to set a size (%dx%d) violating the constraints" + "(%dx%d - %dx%d) on window '%s'.", __FUNCTION__, + newSize.width(), newSize.height(), + hint.minimumSize.width(), hint.minimumSize.height(), + hint.maximumSize.width(), hint.maximumSize.height(), + qPrintable(window()->objectName())); + } + } + if (m_data.hwnd) { + // A ResizeEvent with resulting geometry will be sent. If we cannot + // achieve that size (for example, window title minimal constraint), + // notify and warn. + setGeometry_sys(rect); + if (m_data.geometry != rect) { + qWarning("%s: Unable to set geometry %dx%d+%d+%d on '%s'." + " Resulting geometry: %dx%d+%d+%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()); + } + } else { + QPlatformWindow::setGeometry(rect); + } +} + +void QWindowsWindow::handleMoved() +{ + if (!IsIconic(m_data.hwnd)) // Minimize can send nonsensical move events. + handleGeometryChange(); +} + +void QWindowsWindow::handleResized(int wParam) +{ + switch (wParam) { + case SIZE_MAXHIDE: // Some other window affected. + case SIZE_MAXSHOW: + return; + case SIZE_MINIMIZED: + handleWindowStateChange(Qt::WindowMinimized); + return; + case SIZE_MAXIMIZED: + handleWindowStateChange(Qt::WindowMaximized); + handleGeometryChange(); + break; + case SIZE_RESTORED: + if (m_windowState != Qt::WindowNoState) + handleWindowStateChange(Qt::WindowNoState); + handleGeometryChange(); + break; + } +} + +void QWindowsWindow::handleGeometryChange() +{ + m_data.geometry = geometry_sys(); + QPlatformWindow::setGeometry(m_data.geometry); + QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry); + + if (QWindowsContext::verboseEvents || QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << this << window() << m_data.geometry; +} + +void QWindowsWindow::setGeometry_sys(const QRect &rect) const +{ + const QRect frameGeometry = rect + frameMargins(); + + if (QWindowsContext::verboseWindows) + qDebug() << '>' << __FUNCTION__ << this << window() + << " \n from " << geometry_sys() << " to " <' << __FUNCTION__ << this << window() << "\n from: " + << QWindowsWindow::debugWindowFlags(m_data.flags) + << "\n to: " << QWindowsWindow::debugWindowFlags(flags); + if (m_data.flags != flags) { + m_data.flags = flags; + if (m_data.hwnd) + m_data = setWindowFlags_sys(flags); + } + if (QWindowsContext::verboseWindows) + qDebug() << '<' << __FUNCTION__ << "\n returns: " + << QWindowsWindow::debugWindowFlags(m_data.flags); + return m_data.flags; +} + +QWindowsWindow::WindowData QWindowsWindow::setWindowFlags_sys(Qt::WindowFlags wt) const +{ + // Geometry changes have not been observed here. Frames change, though. + WindowCreationData creationData; + creationData.fromWindow(window(), wt, window()->surfaceType() == QWindow::OpenGLSurface); + creationData.applyWindowFlags(m_data.hwnd); + creationData.initialize(m_data.hwnd, true); + WindowData result = m_data; + result.flags = creationData.flags; + setFlag(FrameDirty); + return result; +} + +void QWindowsWindow::handleWindowStateChange(Qt::WindowState state) +{ + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << this << window() + << "\n from " << debugWindowStates(m_windowState) + << " to " << debugWindowStates(state); + setFlag(FrameDirty); + m_windowState = state; + QWindowSystemInterface::handleWindowStateChanged(window(), state); +} + +Qt::WindowState QWindowsWindow::setWindowState(Qt::WindowState state) +{ + if (m_data.hwnd) { + setWindowState_sys(state); + m_windowState = state; + } + return state; +} + +Qt::WindowState QWindowsWindow::windowState_sys() const +{ + if (IsIconic(m_data.hwnd)) + return Qt::WindowMinimized; + if (IsZoomed(m_data.hwnd)) + return Qt::WindowMaximized; + if (geometry_sys() == window()->screen()->geometry()) + return Qt::WindowFullScreen; + return Qt::WindowNoState; +} + +Qt::WindowStates QWindowsWindow::windowStates_sys() const +{ + Qt::WindowStates result = windowState_sys(); + if (GetActiveWindow() == m_data.hwnd) + result |= Qt::WindowActive; + return result; +} + +/*! + \brief Change the window state. + + \note Window frames change when maximized; + the top margin shrinks somewhat but that cannot be obtained using + AdjustWindowRectEx(). + + \note Some calls to SetWindowLong require a subsequent call + to ShowWindow. +*/ + +void QWindowsWindow::setWindowState_sys(Qt::WindowState newState) +{ + const Qt::WindowStates oldStates = windowStates_sys(); + // Maintain the active flag as the platform window API does not + // use it. + Qt::WindowStates newStates = newState; + if (oldStates & Qt::WindowActive) + newStates |= Qt::WindowActive; + if (oldStates == newStates) + return; + if (QWindowsContext::verboseWindows) + qDebug() << '>' << __FUNCTION__ << this << window() + << " from " << debugWindowStates(oldStates) + << " to " << debugWindowStates(newStates); + + const bool isActive = newStates & Qt::WindowActive; + const int max = isActive ? SW_SHOWMAXIMIZED : SW_MAXIMIZE; + const int normal = isActive ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE; + const int min = isActive ? SW_SHOWMINIMIZED : SW_MINIMIZE; + const bool visible = isVisible(); + + setFlag(FrameDirty); + + if ((oldStates & Qt::WindowMaximized) != (newStates & Qt::WindowMaximized)) { + if (visible && !(newStates & Qt::WindowMinimized)) + ShowWindow(m_data.hwnd, (newStates & Qt::WindowMaximized) ? max : normal); + } + + if ((oldStates & Qt::WindowFullScreen) != (newStates & Qt::WindowFullScreen)) { + if (newStates & Qt::WindowFullScreen) { +#ifndef Q_FLATTEN_EXPOSE + UINT newStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP; +#else + UINT newStyle = WS_POPUP; +#endif + if (style() & WS_SYSMENU) + newStyle |= WS_SYSMENU; + if (visible) + newStyle |= WS_VISIBLE; + setStyle(newStyle); + + const QRect r = window()->screen()->geometry(); + UINT swpf = SWP_FRAMECHANGED; + if (newStates & Qt::WindowActive) + swpf |= SWP_NOACTIVATE; + + SetWindowPos(m_data.hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf); + } else { + if (visible) + setStyle(style() | WS_VISIBLE); + UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE; + if (newStates & Qt::WindowActive) + swpf |= SWP_NOACTIVATE; + SetWindowPos(m_data.hwnd, 0, 0, 0, 0, 0, swpf); + + // preserve maximized state + if (visible) + ShowWindow(m_data.hwnd, (newStates & Qt::WindowMaximized) ? max : normal); + } + } + + if ((oldStates & Qt::WindowMinimized) != (newStates & Qt::WindowMinimized)) { + if (visible) + ShowWindow(m_data.hwnd, (newStates & Qt::WindowMinimized) ? min : + (newStates & Qt::WindowMaximized) ? max : normal); + } + if (QWindowsContext::verboseWindows) + qDebug() << '<' << __FUNCTION__ << this << window() + << debugWindowStates(newStates); +} + +void QWindowsWindow::setStyle(unsigned s) const +{ + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << this << window() << debugWinStyle(s); + setFlag(FrameDirty); + SetWindowLongPtr(m_data.hwnd, GWL_STYLE, s); +} + +void QWindowsWindow::setExStyle(unsigned s) const +{ + if (QWindowsContext::verboseWindows) + qDebug().nospace() << __FUNCTION__ << ' ' << this << ' ' << window() + << " 0x" << QByteArray::number(s, 16); + setFlag(FrameDirty); + SetWindowLongPtr(m_data.hwnd, GWL_EXSTYLE, s); +} + +void QWindowsWindow::raise() +{ + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << this << window(); + SetWindowPos(m_data.hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); +} + +void QWindowsWindow::lower() +{ + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << this << window(); + if (m_data.hwnd) + SetWindowPos(m_data.hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); +} + +void QWindowsWindow::propagateSizeHints() +{ + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << this << window(); +} + +QMargins QWindowsWindow::frameMargins() const +{ + // Frames are invalidated by style changes (window state, flags). + // As they are also required for geometry calculations in resize + // event sequences, introduce a dirty flag mechanism to be able + // to cache results. + if (testFlag(FrameDirty)) { + m_data.frame = QWindowsGeometryHint::frame(style(), exStyle()); + clearFlag(FrameDirty); + } + return m_data.frame; +} + +void QWindowsWindow::setOpacity(qreal level) +{ + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << level; + if (m_opacity != level) { + m_opacity = level; + if (m_data.hwnd) + setOpacity_sys(level); + } +} + +void QWindowsWindow::setOpacity_sys(qreal level) const +{ + const long wl = GetWindowLong(m_data.hwnd, GWL_EXSTYLE); + const bool isOpaque = level == 1.0; + + if (isOpaque) { + if (wl & WS_EX_LAYERED) + SetWindowLong(m_data.hwnd, GWL_EXSTYLE, wl & ~WS_EX_LAYERED); + } else { + if ((wl & WS_EX_LAYERED) == 0) + SetWindowLong(m_data.hwnd, GWL_EXSTYLE, wl | WS_EX_LAYERED); + if (m_data.flags & Qt::FramelessWindowHint) { + BLENDFUNCTION blend = {AC_SRC_OVER, 0, (int)(255.0 * level), AC_SRC_ALPHA}; + QWindowsContext::user32dll.updateLayeredWindow(m_data.hwnd, NULL, NULL, NULL, NULL, NULL, 0, &blend, ULW_ALPHA); + } else { + QWindowsContext::user32dll.setLayeredWindowAttributes(m_data.hwnd, 0, (int)(level * 255), LWA_ALPHA); + } + } +} + +void QWindowsWindow::requestActivateWindow() +{ + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << this << window(); + if (m_data.hwnd) + SetForegroundWindow(m_data.hwnd); +} + +bool QWindowsWindow::setKeyboardGrabEnabled(bool grab) +{ + if (!m_data.hwnd) { + qWarning("%s: No handle", __FUNCTION__); + return false; + } + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << this << window() << grab; + + QWindowsContext *context = QWindowsContext::instance(); + if (grab) { + context->setKeyGrabber(window()); + } else { + if (context->keyGrabber() == window()) + context->setKeyGrabber(0); + } + return true; +} + +bool QWindowsWindow::setMouseGrabEnabled(bool grab) +{ + bool result = false; + if (!m_data.hwnd) { + qWarning("%s: No handle", __FUNCTION__); + return result; + } + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << window() << grab; + + if (m_mouseGrab != grab) { + m_mouseGrab = grab; + if (isVisible()) + setMouseGrabEnabled_sys(grab); + } + return grab; +} + +void QWindowsWindow::setMouseGrabEnabled_sys(bool grab) +{ + if (grab) { + SetCapture(m_data.hwnd); + } else { + ReleaseCapture(); + } +} + +void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const +{ + const QWindowsGeometryHint hint(window()); + hint.applyToMinMaxInfo(m_data.hwnd, mmi); + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << window() << *mmi; +} + +/*! + \brief Applies to cursor property set on the window to the global cursor + unless there is an override cursor. + + \sa QWindowsCursor +*/ + +void QWindowsWindow::applyCursor() +{ + if (!QGuiApplication::overrideCursor()) + SetCursor(m_cursor.handle()); +} + +void QWindowsWindow::setCursor(const QWindowsWindowCursor &c) +{ + if (c.handle() != m_cursor.handle()) { + const bool underMouse = QWindowsContext::instance()->windowUnderMouse() == window(); + if (QWindowsContext::verboseWindows) + qDebug() << window() << __FUNCTION__ << "Shape=" << c.cursor().shape() + << " isWUM=" << underMouse; + m_cursor = c; + if (underMouse) + applyCursor(); + } +} + +/*! + \brief Find a child window using flags from ChildWindowFromPointEx. +*/ + +QWindowsWindow *QWindowsWindow::childAtScreenPoint(const QPoint &screenPoint, + unsigned cwexflags) const +{ + if (m_data.hwnd) + return QWindowsContext::instance()->findPlatformWindowAt(m_data.hwnd, screenPoint, cwexflags); + return 0; +} + +QWindowsWindow *QWindowsWindow::childAt(const QPoint &clientPoint, unsigned cwexflags) const +{ + if (m_data.hwnd) + return childAtScreenPoint(QWindowsGeometryHint::mapToGlobal(m_data.hwnd, clientPoint), + cwexflags); + return 0; +} + +QByteArray QWindowsWindow::debugWindowFlags(Qt::WindowFlags wf) +{ + const int iwf = int(wf); + QByteArray rc = "0x"; + rc += QByteArray::number(iwf, 16); + rc += " ["; + + switch ((iwf & Qt::WindowType_Mask)) { + case Qt::Widget: + rc += " Widget"; + break; + case Qt::Window: + rc += " Window"; + break; + case Qt::Dialog: + rc += " Dialog"; + break; + case Qt::Sheet: + rc += " Sheet"; + break; + case Qt::Popup: + rc += " Popup"; + break; + case Qt::Tool: + rc += " Tool"; + break; + case Qt::ToolTip: + rc += " ToolTip"; + break; + case Qt::SplashScreen: + rc += " SplashScreen"; + break; + case Qt::Desktop: + rc += " Desktop"; + break; + case Qt::SubWindow: + rc += " SubWindow"; + break; + } + if (iwf & Qt::MSWindowsFixedSizeDialogHint) rc += " MSWindowsFixedSizeDialogHint"; + if (iwf & Qt::MSWindowsOwnDC) rc += " MSWindowsOwnDC"; + if (iwf & Qt::FramelessWindowHint) rc += " FramelessWindowHint"; + if (iwf & Qt::WindowTitleHint) rc += " WindowTitleHint"; + if (iwf & Qt::WindowSystemMenuHint) rc += " WindowSystemMenuHint"; + if (iwf & Qt::WindowMinimizeButtonHint) rc += " WindowMinimizeButtonHint"; + if (iwf & Qt::WindowMaximizeButtonHint) rc += " WindowMaximizeButtonHint"; + if (iwf & Qt::WindowContextHelpButtonHint) rc += " WindowContextHelpButtonHint"; + if (iwf & Qt::WindowShadeButtonHint) rc += " WindowShadeButtonHint"; + if (iwf & Qt::WindowStaysOnTopHint) rc += " WindowStaysOnTopHint"; + if (iwf & Qt::CustomizeWindowHint) rc += " CustomizeWindowHint"; + if (iwf & Qt::WindowStaysOnBottomHint) rc += " WindowStaysOnBottomHint"; + if (iwf & Qt::WindowCloseButtonHint) rc += " WindowCloseButtonHint"; + rc += ']'; + return rc; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h new file mode 100644 index 0000000000..dfaeb2a86d --- /dev/null +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSWINDOW_H +#define QWINDOWSWINDOW_H + +#include "qtwindows_additional.h" +#include "qwindowscursor.h" + +#include + +QT_BEGIN_NAMESPACE + +class QWindowsOleDropTarget; +class QDebug; + +struct QWindowsGeometryHint +{ + QWindowsGeometryHint() {} + explicit QWindowsGeometryHint(const QWindow *w); + static QMargins frame(DWORD style, DWORD exStyle); + void applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const; + void applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const; + bool validSize(const QSize &s) const; + + static inline QPoint mapToGlobal(HWND hwnd, const QPoint &); + static inline QPoint mapToGlobal(const QWindow *w, const QPoint &); + static inline QPoint mapFromGlobal(const HWND hwnd, const QPoint &); + static inline QPoint mapFromGlobal(const QWindow *w, const QPoint &); + + QSize minimumSize; + QSize maximumSize; +}; + +struct QWindowCreationContext +{ + QWindowCreationContext(const QWindow *w, const QRect &r, + DWORD style, DWORD exStyle); + + void applyToMinMaxInfo(MINMAXINFO *mmi) const + { geometryHint.applyToMinMaxInfo(style, exStyle, mmi); } + + QWindowsGeometryHint geometryHint; + DWORD style; + DWORD exStyle; + QRect requestedGeometry; + QRect obtainedGeometry; + QMargins margins; + int frameX; // Passed on to CreateWindowEx(), including frame. + int frameY; + int frameWidth; + int frameHeight; +}; + +class QWindowsWindow : public QPlatformWindow +{ +public: + enum Flags + { + OpenGL_Surface = 0x1, + WithinWmPaint = 0x2, + PixelFormatInitialized = 0x4, + FrameDirty = 0x8 //! Frame outdated by setStyle, recalculate in next query. + }; + + struct WindowData + { + WindowData() : hwnd(0) {} + + Qt::WindowFlags flags; + QRect geometry; + QMargins frame; // Do not use directly for windows, see FrameDirty. + HWND hwnd; + + static WindowData create(const QWindow *w, + const WindowData ¶meters, + const QString &title, + bool isGL); + }; + + QWindowsWindow(QWindow *window, const WindowData &data); + ~QWindowsWindow(); + + virtual void setGeometry(const QRect &rect); + virtual QRect geometry() const { return m_data.geometry; } + + virtual void setVisible(bool visible); + bool isVisible() const; + virtual Qt::WindowFlags setWindowFlags(Qt::WindowFlags flags); + virtual Qt::WindowState setWindowState(Qt::WindowState state); + + HWND handle() const { return m_data.hwnd; } + + virtual WId winId() const { return WId(m_data.hwnd); } + virtual void setParent(const QPlatformWindow *window); + + virtual void setWindowTitle(const QString &title); + virtual void raise(); + virtual void lower(); + + virtual void propagateSizeHints(); + virtual QMargins frameMargins() const; + + virtual void setOpacity(qreal level); + virtual void requestActivateWindow(); + + virtual bool setKeyboardGrabEnabled(bool grab); + virtual bool setMouseGrabEnabled(bool grab); + + Qt::WindowState windowState_sys() const; + Qt::WindowStates windowStates_sys() const; + + inline unsigned style() const + { return GetWindowLongPtr(m_data.hwnd, GWL_STYLE); } + void setStyle(unsigned s) const; + inline unsigned exStyle() const + { return GetWindowLongPtr(m_data.hwnd, GWL_EXSTYLE); } + void setExStyle(unsigned s) const; + + void handleWmPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + + void handleMoved(); + void handleResized(int wParam); + void handleShown(); + void handleHidden(); + + static inline HWND handleOf(const QWindow *w); + static inline QWindowsWindow *baseWindowOf(const QWindow *w); + static QWindow *topLevelOf(QWindow *w); + static inline void *userDataOf(HWND hwnd); + static inline void setUserDataOf(HWND hwnd, void *ud); + + HDC getDC(); + void releaseDC(); + + void getSizeHints(MINMAXINFO *mmi) const; + + QWindowsWindowCursor cursor() const { return m_cursor; } + void setCursor(const QWindowsWindowCursor &c); + void applyCursor(); + + QWindowsWindow *childAt(const QPoint &clientPoint, + unsigned cwexflags = CWP_SKIPINVISIBLE) const; + QWindowsWindow *childAtScreenPoint(const QPoint &screenPoint, + unsigned cwexflags = CWP_SKIPINVISIBLE) const; + + static QByteArray debugWindowFlags(Qt::WindowFlags wf); + + inline bool testFlag(unsigned f) const { return (m_flags & f) != 0; } + inline void setFlag(unsigned f) const { m_flags |= f; } + inline void clearFlag(unsigned f) const { m_flags &= ~f; } + +private: + inline void show_sys() const; + inline void hide_sys() const; + inline void setGeometry_sys(const QRect &rect) const; + inline QRect geometry_sys() const; + inline WindowData setWindowFlags_sys(Qt::WindowFlags wt) const; + inline void setWindowState_sys(Qt::WindowState newState); + inline void setParent_sys(const QPlatformWindow *parent) const; + inline void setOpacity_sys(qreal level) const; + inline void setMouseGrabEnabled_sys(bool grab); + void destroyWindow(); + void registerDropSite(); + void unregisterDropSite(); + void handleGeometryChange(); + void handleWindowStateChange(Qt::WindowState state); + + mutable WindowData m_data; + mutable unsigned m_flags; + HDC m_hdc; + Qt::WindowState m_windowState; + qreal m_opacity; + bool m_mouseGrab; + QWindowsWindowCursor m_cursor; + QWindowsOleDropTarget *m_dropTarget; +}; + +// 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); +QDebug operator<<(QDebug d, const MINMAXINFO &i); +QDebug operator<<(QDebug d, const NCCALCSIZE_PARAMS &p); + +// ---------- QWindowsGeometryHint inline functions. +QPoint QWindowsGeometryHint::mapToGlobal(HWND hwnd, const QPoint &qp) +{ + POINT p = { qp.x(), qp.y() }; + ClientToScreen(hwnd, &p); + return QPoint(p.x, p.y); +} + +QPoint QWindowsGeometryHint::mapFromGlobal(const HWND hwnd, const QPoint &qp) +{ + POINT p = { qp.x(), qp.y() }; + ScreenToClient(hwnd, &p); + return QPoint(p.x, p.y); +} + +QPoint QWindowsGeometryHint::mapToGlobal(const QWindow *w, const QPoint &p) + { return QWindowsGeometryHint::mapToGlobal(QWindowsWindow::handleOf(w), p); } + +QPoint QWindowsGeometryHint::mapFromGlobal(const QWindow *w, const QPoint &p) + { return QWindowsGeometryHint::mapFromGlobal(QWindowsWindow::handleOf(w), p); } + + +// ---------- QWindowsBaseWindow inline functions. + +QWindowsWindow *QWindowsWindow::baseWindowOf(const QWindow *w) +{ + if (w) + if (QPlatformWindow *pw = w->handle()) + return static_cast(pw); + return 0; +} + +HWND QWindowsWindow::handleOf(const QWindow *w) +{ + if (const QWindowsWindow *bw = QWindowsWindow::baseWindowOf(w)) + return bw->handle(); + return 0; +} + +void *QWindowsWindow::userDataOf(HWND hwnd) +{ + return (void *)GetWindowLongPtr(hwnd, GWLP_USERDATA); +} + +void QWindowsWindow::setUserDataOf(HWND hwnd, void *ud) +{ + SetWindowLongPtr(hwnd, GWLP_USERDATA, LONG_PTR(ud)); +} + +QT_END_NAMESPACE + +#endif // QWINDOWSWINDOW_H diff --git a/src/plugins/platforms/windows/windows.pro b/src/plugins/platforms/windows/windows.pro new file mode 100644 index 0000000000..c9be003984 --- /dev/null +++ b/src/plugins/platforms/windows/windows.pro @@ -0,0 +1,70 @@ +TARGET = windows +load(qt_plugin) + +QT *= core-private +QT *= gui-private + +INCLUDEPATH += ../../../3rdparty/harfbuzz/src +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/platforms + +# Note: OpenGL32 must precede Gdi32 as it overwrites some functions. +LIBS *= -lOpenGL32 -lGdi32 -lUser32 -lOle32 -lWinspool +win32-g++: LIBS *= -luuid + +contains(QT_CONFIG, directwrite) { + LIBS *= -ldwrite + SOURCES += qwindowsfontenginedirectwrite.cpp + HEADERS += qwindowsfontenginedirectwrite.h +} else { + DEFINES *= QT_NO_DIRECTWRITE +} + +SOURCES += \ + main.cpp \ + qwindowsnativeimage.cpp \ + qwindowswindow.cpp \ + qwindowsintegration.cpp \ + qwindowscontext.cpp \ + qwindowsbackingstore.cpp \ + qwindowsscreen.cpp \ + qwindowsprintersupport.cpp \ + qwindowskeymapper.cpp \ + qwindowsfontengine.cpp \ + qwindowsfontdatabase.cpp \ + qwindowsmousehandler.cpp \ + qwindowsguieventdispatcher.cpp \ + qwindowsglcontext.cpp \ + qwindowsclipboard.cpp \ + qwindowsole.cpp \ + qwindowsmime.cpp \ + qwindowsdrag.cpp \ + qwindowscursor.cpp \ + pixmaputils.cpp + +HEADERS += \ + qwindowsnativeimage.h \ + qwindowswindow.h \ + qwindowsintegration.h \ + qwindowscontext.h \ + qwindowsbackingstore.h \ + qwindowsscreen.h \ + qwindowsprintersupport.h \ + qwindowskeymapper.h \ + qwindowsfontengine.h \ + qwindowsfontdatabase.h \ + qwindowsmousehandler.h \ + qwindowsguieventdispatcher.h \ + qtwindowsglobal.h \ + qtwindows_additional.h \ + qwindowsglcontext.h \ + qwindowsclipboard.h \ + qwindowsole.h \ + qwindowsmime.h \ + qwindowsdrag.h \ + qwindowsinternalmimedata.h \ + qwindowscursor.h \ + pixmaputils.h \ + array.h + +target.path += $$[QT_INSTALL_PLUGINS]/platforms +INSTALLS += target -- cgit v1.2.3