diff options
Diffstat (limited to 'src/gui/platform')
75 files changed, 7393 insertions, 3209 deletions
diff --git a/src/gui/platform/android/qandroidnativeinterface.cpp b/src/gui/platform/android/qandroidnativeinterface.cpp new file mode 100644 index 0000000000..c1c4b7149f --- /dev/null +++ b/src/gui/platform/android/qandroidnativeinterface.cpp @@ -0,0 +1,54 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <qpa/qplatformoffscreensurface.h> +#include <qpa/qplatformintegration.h> + +#include <QtGui/qoffscreensurface_platform.h> +#include <QtGui/private/qguiapplication_p.h> + +#include <QtGui/qpa/qplatformscreen_p.h> + +QT_BEGIN_NAMESPACE + +using namespace QNativeInterface::Private; + +/*! + \class QNativeInterface::QAndroidOffscreenSurface + \since 6.0 + \brief Native interface to a offscreen surface on Android. + + Accessed through QOffscreenSurface::nativeInterface(). + + \inmodule QtGui + \inheaderfile QOffscreenSurface + \ingroup native-interfaces + \ingroup native-interfaces-qoffscreensurface +*/ + +QT_DEFINE_NATIVE_INTERFACE(QAndroidOffscreenSurface); +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QAndroidOffScreenIntegration); + +QOffscreenSurface *QNativeInterface::QAndroidOffscreenSurface::fromNative(ANativeWindow *nativeSurface) +{ + return QGuiApplicationPrivate::platformIntegration()->call< + &QAndroidOffScreenIntegration::createOffscreenSurface>(nativeSurface); +} + +/*! + \class QNativeInterface::QAndroidScreen + \since 6.7 + \brief Native interface to a screen. + + Accessed through QScreen::nativeInterface(). + \inmodule QtGui + \ingroup native-interfaces + \ingroup native-interfaces-qscreen +*/ +/*! + \fn int QNativeInterface::QAndroidScreen::displayId() const; + \return the id of the underlying Android display. +*/ +QT_DEFINE_NATIVE_INTERFACE(QAndroidScreen); + +QT_END_NAMESPACE diff --git a/src/gui/platform/darwin/darwin.pri b/src/gui/platform/darwin/darwin.pri deleted file mode 100644 index c8d26c997b..0000000000 --- a/src/gui/platform/darwin/darwin.pri +++ /dev/null @@ -1,4 +0,0 @@ -HEADERS += $$PWD/qmacmime_p.h -SOURCES += $$PWD/qmacmime.mm -LIBS += -framework ImageIO -macos: LIBS_PRIVATE += -framework AppKit diff --git a/src/gui/platform/darwin/qappleiconengine.mm b/src/gui/platform/darwin/qappleiconengine.mm new file mode 100644 index 0000000000..7e0ed184dc --- /dev/null +++ b/src/gui/platform/darwin/qappleiconengine.mm @@ -0,0 +1,464 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qappleiconengine_p.h" + +#if defined(Q_OS_MACOS) +# include <AppKit/AppKit.h> +#elif defined(QT_PLATFORM_UIKIT) +# include <UIKit/UIKit.h> +#endif + +#include <QtGui/qguiapplication.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpalette.h> +#include <QtGui/qstylehints.h> + +#include <QtGui/private/qcoregraphics_p.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +namespace { +auto *loadImage(const QString &iconName) +{ + static constexpr std::pair<QLatin1StringView, NSString *> iconMap[] = { + {"address-book-new"_L1, @"book.closed"}, + {"application-exit"_L1, @"xmark.circle"}, + {"appointment-new"_L1, @"calendar.badge.plus"}, + {"call-start"_L1, @"phone.arrow.up.right"}, + {"call-stop"_L1, @"phone.down"}, + {"contact-new"_L1, @"person.crop.circle.badge.plus"}, + {"document-new"_L1, @"doc.badge.plus"}, + {"document-open"_L1, @"folder"}, + {"document-open-recent"_L1, @"doc.badge.clock"}, + {"document-page-setup"_L1, @"doc.badge.gearshape"}, + {"document-print"_L1, @"printer"}, + //{"document-print-preview"_L1, @""}, + {"document-properties"_L1, @"doc.badge.ellipsis"}, + //{"document-revert"_L1, @""}, + {"document-save"_L1, @"square.and.arrow.down"}, + //{"document-save-as"_L1, @""}, + {"document-send"_L1, @"paperplane"}, + {"edit-clear"_L1, @"xmark.circle"}, + {"edit-copy"_L1, @"doc.on.doc"}, + {"edit-cut"_L1, @"scissors"}, + {"edit-delete"_L1, @"delete.left"}, + {"edit-find"_L1, @"magnifyingglass"}, + //{"edit-find-replace"_L1, @"arrow.up.left.and.down.right.magnifyingglass"}, + {"edit-paste"_L1, @"clipboard"}, + {"edit-redo"_L1, @"arrowshape.turn.up.right"}, + //{"edit-select-all"_L1, @""}, + {"edit-undo"_L1, @"arrowshape.turn.up.left"}, + {"folder-new"_L1, @"folder.badge.plus"}, + {"format-indent-less"_L1, @"decrease.indent"}, + {"format-indent-more"_L1, @"increase.indent"}, + {"format-justify-center"_L1, @"text.aligncenter"}, + {"format-justify-fill"_L1, @"text.justify"}, + {"format-justify-left"_L1, @"text.justify.left"}, + {"format-justify-right"_L1, @"text.justify.right"}, + {"format-text-direction-ltr"_L1, @"text.justify.leading"}, + {"format-text-direction-rtl"_L1, @"text.justify.trailing"}, + {"format-text-bold"_L1, @"bold"}, + {"format-text-italic"_L1, @"italic"}, + {"format-text-underline"_L1, @"underline"}, + {"format-text-strikethrough"_L1, @"strikethrough"}, + //{"go-bottom"_L1, @""}, + {"go-down"_L1, @"arrowshape.down"}, + {"go-first"_L1, @"increase.indent"}, + {"go-home"_L1, @"house"}, + //{"go-jump"_L1, @""}, + //{"go-last"_L1, @""}, + {"go-next"_L1, @"arrowshape.right"}, + {"go-previous"_L1, @"arrowshape.left"}, + //{"go-top"_L1, @""}, + {"go-up"_L1, @"arrowshape.up"}, + {"help-about"_L1, @"info.circle"}, + //{"help-contents"_L1, @""}, + {"help-faq"_L1, @"questionmark.app"}, + {"insert-image"_L1, @"photo.badge.plus"}, + {"insert-link"_L1, @"link.badge.plus"}, + //{"insert-object"_L1, @""}, + {"insert-text"_L1, @"textformat"}, + {"list-add"_L1, @"plus.circle"}, + {"list-remove"_L1, @"minus.circle"}, + {"mail-forward"_L1, @"arrowshape.turn.up.right"}, + {"mail-mark-important"_L1, @"star"}, + {"mail-mark-junk"_L1, @"xmark.bin"}, + {"mail-mark-notjunk"_L1, @"trash.slash"}, + {"mail-mark-read"_L1, @"envelope.open"}, + {"mail-mark-unread"_L1, @"envelope.fill"}, + {"mail-message-new"_L1, @"square.and.pencil"}, + {"mail-reply-all"_L1, @"arrowshape.turn.up.left.2"}, + {"mail-reply-sender"_L1, @"arrowshape.turn.up.left"}, + {"mail-send"_L1, @"paperplane"}, + {"mail-send-receive"_L1, @"envelope.arrow.triangle.branch"}, + {"media-eject"_L1, @"eject"}, + {"media-playback-pause"_L1, @"pause"}, + {"media-playback-start"_L1, @"play"}, + {"media-playback-stop"_L1, @"stop"}, + {"media-record"_L1, @"record.circle"}, + {"media-seek-backward"_L1, @"backward"}, + {"media-seek-forward"_L1, @"forward"}, + {"media-skip-backward"_L1, @"backward.end.alt"}, + {"media-skip-forward"_L1, @"forward.end.alt"}, + {"object-flip-horizontal"_L1, @"rectangle.landscape.rotate"}, + {"object-flip-vertical"_L1, @"rectangle.portrait.rotate"}, + {"object-rotate-left"_L1, @"rotate.left"}, + {"object-rotate-right"_L1, @"rotate.right"}, + {"process-stop"_L1, @"stop.circle"}, + {"system-lock-screen"_L1, @"lock.display"}, + {"system-log-out"_L1, @"door.left.hand.open"}, + //{"system-run"_L1, @""}, + {"system-search"_L1, @"magnifyingglass"}, + //{"system-reboot"_L1, @""}, + {"system-shutdown"_L1, @"power"}, + //{"tools-check-spelling"_L1, @""}, + {"view-fullscreen"_L1, @"arrow.up.left.and.arrow.down.right"}, + {"view-refresh"_L1, @"arrow.clockwise"}, + {"view-restore"_L1, @"arrow.down.right.and.arrow.up.left"}, + //{"view-sort-ascending"_L1, @""}, + //{"view-sort-descending"_L1, @""}, + {"window-close"_L1, @"xmark.circle"}, + {"window-new"_L1, @"macwindow.badge.plus"}, + {"zoom-fit-best"_L1, @"square.arrowtriangle.4.outward"}, + {"zoom-in"_L1, @"plus.magnifyingglass"}, + //{"zoom-original"_L1, @""}, + {"zoom-out"_L1, @"minus.magnifyingglass"}, + {"process-working"_L1, @"circle.dotted"}, + //{"accessories-calculator"_L1, @""}, + //{"accessories-character-map"_L1, @""}, + {"accessories-dictionary"_L1, @"character.book.closed"}, + {"accessories-text-editor"_L1, @"textformat"}, + {"help-browser"_L1, @"folder.badge.questionmark"}, + {"multimedia-volume-control"_L1, @"speaker.wave.3"}, + {"preferences-desktop-accessibility"_L1, @"accessibility"}, + //{"preferences-desktop-font"_L1, @""}, + {"preferences-desktop-keyboard"_L1, @"keyboard.badge.ellipsis"}, + //{"preferences-desktop-locale"_L1, @""}, + //{"preferences-desktop-multimedia"_L1, @""}, + //{"preferences-desktop-screensaver"_L1, @""}, + //{"preferences-desktop-theme"_L1, @""}, + //{"preferences-desktop-wallpaper"_L1, @""}, + {"system-file-manager"_L1, @"folder.badge.gearshape"}, + //{"system-software-install"_L1, @""}, + //{"system-software-update"_L1, @""}, d + //{"utilities-system-monitor"_L1, @""}, + {"utilities-terminal"_L1, @"apple.terminal"}, + //{"applications-accessories"_L1, @""}, + //{"applications-development"_L1, @""}, + //{"applications-engineering"_L1, @""}, + {"applications-games"_L1, @"gamecontroller"}, + //{"applications-graphics"_L1, @""}, + {"applications-internet"_L1, @"network"}, + {"applications-multimedia"_L1, @"tv.and.mediabox"}, + //{"applications-office"_L1, @""}, + //{"applications-other"_L1, @""}, + {"applications-science"_L1, @"atom"}, + //{"applications-system"_L1, @""}, + //{"applications-utilities"_L1, @""}, + {"preferences-desktop"_L1, @"menubar.dock.rectangle"}, + //{"preferences-desktop-peripherals"_L1, @""}, + //{"preferences-desktop-personal"_L1, @""}, + //{"preferences-other"_L1, @""}, + //{"preferences-system"_L1, @""}, + {"preferences-system-network"_L1, @"network"}, + {"system-help"_L1, @"questionmark.diamond"}, + {"audio-card"_L1, @"waveform.circle"}, + {"audio-input-microphone"_L1, @"mic"}, + {"battery"_L1, @"battery.100percent"}, + {"camera-photo"_L1, @"camera"}, + {"camera-video"_L1, @"video"}, + {"camera-web"_L1, @"web.camera"}, + {"computer"_L1, @"desktopcomputer"}, + {"drive-harddisk"_L1, @"internaldrive"}, + {"drive-optical"_L1, @"opticaldiscdrive"}, + {"drive-removable-media"_L1, @"externaldrive"}, + {"input-gaming"_L1, @"gamecontroller"}, // "games" also using this one + {"input-keyboard"_L1, @"keyboard"}, + {"input-mouse"_L1, @"computermouse"}, + {"input-tablet"_L1, @"ipad"}, + {"media-flash"_L1, @"mediastick"}, + //{"media-floppy"_L1, @""}, + //{"media-optical"_L1, @""}, + {"media-tape"_L1, @"recordingtape"}, + //{"modem"_L1, @""}, + {"multimedia-player"_L1, @"play.rectangle"}, + {"network-wired"_L1, @"app.connected.to.app.below.fill"}, + {"network-wireless"_L1, @"wifi"}, + //{"pda"_L1, @""}, + {"phone"_L1, @"iphone"}, + {"printer"_L1, @"printer"}, + {"scanner"_L1, @"scanner"}, + {"video-display"_L1, @"play.display"}, + //{"emblem-default"_L1, @""}, + {"emblem-documents"_L1, @"doc.circle"}, + {"emblem-downloads"_L1, @"arrow.down.circle"}, + {"emblem-favorite"_L1, @"star"}, + {"emblem-important"_L1, @"exclamationmark.bubble.circle"}, + {"emblem-mail"_L1, @"envelope"}, + {"emblem-photos"_L1, @"photo.stack"}, + //{"emblem-readonly"_L1, @""}, + {"emblem-shared"_L1, @"folder.badge.person.crop"}, + {"emblem-symbolic-link"_L1, @"link.circle"}, + {"emblem-synchronized"_L1, @"arrow.triangle.2.circlepath.circle"}, + {"emblem-system"_L1, @"gear"}, + //{"emblem-unreadable"_L1, @""}, + {"folder"_L1, @"folder"}, + //{"folder-remote"_L1, @""}, + {"network-server"_L1, @"server.rack"}, + //{"network-workgroup"_L1, @""}, + //{"start-here"_L1, @""}, + {"user-bookmarks"_L1, @"bookmark.circle"}, + {"user-desktop"_L1, @"desktopcomputer"}, //"computer" also using this one + {"user-home"_L1, @"house"}, //"go-home" also using this one + {"user-trash"_L1, @"trash"}, + {"appointment-missed"_L1, @"calendar.badge.exclamationmark"}, + {"appointment-soon"_L1, @"calendar.badge.clock"}, + {"audio-volume-high"_L1, @"speaker.wave.3"}, + {"audio-volume-low"_L1, @"speaker.wave.1"}, + {"audio-volume-medium"_L1, @"speaker.wave.2"}, + {"audio-volume-muted"_L1, @"speaker.slash"}, + {"battery-caution"_L1, @"minus.plus.batteryblock.exclamationmark"}, + {"battery-low"_L1, @"battery.25percent"}, // there are different levels that can be low battery + {"dialog-error"_L1, @"exclamationmark.bubble"}, + {"dialog-information"_L1, @"info.circle"}, + {"dialog-password"_L1, @"lock"}, + {"dialog-question"_L1, @"questionmark.circle"}, + {"dialog-warning"_L1, @"exclamationmark.octagon"}, + {"folder-drag-accept"_L1, @"plus.rectangle.on.folder"}, + //{"folder-open"_L1, @""}, + {"folder-visiting"_L1, @"folder.circle"}, + {"image-loading"_L1, @"photo.circle"}, + {"image-missing"_L1, @"photo"}, + {"mail-attachment"_L1, @"paperclip"}, + {"mail-unread"_L1, @"envelope.badge"}, + {"mail-read"_L1, @"envelope.open"}, + {"mail-replied"_L1, @"arrowshape.turn.up.left"}, + //{"mail-signed"_L1, @""}, + //{"mail-signed-verified"_L1, @""}, + {"media-playlist-repeat"_L1, @"repet"}, + {"media-playlist-shuffle"_L1, @"shuffle"}, + //{"network-error"_L1, @""}, + //{"network-idle"_L1, @""}, + {"network-offline"_L1, @"network.slash"}, + //{"network-receive"_L1, @""}, + //{"network-transmit"_L1, @""}, + //{"network-transmit-receive"_L1, @""}, + //{"printer-error"_L1, @""}, + {"printer-printing"_L1, @"printer.dotmatrix.filled.and.paper"}, // not sure + {"security-high"_L1, @"lock.shield"}, + //{"security-medium"_L1, @""}, + {"security-low"_L1, @"lock.trianglebadge.exclamationmark"}, + {"software-update-available"_L1, @"arrowshape.up.circle"}, + {"software-update-urgent"_L1, @"exclamationmark.transmission"}, + {"sync-error"_L1, @"exclamationmark.arrow.triangle.2.circlepath"}, + {"sync-synchronizing"_L1, @"arrow.triangle.2.circlepath"}, + {"task-due"_L1, @"clock.badge.exclamationmark"}, + {"task-past-due"_L1, @"clock.badge.xmark"}, + {"user-available"_L1, @"person.crop.circle.badge.checkmark"}, + {"user-away"_L1, @"person.crop.circle.badge.clock"}, + //{"user-idle"_L1, @""}, + {"user-offline"_L1, @"person.crop.circle.badge.xmark"}, + //{"user-trash-full"_L1, @""}, + {"weather-clear"_L1, @"sun.max"}, + {"weather-clear-night"_L1, @"moon"}, + {"weather-few-clouds"_L1, @"cloud.sun"}, + {"weather-few-clouds-night"_L1, @"cloud.moon"}, + {"weather-fog"_L1, @"cloud.fog"}, + {"weather-overcast"_L1, @"cloud"}, + //{"weather-severe-alert"_L1, @""}, + {"weather-showers"_L1, @"cloud.rain"}, + //{"weather-showers-scattered"_L1, @""}, + {"weather-snow"_L1, @"cloud.snow"}, + {"weather-storm"_L1, @"tropicalstorm"}, + }; + const auto it = std::find_if(std::begin(iconMap), std::end(iconMap), [iconName](const auto &c){ + return c.first == iconName; + }); + NSString *systemIconName = it != std::end(iconMap) ? it->second : iconName.toNSString(); +#if defined(Q_OS_MACOS) + return [NSImage imageWithSystemSymbolName:systemIconName accessibilityDescription:nil]; +#elif defined(QT_PLATFORM_UIKIT) + return [UIImage systemImageNamed:systemIconName]; +#endif +} +} + +QAppleIconEngine::QAppleIconEngine(const QString &iconName) + : m_iconName(iconName), m_image(loadImage(iconName)) +{ + if (m_image) + [m_image retain]; +} + +QAppleIconEngine::~QAppleIconEngine() +{ + if (m_image) + [m_image release]; +} + +QIconEngine *QAppleIconEngine::clone() const +{ + return new QAppleIconEngine(m_iconName); +} + +QString QAppleIconEngine::key() const +{ + return u"QAppleIconEngine"_s; +} + +QString QAppleIconEngine::iconName() +{ + return m_iconName; +} + +bool QAppleIconEngine::isNull() +{ + return m_image == nullptr; +} + +QList<QSize> QAppleIconEngine::availableIconSizes(double aspectRatio) +{ + const qreal devicePixelRatio = qGuiApp->devicePixelRatio(); + const QList<QSize> sizes = { + {qRound(16 * devicePixelRatio), qRound(16. * devicePixelRatio / aspectRatio)}, + {qRound(32 * devicePixelRatio), qRound(32. * devicePixelRatio / aspectRatio)}, + {qRound(64 * devicePixelRatio), qRound(64. * devicePixelRatio / aspectRatio)}, + {qRound(128 * devicePixelRatio), qRound(128. * devicePixelRatio / aspectRatio)}, + {qRound(256 * devicePixelRatio), qRound(256. * devicePixelRatio / aspectRatio)}, + }; + return sizes; +} + +QList<QSize> QAppleIconEngine::availableSizes(QIcon::Mode, QIcon::State) +{ + const double aspectRatio = isNull() ? 1.0 : m_image.size.width / m_image.size.height; + return availableIconSizes(aspectRatio); +} + +QSize QAppleIconEngine::actualSize(const QSize &size, QIcon::Mode /*mode*/, QIcon::State /*state*/) +{ + const double inputAspectRatio = isNull() ? 1.0 : m_image.size.width / m_image.size.height; + const double outputAspectRatio = size.width() / size.height(); + QSize result = size; + if (outputAspectRatio > inputAspectRatio) + result.rwidth() = result.height() * inputAspectRatio; + else + result.rheight() = result.width() / inputAspectRatio; + return result; +} + +QPixmap QAppleIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + return scaledPixmap(size, mode, state, 1.0); +} + +namespace { +#if defined(Q_OS_MACOS) +auto *configuredImage(const NSImage *image, const QColor &color) +{ + auto *config = [NSImageSymbolConfiguration configurationWithPointSize:48 + weight:NSFontWeightRegular + scale:NSImageSymbolScaleLarge]; + if (@available(macOS 12, *)) { + auto *primaryColor = [NSColor colorWithSRGBRed:color.redF() + green:color.greenF() + blue:color.blueF() + alpha:color.alphaF()]; + + auto *colorConfig = [NSImageSymbolConfiguration configurationWithHierarchicalColor:primaryColor]; + config = [config configurationByApplyingConfiguration:colorConfig]; + } + + return [image imageWithSymbolConfiguration:config]; +} +#elif defined(QT_PLATFORM_UIKIT) +auto *configuredImage(const UIImage *image, const QColor &color) +{ + auto *config = [UIImageSymbolConfiguration configurationWithPointSize:48 + weight:UIImageSymbolWeightRegular + scale:UIImageSymbolScaleLarge]; + + if (@available(iOS 15, *)) { + auto *primaryColor = [UIColor colorWithRed:color.redF() + green:color.greenF() + blue:color.blueF() + alpha:color.alphaF()]; + + auto *colorConfig = [UIImageSymbolConfiguration configurationWithHierarchicalColor:primaryColor]; + config = [config configurationByApplyingConfiguration:colorConfig]; + } + return [image imageByApplyingSymbolConfiguration:config]; +} +#endif +} + +QPixmap QAppleIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) +{ + const quint64 cacheKey = calculateCacheKey(mode, state); + if (cacheKey != m_cacheKey || m_pixmap.size() != size || m_pixmap.devicePixelRatio() != scale) { + const QSize paintSize = actualSize(size, mode, state); + const QSize paintOffset = paintSize != size + ? (QSizeF(size - paintSize) * 0.5).toSize() + : QSize(); + + m_pixmap = QPixmap(size * scale); + m_pixmap.setDevicePixelRatio(scale); + m_pixmap.fill(Qt::transparent); + + QPainter painter(&m_pixmap); + paint(&painter, QRect(paintOffset.width(), paintOffset.height(), + paintSize.width(), paintSize.height()), mode, state); + + m_cacheKey = cacheKey; + } + return m_pixmap; +} + +void QAppleIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) +{ + Q_UNUSED(state); + + QColor color; + const QPalette palette; + switch (mode) { + case QIcon::Normal: + color = palette.color(QPalette::Inactive, QPalette::Text); + break; + case QIcon::Disabled: + color = palette.color(QPalette::Disabled, QPalette::Text); + break; + case QIcon::Active: + color = palette.color(QPalette::Active, QPalette::Text); + break; + case QIcon::Selected: + color = palette.color(QPalette::Active, QPalette::HighlightedText); + break; + } + const auto *image = configuredImage(m_image, color); + + QMacCGContext ctx(painter); + +#if defined(Q_OS_MACOS) + NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:YES]; + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext:gc]; + + const NSSize pixmapSize = NSMakeSize(rect.width(), rect.height()); + [image setSize:pixmapSize]; + const NSRect sourceRect = NSMakeRect(0, 0, pixmapSize.width, pixmapSize.height); + const NSRect iconRect = NSMakeRect(rect.x(), rect.y(), pixmapSize.width, pixmapSize.height); + + [image drawInRect:iconRect fromRect:sourceRect operation:NSCompositingOperationSourceOver fraction:1.0 respectFlipped:YES hints:nil]; + [NSGraphicsContext restoreGraphicsState]; +#elif defined(QT_PLATFORM_UIKIT) + UIGraphicsPushContext(ctx); + const CGRect cgrect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()); + [image drawInRect:cgrect]; + UIGraphicsPopContext(); +#endif +} + +QT_END_NAMESPACE diff --git a/src/gui/platform/darwin/qappleiconengine_p.h b/src/gui/platform/darwin/qappleiconengine_p.h new file mode 100644 index 0000000000..2a4ff7fc64 --- /dev/null +++ b/src/gui/platform/darwin/qappleiconengine_p.h @@ -0,0 +1,64 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QAPPLEICONENGINE_P_H +#define QAPPLEICONENGINE_P_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. +// + +#include <QtGui/qiconengine.h> + +#include <QtCore/private/qcore_mac_p.h> + +Q_FORWARD_DECLARE_OBJC_CLASS(UIImage); +Q_FORWARD_DECLARE_OBJC_CLASS(NSImage); + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QAppleIconEngine : public QIconEngine +{ +public: + QAppleIconEngine(const QString &iconName); + ~QAppleIconEngine(); + QIconEngine *clone() const override; + QString key() const override; + QString iconName() override; + bool isNull() override; + + QList<QSize> availableSizes(QIcon::Mode, QIcon::State) override; + QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override; + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override; + QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override; + void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override; + + static QList<QSize> availableIconSizes(double aspectRatio = 1.0); + +private: + static constexpr quint64 calculateCacheKey(QIcon::Mode mode, QIcon::State state) + { + return (quint64(mode) << 32) | state; + } + + const QString m_iconName; +#if defined(Q_OS_MACOS) + const NSImage *m_image; +#elif defined(QT_PLATFORM_UIKIT) + const UIImage *m_image; +#endif + mutable QPixmap m_pixmap; + mutable quint64 m_cacheKey = {}; +}; + + +QT_END_NAMESPACE + +#endif // QAPPLEICONENGINE_P_H diff --git a/src/gui/platform/darwin/qapplekeymapper.mm b/src/gui/platform/darwin/qapplekeymapper.mm new file mode 100644 index 0000000000..9166d8baba --- /dev/null +++ b/src/gui/platform/darwin/qapplekeymapper.mm @@ -0,0 +1,715 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <qglobal.h> + +#ifdef Q_OS_MACOS +#include <AppKit/AppKit.h> +#endif + +#if defined(QT_PLATFORM_UIKIT) +#include <UIKit/UIKit.h> +#endif + +#include "qapplekeymapper_p.h" + +#include <QtCore/qloggingcategory.h> +#include <QtGui/QGuiApplication> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcQpaKeyMapperKeys, "qt.qpa.keymapper.keys"); + +static Qt::KeyboardModifiers swapModifiersIfNeeded(const Qt::KeyboardModifiers modifiers) +{ + if (QCoreApplication::testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) + return modifiers; + + Qt::KeyboardModifiers swappedModifiers = modifiers; + swappedModifiers &= ~(Qt::MetaModifier | Qt::ControlModifier); + + if (modifiers & Qt::ControlModifier) + swappedModifiers |= Qt::MetaModifier; + if (modifiers & Qt::MetaModifier) + swappedModifiers |= Qt::ControlModifier; + + return swappedModifiers; +} + +#ifdef Q_OS_MACOS +static constexpr std::tuple<NSEventModifierFlags, Qt::KeyboardModifier> cocoaModifierMap[] = { + { NSEventModifierFlagShift, Qt::ShiftModifier }, + { NSEventModifierFlagControl, Qt::ControlModifier }, + { NSEventModifierFlagCommand, Qt::MetaModifier }, + { NSEventModifierFlagOption, Qt::AltModifier }, + { NSEventModifierFlagNumericPad, Qt::KeypadModifier } +}; + +Qt::KeyboardModifiers QAppleKeyMapper::fromCocoaModifiers(NSEventModifierFlags cocoaModifiers) +{ + Qt::KeyboardModifiers qtModifiers = Qt::NoModifier; + for (const auto &[cocoaModifier, qtModifier] : cocoaModifierMap) { + if (cocoaModifiers & cocoaModifier) + qtModifiers |= qtModifier; + } + + return swapModifiersIfNeeded(qtModifiers); +} + +NSEventModifierFlags QAppleKeyMapper::toCocoaModifiers(Qt::KeyboardModifiers qtModifiers) +{ + qtModifiers = swapModifiersIfNeeded(qtModifiers); + + NSEventModifierFlags cocoaModifiers = 0; + for (const auto &[cocoaModifier, qtModifier] : cocoaModifierMap) { + if (qtModifiers & qtModifier) + cocoaModifiers |= cocoaModifier; + } + + return cocoaModifiers; +} + +using CarbonModifiers = UInt32; // As opposed to EventModifiers which is UInt16 + +static CarbonModifiers toCarbonModifiers(Qt::KeyboardModifiers qtModifiers) +{ + qtModifiers = swapModifiersIfNeeded(qtModifiers); + + static constexpr std::tuple<int, Qt::KeyboardModifier> carbonModifierMap[] = { + { shiftKey, Qt::ShiftModifier }, + { controlKey, Qt::ControlModifier }, + { cmdKey, Qt::MetaModifier }, + { optionKey, Qt::AltModifier }, + { kEventKeyModifierNumLockMask, Qt::KeypadModifier } + }; + + CarbonModifiers carbonModifiers = 0; + for (const auto &[carbonModifier, qtModifier] : carbonModifierMap) { + if (qtModifiers & qtModifier) + carbonModifiers |= carbonModifier; + } + + return carbonModifiers; +} + +// Keyboard keys (non-modifiers) +static QHash<char16_t, Qt::Key> standardKeys = { + { kHomeCharCode, Qt::Key_Home }, + { kEnterCharCode, Qt::Key_Enter }, + { kEndCharCode, Qt::Key_End }, + { kBackspaceCharCode, Qt::Key_Backspace }, + { kTabCharCode, Qt::Key_Tab }, + { kPageUpCharCode, Qt::Key_PageUp }, + { kPageDownCharCode, Qt::Key_PageDown }, + { kReturnCharCode, Qt::Key_Return }, + { kEscapeCharCode, Qt::Key_Escape }, + { kLeftArrowCharCode, Qt::Key_Left }, + { kRightArrowCharCode, Qt::Key_Right }, + { kUpArrowCharCode, Qt::Key_Up }, + { kDownArrowCharCode, Qt::Key_Down }, + { kHelpCharCode, Qt::Key_Help }, + { kDeleteCharCode, Qt::Key_Delete }, + // ASCII maps, for debugging + { ':', Qt::Key_Colon }, + { ';', Qt::Key_Semicolon }, + { '<', Qt::Key_Less }, + { '=', Qt::Key_Equal }, + { '>', Qt::Key_Greater }, + { '?', Qt::Key_Question }, + { '@', Qt::Key_At }, + { ' ', Qt::Key_Space }, + { '!', Qt::Key_Exclam }, + { '"', Qt::Key_QuoteDbl }, + { '#', Qt::Key_NumberSign }, + { '$', Qt::Key_Dollar }, + { '%', Qt::Key_Percent }, + { '&', Qt::Key_Ampersand }, + { '\'', Qt::Key_Apostrophe }, + { '(', Qt::Key_ParenLeft }, + { ')', Qt::Key_ParenRight }, + { '*', Qt::Key_Asterisk }, + { '+', Qt::Key_Plus }, + { ',', Qt::Key_Comma }, + { '-', Qt::Key_Minus }, + { '.', Qt::Key_Period }, + { '/', Qt::Key_Slash }, + { '[', Qt::Key_BracketLeft }, + { ']', Qt::Key_BracketRight }, + { '\\', Qt::Key_Backslash }, + { '_', Qt::Key_Underscore }, + { '`', Qt::Key_QuoteLeft }, + { '{', Qt::Key_BraceLeft }, + { '}', Qt::Key_BraceRight }, + { '|', Qt::Key_Bar }, + { '~', Qt::Key_AsciiTilde }, + { '^', Qt::Key_AsciiCircum } +}; + +static QHash<char16_t, Qt::Key> virtualKeys = { + { kVK_F1, Qt::Key_F1 }, + { kVK_F2, Qt::Key_F2 }, + { kVK_F3, Qt::Key_F3 }, + { kVK_F4, Qt::Key_F4 }, + { kVK_F5, Qt::Key_F5 }, + { kVK_F6, Qt::Key_F6 }, + { kVK_F7, Qt::Key_F7 }, + { kVK_F8, Qt::Key_F8 }, + { kVK_F9, Qt::Key_F9 }, + { kVK_F10, Qt::Key_F10 }, + { kVK_F11, Qt::Key_F11 }, + { kVK_F12, Qt::Key_F12 }, + { kVK_F13, Qt::Key_F13 }, + { kVK_F14, Qt::Key_F14 }, + { kVK_F15, Qt::Key_F15 }, + { kVK_F16, Qt::Key_F16 }, + { kVK_Return, Qt::Key_Return }, + { kVK_Tab, Qt::Key_Tab }, + { kVK_Escape, Qt::Key_Escape }, + { kVK_Help, Qt::Key_Help }, + { kVK_UpArrow, Qt::Key_Up }, + { kVK_DownArrow, Qt::Key_Down }, + { kVK_LeftArrow, Qt::Key_Left }, + { kVK_RightArrow, Qt::Key_Right }, + { kVK_PageUp, Qt::Key_PageUp }, + { kVK_PageDown, Qt::Key_PageDown } +}; + +static QHash<char16_t, Qt::Key> functionKeys = { + { NSUpArrowFunctionKey, Qt::Key_Up }, + { NSDownArrowFunctionKey, Qt::Key_Down }, + { NSLeftArrowFunctionKey, Qt::Key_Left }, + { NSRightArrowFunctionKey, Qt::Key_Right }, + // F1-35 function keys handled manually below + { NSInsertFunctionKey, Qt::Key_Insert }, + { NSDeleteFunctionKey, Qt::Key_Delete }, + { NSHomeFunctionKey, Qt::Key_Home }, + { NSEndFunctionKey, Qt::Key_End }, + { NSPageUpFunctionKey, Qt::Key_PageUp }, + { NSPageDownFunctionKey, Qt::Key_PageDown }, + { NSPrintScreenFunctionKey, Qt::Key_Print }, + { NSScrollLockFunctionKey, Qt::Key_ScrollLock }, + { NSPauseFunctionKey, Qt::Key_Pause }, + { NSSysReqFunctionKey, Qt::Key_SysReq }, + { NSMenuFunctionKey, Qt::Key_Menu }, + { NSPrintFunctionKey, Qt::Key_Printer }, + { NSClearDisplayFunctionKey, Qt::Key_Clear }, + { NSInsertCharFunctionKey, Qt::Key_Insert }, + { NSDeleteCharFunctionKey, Qt::Key_Delete }, + { NSSelectFunctionKey, Qt::Key_Select }, + { NSExecuteFunctionKey, Qt::Key_Execute }, + { NSUndoFunctionKey, Qt::Key_Undo }, + { NSRedoFunctionKey, Qt::Key_Redo }, + { NSFindFunctionKey, Qt::Key_Find }, + { NSHelpFunctionKey, Qt::Key_Help }, + { NSModeSwitchFunctionKey, Qt::Key_Mode_switch } +}; + +static int toKeyCode(const QChar &key, int virtualKey, int modifiers) +{ + qCDebug(lcQpaKeyMapperKeys, "Mapping key: %d (0x%04x) / vk %d (0x%04x)", + key.unicode(), key.unicode(), virtualKey, virtualKey); + + if (key == QChar(kClearCharCode) && virtualKey == 0x47) + return Qt::Key_Clear; + + if (key.isDigit()) { + qCDebug(lcQpaKeyMapperKeys, "Got digit key: %d", key.digitValue()); + return key.digitValue() + Qt::Key_0; + } + + if (key.isLetter()) { + qCDebug(lcQpaKeyMapperKeys, "Got letter key: %d", (key.toUpper().unicode() - 'A')); + return (key.toUpper().unicode() - 'A') + Qt::Key_A; + } + if (key.isSymbol()) { + qCDebug(lcQpaKeyMapperKeys, "Got symbol key: %d", (key.unicode())); + return key.unicode(); + } + + if (auto qtKey = standardKeys.value(key.unicode())) { + // To work like Qt for X11 we issue Backtab when Shift + Tab are pressed + if (qtKey == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) { + qCDebug(lcQpaKeyMapperKeys, "Got key: Qt::Key_Backtab"); + return Qt::Key_Backtab; + } + + qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey; + return qtKey; + } + + // Last ditch try to match the scan code + if (auto qtKey = virtualKeys.value(virtualKey)) { + qCDebug(lcQpaKeyMapperKeys) << "Got scancode" << qtKey; + return qtKey; + } + + // Check if they belong to key codes in private unicode range + if (key >= QChar(NSUpArrowFunctionKey) && key <= QChar(NSModeSwitchFunctionKey)) { + if (auto qtKey = functionKeys.value(key.unicode())) { + qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey; + return qtKey; + } else if (key >= QChar(NSF1FunctionKey) && key <= QChar(NSF35FunctionKey)) { + auto functionKey = Qt::Key_F1 + (key.unicode() - NSF1FunctionKey) ; + qCDebug(lcQpaKeyMapperKeys) << "Got" << functionKey; + return functionKey; + } + } + + qCDebug(lcQpaKeyMapperKeys, "Unknown case.. %d[%d] %d", key.unicode(), key.toLatin1(), virtualKey); + return Qt::Key_unknown; +} + +// --------- Cocoa key mapping moved from Qt Core --------- + +static const int NSEscapeCharacter = 27; // not defined by Cocoa headers + +static const QHash<char16_t, Qt::Key> cocoaKeys = { + { NSEnterCharacter, Qt::Key_Enter }, + { NSBackspaceCharacter, Qt::Key_Backspace }, + { NSTabCharacter, Qt::Key_Tab }, + { NSNewlineCharacter, Qt::Key_Return }, + { NSCarriageReturnCharacter, Qt::Key_Return }, + { NSBackTabCharacter, Qt::Key_Backtab }, + { NSEscapeCharacter, Qt::Key_Escape }, + { NSDeleteCharacter, Qt::Key_Backspace }, + { NSUpArrowFunctionKey, Qt::Key_Up }, + { NSDownArrowFunctionKey, Qt::Key_Down }, + { NSLeftArrowFunctionKey, Qt::Key_Left }, + { NSRightArrowFunctionKey, Qt::Key_Right }, + { NSF1FunctionKey, Qt::Key_F1 }, + { NSF2FunctionKey, Qt::Key_F2 }, + { NSF3FunctionKey, Qt::Key_F3 }, + { NSF4FunctionKey, Qt::Key_F4 }, + { NSF5FunctionKey, Qt::Key_F5 }, + { NSF6FunctionKey, Qt::Key_F6 }, + { NSF7FunctionKey, Qt::Key_F7 }, + { NSF8FunctionKey, Qt::Key_F8 }, + { NSF9FunctionKey, Qt::Key_F9 }, + { NSF10FunctionKey, Qt::Key_F10 }, + { NSF11FunctionKey, Qt::Key_F11 }, + { NSF12FunctionKey, Qt::Key_F12 }, + { NSF13FunctionKey, Qt::Key_F13 }, + { NSF14FunctionKey, Qt::Key_F14 }, + { NSF15FunctionKey, Qt::Key_F15 }, + { NSF16FunctionKey, Qt::Key_F16 }, + { NSF17FunctionKey, Qt::Key_F17 }, + { NSF18FunctionKey, Qt::Key_F18 }, + { NSF19FunctionKey, Qt::Key_F19 }, + { NSF20FunctionKey, Qt::Key_F20 }, + { NSF21FunctionKey, Qt::Key_F21 }, + { NSF22FunctionKey, Qt::Key_F22 }, + { NSF23FunctionKey, Qt::Key_F23 }, + { NSF24FunctionKey, Qt::Key_F24 }, + { NSF25FunctionKey, Qt::Key_F25 }, + { NSF26FunctionKey, Qt::Key_F26 }, + { NSF27FunctionKey, Qt::Key_F27 }, + { NSF28FunctionKey, Qt::Key_F28 }, + { NSF29FunctionKey, Qt::Key_F29 }, + { NSF30FunctionKey, Qt::Key_F30 }, + { NSF31FunctionKey, Qt::Key_F31 }, + { NSF32FunctionKey, Qt::Key_F32 }, + { NSF33FunctionKey, Qt::Key_F33 }, + { NSF34FunctionKey, Qt::Key_F34 }, + { NSF35FunctionKey, Qt::Key_F35 }, + { NSInsertFunctionKey, Qt::Key_Insert }, + { NSDeleteFunctionKey, Qt::Key_Delete }, + { NSHomeFunctionKey, Qt::Key_Home }, + { NSEndFunctionKey, Qt::Key_End }, + { NSPageUpFunctionKey, Qt::Key_PageUp }, + { NSPageDownFunctionKey, Qt::Key_PageDown }, + { NSPrintScreenFunctionKey, Qt::Key_Print }, + { NSScrollLockFunctionKey, Qt::Key_ScrollLock }, + { NSPauseFunctionKey, Qt::Key_Pause }, + { NSSysReqFunctionKey, Qt::Key_SysReq }, + { NSMenuFunctionKey, Qt::Key_Menu }, + { NSHelpFunctionKey, Qt::Key_Help }, +}; + +QChar QAppleKeyMapper::toCocoaKey(Qt::Key key) +{ + // Prioritize overloaded keys + if (key == Qt::Key_Return) + return QChar(NSCarriageReturnCharacter); + if (key == Qt::Key_Backspace) + return QChar(NSBackspaceCharacter); + + Q_CONSTINIT static QHash<Qt::Key, char16_t> reverseCocoaKeys; + if (reverseCocoaKeys.isEmpty()) { + reverseCocoaKeys.reserve(cocoaKeys.size()); + for (auto it = cocoaKeys.begin(); it != cocoaKeys.end(); ++it) + reverseCocoaKeys.insert(it.value(), it.key()); + } + + return reverseCocoaKeys.value(key); +} + +Qt::Key QAppleKeyMapper::fromCocoaKey(QChar keyCode) +{ + if (auto key = cocoaKeys.value(keyCode.unicode())) + return key; + + return Qt::Key(keyCode.toUpper().unicode()); +} + +// ------------------------------------------------ + +Qt::KeyboardModifiers QAppleKeyMapper::queryKeyboardModifiers() const +{ + return fromCocoaModifiers(NSEvent.modifierFlags); +} + +bool QAppleKeyMapper::updateKeyboard() +{ + QCFType<TISInputSourceRef> source = TISCopyInputMethodKeyboardLayoutOverride(); + if (!source) + source = TISCopyCurrentKeyboardInputSource(); + + if (m_keyboardMode != NullMode && source == m_currentInputSource) + return false; + + Q_ASSERT(source); + m_currentInputSource = source; + m_keyboardKind = LMGetKbdType(); + + m_keyMap.clear(); + + if (auto data = CFDataRef(TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData))) { + const UCKeyboardLayout *uchrData = reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)); + Q_ASSERT(uchrData); + m_keyboardLayoutFormat = uchrData; + m_keyboardMode = UnicodeMode; + } else { + m_keyboardLayoutFormat = nullptr; + m_keyboardMode = NullMode; + } + + qCDebug(lcQpaKeyMapper) << "Updated keyboard to" + << QString::fromCFString(CFStringRef(TISGetInputSourceProperty( + m_currentInputSource, kTISPropertyLocalizedName))); + + return true; +} + +static constexpr Qt::KeyboardModifiers modifierCombinations[] = { + 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::MetaModifier, // 8 + Qt::MetaModifier | Qt::ShiftModifier, // 9 + Qt::MetaModifier | Qt::ControlModifier, // 10 + Qt::MetaModifier | Qt::ControlModifier | Qt::ShiftModifier, // 11 + Qt::MetaModifier | Qt::AltModifier, // 12 + Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier, // 13 + Qt::MetaModifier | Qt::AltModifier | Qt::ControlModifier, // 14 + Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 15 +}; + +/* + Returns a key map for the given \virtualKey based on all + possible modifier combinations. +*/ +const QAppleKeyMapper::KeyMap &QAppleKeyMapper::keyMapForKey(VirtualKeyCode virtualKey) const +{ + static_assert(sizeof(modifierCombinations) / sizeof(Qt::KeyboardModifiers) == kNumModifierCombinations); + + const_cast<QAppleKeyMapper *>(this)->updateKeyboard(); + + auto &keyMap = m_keyMap[virtualKey]; + if (keyMap[Qt::NoModifier] != Qt::Key_unknown) + return keyMap; // Already filled + + qCDebug(lcQpaKeyMapper, "Updating key map for virtual key 0x%02x", (uint)virtualKey); + + // Key mapping via [NSEvent charactersByApplyingModifiers:] only works for key down + // events, but we might (wrongly) get into this code path for other key events such + // as NSEventTypeFlagsChanged. + const bool canMapCocoaEvent = NSApp.currentEvent.type == NSEventTypeKeyDown; + + if (!canMapCocoaEvent) + qCWarning(lcQpaKeyMapper) << "Could not map key to character for event" << NSApp.currentEvent; + + for (int i = 0; i < kNumModifierCombinations; ++i) { + Q_ASSERT(!i || keyMap[i] == 0); + + auto qtModifiers = modifierCombinations[i]; + auto carbonModifiers = toCarbonModifiers(qtModifiers); + const UInt32 modifierKeyState = (carbonModifiers >> 8) & 0xFF; + + UInt32 deadKeyState = 0; + static const UniCharCount maxStringLength = 10; + static UniChar unicodeString[maxStringLength]; + UniCharCount actualStringLength = 0; + OSStatus err = UCKeyTranslate(m_keyboardLayoutFormat, virtualKey, + kUCKeyActionDown, modifierKeyState, m_keyboardKind, + kUCKeyTranslateNoDeadKeysMask, &deadKeyState, + maxStringLength, &actualStringLength, + unicodeString); + + // Use translated Unicode key if valid + QChar carbonUnicodeKey; + if (err == noErr && actualStringLength) + carbonUnicodeKey = QChar(unicodeString[0]); + + if (canMapCocoaEvent) { + // Until we've verified that the Cocoa API works as expected + // we first run the event through the Carbon APIs and then + // compare the results to Cocoa. + auto cocoaModifiers = toCocoaModifiers(qtModifiers); + auto *charactersWithModifiers = [NSApp.currentEvent charactersByApplyingModifiers:cocoaModifiers]; + + QChar cocoaUnicodeKey; + if (charactersWithModifiers.length > 0) + cocoaUnicodeKey = QChar([charactersWithModifiers characterAtIndex:0]); + + if (cocoaUnicodeKey != carbonUnicodeKey) { + qCWarning(lcQpaKeyMapper) << "Mismatch between Cocoa" << cocoaUnicodeKey + << "and Carbon" << carbonUnicodeKey << "for virtual key" << virtualKey + << "with" << qtModifiers; + } + } + + int qtKey = toKeyCode(carbonUnicodeKey, virtualKey, qtModifiers); + if (qtKey == Qt::Key_unknown) + qtKey = carbonUnicodeKey.unicode(); + + keyMap[i] = qtKey; + + qCDebug(lcQpaKeyMapper).verbosity(0) << "\t" << qtModifiers + << "+" << qUtf8Printable(QString::asprintf("0x%02x", virtualKey)) + << "=" << qUtf8Printable(QString::asprintf("%d / 0x%02x /", qtKey, qtKey)) + << QKeySequence(qtKey).toString(); + } + + return keyMap; +} + +/* + Compute the possible key combinations that can map to the event's + virtual key and modifiers, in the current keyboard layout. + + For example, given a normal US keyboard layout, the virtual key + 23 combined with the Alt (⌥) and Shift (⇧) modifiers, can map + to the following key combinations: + + - Alt+Shift+5 + - Alt+% + - Shift+∞ + - fi + + The function builds on a key map produced by keyMapForKey(), + where each modifier-key combination has been mapped to the + key it will produce. +*/ +QList<QKeyCombination> QAppleKeyMapper::possibleKeyCombinations(const QKeyEvent *event) const +{ + QList<QKeyCombination> ret; + + const auto nativeVirtualKey = event->nativeVirtualKey(); + if (!nativeVirtualKey) + return ret; + + auto keyMap = keyMapForKey(nativeVirtualKey); + + auto unmodifiedKey = keyMap[Qt::NoModifier]; + Q_ASSERT(unmodifiedKey != Qt::Key_unknown); + + auto eventModifiers = event->modifiers(); + + int startingModifierLayer = 0; + if (toCocoaModifiers(eventModifiers) & NSEventModifierFlagCommand) { + // When the Command key is pressed AppKit seems to do key equivalent + // matching using a Latin/Roman interpretation of the current keyboard + // layout. For example, for a Greek layout, pressing Option+Command+C + // produces a key event with chars="ç" and unmodchars="ψ", but AppKit + // still treats this as a match for a key equivalent of Option+Command+C. + // We can't do the same by just applying the modifiers to our key map, + // as that too contains "ψ" for the Option+Command combination. What we + // can do instead is take advantage of the fact that the Command + // modifier layer in all/most keyboard layouts contains a Latin + // layer. We then combine that with the modifiers of the event + // to produce the resulting "Latin" key combination. + static constexpr int kCommandLayer = 2; + ret << QKeyCombination::fromCombined( + int(eventModifiers) + int(keyMap[kCommandLayer])); + + // If the unmodified key is outside of Latin1, we also treat + // that as a valid key combination, even if AppKit natively + // does not. For example, for a Greek layout, we still want + // to support Option+Command+ψ as a key combination, as it's + // unlikely to clash with the Latin key combination we added + // above. + + // However, if the unmodified key is within Latin1, we skip + // it, to avoid these types of conflicts. For example, in + // the same Greek layout, pressing the key next to Tab will + // produce a Latin ';' symbol, but we've already treated that + // as 'q' above, thanks to the Command modifier, so we skip + // the potential Command+; key combination. This is also in + // line with what AppKit natively does. + + // Skipping Latin1 unmodified keys also handles the case of + // a Latin layout, where the unmodified and modified keys + // are the same. + + if (unmodifiedKey <= 0xff) + startingModifierLayer = 1; + } + + // FIXME: We only compute the first 8 combinations. Why? + for (int i = startingModifierLayer; i < 15; ++i) { + auto keyAfterApplyingModifiers = keyMap[i]; + if (!keyAfterApplyingModifiers) + continue; + + // Include key if the event modifiers match exactly, + // or are a superset of the current candidate modifiers. + auto candidateModifiers = modifierCombinations[i]; + if ((eventModifiers & candidateModifiers) == candidateModifiers) { + // If the event includes more modifiers than the candidate they + // will need to be included in the resulting key combination. + auto additionalModifiers = eventModifiers & ~candidateModifiers; + + auto keyCombination = QKeyCombination::fromCombined( + int(additionalModifiers) + int(keyAfterApplyingModifiers)); + + // If there's an existing key combination with the same key, + // but a different set of modifiers, we want to choose only + // one of them, by priority (see below). + const auto existingCombination = std::find_if( + ret.begin(), ret.end(), [&](auto existingCombination) { + return existingCombination.key() == keyAfterApplyingModifiers; + }); + + if (existingCombination != ret.end()) { + // We prioritize the combination with the more specific + // modifiers. In the case where the number of modifiers + // are the same, we want to prioritize Command over Option + // over Control over Shift. Unfortunately the order (and + // hence value) of the modifiers in Qt::KeyboardModifier + // does not match our preferred order when Control and + // Meta is switched, but we can work around that by + // explicitly swapping the modifiers and using that + // for the comparison. This also works when the + // Qt::AA_MacDontSwapCtrlAndMeta application attribute + // is set, as the incoming modifiers are then left + // as is, and we can still trust the order. + auto existingModifiers = swapModifiersIfNeeded(existingCombination->keyboardModifiers()); + auto replacementModifiers = swapModifiersIfNeeded(additionalModifiers); + if (replacementModifiers > existingModifiers) + *existingCombination = keyCombination; + } else { + // All is good, no existing combination has this key + ret << keyCombination; + } + } + } + + return ret; +} + + + +#else // iOS + +Qt::Key QAppleKeyMapper::fromNSString(Qt::KeyboardModifiers qtModifiers, NSString *characters, + NSString *charactersIgnoringModifiers, QString &text) +{ + if ([characters isEqualToString:@"\t"]) { + if (qtModifiers & Qt::ShiftModifier) + return Qt::Key_Backtab; + return Qt::Key_Tab; + } else if ([characters isEqualToString:@"\r"]) { + if (qtModifiers & Qt::KeypadModifier) + return Qt::Key_Enter; + return Qt::Key_Return; + } + if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) { + QChar ch; + if (((qtModifiers & Qt::MetaModifier) || (qtModifiers & Qt::AltModifier)) && + ([charactersIgnoringModifiers length] != 0)) { + ch = QChar([charactersIgnoringModifiers characterAtIndex:0]); + } else if ([characters length] != 0) { + ch = QChar([characters characterAtIndex:0]); + } + if (!(qtModifiers & (Qt::ControlModifier | Qt::MetaModifier)) && + (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) { + text = QString::fromNSString(characters); + } + if (!ch.isNull()) + return Qt::Key(ch.toUpper().unicode()); + } + return Qt::Key_unknown; +} + +// Keyboard keys (non-modifiers) +API_AVAILABLE(ios(13.4)) Qt::Key QAppleKeyMapper::fromUIKitKey(NSString *keyCode) +{ + static QHash<NSString *, Qt::Key> uiKitKeys = { + { UIKeyInputF1, Qt::Key_F1 }, + { UIKeyInputF2, Qt::Key_F2 }, + { UIKeyInputF3, Qt::Key_F3 }, + { UIKeyInputF4, Qt::Key_F4 }, + { UIKeyInputF5, Qt::Key_F5 }, + { UIKeyInputF6, Qt::Key_F6 }, + { UIKeyInputF7, Qt::Key_F7 }, + { UIKeyInputF8, Qt::Key_F8 }, + { UIKeyInputF9, Qt::Key_F9 }, + { UIKeyInputF10, Qt::Key_F10 }, + { UIKeyInputF11, Qt::Key_F11 }, + { UIKeyInputF12, Qt::Key_F12 }, + { UIKeyInputHome, Qt::Key_Home }, + { UIKeyInputEnd, Qt::Key_End }, + { UIKeyInputPageUp, Qt::Key_PageUp }, + { UIKeyInputPageDown, Qt::Key_PageDown }, + { UIKeyInputEscape, Qt::Key_Escape }, + { UIKeyInputUpArrow, Qt::Key_Up }, + { UIKeyInputDownArrow, Qt::Key_Down }, + { UIKeyInputLeftArrow, Qt::Key_Left }, + { UIKeyInputRightArrow, Qt::Key_Right } + }; + + if (auto key = uiKitKeys.value(keyCode)) + return key; + + return Qt::Key_unknown; +} + +static constexpr std::tuple<ulong, Qt::KeyboardModifier> uiKitModifierMap[] = { + { UIKeyModifierShift, Qt::ShiftModifier }, + { UIKeyModifierControl, Qt::ControlModifier }, + { UIKeyModifierCommand, Qt::MetaModifier }, + { UIKeyModifierAlternate, Qt::AltModifier }, + { UIKeyModifierNumericPad, Qt::KeypadModifier } +}; + +ulong QAppleKeyMapper::toUIKitModifiers(Qt::KeyboardModifiers qtModifiers) +{ + qtModifiers = swapModifiersIfNeeded(qtModifiers); + + ulong nativeModifiers = 0; + for (const auto &[nativeModifier, qtModifier] : uiKitModifierMap) { + if (qtModifiers & qtModifier) + nativeModifiers |= nativeModifier; + } + + return nativeModifiers; +} + +Qt::KeyboardModifiers QAppleKeyMapper::fromUIKitModifiers(ulong nativeModifiers) +{ + Qt::KeyboardModifiers qtModifiers = Qt::NoModifier; + for (const auto &[nativeModifier, qtModifier] : uiKitModifierMap) { + if (nativeModifiers & nativeModifier) + qtModifiers |= qtModifier; + } + + return swapModifiersIfNeeded(qtModifiers); +} +#endif + +QT_END_NAMESPACE diff --git a/src/gui/platform/darwin/qapplekeymapper_p.h b/src/gui/platform/darwin/qapplekeymapper_p.h new file mode 100644 index 0000000000..1f3494d16f --- /dev/null +++ b/src/gui/platform/darwin/qapplekeymapper_p.h @@ -0,0 +1,82 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QAPPLEKEYMAPPER_H +#define QAPPLEKEYMAPPER_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. +// + +#ifdef Q_OS_MACOS +#include <Carbon/Carbon.h> +#endif + +#include <qpa/qplatformkeymapper.h> + +#include <QtCore/QList> +#include <QtCore/QHash> +#include <QtGui/QKeyEvent> + +#include <QtCore/private/qcore_mac_p.h> + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QAppleKeyMapper : public QPlatformKeyMapper +{ +public: + Qt::KeyboardModifiers queryKeyboardModifiers() const override; + QList<QKeyCombination> possibleKeyCombinations(const QKeyEvent *event) const override; + +#ifdef Q_OS_MACOS + static Qt::KeyboardModifiers fromCocoaModifiers(NSEventModifierFlags cocoaModifiers); + static NSEventModifierFlags toCocoaModifiers(Qt::KeyboardModifiers); + + static QChar toCocoaKey(Qt::Key key); + static Qt::Key fromCocoaKey(QChar keyCode); +#else + static Qt::Key fromNSString(Qt::KeyboardModifiers qtMods, NSString *characters, + NSString *charactersIgnoringModifiers, QString &text); + + static Qt::Key fromUIKitKey(NSString *keyCode); + static Qt::KeyboardModifiers fromUIKitModifiers(ulong uikitModifiers); + static ulong toUIKitModifiers(Qt::KeyboardModifiers); +#endif +private: +#ifdef Q_OS_MACOS + static constexpr int kNumModifierCombinations = 16; + struct KeyMap : std::array<char32_t, kNumModifierCombinations> + { + // Initialize first element to a sentinel that allows us + // to distinguish an uninitialized map from an initialized. + // Using 0 would not allow us to map U+0000 (NUL), however + // unlikely that is. + KeyMap() : std::array<char32_t, 16>{Qt::Key_unknown} {} + }; + + bool updateKeyboard(); + + using VirtualKeyCode = unsigned short; + const KeyMap &keyMapForKey(VirtualKeyCode virtualKey) const; + + QCFType<TISInputSourceRef> m_currentInputSource = nullptr; + + enum { NullMode, UnicodeMode, OtherMode } m_keyboardMode = NullMode; + const UCKeyboardLayout *m_keyboardLayoutFormat = nullptr; + KeyboardLayoutKind m_keyboardKind = kKLKCHRuchrKind; + + mutable QHash<VirtualKeyCode, KeyMap> m_keyMap; +#endif +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/gui/platform/darwin/qmacmime.mm b/src/gui/platform/darwin/qmacmime.mm deleted file mode 100644 index 6e588f2ca0..0000000000 --- a/src/gui/platform/darwin/qmacmime.mm +++ /dev/null @@ -1,1046 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <ImageIO/ImageIO.h> - -#include <QtCore/qsystemdetection.h> -#include <QtCore/qurl.h> -#include <QtGui/qimage.h> -#include <QtCore/qmimedata.h> -#include <QtCore/qstringconverter.h> - -#if defined(Q_OS_MACOS) -#import <AppKit/AppKit.h> -#else -#include <MobileCoreServices/MobileCoreServices.h> -#endif - -#if defined(QT_PLATFORM_UIKIT) -#import <UIKit/UIKit.h> -#endif - -#include "qmacmime_p.h" -#include "qguiapplication.h" -#include "private/qcore_mac_p.h" - -QT_BEGIN_NAMESPACE - -typedef QList<QMacInternalPasteboardMime*> MimeList; -Q_GLOBAL_STATIC(MimeList, globalMimeList) -Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList) - -void qt_mac_addToGlobalMimeList(QMacInternalPasteboardMime *macMime) -{ - // globalMimeList is in decreasing priority order. Recently added - // converters take prioity over previously added converters: prepend - // to the list. - globalMimeList()->prepend(macMime); -} - -void qt_mac_removeFromGlobalMimeList(QMacInternalPasteboardMime *macMime) -{ - if (!QGuiApplication::closingDown()) - globalMimeList()->removeAll(macMime); -} - -/*! - \fn void qRegisterDraggedTypes(const QStringList &types) - \relates QMacPasteboardMime - - Registers the given \a types as custom pasteboard types. - - This function should be called to enable the Drag and Drop events - for custom pasteboard types on Cocoa implementations. This is required - in addition to a QMacPasteboardMime subclass implementation. By default - drag and drop is enabled for all standard pasteboard types. - - \sa QMacPasteboardMime -*/ -void qt_mac_registerDraggedTypes(const QStringList &types) -{ - (*globalDraggedTypesList()) += types; -} - -const QStringList& qt_mac_enabledDraggedTypes() -{ - return (*globalDraggedTypesList()); -} - -/***************************************************************************** - QDnD debug facilities - *****************************************************************************/ -//#define DEBUG_MIME_MAPS - -/*! - \class QMacPasteboardMime - \brief The QMacPasteboardMime class converts between a MIME type and a - \l{http://developer.apple.com/macosx/uniformtypeidentifiers.html}{Uniform - Type Identifier (UTI)} format. - \since 4.2 - - \ingroup draganddrop - \inmodule QtWidgets - - Qt's drag and drop and clipboard facilities use the MIME - standard. On X11, this maps trivially to the Xdnd protocol. On - Mac, although some applications use MIME to describe clipboard - contents, it is more common to use Apple's UTI format. - - QMacPasteboardMime's role is to bridge the gap between MIME and UTI; - By subclasses this class, one can extend Qt's drag and drop - and clipboard handling to convert to and from unsupported, or proprietary, UTI formats. - - A subclass of QMacPasteboardMime will automatically be registered, and active, upon instantiation. - - Qt has predefined support for the following UTIs: - \list - \li public.utf8-plain-text - converts to "text/plain" - \li public.utf16-plain-text - converts to "text/plain" - \li public.text - converts to "text/plain" - \li public.html - converts to "text/html" - \li public.url - converts to "text/uri-list" - \li public.file-url - converts to "text/uri-list" - \li public.tiff - converts to "application/x-qt-image" - \li public.vcard - converts to "text/plain" - \li com.apple.traditional-mac-plain-text - converts to "text/plain" - \li com.apple.pict - converts to "application/x-qt-image" - \endlist - - When working with MIME data, Qt will interate through all instances of QMacPasteboardMime to - find an instance that can convert to, or from, a specific MIME type. It will do this by calling - canConvert() on each instance, starting with (and choosing) the last created instance first. - The actual conversions will be done by using convertToMime() and convertFromMime(). - - \note The API uses the term "flavor" in some cases. This is for backwards - compatibility reasons, and should now be understood as UTIs. -*/ - -/*! \enum QMacPasteboardMime::QMacPasteboardMimeType - \internal -*/ - -/*! - Constructs a new conversion object of type \a t, adding it to the - globally accessed list of available convertors. -*/ -QMacInternalPasteboardMime::QMacInternalPasteboardMime(char t) : type(t) -{ - qt_mac_addToGlobalMimeList(this); -} - -/*! - Destroys a conversion object, removing it from the global - list of available convertors. -*/ -QMacInternalPasteboardMime::~QMacInternalPasteboardMime() -{ - qt_mac_removeFromGlobalMimeList(this); -} - -/*! - Returns the item count for the given \a mimeData -*/ -int QMacInternalPasteboardMime::count(QMimeData *mimeData) -{ - Q_UNUSED(mimeData); - return 1; -} - -class QMacPasteboardMimeAny : public QMacInternalPasteboardMime { -private: - -public: - QMacPasteboardMimeAny() : QMacInternalPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) { - } - ~QMacPasteboardMimeAny() { - } - QString convertorName(); - - QString flavorFor(const QString &mime); - QString mimeFor(QString flav); - bool canConvert(const QString &mime, QString flav); - QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); - QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); -}; - -QString QMacPasteboardMimeAny::convertorName() -{ - return QLatin1String("Any-Mime"); -} - -QString QMacPasteboardMimeAny::flavorFor(const QString &mime) -{ - // do not handle the mime type name in the drag pasteboard - if (mime == QLatin1String("application/x-qt-mime-type-name")) - return QString(); - QString ret = QLatin1String("com.trolltech.anymime.") + mime; - return ret.replace(QLatin1Char('/'), QLatin1String("--")); -} - -QString QMacPasteboardMimeAny::mimeFor(QString flav) -{ - const QString any_prefix = QLatin1String("com.trolltech.anymime."); - if (flav.size() > any_prefix.length() && flav.startsWith(any_prefix)) - return flav.mid(any_prefix.length()).replace(QLatin1String("--"), QLatin1String("/")); - return QString(); -} - -bool QMacPasteboardMimeAny::canConvert(const QString &mime, QString flav) -{ - return mimeFor(flav) == mime; -} - -QVariant QMacPasteboardMimeAny::convertToMime(const QString &mime, QList<QByteArray> data, QString) -{ - if (data.count() > 1) - qWarning("QMacPasteboardMimeAny: Cannot handle multiple member data"); - QVariant ret; - if (mime == QLatin1String("text/plain")) - ret = QString::fromUtf8(data.first()); - else - ret = data.first(); - return ret; -} - -QList<QByteArray> QMacPasteboardMimeAny::convertFromMime(const QString &mime, QVariant data, QString) -{ - QList<QByteArray> ret; - if (mime == QLatin1String("text/plain")) - ret.append(data.toString().toUtf8()); - else - ret.append(data.toByteArray()); - return ret; -} - -class QMacPasteboardMimeTypeName : public QMacInternalPasteboardMime { -private: - -public: - QMacPasteboardMimeTypeName() : QMacInternalPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) { - } - ~QMacPasteboardMimeTypeName() { - } - QString convertorName(); - - QString flavorFor(const QString &mime); - QString mimeFor(QString flav); - bool canConvert(const QString &mime, QString flav); - QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); - QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); -}; - -QString QMacPasteboardMimeTypeName::convertorName() -{ - return QLatin1String("Qt-Mime-Type"); -} - -QString QMacPasteboardMimeTypeName::flavorFor(const QString &mime) -{ - if (mime == QLatin1String("application/x-qt-mime-type-name")) - return QLatin1String("com.trolltech.qt.MimeTypeName"); - return QString(); -} - -QString QMacPasteboardMimeTypeName::mimeFor(QString) -{ - return QString(); -} - -bool QMacPasteboardMimeTypeName::canConvert(const QString &, QString) -{ - return false; -} - -QVariant QMacPasteboardMimeTypeName::convertToMime(const QString &, QList<QByteArray>, QString) -{ - QVariant ret; - return ret; -} - -QList<QByteArray> QMacPasteboardMimeTypeName::convertFromMime(const QString &, QVariant, QString) -{ - QList<QByteArray> ret; - ret.append(QString(QLatin1String("x-qt-mime-type-name")).toUtf8()); - return ret; -} - -class QMacPasteboardMimePlainTextFallback : public QMacInternalPasteboardMime { -public: - QMacPasteboardMimePlainTextFallback() : QMacInternalPasteboardMime(MIME_ALL) { } - QString convertorName(); - - QString flavorFor(const QString &mime); - QString mimeFor(QString flav); - bool canConvert(const QString &mime, QString flav); - QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); - QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); -}; - -QString QMacPasteboardMimePlainTextFallback::convertorName() -{ - return QLatin1String("PlainText (public.text)"); -} - -QString QMacPasteboardMimePlainTextFallback::flavorFor(const QString &mime) -{ - if (mime == QLatin1String("text/plain")) - return QLatin1String("public.text"); - return QString(); -} - -QString QMacPasteboardMimePlainTextFallback::mimeFor(QString flav) -{ - if (flav == QLatin1String("public.text")) - return QLatin1String("text/plain"); - return QString(); -} - -bool QMacPasteboardMimePlainTextFallback::canConvert(const QString &mime, QString flav) -{ - return mime == mimeFor(flav); -} - -QVariant QMacPasteboardMimePlainTextFallback::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor) -{ - if (data.count() > 1) - qWarning("QMacPasteboardMimePlainTextFallback: Cannot handle multiple member data"); - - if (flavor == QLatin1String("public.text")) { - // Note that public.text is documented by Apple to have an undefined encoding. From - // testing it seems that utf8 is normally used, at least by Safari on iOS. - const QByteArray &firstData = data.first(); - return QString(QCFString(CFStringCreateWithBytes(kCFAllocatorDefault, - reinterpret_cast<const UInt8 *>(firstData.constData()), - firstData.size(), kCFStringEncodingUTF8, false))); - } else { - qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); - } - return QVariant(); -} - -QList<QByteArray> QMacPasteboardMimePlainTextFallback::convertFromMime(const QString &, QVariant data, QString flavor) -{ - QList<QByteArray> ret; - QString string = data.toString(); - if (flavor == QLatin1String("public.text")) - ret.append(string.toUtf8()); - return ret; -} - -class QMacPasteboardMimeUnicodeText : public QMacInternalPasteboardMime { -public: - QMacPasteboardMimeUnicodeText() : QMacInternalPasteboardMime(MIME_ALL) { } - QString convertorName(); - - QString flavorFor(const QString &mime); - QString mimeFor(QString flav); - bool canConvert(const QString &mime, QString flav); - QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); - QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); -}; - -QString QMacPasteboardMimeUnicodeText::convertorName() -{ - return QLatin1String("UnicodeText"); -} - -QString QMacPasteboardMimeUnicodeText::flavorFor(const QString &mime) -{ - if (mime == QLatin1String("text/plain")) - return QLatin1String("public.utf16-plain-text"); - int i = mime.indexOf(QLatin1String("charset=")); - if (i >= 0) { - QString cs(mime.mid(i+8).toLower()); - i = cs.indexOf(QLatin1Char(';')); - if (i>=0) - cs = cs.left(i); - if (cs == QLatin1String("system")) - return QLatin1String("public.utf8-plain-text"); - else if (cs == QLatin1String("iso-10646-ucs-2") - || cs == QLatin1String("utf16")) - return QLatin1String("public.utf16-plain-text"); - } - return QString(); -} - -QString QMacPasteboardMimeUnicodeText::mimeFor(QString flav) -{ - if (flav == QLatin1String("public.utf16-plain-text") || flav == QLatin1String("public.utf8-plain-text")) - return QLatin1String("text/plain"); - return QString(); -} - -bool QMacPasteboardMimeUnicodeText::canConvert(const QString &mime, QString flav) -{ - return (mime == QLatin1String("text/plain") - && (flav == QLatin1String("public.utf8-plain-text") || (flav == QLatin1String("public.utf16-plain-text")))); -} - -QVariant QMacPasteboardMimeUnicodeText::convertToMime(const QString &mimetype, QList<QByteArray> data, QString flavor) -{ - if (data.count() > 1) - qWarning("QMacPasteboardMimeUnicodeText: Cannot handle multiple member data"); - const QByteArray &firstData = data.first(); - // I can only handle two types (system and unicode) so deal with them that way - QVariant ret; - if (flavor == QLatin1String("public.utf8-plain-text")) { - ret = QString::fromUtf8(firstData); - } else if (flavor == QLatin1String("public.utf16-plain-text")) { - QString str = QStringDecoder(QStringDecoder::Utf16)(firstData); - ret = str; - } else { - qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); - } - return ret; -} - -QList<QByteArray> QMacPasteboardMimeUnicodeText::convertFromMime(const QString &, QVariant data, QString flavor) -{ - QList<QByteArray> ret; - QString string = data.toString(); - if (flavor == QLatin1String("public.utf8-plain-text")) - ret.append(string.toUtf8()); - else if (flavor == QLatin1String("public.utf16-plain-text")) { - QStringEncoder::Flags f; -#if defined(Q_OS_MACOS) - // Some applications such as Microsoft Excel, don't deal well with - // a BOM present, so we follow the traditional approach of Qt on - // macOS to not generate public.utf16-plain-text with a BOM. - f = QStringEncoder::Flag::Default; -#else - // Whereas iOS applications will fail to paste if we do _not_ - // include a BOM in the public.utf16-plain-text content, most - // likely due to converting the data using NSUTF16StringEncoding - // which assumes big-endian byte order if there is no BOM. - f = QStringEncoder::Flag::WriteBom; -#endif - QStringEncoder encoder(QStringEncoder::Utf16, f); - ret.append(encoder(string)); - } - return ret; -} - -class QMacPasteboardMimeHTMLText : public QMacInternalPasteboardMime { -public: - QMacPasteboardMimeHTMLText() : QMacInternalPasteboardMime(MIME_ALL) { } - QString convertorName(); - - QString flavorFor(const QString &mime); - QString mimeFor(QString flav); - bool canConvert(const QString &mime, QString flav); - QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); - QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); -}; - -QString QMacPasteboardMimeHTMLText::convertorName() -{ - return QLatin1String("HTML"); -} - -QString QMacPasteboardMimeHTMLText::flavorFor(const QString &mime) -{ - if (mime == QLatin1String("text/html")) - return QLatin1String("public.html"); - return QString(); -} - -QString QMacPasteboardMimeHTMLText::mimeFor(QString flav) -{ - if (flav == QLatin1String("public.html")) - return QLatin1String("text/html"); - return QString(); -} - -bool QMacPasteboardMimeHTMLText::canConvert(const QString &mime, QString flav) -{ - return flavorFor(mime) == flav; -} - -QVariant QMacPasteboardMimeHTMLText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor) -{ - if (!canConvert(mimeType, flavor)) - return QVariant(); - if (data.count() > 1) - qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data"); - return data.first(); -} - -QList<QByteArray> QMacPasteboardMimeHTMLText::convertFromMime(const QString &mime, QVariant data, QString flavor) -{ - QList<QByteArray> ret; - if (!canConvert(mime, flavor)) - return ret; - ret.append(data.toByteArray()); - return ret; -} - -class QMacPasteboardMimeRtfText : public QMacInternalPasteboardMime { -public: - QMacPasteboardMimeRtfText() : QMacInternalPasteboardMime(MIME_ALL) { } - QString convertorName(); - - QString flavorFor(const QString &mime); - QString mimeFor(QString flav); - bool canConvert(const QString &mime, QString flav); - QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); - QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); -}; - -QString QMacPasteboardMimeRtfText::convertorName() -{ - return QLatin1String("Rtf"); -} - -QString QMacPasteboardMimeRtfText::flavorFor(const QString &mime) -{ - if (mime == QLatin1String("text/html")) - return QLatin1String("public.rtf"); - return QString(); -} - -QString QMacPasteboardMimeRtfText::mimeFor(QString flav) -{ - if (flav == QLatin1String("public.rtf")) - return QLatin1String("text/html"); - return QString(); -} - -bool QMacPasteboardMimeRtfText::canConvert(const QString &mime, QString flav) -{ - return mime == mimeFor(flav); -} - -QVariant QMacPasteboardMimeRtfText::convertToMime(const QString &mimeType, QList<QByteArray> data, QString flavor) -{ - if (!canConvert(mimeType, flavor)) - return QVariant(); - if (data.count() > 1) - qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data"); - - // Read RTF into to NSAttributedString, then convert the string to HTML - NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.at(0).toNSData() - options:@{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType} - documentAttributes:nil - error:nil]; - - NSError *error; - NSRange range = NSMakeRange(0, [string length]); - NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}; - NSData *htmlData = [string dataFromRange:range documentAttributes:dict error:&error]; - return QByteArray::fromNSData(htmlData); -} - -QList<QByteArray> QMacPasteboardMimeRtfText::convertFromMime(const QString &mime, QVariant data, QString flavor) -{ - QList<QByteArray> ret; - if (!canConvert(mime, flavor)) - return ret; - - NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.toByteArray().toNSData() - options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} - documentAttributes:nil - error:nil]; - - NSError *error; - NSRange range = NSMakeRange(0, [string length]); - NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType}; - NSData *rtfData = [string dataFromRange:range documentAttributes:dict error:&error]; - ret << QByteArray::fromNSData(rtfData); - return ret; -} - -class QMacPasteboardMimeFileUri : public QMacInternalPasteboardMime { -public: - QMacPasteboardMimeFileUri() : QMacInternalPasteboardMime(MIME_ALL) { } - QString convertorName(); - - QString flavorFor(const QString &mime); - QString mimeFor(QString flav); - bool canConvert(const QString &mime, QString flav); - QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); - QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); - int count(QMimeData *mimeData); -}; - -QString QMacPasteboardMimeFileUri::convertorName() -{ - return QLatin1String("FileURL"); -} - -QString QMacPasteboardMimeFileUri::flavorFor(const QString &mime) -{ - if (mime == QLatin1String("text/uri-list")) - return QLatin1String("public.file-url"); - return QString(); -} - -QString QMacPasteboardMimeFileUri::mimeFor(QString flav) -{ - if (flav == QLatin1String("public.file-url")) - return QLatin1String("text/uri-list"); - return QString(); -} - -bool QMacPasteboardMimeFileUri::canConvert(const QString &mime, QString flav) -{ - return mime == QLatin1String("text/uri-list") && flav == QLatin1String("public.file-url"); -} - -QVariant QMacPasteboardMimeFileUri::convertToMime(const QString &mime, QList<QByteArray> data, QString flav) -{ - if (!canConvert(mime, flav)) - return QVariant(); - QList<QVariant> ret; - for (int i = 0; i < data.size(); ++i) { - const QByteArray &a = data.at(i); - NSString *urlString = [[[NSString alloc] initWithBytesNoCopy:(void *)a.data() length:a.size() - encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease]; - NSURL *nsurl = [NSURL URLWithString:urlString]; - QUrl url; - // OS X 10.10 sends file references instead of file paths - if ([nsurl isFileReferenceURL]) { - url = QUrl::fromNSURL([nsurl filePathURL]); - } else { - url = QUrl::fromNSURL(nsurl); - } - - if (url.host().toLower() == QLatin1String("localhost")) - url.setHost(QString()); - - url.setPath(url.path().normalized(QString::NormalizationForm_C)); - ret.append(url); - } - return QVariant(ret); -} - -QList<QByteArray> QMacPasteboardMimeFileUri::convertFromMime(const QString &mime, QVariant data, QString flav) -{ - QList<QByteArray> ret; - if (!canConvert(mime, flav)) - return ret; - QList<QVariant> urls = data.toList(); - for (int i = 0; i < urls.size(); ++i) { - QUrl url = urls.at(i).toUrl(); - if (url.scheme().isEmpty()) - url.setScheme(QLatin1String("file")); - if (url.scheme() == QLatin1String("file")) { - if (url.host().isEmpty()) - url.setHost(QLatin1String("localhost")); - url.setPath(url.path().normalized(QString::NormalizationForm_D)); - } - if (url.isLocalFile()) - ret.append(url.toEncoded()); - } - return ret; -} - -int QMacPasteboardMimeFileUri::count(QMimeData *mimeData) -{ - return mimeData->urls().count(); -} - -class QMacPasteboardMimeUrl : public QMacInternalPasteboardMime { -public: - QMacPasteboardMimeUrl() : QMacInternalPasteboardMime(MIME_ALL) { } - QString convertorName(); - - QString flavorFor(const QString &mime); - QString mimeFor(QString flav); - bool canConvert(const QString &mime, QString flav); - QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); - QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); -}; - -QString QMacPasteboardMimeUrl::convertorName() -{ - return QLatin1String("URL"); -} - -QString QMacPasteboardMimeUrl::flavorFor(const QString &mime) -{ - if (mime.startsWith(QLatin1String("text/uri-list"))) - return QLatin1String("public.url"); - return QString(); -} - -QString QMacPasteboardMimeUrl::mimeFor(QString flav) -{ - if (flav == QLatin1String("public.url")) - return QLatin1String("text/uri-list"); - return QString(); -} - -bool QMacPasteboardMimeUrl::canConvert(const QString &mime, QString flav) -{ - return flav == QLatin1String("public.url") - && mime == QLatin1String("text/uri-list"); -} - -QVariant QMacPasteboardMimeUrl::convertToMime(const QString &mime, QList<QByteArray> data, QString flav) -{ - if (!canConvert(mime, flav)) - return QVariant(); - - QList<QVariant> ret; - for (int i=0; i<data.size(); ++i) { - QUrl url = QUrl::fromEncoded(data.at(i)); - if (url.host().toLower() == QLatin1String("localhost")) - url.setHost(QString()); - url.setPath(url.path().normalized(QString::NormalizationForm_C)); - ret.append(url); - } - return QVariant(ret); -} - -QList<QByteArray> QMacPasteboardMimeUrl::convertFromMime(const QString &mime, QVariant data, QString flav) -{ - QList<QByteArray> ret; - if (!canConvert(mime, flav)) - return ret; - - QList<QVariant> urls = data.toList(); - for (int i=0; i<urls.size(); ++i) { - QUrl url = urls.at(i).toUrl(); - if (url.scheme().isEmpty()) - url.setScheme(QLatin1String("file")); - if (url.scheme() == QLatin1String("file")) { - if (url.host().isEmpty()) - url.setHost(QLatin1String("localhost")); - url.setPath(url.path().normalized(QString::NormalizationForm_D)); - } - ret.append(url.toEncoded()); - } - return ret; -} - -class QMacPasteboardMimeVCard : public QMacInternalPasteboardMime -{ -public: - QMacPasteboardMimeVCard() : QMacInternalPasteboardMime(MIME_ALL){ } - QString convertorName(); - - QString flavorFor(const QString &mime); - QString mimeFor(QString flav); - bool canConvert(const QString &mime, QString flav); - QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); - QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); -}; - -QString QMacPasteboardMimeVCard::convertorName() -{ - return QLatin1String("VCard"); -} - -bool QMacPasteboardMimeVCard::canConvert(const QString &mime, QString flav) -{ - return mimeFor(flav) == mime; -} - -QString QMacPasteboardMimeVCard::flavorFor(const QString &mime) -{ - if (mime.startsWith(QLatin1String("text/vcard"))) - return QLatin1String("public.vcard"); - return QString(); -} - -QString QMacPasteboardMimeVCard::mimeFor(QString flav) -{ - if (flav == QLatin1String("public.vcard")) - return QLatin1String("text/vcard"); - return QString(); -} - -QVariant QMacPasteboardMimeVCard::convertToMime(const QString &mime, QList<QByteArray> data, QString) -{ - QByteArray cards; - if (mime == QLatin1String("text/vcard")) { - for (int i=0; i<data.size(); ++i) - cards += data[i]; - } - return QVariant(cards); -} - -QList<QByteArray> QMacPasteboardMimeVCard::convertFromMime(const QString &mime, QVariant data, QString) -{ - QList<QByteArray> ret; - if (mime == QLatin1String("text/vcard")) - ret.append(data.toString().toUtf8()); - return ret; -} - -extern QImage qt_mac_toQImage(CGImageRef image); -extern CGImageRef qt_mac_toCGImage(const QImage &qImage); - -class QMacPasteboardMimeTiff : public QMacInternalPasteboardMime { -public: - QMacPasteboardMimeTiff() : QMacInternalPasteboardMime(MIME_ALL) { } - QString convertorName(); - - QString flavorFor(const QString &mime); - QString mimeFor(QString flav); - bool canConvert(const QString &mime, QString flav); - QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav); - QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav); -}; - -QString QMacPasteboardMimeTiff::convertorName() -{ - return QLatin1String("Tiff"); -} - -QString QMacPasteboardMimeTiff::flavorFor(const QString &mime) -{ - if (mime.startsWith(QLatin1String("application/x-qt-image"))) - return QLatin1String("public.tiff"); - return QString(); -} - -QString QMacPasteboardMimeTiff::mimeFor(QString flav) -{ - if (flav == QLatin1String("public.tiff")) - return QLatin1String("application/x-qt-image"); - return QString(); -} - -bool QMacPasteboardMimeTiff::canConvert(const QString &mime, QString flav) -{ - return flav == QLatin1String("public.tiff") && mime == QLatin1String("application/x-qt-image"); -} - -QVariant QMacPasteboardMimeTiff::convertToMime(const QString &mime, QList<QByteArray> data, QString flav) -{ - if (data.count() > 1) - qWarning("QMacPasteboardMimeTiff: Cannot handle multiple member data"); - - if (!canConvert(mime, flav)) - return QVariant(); - - QCFType<CFDataRef> tiffData = data.first().toRawCFData(); - QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0); - - if (QCFType<CGImageRef> image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0)) - return QVariant(qt_mac_toQImage(image)); - - return QVariant(); -} - -QList<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, QVariant variant, QString flav) -{ - if (!canConvert(mime, flav)) - return QList<QByteArray>(); - - QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0); - QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0); - - if (!imageDestination) - return QList<QByteArray>(); - - QImage img = qvariant_cast<QImage>(variant); - NSDictionary *props = @{ - static_cast<NSString *>(kCGImagePropertyPixelWidth): @(img.width()), - static_cast<NSString *>(kCGImagePropertyPixelHeight): @(img.height()) - }; - - CGImageDestinationAddImage(imageDestination, qt_mac_toCGImage(img), static_cast<CFDictionaryRef>(props)); - CGImageDestinationFinalize(imageDestination); - - return QList<QByteArray>() << QByteArray::fromCFData(data); -} - -/*! - \internal - - This is an internal function. -*/ -void QMacInternalPasteboardMime::initializeMimeTypes() -{ - if (globalMimeList()->isEmpty()) { - // Create QMacPasteboardMimeAny first to put it at the end of globalMimeList - // with lowest priority. (the constructor prepends to the list) - new QMacPasteboardMimeAny; - - //standard types that we wrap - new QMacPasteboardMimeTiff; - new QMacPasteboardMimePlainTextFallback; - new QMacPasteboardMimeUnicodeText; - new QMacPasteboardMimeRtfText; - new QMacPasteboardMimeHTMLText; - new QMacPasteboardMimeFileUri; - new QMacPasteboardMimeUrl; - new QMacPasteboardMimeTypeName; - new QMacPasteboardMimeVCard; - } -} - -/*! - \internal -*/ -void QMacInternalPasteboardMime::destroyMimeTypes() -{ - MimeList *mimes = globalMimeList(); - while (!mimes->isEmpty()) - delete mimes->takeFirst(); -} - -/*! - Returns the most-recently created QMacPasteboardMime of type \a t that can convert - between the \a mime and \a flav formats. Returns 0 if no such convertor - exists. -*/ -QMacInternalPasteboardMime* -QMacInternalPasteboardMime::convertor(uchar t, const QString &mime, QString flav) -{ - MimeList *mimes = globalMimeList(); - for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) { -#ifdef DEBUG_MIME_MAPS - qDebug("QMacPasteboardMime::convertor: seeing if %s (%d) can convert %s to %d[%c%c%c%c] [%d]", - (*it)->convertorName().toLatin1().constData(), - (*it)->type & t, mime.toLatin1().constData(), - flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF, - (*it)->canConvert(mime,flav)); - for (int i = 0; i < (*it)->countFlavors(); ++i) { - int f = (*it)->flavor(i); - qDebug(" %d) %d[%c%c%c%c] [%s]", i, f, - (f >> 24) & 0xFF, (f >> 16) & 0xFF, (f >> 8) & 0xFF, (f) & 0xFF, - (*it)->convertorName().toLatin1().constData()); - } -#endif - if (((*it)->type & t) && (*it)->canConvert(mime, flav)) - return (*it); - } - return 0; -} -/*! - Returns a MIME type of type \a t for \a flav, or 0 if none exists. -*/ -QString QMacInternalPasteboardMime::flavorToMime(uchar t, QString flav) -{ - MimeList *mimes = globalMimeList(); - for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) { -#ifdef DEBUG_MIME_MAPS - qDebug("QMacMIme::flavorToMime: attempting %s (%d) for flavor %d[%c%c%c%c] [%s]", - (*it)->convertorName().toLatin1().constData(), - (*it)->type & t, flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF, - (*it)->mimeFor(flav).toLatin1().constData()); - -#endif - if ((*it)->type & t) { - QString mimeType = (*it)->mimeFor(flav); - if (!mimeType.isNull()) - return mimeType; - } - } - return QString(); -} - -/*! - Returns a list of all currently defined QMacPasteboardMime objects of type \a t. -*/ -QList<QMacInternalPasteboardMime*> QMacInternalPasteboardMime::all(uchar t) -{ - MimeList ret; - MimeList *mimes = globalMimeList(); - for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) { - if ((*it)->type & t) - ret.append((*it)); - } - return ret; -} - - -/*! - \fn QString QMacPasteboardMime::convertorName() - - Returns a name for the convertor. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn bool QMacPasteboardMime::canConvert(const QString &mime, QString flav) - - Returns \c true if the convertor can convert (both ways) between - \a mime and \a flav; otherwise returns \c false. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn QString QMacPasteboardMime::mimeFor(QString flav) - - Returns the MIME UTI used for Mac flavor \a flav, or 0 if this - convertor does not support \a flav. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn QString QMacPasteboardMime::flavorFor(const QString &mime) - - Returns the Mac UTI used for MIME type \a mime, or 0 if this - convertor does not support \a mime. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn QVariant QMacPasteboardMime::convertToMime(const QString &mime, QList<QByteArray> data, QString flav) - - Returns \a data converted from Mac UTI \a flav to MIME type \a - mime. - - Note that Mac flavors must all be self-terminating. The input \a - data may contain trailing data. - - All subclasses must reimplement this pure virtual function. -*/ - -/*! - \fn QList<QByteArray> QMacPasteboardMime::convertFromMime(const QString &mime, QVariant data, QString flav) - - Returns \a data converted from MIME type \a mime - to Mac UTI \a flav. - - Note that Mac flavors must all be self-terminating. The return - value may contain trailing data. - - All subclasses must reimplement this pure virtual function. -*/ - -QT_END_NAMESPACE diff --git a/src/gui/platform/darwin/qmacmime_p.h b/src/gui/platform/darwin/qmacmime_p.h deleted file mode 100644 index 3082683834..0000000000 --- a/src/gui/platform/darwin/qmacmime_p.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QMACMIME_H -#define QMACMIME_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. -// - - -#include <QtGui/private/qtguiglobal_p.h> - -#include <CoreFoundation/CoreFoundation.h> - -QT_BEGIN_NAMESPACE - -// Duplicate of QMacPasteboardMime in QtMacExtras. Keep in sync! -class Q_GUI_EXPORT QMacInternalPasteboardMime { - char type; -public: - enum QMacPasteboardMimeType { MIME_DND=0x01, - MIME_CLIP=0x02, - MIME_QT_CONVERTOR=0x04, - MIME_QT3_CONVERTOR=0x08, - MIME_ALL=MIME_DND|MIME_CLIP - }; - explicit QMacInternalPasteboardMime(char); - virtual ~QMacInternalPasteboardMime(); - - static void initializeMimeTypes(); - static void destroyMimeTypes(); - - static QList<QMacInternalPasteboardMime*> all(uchar); - static QMacInternalPasteboardMime *convertor(uchar, const QString &mime, QString flav); - static QString flavorToMime(uchar, QString flav); - - virtual QString convertorName() = 0; - - virtual bool canConvert(const QString &mime, QString flav) = 0; - virtual QString mimeFor(QString flav) = 0; - virtual QString flavorFor(const QString &mime) = 0; - virtual QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav) = 0; - virtual QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav) = 0; - virtual int count(QMimeData *mimeData); -}; - -Q_GUI_EXPORT void qt_mac_addToGlobalMimeList(QMacInternalPasteboardMime *macMime); -Q_GUI_EXPORT void qt_mac_removeFromGlobalMimeList(QMacInternalPasteboardMime *macMime); -Q_GUI_EXPORT void qt_mac_registerDraggedTypes(const QStringList &types); -Q_GUI_EXPORT const QStringList& qt_mac_enabledDraggedTypes(); - -QT_END_NAMESPACE - -#endif - diff --git a/src/gui/platform/darwin/qmacmimeregistry.mm b/src/gui/platform/darwin/qmacmimeregistry.mm new file mode 100644 index 0000000000..6710a0656f --- /dev/null +++ b/src/gui/platform/darwin/qmacmimeregistry.mm @@ -0,0 +1,118 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtCore/qmimedata.h> + +#include "qutimimeconverter.h" +#include "qmacmimeregistry_p.h" +#include "qguiapplication.h" +#include "private/qcore_mac_p.h" + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +namespace QMacMimeRegistry { + +typedef QList<QUtiMimeConverter*> MimeList; +Q_GLOBAL_STATIC(MimeList, globalMimeList) +Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList) + +// implemented in qutimimeconverter.mm +void registerBuiltInTypes(); + +void registerDraggedTypes(const QStringList &types) +{ + (*globalDraggedTypesList()) += types; +} + +const QStringList& enabledDraggedTypes() +{ + return (*globalDraggedTypesList()); +} + +/***************************************************************************** + QDnD debug facilities + *****************************************************************************/ +//#define DEBUG_MIME_MAPS + +/*! + \class QMacMimeRegistry + \internal + \ingroup draganddrop +*/ + +/*! + \internal + + This is an internal function. +*/ +void initializeMimeTypes() +{ + if (globalMimeList()->isEmpty()) + registerBuiltInTypes(); +} + +/*! + \internal +*/ +void destroyMimeTypes() +{ + MimeList *mimes = globalMimeList(); + while (!mimes->isEmpty()) + delete mimes->takeFirst(); +} + +/* + Returns a MIME type of for scope \a scope for \a uti, or \nullptr if none exists. +*/ +QString flavorToMime(QUtiMimeConverter::HandlerScope scope, const QString &uti) +{ + const MimeList &mimes = *globalMimeList(); + for (const auto &mime : mimes) { + const bool relevantScope = mime->scope() & scope; +#ifdef DEBUG_MIME_MAPS + qDebug("QMacMimeRegistry::flavorToMime: attempting (%d) for uti %s [%s]", + relevantScope, qPrintable(uti), qPrintable((*it)->mimeForUti(uti))); +#endif + if (relevantScope) { + const QString mimeType = mime->mimeForUti(uti); + if (!mimeType.isNull()) + return mimeType; + } + } + return QString(); +} + +void registerMimeConverter(QUtiMimeConverter *macMime) +{ + // globalMimeList is in decreasing priority order. Recently added + // converters take prioity over previously added converters: prepend + // to the list. + globalMimeList()->prepend(macMime); +} + +void unregisterMimeConverter(QUtiMimeConverter *macMime) +{ + if (!QGuiApplication::closingDown()) + globalMimeList()->removeAll(macMime); +} + + +/* + Returns a list of all currently defined QUtiMimeConverter objects for scope \a scope. +*/ +QList<QUtiMimeConverter *> all(QUtiMimeConverter::HandlerScope scope) +{ + MimeList ret; + const MimeList &mimes = *globalMimeList(); + for (const auto &mime : mimes) { + if (mime->scope() & scope) + ret.append(mime); + } + return ret; +} + +} // namespace QMacMimeRegistry + +QT_END_NAMESPACE diff --git a/src/gui/platform/darwin/qmacmimeregistry_p.h b/src/gui/platform/darwin/qmacmimeregistry_p.h new file mode 100644 index 0000000000..5928b81959 --- /dev/null +++ b/src/gui/platform/darwin/qmacmimeregistry_p.h @@ -0,0 +1,42 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QMACMIMEREGISTRY_H +#define QMACMIMEREGISTRY_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. +// + + +#include <QtGui/private/qtguiglobal_p.h> +#include <QtGui/qutimimeconverter.h> + +#include <CoreFoundation/CoreFoundation.h> + +QT_BEGIN_NAMESPACE + +namespace QMacMimeRegistry { + Q_GUI_EXPORT void initializeMimeTypes(); + Q_GUI_EXPORT void destroyMimeTypes(); + + Q_GUI_EXPORT void registerMimeConverter(QUtiMimeConverter *); + Q_GUI_EXPORT void unregisterMimeConverter(QUtiMimeConverter *); + + Q_GUI_EXPORT QList<QUtiMimeConverter *> all(QUtiMimeConverter::HandlerScope scope); + Q_GUI_EXPORT QString flavorToMime(QUtiMimeConverter::HandlerScope scope, const QString &flav); + + Q_GUI_EXPORT void registerDraggedTypes(const QStringList &types); + Q_GUI_EXPORT const QStringList& enabledDraggedTypes(); +}; + +QT_END_NAMESPACE + +#endif // QMACMIMEREGISTRY_H diff --git a/src/gui/platform/darwin/qmetallayer.mm b/src/gui/platform/darwin/qmetallayer.mm new file mode 100644 index 0000000000..e8a27a7b06 --- /dev/null +++ b/src/gui/platform/darwin/qmetallayer.mm @@ -0,0 +1,73 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qmetallayer_p.h" + +#include <QtCore/qreadwritelock.h> +#include <QtCore/qrect.h> + +using namespace std::chrono_literals; + +QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcMetalLayer, "qt.gui.metal") +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +@implementation QMetalLayer +{ + std::unique_ptr<QReadWriteLock> m_displayLock; +} + +- (instancetype)init +{ + if ((self = [super init])) { + m_displayLock.reset(new QReadWriteLock(QReadWriteLock::Recursive)); + self.mainThreadPresentation = nil; + } + + return self; +} + +- (QReadWriteLock &)displayLock +{ + return *m_displayLock.get(); +} + +- (void)setNeedsDisplay +{ + [self setNeedsDisplayInRect:CGRectInfinite]; +} + +- (void)setNeedsDisplayInRect:(CGRect)rect +{ + if (!self.needsDisplay) { + // We lock for writing here, blocking in case a secondary thread is in + // the middle of presenting to the layer, as we want the main thread's + // display to happen after the secondary thread finishes presenting. + qCDebug(lcMetalLayer) << "Locking" << self << "for writing" + << "due to needing display in rect" << QRectF::fromCGRect(rect); + + // For added safety, we use a 5 second timeout, and try to fail + // gracefully by not marking the layer as needing display, as + // doing so would lead us to unlock and unheld lock in displayLayer. + if (!self.displayLock.tryLockForWrite(5s)) { + qCWarning(lcMetalLayer) << "Timed out waiting for display lock"; + return; + } + } + + [super setNeedsDisplayInRect:rect]; +} + +- (id<CAMetalDrawable>)nextDrawable +{ + // Drop the presentation block early, so that if the main thread for + // some reason doesn't handle the presentation, the block won't hold on + // to a drawable unnecessarily. + self.mainThreadPresentation = nil; + return [super nextDrawable]; +} + + +@end diff --git a/src/gui/platform/darwin/qmetallayer_p.h b/src/gui/platform/darwin/qmetallayer_p.h new file mode 100644 index 0000000000..81f8760ec2 --- /dev/null +++ b/src/gui/platform/darwin/qmetallayer_p.h @@ -0,0 +1,41 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QMETALLAYER_P_H +#define QMETALLAYER_P_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. +// + +#include <QtGui/qtguiglobal.h> + +#include <QtCore/qreadwritelock.h> +#include <QtCore/qloggingcategory.h> +#include <QtCore/private/qcore_mac_p.h> + +#include <QuartzCore/CAMetalLayer.h> + +QT_BEGIN_NAMESPACE +class QReadWriteLock; + +QT_DECLARE_EXPORTED_QT_LOGGING_CATEGORY(lcMetalLayer, Q_GUI_EXPORT) + +QT_END_NAMESPACE + +#if defined(__OBJC__) +Q_GUI_EXPORT +#endif +QT_DECLARE_NAMESPACED_OBJC_INTERFACE(QMetalLayer, CAMetalLayer +@property (nonatomic, readonly) QT_PREPEND_NAMESPACE(QReadWriteLock) &displayLock; +@property (atomic, copy) void (^mainThreadPresentation)(); +) + +#endif // QMETALLAYER_P_H diff --git a/src/gui/platform/darwin/qutimimeconverter.h b/src/gui/platform/darwin/qutimimeconverter.h new file mode 100644 index 0000000000..e9297b5fa0 --- /dev/null +++ b/src/gui/platform/darwin/qutimimeconverter.h @@ -0,0 +1,62 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QUTIMIMECONVERTER_H +#define QUTIMIMECONVERTER_H + +#include <QtGui/qtguiglobal.h> + +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +class QByteArray; +class QString; +class QVariant; +class QMimeData; + +class Q_GUI_EXPORT QUtiMimeConverter +{ + Q_DISABLE_COPY(QUtiMimeConverter) +public: + enum class HandlerScopeFlag : uint8_t + { + DnD = 0x01, + Clipboard = 0x02, + Qt_compatible = 0x04, + Qt3_compatible = 0x08, + All = DnD|Clipboard, + AllCompatible = All|Qt_compatible + }; + Q_DECLARE_FLAGS(HandlerScope, HandlerScopeFlag) + + QUtiMimeConverter(); + virtual ~QUtiMimeConverter(); + + HandlerScope scope() const { return m_scope; } + bool canConvert(const QString &mime, const QString &uti) const { return mimeForUti(uti) == mime; } + + // for converting from Qt + virtual QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, const QString &uti) const = 0; + virtual QString utiForMime(const QString &mime) const = 0; + + // for converting to Qt + virtual QString mimeForUti(const QString &uti) const = 0; + virtual QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, const QString &uti) const = 0; + virtual int count(const QMimeData *mimeData) const; + +private: + friend class QMacMimeTypeName; + friend class QMacMimeAny; + + explicit QUtiMimeConverter(HandlerScope scope); + + const HandlerScope m_scope; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QUtiMimeConverter::HandlerScope) + + +QT_END_NAMESPACE + +#endif + diff --git a/src/gui/platform/darwin/qutimimeconverter.mm b/src/gui/platform/darwin/qutimimeconverter.mm new file mode 100644 index 0000000000..ee643fd0c6 --- /dev/null +++ b/src/gui/platform/darwin/qutimimeconverter.mm @@ -0,0 +1,823 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <ImageIO/ImageIO.h> +#include <CoreFoundation/CoreFoundation.h> +#include <UniformTypeIdentifiers/UTCoreTypes.h> + +#include <QtCore/qsystemdetection.h> +#include <QtCore/qurl.h> +#include <QtGui/qimage.h> +#include <QtCore/qmimedata.h> +#include <QtCore/qstringconverter.h> + +#if defined(Q_OS_MACOS) +#import <AppKit/AppKit.h> +#else +#include <MobileCoreServices/MobileCoreServices.h> +#endif + +#if defined(QT_PLATFORM_UIKIT) +#import <UIKit/UIKit.h> +#endif + +#include "qutimimeconverter.h" +#include "qmacmimeregistry_p.h" +#include "qguiapplication.h" +#include "private/qcore_mac_p.h" + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +/***************************************************************************** + QDnD debug facilities + *****************************************************************************/ +//#define DEBUG_MIME_MAPS + +/*! + \class QUtiMimeConverter + \brief The QUtiMimeConverter class converts between a MIME type and a + \l{https://developer.apple.com/documentation/uniformtypeidentifiers} + {Uniform Type Identifier (UTI)} format. + \since 6.5 + + \ingroup draganddrop + \inmodule QtGui + + Qt's drag and drop and clipboard facilities use the MIME + standard. On X11, this maps trivially to the Xdnd protocol. On + Mac, although some applications use MIME to describe clipboard + contents, it is more common to use Apple's UTI format. + + QUtiMimeConverter's role is to bridge the gap between MIME and UTI; + By subclasses this class, one can extend Qt's drag and drop + and clipboard handling to convert to and from unsupported, or proprietary, UTI formats. + + Construct an instance of your converter implementation after instantiating + QGuiApplication: + + \code + int main(int argc, char **argv) + { + QGuiApplication app(argc, argv); + JsonMimeConverter jsonConverter; + } + \endcode + + Destroying the instance will unregister the converter and remove support + for the conversion. It is also valid to heap-allocate the converter + instance; Qt takes ownership and will delete the converter object during + QGuiApplication shut-down. + + Qt has predefined support for the following UTIs: + \list + \li public.utf8-plain-text - converts to "text/plain" + \li public.utf16-plain-text - converts to "text/plain" + \li public.text - converts to "text/plain" + \li public.html - converts to "text/html" + \li public.url - converts to "text/uri-list" + \li public.file-url - converts to "text/uri-list" + \li public.tiff - converts to "application/x-qt-image" + \li public.vcard - converts to "text/plain" + \li com.apple.traditional-mac-plain-text - converts to "text/plain" + \li com.apple.pict - converts to "application/x-qt-image" + \endlist + + When working with MIME data, Qt will iterate through all instances of QUtiMimeConverter to find + find an instance that can convert to, or from, a specific MIME type. It will do this by calling + mimeForUti() or utiForMime() on each instance, starting with (and choosing) the last created + instance first. The actual conversions will be done by using convertToMime() and convertFromMime(). +*/ + +/*! + \enum QUtiMimeConverter::HandlerScope + \internal +*/ + +/*! + \internal + Constructs a new conversion object of type \a scope, adding it to the + globally accessed list of available converters. +*/ +QUtiMimeConverter::QUtiMimeConverter(HandlerScope scope) + : m_scope(scope) +{ + QMacMimeRegistry::registerMimeConverter(this); +} + +/*! + Constructs a new conversion object and adds it to the + globally accessed list of available converters. + + Call this constructor after QGuiApplication has been created. +*/ +QUtiMimeConverter::QUtiMimeConverter() + : QUtiMimeConverter(HandlerScopeFlag::All) +{ +} + +/*! + Destroys a conversion object, removing it from the global + list of available converters. +*/ +QUtiMimeConverter::~QUtiMimeConverter() +{ + QMacMimeRegistry::unregisterMimeConverter(this); +} + +/*! + Returns the item count for the given \a mimeData +*/ +int QUtiMimeConverter::count(const QMimeData *mimeData) const +{ + Q_UNUSED(mimeData); + return 1; +} + +/*! + \fn bool QUtiMimeConverter::canConvert(const QString &mime, const QString &uti) const + + Returns \c true if the converter can convert (both ways) between + \a mime and \a uti; otherwise returns \c false. +*/ + +/*! + \fn QString QUtiMimeConverter::mimeForUti(const QString &uti) const + + Returns the MIME type used for Mac UTI \a uti, or an empty string if + this converter does not support converting from \a uti. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QString QUtiMimeConverter::utiForMime(const QString &mime) const + + Returns the Mac UTI used for MIME type \a mime, or an empty string if + this converter does not support converting from \a mime. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QVariant QUtiMimeConverter::convertToMime(const QString &mime, + const QList<QByteArray> &data, const QString &uti) const + + Returns \a data converted from Mac UTI \a uti to MIME type \a mime. + + Note that Mac UTIs must all be self-terminating. The input \a data + may contain trailing data. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QList<QByteArray> QUtiMimeConverter::convertFromMime(const QString &mime, + const QVariant &data, const QString & uti) const + + Returns \a data converted from MIME type \a mime to Mac UTI \a uti. + + Note that Mac UTIs must all be self-terminating. The return + value may contain trailing data. + + All subclasses must reimplement this pure virtual function. +*/ + + +class QMacMimeAny : public QUtiMimeConverter { +public: + QMacMimeAny() : QUtiMimeConverter(HandlerScopeFlag::AllCompatible) {} + + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, + const QString &uti) const override; + QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeAny::utiForMime(const QString &mime) const +{ + // do not handle the mime type name in the drag pasteboard + if (mime == "application/x-qt-mime-type-name"_L1) + return QString(); + QString ret = "com.trolltech.anymime."_L1 + mime; + return ret.replace(u'/', "--"_L1); +} + +QString QMacMimeAny::mimeForUti(const QString &uti) const +{ + const QString any_prefix = "com.trolltech.anymime."_L1; + if (uti.size() > any_prefix.length() && uti.startsWith(any_prefix)) + return uti.mid(any_prefix.length()).replace("--"_L1, "/"_L1); + return QString(); +} + +QVariant QMacMimeAny::convertToMime(const QString &mime, const QList<QByteArray> &data, + const QString &) const +{ + if (data.count() > 1) + qWarning("QMacMimeAny: Cannot handle multiple member data"); + QVariant ret; + if (mime == "text/plain"_L1) + ret = QString::fromUtf8(data.first()); + else + ret = data.first(); + return ret; +} + +QList<QByteArray> QMacMimeAny::convertFromMime(const QString &mime, const QVariant &data, + const QString &) const +{ + QList<QByteArray> ret; + if (mime == "text/plain"_L1) + ret.append(data.toString().toUtf8()); + else + ret.append(data.toByteArray()); + return ret; +} + +class QMacMimeTypeName : public QUtiMimeConverter { +private: + +public: + QMacMimeTypeName(): QUtiMimeConverter(HandlerScopeFlag::AllCompatible) {} + + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, const QString &uti) const override; + QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, const QString &uti) const override; +}; + +QString QMacMimeTypeName::utiForMime(const QString &mime) const +{ + if (mime == "application/x-qt-mime-type-name"_L1) + return u"com.trolltech.qt.MimeTypeName"_s; + return QString(); +} + +QString QMacMimeTypeName::mimeForUti(const QString &) const +{ + return QString(); +} + +QVariant QMacMimeTypeName::convertToMime(const QString &, const QList<QByteArray> &, const QString &) const +{ + QVariant ret; + return ret; +} + +QList<QByteArray> QMacMimeTypeName::convertFromMime(const QString &, const QVariant &, const QString &) const +{ + QList<QByteArray> ret; + ret.append(QString("x-qt-mime-type-name"_L1).toUtf8()); + return ret; +} + +class QMacMimePlainTextFallback : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, + const QString &uti) const override; + QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimePlainTextFallback::utiForMime(const QString &mime) const +{ + if (mime == "text/plain"_L1) + return "public.text"_L1; + return QString(); +} + +QString QMacMimePlainTextFallback::mimeForUti(const QString &uti) const +{ + if (uti == "public.text"_L1) + return "text/plain"_L1; + return QString(); +} + +QVariant +QMacMimePlainTextFallback::convertToMime(const QString &mimetype, + const QList<QByteArray> &data, const QString &uti) const +{ + if (data.count() > 1) + qWarning("QMacMimePlainTextFallback: Cannot handle multiple member data"); + + if (uti == "public.text"_L1) { + // Note that public.text is documented by Apple to have an undefined encoding. From + // testing it seems that utf8 is normally used, at least by Safari on iOS. + const QByteArray &firstData = data.first(); + return QString(QCFString(CFStringCreateWithBytes(kCFAllocatorDefault, + reinterpret_cast<const UInt8 *>(firstData.constData()), + firstData.size(), kCFStringEncodingUTF8, false))); + } else { + qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); + } + return QVariant(); +} + +QList<QByteArray> +QMacMimePlainTextFallback::convertFromMime(const QString &, const QVariant &data, + const QString &uti) const +{ + QList<QByteArray> ret; + QString string = data.toString(); + if (uti == "public.text"_L1) + ret.append(string.toUtf8()); + return ret; +} + +class QMacMimeUnicodeText : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, + const QString &uti) const override; + QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeUnicodeText::utiForMime(const QString &mime) const +{ + if (mime == "text/plain"_L1) + return "public.utf16-plain-text"_L1; + if (qsizetype i = mime.indexOf("charset="_L1); i >= 0) { + QString cs(mime.mid(i + 8).toLower()); + i = cs.indexOf(u';'); + if (i >= 0) + cs = cs.left(i); + if (cs == "system"_L1) + return "public.utf8-plain-text"_L1; + else if (cs == "iso-10646-ucs-2"_L1 || cs == "utf16"_L1) + return "public.utf16-plain-text"_L1; + } + return QString(); +} + +QString QMacMimeUnicodeText::mimeForUti(const QString &uti) const +{ + if (uti == "public.utf16-plain-text"_L1 || uti == "public.utf8-plain-text"_L1) + return "text/plain"_L1; + return QString(); +} + +QVariant +QMacMimeUnicodeText::convertToMime(const QString &mimetype, + const QList<QByteArray> &data, const QString &uti) const +{ + if (data.count() > 1) + qWarning("QMacMimeUnicodeText: Cannot handle multiple member data"); + const QByteArray &firstData = data.first(); + // I can only handle two types (system and unicode) so deal with them that way + QVariant ret; + if (uti == "public.utf8-plain-text"_L1) { + ret = QString::fromUtf8(firstData); + } else if (uti == "public.utf16-plain-text"_L1) { + QString str = QStringDecoder(QStringDecoder::Utf16)(firstData); + ret = str; + } else { + qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); + } + return ret; +} + +QList<QByteArray> +QMacMimeUnicodeText::convertFromMime(const QString &, const QVariant &data, + const QString &uti) const +{ + QList<QByteArray> ret; + QString string = data.toString(); + if (uti == "public.utf8-plain-text"_L1) + ret.append(string.toUtf8()); + else if (uti == "public.utf16-plain-text"_L1) { + QStringEncoder::Flags f; +#if defined(Q_OS_MACOS) + // Some applications such as Microsoft Excel, don't deal well with + // a BOM present, so we follow the traditional approach of Qt on + // macOS to not generate public.utf16-plain-text with a BOM. + f = QStringEncoder::Flag::Default; +#else + // Whereas iOS applications will fail to paste if we do _not_ + // include a BOM in the public.utf16-plain-text content, most + // likely due to converting the data using NSUTF16StringEncoding + // which assumes big-endian byte order if there is no BOM. + f = QStringEncoder::Flag::WriteBom; +#endif + QStringEncoder encoder(QStringEncoder::Utf16, f); + ret.append(encoder(string)); + } + return ret; +} + +class QMacMimeHTMLText : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, + const QString &uti) const override; + QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeHTMLText::utiForMime(const QString &mime) const +{ + if (mime == "text/html"_L1) + return "public.html"_L1; + return QString(); +} + +QString QMacMimeHTMLText::mimeForUti(const QString &uti) const +{ + if (uti == "public.html"_L1) + return "text/html"_L1; + return QString(); +} + +QVariant +QMacMimeHTMLText::convertToMime(const QString &mimeType, + const QList<QByteArray> &data, const QString &uti) const +{ + if (!canConvert(mimeType, uti)) + return QVariant(); + if (data.count() > 1) + qWarning("QMacMimeHTMLText: Cannot handle multiple member data"); + return data.first(); +} + +QList<QByteArray> +QMacMimeHTMLText::convertFromMime(const QString &mime, + const QVariant &data, const QString &uti) const +{ + QList<QByteArray> ret; + if (!canConvert(mime, uti)) + return ret; + ret.append(data.toByteArray()); + return ret; +} + +class QMacMimeRtfText : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, + const QString &uti) const override; + QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeRtfText::utiForMime(const QString &mime) const +{ + if (mime == "text/html"_L1) + return "public.rtf"_L1; + return QString(); +} + +QString QMacMimeRtfText::mimeForUti(const QString &uti) const +{ + if (uti == "public.rtf"_L1) + return "text/html"_L1; + return QString(); +} + +QVariant +QMacMimeRtfText::convertToMime(const QString &mimeType, + const QList<QByteArray> &data, const QString &uti) const +{ + if (!canConvert(mimeType, uti)) + return QVariant(); + if (data.count() > 1) + qWarning("QMacMimeHTMLText: Cannot handle multiple member data"); + + // Read RTF into to NSAttributedString, then convert the string to HTML + NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.at(0).toNSData() + options:@{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType} + documentAttributes:nil + error:nil]; + + NSError *error; + NSRange range = NSMakeRange(0, [string length]); + NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}; + NSData *htmlData = [string dataFromRange:range documentAttributes:dict error:&error]; + return QByteArray::fromNSData(htmlData); +} + +QList<QByteArray> +QMacMimeRtfText::convertFromMime(const QString &mime, + const QVariant &data, const QString &uti) const +{ + QList<QByteArray> ret; + if (!canConvert(mime, uti)) + return ret; + + NSAttributedString *string = [[NSAttributedString alloc] initWithData:data.toByteArray().toNSData() + options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} + documentAttributes:nil + error:nil]; + + NSError *error; + NSRange range = NSMakeRange(0, [string length]); + NSDictionary *dict = @{NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType}; + NSData *rtfData = [string dataFromRange:range documentAttributes:dict error:&error]; + ret << QByteArray::fromNSData(rtfData); + return ret; +} + +class QMacMimeFileUri : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, + const QString &uti) const override; + QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; + int count(const QMimeData *mimeData) const override; +}; + +QString QMacMimeFileUri::utiForMime(const QString &mime) const +{ + if (mime == "text/uri-list"_L1) + return "public.file-url"_L1; + return QString(); +} + +QString QMacMimeFileUri::mimeForUti(const QString &uti) const +{ + if (uti == "public.file-url"_L1) + return "text/uri-list"_L1; + return QString(); +} + +QVariant +QMacMimeFileUri::convertToMime(const QString &mime, + const QList<QByteArray> &data, const QString &uti) const +{ + if (!canConvert(mime, uti)) + return QVariant(); + QList<QVariant> ret; + for (int i = 0; i < data.size(); ++i) { + const QByteArray &a = data.at(i); + NSString *urlString = [[[NSString alloc] initWithBytesNoCopy:(void *)a.data() length:a.size() + encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease]; + NSURL *nsurl = [NSURL URLWithString:urlString]; + QUrl url; + // OS X 10.10 sends file references instead of file paths + if ([nsurl isFileReferenceURL]) { + url = QUrl::fromNSURL([nsurl filePathURL]); + } else { + url = QUrl::fromNSURL(nsurl); + } + + if (url.host().toLower() == "localhost"_L1) + url.setHost(QString()); + + url.setPath(url.path().normalized(QString::NormalizationForm_C)); + ret.append(url); + } + return QVariant(ret); +} + +QList<QByteArray> +QMacMimeFileUri::convertFromMime(const QString &mime, + const QVariant &data, const QString &uti) const +{ + QList<QByteArray> ret; + if (!canConvert(mime, uti)) + return ret; + QList<QVariant> urls = data.toList(); + for (int i = 0; i < urls.size(); ++i) { + QUrl url = urls.at(i).toUrl(); + if (url.scheme().isEmpty()) + url.setScheme("file"_L1); + if (url.scheme() == "file"_L1) { + if (url.host().isEmpty()) + url.setHost("localhost"_L1); + url.setPath(url.path().normalized(QString::NormalizationForm_D)); + } + if (url.isLocalFile()) + ret.append(url.toEncoded()); + } + return ret; +} + +int QMacMimeFileUri::count(const QMimeData *mimeData) const +{ + return mimeData->urls().count(); +} + +class QMacMimeUrl : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, + const QString &uti) const override; + QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeUrl::utiForMime(const QString &mime) const +{ + if (mime.startsWith("text/uri-list"_L1)) + return "public.url"_L1; + return QString(); +} + +QString QMacMimeUrl::mimeForUti(const QString &uti) const +{ + if (uti == "public.url"_L1) + return "text/uri-list"_L1; + return QString(); +} + +QVariant QMacMimeUrl::convertToMime(const QString &mime, + const QList<QByteArray> &data, const QString &uti) const +{ + if (!canConvert(mime, uti)) + return QVariant(); + + QList<QVariant> ret; + for (int i=0; i<data.size(); ++i) { + QUrl url = QUrl::fromEncoded(data.at(i)); + if (url.host().toLower() == "localhost"_L1) + url.setHost(QString()); + url.setPath(url.path().normalized(QString::NormalizationForm_C)); + ret.append(url); + } + return QVariant(ret); +} + +QList<QByteArray> QMacMimeUrl::convertFromMime(const QString &mime, + const QVariant &data, const QString &uti) const +{ + QList<QByteArray> ret; + if (!canConvert(mime, uti)) + return ret; + + QList<QVariant> urls = data.toList(); + for (int i=0; i<urls.size(); ++i) { + QUrl url = urls.at(i).toUrl(); + if (url.scheme().isEmpty()) + url.setScheme("file"_L1); + if (url.scheme() == "file"_L1) { + if (url.host().isEmpty()) + url.setHost("localhost"_L1); + url.setPath(url.path().normalized(QString::NormalizationForm_D)); + } + ret.append(url.toEncoded()); + } + return ret; +} + +class QMacMimeVCard : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, + const QString &uti) const override; + QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeVCard::utiForMime(const QString &mime) const +{ + if (mime.startsWith("text/vcard"_L1)) + return "public.vcard"_L1; + return QString(); +} + +QString QMacMimeVCard::mimeForUti(const QString &uti) const +{ + if (uti == "public.vcard"_L1) + return "text/vcard"_L1; + return QString(); +} + +QVariant QMacMimeVCard::convertToMime(const QString &mime, + const QList<QByteArray> &data, const QString &uti) const +{ + if (!canConvert(mime, uti)) + return QVariant(); + QByteArray cards; + if (uti == "public.vcard"_L1) { + for (int i=0; i<data.size(); ++i) + cards += data[i]; + } + return QVariant(cards); +} + +QList<QByteArray> QMacMimeVCard::convertFromMime(const QString &mime, + const QVariant &data, const QString &uti) const +{ + QList<QByteArray> ret; + if (!canConvert(mime, uti)) + return ret; + + if (mime == "text/vcard"_L1) + ret.append(data.toString().toUtf8()); + return ret; +} + +extern QImage qt_mac_toQImage(CGImageRef image); +extern CGImageRef qt_mac_toCGImage(const QImage &qImage); + +class QMacMimeTiff : public QUtiMimeConverter +{ +public: + QString utiForMime(const QString &mime) const override; + QString mimeForUti(const QString &uti) const override; + QVariant convertToMime(const QString &mime, const QList<QByteArray> &data, + const QString &uti) const override; + QList<QByteArray> convertFromMime(const QString &mime, const QVariant &data, + const QString &uti) const override; +}; + +QString QMacMimeTiff::utiForMime(const QString &mime) const +{ + if (mime.startsWith("application/x-qt-image"_L1)) + return "public.tiff"_L1; + return QString(); +} + +QString QMacMimeTiff::mimeForUti(const QString &uti) const +{ + if (uti == "public.tiff"_L1) + return "application/x-qt-image"_L1; + return QString(); +} + +QVariant QMacMimeTiff::convertToMime(const QString &mime, + const QList<QByteArray> &data, const QString &uti) const +{ + if (data.count() > 1) + qWarning("QMacMimeTiff: Cannot handle multiple member data"); + + if (!canConvert(mime, uti)) + return QVariant(); + + QCFType<CFDataRef> tiffData = data.first().toRawCFData(); + QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0); + + if (QCFType<CGImageRef> image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0)) + return QVariant(qt_mac_toQImage(image)); + + return QVariant(); +} + +QList<QByteArray> QMacMimeTiff::convertFromMime(const QString &mime, + const QVariant &variant, const QString &uti) const +{ + if (!canConvert(mime, uti)) + return QList<QByteArray>(); + + QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0); + QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, + (CFStringRef)UTTypeTIFF.identifier, 1, 0); + + if (!imageDestination) + return QList<QByteArray>(); + + QImage img = qvariant_cast<QImage>(variant); + NSDictionary *props = @{ + static_cast<NSString *>(kCGImagePropertyPixelWidth): @(img.width()), + static_cast<NSString *>(kCGImagePropertyPixelHeight): @(img.height()) + }; + + CGImageDestinationAddImage(imageDestination, qt_mac_toCGImage(img), + static_cast<CFDictionaryRef>(props)); + CGImageDestinationFinalize(imageDestination); + + return QList<QByteArray>() << QByteArray::fromCFData(data); +} + +namespace QMacMimeRegistry { + +void registerBuiltInTypes() +{ + // Create QMacMimeAny first to put it at the end of globalMimeList + // with lowest priority. (the constructor prepends to the list) + new QMacMimeAny; + + //standard types that we wrap + new QMacMimeTiff; + new QMacMimePlainTextFallback; + new QMacMimeUnicodeText; + new QMacMimeRtfText; + new QMacMimeHTMLText; + new QMacMimeFileUri; + new QMacMimeUrl; + new QMacMimeTypeName; + new QMacMimeVCard; +} + +} + +QT_END_NAMESPACE diff --git a/src/gui/platform/ios/PrivacyInfo.xcprivacy b/src/gui/platform/ios/PrivacyInfo.xcprivacy new file mode 100644 index 0000000000..bde2b167c7 --- /dev/null +++ b/src/gui/platform/ios/PrivacyInfo.xcprivacy @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>NSPrivacyTracking</key> + <false/> + <key>NSPrivacyCollectedDataTypes</key> + <array/> + <key>NSPrivacyTrackingDomains</key> + <array/> + <key>NSPrivacyAccessedAPITypes</key> + <array> + <dict> + <key>NSPrivacyAccessedAPIType</key> + <string>NSPrivacyAccessedAPICategorySystemBootTime</string> + <key>NSPrivacyAccessedAPITypeReasons</key> + <array> + <string>35F9.1</string> <!-- QUIView event handling --> + </array> + </dict> + </array> +</dict> +</plist> diff --git a/src/gui/platform/ios/qiosnativeinterface.cpp b/src/gui/platform/ios/qiosnativeinterface.cpp new file mode 100644 index 0000000000..c942709e33 --- /dev/null +++ b/src/gui/platform/ios/qiosnativeinterface.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtGui/private/qguiapplication_p.h> + +QT_BEGIN_NAMESPACE + +using namespace QNativeInterface::Private; + +#if defined(Q_OS_VISIONOS) + +/*! + \class QNativeInterface::QVisionOSApplication + \since 6.8 + \internal + \preliminary + \brief Native interface to QGuiApplication, to be retrieved from QPlatformIntegration. + \inmodule QtGui + \ingroup native-interfaces +*/ + +QT_DEFINE_NATIVE_INTERFACE(QVisionOSApplication); + +#endif // Q_OS_VISIONOS + +QT_END_NAMESPACE diff --git a/src/gui/platform/macos/macos.pri b/src/gui/platform/macos/macos.pri deleted file mode 100644 index f0107c6944..0000000000 --- a/src/gui/platform/macos/macos.pri +++ /dev/null @@ -1,4 +0,0 @@ -SOURCES += \ - $$PWD/qcocoaplatforminterface.mm - -LIBS_PRIVATE += -framework AppKit diff --git a/src/gui/platform/macos/qcocoanativeinterface.mm b/src/gui/platform/macos/qcocoanativeinterface.mm new file mode 100644 index 0000000000..cb6acb4496 --- /dev/null +++ b/src/gui/platform/macos/qcocoanativeinterface.mm @@ -0,0 +1,86 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtGui/qtgui-config.h> +#ifndef QT_NO_OPENGL +# include <QtGui/private/qopenglcontext_p.h> +#endif + +#include <QtGui/private/qguiapplication_p.h> +#include <qpa/qplatformopenglcontext.h> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformwindow_p.h> +#include <qpa/qplatformmenu_p.h> + +#include <AppKit/AppKit.h> + +QT_BEGIN_NAMESPACE + +using namespace QNativeInterface::Private; + +/*! + \class QNativeInterface::Private::QCocoaWindow + \since 6.0 + \internal + \brief Native interface for QPlatformWindow on \macos. + \inmodule QtGui + \ingroup native-interfaces +*/ + +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QCocoaWindow); + + +/*! + \class QNativeInterface::Private::QCocoaMenu + \since 6.0 + \internal + \brief Native interface for QPlatformMenu on \macos. + \inmodule QtGui + \ingroup native-interfaces +*/ + +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QCocoaMenu); +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QCocoaMenuBar); + +#ifndef QT_NO_OPENGL + +/*! + \class QNativeInterface::QCocoaGLContext + \since 6.0 + \brief Native interface to an NSOpenGLContext on \macos. + + Accessed through QOpenGLContext::nativeInterface(). + + \inmodule QtGui + \inheaderfile QOpenGLContext + \ingroup native-interfaces + \ingroup native-interfaces-qopenglcontext +*/ + +/*! + \fn QOpenGLContext *QNativeInterface::QCocoaGLContext::fromNative(NSOpenGLContext *context, QOpenGLContext *shareContext = nullptr) + + \brief Adopts an NSOpenGLContext. + + The adopted NSOpenGLContext \a context is retained. Ownership of the + created QOpenGLContext \a shareContext is transferred to the caller. +*/ + +/*! + \fn NSOpenGLContext *QNativeInterface::QCocoaGLContext::nativeContext() const + + \return the underlying NSOpenGLContext. +*/ + +QT_DEFINE_NATIVE_INTERFACE(QCocoaGLContext); +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QCocoaGLIntegration); + +QOpenGLContext *QNativeInterface::QCocoaGLContext::fromNative(NSOpenGLContext *nativeContext, QOpenGLContext *shareContext) +{ + return QGuiApplicationPrivate::platformIntegration()->call< + &QCocoaGLIntegration::createOpenGLContext>(nativeContext, shareContext); +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE diff --git a/src/gui/platform/macos/qcocoaplatforminterface.mm b/src/gui/platform/macos/qcocoaplatforminterface.mm deleted file mode 100644 index 2fc9228ab1..0000000000 --- a/src/gui/platform/macos/qcocoaplatforminterface.mm +++ /dev/null @@ -1,64 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtGui/private/qopenglcontext_p.h> -#include <QtGui/private/qguiapplication_p.h> -#include <qpa/qplatformopenglcontext.h> -#include <qpa/qplatformintegration.h> - -#include <AppKit/AppKit.h> - -QT_BEGIN_NAMESPACE - -using namespace QPlatformInterface::Private; - -#ifndef QT_NO_OPENGL - -QT_DEFINE_PLATFORM_INTERFACE(QCocoaGLContext, QOpenGLContext); -QT_DEFINE_PRIVATE_PLATFORM_INTERFACE(QCocoaGLIntegration); - -QOpenGLContext *QPlatformInterface::QCocoaGLContext::fromNative(NSOpenGLContext *nativeContext, QOpenGLContext *shareContext) -{ - return QGuiApplicationPrivate::platformIntegration()->call< - &QCocoaGLIntegration::createOpenGLContext>(nativeContext, shareContext); -} - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE diff --git a/src/gui/platform/platform.pri b/src/gui/platform/platform.pri deleted file mode 100644 index 71cffb403e..0000000000 --- a/src/gui/platform/platform.pri +++ /dev/null @@ -1,5 +0,0 @@ -wasm:include(wasm/wasm.pri) -darwin:include(darwin/darwin.pri) -win32:include(windows/windows.pri) -unix:include(unix/unix.pri) -macos:include(macos/macos.pri) diff --git a/src/gui/platform/unix/dbusmenu/dbusmenu.pri b/src/gui/platform/unix/dbusmenu/dbusmenu.pri deleted file mode 100644 index c328f23144..0000000000 --- a/src/gui/platform/unix/dbusmenu/dbusmenu.pri +++ /dev/null @@ -1,15 +0,0 @@ -HEADERS += \ - platform/unix/dbusmenu/qdbusmenuadaptor_p.h \ - platform/unix/dbusmenu/qdbusmenutypes_p.h \ - platform/unix/dbusmenu/qdbusmenuconnection_p.h \ - platform/unix/dbusmenu/qdbusmenubar_p.h \ - platform/unix/dbusmenu/qdbusmenuregistrarproxy_p.h \ - platform/unix/dbusmenu/qdbusplatformmenu_p.h - -SOURCES += \ - platform/unix/dbusmenu/qdbusmenuadaptor.cpp \ - platform/unix/dbusmenu/qdbusmenutypes.cpp \ - platform/unix/dbusmenu/qdbusmenuconnection.cpp \ - platform/unix/dbusmenu/qdbusmenubar.cpp \ - platform/unix/dbusmenu/qdbusmenuregistrarproxy.cpp \ - platform/unix/dbusmenu/qdbusplatformmenu.cpp diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenuadaptor.cpp b/src/gui/platform/unix/dbusmenu/qdbusmenuadaptor.cpp index eabb4b4122..a3b7b6db00 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusmenuadaptor.cpp +++ b/src/gui/platform/unix/dbusmenu/qdbusmenuadaptor.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /* This file was originally created by qdbusxml2cpp version 0.8 @@ -45,18 +9,22 @@ However it is maintained manually. */ -#include "qdbusmenuadaptor_p.h" -#include "qdbusplatformmenu_p.h" -#include <QtCore/QMetaObject> -#include <QtCore/QByteArray> -#include <QtCore/QList> -#include <QtCore/QMap> -#include <QtCore/QString> -#include <QtCore/QStringList> -#include <QtCore/QVariant> +#include <QMetaObject> +#include <QByteArray> +#include <QList> +#include <QMap> +#include <QString> +#include <QStringList> +#include <QVariant> +#include <QLocale> + +#include <private/qdbusmenuadaptor_p.h> +#include <private/qdbusplatformmenu_p.h> QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + QDBusMenuAdaptor::QDBusMenuAdaptor(QDBusPlatformMenu *topLevelMenu) : QDBusAbstractAdaptor(topLevelMenu) , m_topLevelMenu(topLevelMenu) @@ -71,12 +39,12 @@ QDBusMenuAdaptor::~QDBusMenuAdaptor() QString QDBusMenuAdaptor::status() const { qCDebug(qLcMenu); - return QLatin1String("normal"); + return "normal"_L1; } QString QDBusMenuAdaptor::textDirection() const { - return QLocale().textDirection() == Qt::RightToLeft ? QLatin1String("rtl") : QLatin1String("ltr"); + return QLocale().textDirection() == Qt::RightToLeft ? "rtl"_L1 : "ltr"_L1; } uint QDBusMenuAdaptor::version() const @@ -103,7 +71,7 @@ bool QDBusMenuAdaptor::AboutToShow(int id) QList<int> QDBusMenuAdaptor::AboutToShowGroup(const QList<int> &ids, QList<int> &idErrors) { qCDebug(qLcMenu) << ids; - Q_UNUSED(idErrors) + Q_UNUSED(idErrors); idErrors.clear(); for (int id : ids) AboutToShow(id); @@ -112,15 +80,15 @@ QList<int> QDBusMenuAdaptor::AboutToShowGroup(const QList<int> &ids, QList<int> void QDBusMenuAdaptor::Event(int id, const QString &eventId, const QDBusVariant &data, uint timestamp) { - Q_UNUSED(data) - Q_UNUSED(timestamp) + Q_UNUSED(data); + Q_UNUSED(timestamp); QDBusPlatformMenuItem *item = QDBusPlatformMenuItem::byId(id); - qCDebug(qLcMenu) << id << (item ? item->text() : QLatin1String("")) << eventId; - if (item && eventId == QLatin1String("clicked")) + qCDebug(qLcMenu) << id << (item ? item->text() : ""_L1) << eventId; + if (item && eventId == "clicked"_L1) item->trigger(); - if (item && eventId == QLatin1String("hovered")) + if (item && eventId == "hovered"_L1) emit item->hovered(); - if (eventId == QLatin1String("closed")) { + if (eventId == "closed"_L1) { // There is no explicit AboutToHide method, so map closed event to aboutToHide method const QDBusPlatformMenu *menu = nullptr; if (item) @@ -161,3 +129,5 @@ QDBusVariant QDBusMenuAdaptor::GetProperty(int id, const QString &name) } QT_END_NAMESPACE + +#include "moc_qdbusmenuadaptor_p.cpp" diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenuadaptor_p.h b/src/gui/platform/unix/dbusmenu/qdbusmenuadaptor_p.h index 6612f019a7..b6f538f6b3 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusmenuadaptor_p.h +++ b/src/gui/platform/unix/dbusmenu/qdbusmenuadaptor_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtDBus module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /* This file was originally created by qdbusxml2cpp version 0.8 @@ -62,9 +26,10 @@ // We mean it. // -#include <QtCore/QObject> -#include <QtDBus/QtDBus> -#include "qdbusmenutypes_p.h" +#include <QObject> +#include <QDBusAbstractAdaptor> + +#include <private/qdbusmenutypes_p.h> QT_BEGIN_NAMESPACE diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenubar.cpp b/src/gui/platform/unix/dbusmenu/qdbusmenubar.cpp index b13c875854..2c006366cb 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusmenubar.cpp +++ b/src/gui/platform/unix/dbusmenu/qdbusmenubar.cpp @@ -1,52 +1,18 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qdbusmenubar_p.h" #include "qdbusmenuregistrarproxy_p.h" QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + /* note: do not change these to QStringLiteral; we are unloaded before QtDBus is done using the strings. */ -#define REGISTRAR_SERVICE QLatin1String("com.canonical.AppMenu.Registrar") -#define REGISTRAR_PATH QLatin1String("/com/canonical/AppMenu/Registrar") +#define REGISTRAR_SERVICE "com.canonical.AppMenu.Registrar"_L1 +#define REGISTRAR_PATH "/com/canonical/AppMenu/Registrar"_L1 QDBusMenuBar::QDBusMenuBar() : QPlatformMenuBar() @@ -177,3 +143,5 @@ void QDBusMenuBar::unregisterMenuBar() } QT_END_NAMESPACE + +#include "moc_qdbusmenubar_p.cpp" diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenubar_p.h b/src/gui/platform/unix/dbusmenu/qdbusmenubar_p.h index 364e7da4b6..3028df7215 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusmenubar_p.h +++ b/src/gui/platform/unix/dbusmenu/qdbusmenubar_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QDBUSMENUBAR_P_H #define QDBUSMENUBAR_P_H diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenuconnection.cpp b/src/gui/platform/unix/dbusmenu/qdbusmenuconnection.cpp index 429460f9e8..e5506e9718 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusmenuconnection.cpp +++ b/src/gui/platform/unix/dbusmenu/qdbusmenuconnection.cpp @@ -1,65 +1,30 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtGui/qtgui-config.h> +#include <QDBusMessage> +#include <QDBusInterface> +#include <QDBusServiceWatcher> +#include <QDBusConnectionInterface> +#include <QDebug> +#include <QCoreApplication> + #ifndef QT_NO_SYSTEMTRAYICON -#include "../dbustray/qdbustrayicon_p.h" +#include <private/qdbustrayicon_p.h> #endif -#include "qdbusmenuconnection_p.h" -#include "qdbusmenuadaptor_p.h" -#include "qdbusplatformmenu_p.h" - -#include <QtDBus/QDBusMessage> -#include <QtDBus/QDBusServiceWatcher> -#include <QtDBus/QDBusConnectionInterface> -#include <qdebug.h> -#include <qcoreapplication.h> +#include <private/qdbusmenuconnection_p.h> +#include <private/qdbusmenuadaptor_p.h> +#include <private/qdbusplatformmenu_p.h> QT_BEGIN_NAMESPACE -Q_DECLARE_LOGGING_CATEGORY(qLcMenu) +using namespace Qt::StringLiterals; -const QString StatusNotifierWatcherService = QLatin1String("org.kde.StatusNotifierWatcher"); -const QString StatusNotifierWatcherPath = QLatin1String("/StatusNotifierWatcher"); -const QString StatusNotifierItemPath = QLatin1String("/StatusNotifierItem"); -const QString MenuBarPath = QLatin1String("/MenuBar"); +const QString StatusNotifierWatcherService = "org.kde.StatusNotifierWatcher"_L1; +const QString StatusNotifierWatcherPath = "/StatusNotifierWatcher"_L1; +const QString StatusNotifierItemPath = "/StatusNotifierItem"_L1; +const QString MenuBarPath = "/MenuBar"_L1; /*! \class QDBusMenuConnection @@ -69,20 +34,27 @@ const QString MenuBarPath = QLatin1String("/MenuBar"); */ QDBusMenuConnection::QDBusMenuConnection(QObject *parent, const QString &serviceName) : QObject(parent) + , m_serviceName(serviceName) , m_connection(serviceName.isNull() ? QDBusConnection::sessionBus() : QDBusConnection::connectToBus(QDBusConnection::SessionBus, serviceName)) , m_dbusWatcher(new QDBusServiceWatcher(StatusNotifierWatcherService, m_connection, QDBusServiceWatcher::WatchForRegistration, this)) - , m_statusNotifierHostRegistered(false) + , m_watcherRegistered(false) { #ifndef QT_NO_SYSTEMTRAYICON - QDBusInterface systrayHost(StatusNotifierWatcherService, StatusNotifierWatcherPath, StatusNotifierWatcherService, m_connection); - if (systrayHost.isValid() && systrayHost.property("IsStatusNotifierHostRegistered").toBool()) - m_statusNotifierHostRegistered = true; + // Start monitoring if any known tray-related services are registered. + if (m_connection.interface()->isServiceRegistered(StatusNotifierWatcherService)) + m_watcherRegistered = true; else - qCDebug(qLcMenu) << "StatusNotifierHost is not registered"; + qCDebug(qLcMenu) << "failed to find service" << StatusNotifierWatcherService; #endif } +QDBusMenuConnection::~QDBusMenuConnection() +{ + if (!m_serviceName.isEmpty() && m_connection.isConnected()) + QDBusConnection::disconnectFromBus(m_serviceName); +} + void QDBusMenuConnection::dbusError(const QDBusError &error) { qWarning() << "QDBusTrayIcon encountered a D-Bus error:" << error; @@ -105,13 +77,7 @@ void QDBusMenuConnection::unregisterTrayIconMenu(QDBusTrayIcon *item) bool QDBusMenuConnection::registerTrayIcon(QDBusTrayIcon *item) { - bool success = connection().registerService(item->instanceId()); - if (!success) { - qWarning() << "failed to register service" << item->instanceId(); - return false; - } - - success = connection().registerObject(StatusNotifierItemPath, item); + bool success = connection().registerObject(StatusNotifierItemPath, item); if (!success) { unregisterTrayIcon(item); qWarning() << "failed to register" << item->instanceId() << StatusNotifierItemPath; @@ -126,22 +92,21 @@ bool QDBusMenuConnection::registerTrayIcon(QDBusTrayIcon *item) bool QDBusMenuConnection::registerTrayIconWithWatcher(QDBusTrayIcon *item) { + Q_UNUSED(item); QDBusMessage registerMethod = QDBusMessage::createMethodCall( StatusNotifierWatcherService, StatusNotifierWatcherPath, StatusNotifierWatcherService, - QLatin1String("RegisterStatusNotifierItem")); - registerMethod.setArguments(QVariantList() << item->instanceId()); + "RegisterStatusNotifierItem"_L1); + registerMethod.setArguments(QVariantList() << m_connection.baseService()); return m_connection.callWithCallback(registerMethod, this, SIGNAL(trayIconRegistered()), SLOT(dbusError(QDBusError))); } -bool QDBusMenuConnection::unregisterTrayIcon(QDBusTrayIcon *item) +void QDBusMenuConnection::unregisterTrayIcon(QDBusTrayIcon *item) { unregisterTrayIconMenu(item); connection().unregisterObject(StatusNotifierItemPath); - bool success = connection().unregisterService(item->instanceId()); - if (!success) - qWarning() << "failed to unregister service" << item->instanceId(); - return success; } #endif // QT_NO_SYSTEMTRAYICON QT_END_NAMESPACE + +#include "moc_qdbusmenuconnection_p.cpp" diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h b/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h index bbdaad1e89..37033e2fa3 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h +++ b/src/gui/platform/unix/dbusmenu/qdbusmenuconnection_p.h @@ -1,44 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -#ifndef DBUSCONNECTION_H -#define DBUSCONNECTION_H +#ifndef QDBUSMENUCONNECTION_H +#define QDBUSMENUCONNECTION_H // // W A R N I N G @@ -56,6 +20,7 @@ #include <QtDBus/QDBusVariant> #include <QtGui/qtgui-config.h> +#include <QtCore/private/qglobal_p.h> Q_MOC_INCLUDE(<QtDBus/QDBusError>) QT_BEGIN_NAMESPACE @@ -71,15 +36,16 @@ class QDBusMenuConnection : public QObject public: QDBusMenuConnection(QObject *parent = nullptr, const QString &serviceName = QString()); + ~QDBusMenuConnection(); QDBusConnection connection() const { return m_connection; } QDBusServiceWatcher *dbusWatcher() const { return m_dbusWatcher; } - bool isStatusNotifierHostRegistered() const { return m_statusNotifierHostRegistered; } + bool isWatcherRegistered() const { return m_watcherRegistered; } #ifndef QT_NO_SYSTEMTRAYICON bool registerTrayIconMenu(QDBusTrayIcon *item); void unregisterTrayIconMenu(QDBusTrayIcon *item); bool registerTrayIcon(QDBusTrayIcon *item); bool registerTrayIconWithWatcher(QDBusTrayIcon *item); - bool unregisterTrayIcon(QDBusTrayIcon *item); + void unregisterTrayIcon(QDBusTrayIcon *item); #endif // QT_NO_SYSTEMTRAYICON Q_SIGNALS: @@ -91,11 +57,12 @@ private Q_SLOTS: void dbusError(const QDBusError &error); private: + QString m_serviceName; QDBusConnection m_connection; QDBusServiceWatcher *m_dbusWatcher; - bool m_statusNotifierHostRegistered; + bool m_watcherRegistered; }; QT_END_NAMESPACE -#endif // DBUSCONNECTION_H +#endif // QDBUSMENUCONNECTION_H diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy.cpp b/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy.cpp index c59b5a675e..089bf5de47 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy.cpp +++ b/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /* * This file was originally created by qdbusxml2cpp version 0.8 @@ -62,3 +26,5 @@ QDBusMenuRegistrarInterface::~QDBusMenuRegistrarInterface() } QT_END_NAMESPACE + +#include "moc_qdbusmenuregistrarproxy_p.cpp" diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy_p.h b/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy_p.h index cffc080f87..8041f3af0a 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy_p.h +++ b/src/gui/platform/unix/dbusmenu/qdbusmenuregistrarproxy_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Dmitry Shachnev <mitya57@gmail.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /* * This file was originally created by qdbusxml2cpp version 0.8 @@ -66,6 +30,7 @@ #include <QtDBus/QDBusAbstractInterface> #include <QtDBus/QDBusConnection> #include <QtDBus/QDBusReply> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -98,7 +63,7 @@ public Q_SLOTS: // METHODS { QDBusMessage reply = call(QDBus::Block, QStringLiteral("GetMenuForWindow"), windowId); QList<QVariant> arguments = reply.arguments(); - if (reply.type() == QDBusMessage::ReplyMessage && arguments.count() == 2) + if (reply.type() == QDBusMessage::ReplyMessage && arguments.size() == 2) menuObjectPath = qdbus_cast<QDBusObjectPath>(arguments.at(1)); return reply; } diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenutypes.cpp b/src/gui/platform/unix/dbusmenu/qdbusmenutypes.cpp index 6fadea5d28..b7fd035883 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusmenutypes.cpp +++ b/src/gui/platform/unix/dbusmenu/qdbusmenutypes.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qdbusmenutypes_p.h" @@ -56,6 +20,18 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + +QT_IMPL_METATYPE_EXTERN(QDBusMenuItem) +QT_IMPL_METATYPE_EXTERN(QDBusMenuItemList) +QT_IMPL_METATYPE_EXTERN(QDBusMenuItemKeys) +QT_IMPL_METATYPE_EXTERN(QDBusMenuItemKeysList) +QT_IMPL_METATYPE_EXTERN(QDBusMenuLayoutItem) +QT_IMPL_METATYPE_EXTERN(QDBusMenuLayoutItemList) +QT_IMPL_METATYPE_EXTERN(QDBusMenuEvent) +QT_IMPL_METATYPE_EXTERN(QDBusMenuEventList) +QT_IMPL_METATYPE_EXTERN(QDBusMenuShortcut) + const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuItem &item) { arg.beginStructure(); @@ -93,7 +69,7 @@ uint QDBusMenuLayoutItem::populate(int id, int depth, const QStringList &propert qCDebug(qLcMenu) << id << "depth" << depth << propertyNames; m_id = id; if (id == 0) { - m_properties.insert(QLatin1String("children-display"), QLatin1String("submenu")); + m_properties.insert("children-display"_L1, "submenu"_L1); if (topLevelMenu) populate(topLevelMenu, depth, propertyNames); return 1; // revision @@ -182,39 +158,39 @@ QDBusMenuItem::QDBusMenuItem(const QDBusPlatformMenuItem *item) : m_id(item->dbusID()) { if (item->isSeparator()) { - m_properties.insert(QLatin1String("type"), QLatin1String("separator")); + m_properties.insert("type"_L1, "separator"_L1); } else { - m_properties.insert(QLatin1String("label"), convertMnemonic(item->text())); + m_properties.insert("label"_L1, convertMnemonic(item->text())); if (item->menu()) - m_properties.insert(QLatin1String("children-display"), QLatin1String("submenu")); - m_properties.insert(QLatin1String("enabled"), item->isEnabled()); + m_properties.insert("children-display"_L1, "submenu"_L1); + m_properties.insert("enabled"_L1, item->isEnabled()); if (item->isCheckable()) { - QString toggleType = item->hasExclusiveGroup() ? QLatin1String("radio") : QLatin1String("checkmark"); - m_properties.insert(QLatin1String("toggle-type"), toggleType); - m_properties.insert(QLatin1String("toggle-state"), item->isChecked() ? 1 : 0); + QString toggleType = item->hasExclusiveGroup() ? "radio"_L1 : "checkmark"_L1; + m_properties.insert("toggle-type"_L1, toggleType); + m_properties.insert("toggle-state"_L1, item->isChecked() ? 1 : 0); } #ifndef QT_NO_SHORTCUT const QKeySequence &scut = item->shortcut(); if (!scut.isEmpty()) { QDBusMenuShortcut shortcut = convertKeySequence(scut); - m_properties.insert(QLatin1String("shortcut"), QVariant::fromValue(shortcut)); + m_properties.insert("shortcut"_L1, QVariant::fromValue(shortcut)); } #endif const QIcon &icon = item->icon(); if (!icon.name().isEmpty()) { - m_properties.insert(QLatin1String("icon-name"), icon.name()); + m_properties.insert("icon-name"_L1, icon.name()); } else if (!icon.isNull()) { QBuffer buf; icon.pixmap(16).save(&buf, "PNG"); - m_properties.insert(QLatin1String("icon-data"), buf.data()); + m_properties.insert("icon-data"_L1, buf.data()); } } - m_properties.insert(QLatin1String("visible"), item->isVisible()); + m_properties.insert("visible"_L1, item->isVisible()); } QDBusMenuItemList QDBusMenuItem::items(const QList<int> &ids, const QStringList &propertyNames) { - Q_UNUSED(propertyNames) + Q_UNUSED(propertyNames); QDBusMenuItemList ret; const QList<const QDBusPlatformMenuItem *> items = QDBusPlatformMenuItem::byIds(ids); ret.reserve(items.size()); @@ -227,11 +203,11 @@ QString QDBusMenuItem::convertMnemonic(const QString &label) { // convert only the first occurrence of ampersand which is not at the end // dbusmenu uses underscore instead of ampersand - int idx = label.indexOf(QLatin1Char('&')); - if (idx < 0 || idx == label.length() - 1) + int idx = label.indexOf(u'&'); + if (idx < 0 || idx == label.size() - 1) return label; QString ret(label); - ret[idx] = QLatin1Char('_'); + ret[idx] = u'_'; return ret; } @@ -241,22 +217,22 @@ QDBusMenuShortcut QDBusMenuItem::convertKeySequence(const QKeySequence &sequence QDBusMenuShortcut shortcut; for (int i = 0; i < sequence.count(); ++i) { QStringList tokens; - int key = sequence[i]; - if (key & Qt::MetaModifier) + auto modifiers = sequence[i].keyboardModifiers(); + if (modifiers & Qt::MetaModifier) tokens << QStringLiteral("Super"); - if (key & Qt::ControlModifier) + if (modifiers & Qt::ControlModifier) tokens << QStringLiteral("Control"); - if (key & Qt::AltModifier) + if (modifiers & Qt::AltModifier) tokens << QStringLiteral("Alt"); - if (key & Qt::ShiftModifier) + if (modifiers & Qt::ShiftModifier) tokens << QStringLiteral("Shift"); - if (key & Qt::KeypadModifier) + if (modifiers & Qt::KeypadModifier) tokens << QStringLiteral("Num"); - QString keyName = QKeySequencePrivate::keyName(key, QKeySequence::PortableText); - if (keyName == QLatin1String("+")) + QString keyName = QKeySequencePrivate::keyName(sequence[i].key(), QKeySequence::PortableText); + if (keyName == "+"_L1) tokens << QStringLiteral("plus"); - else if (keyName == QLatin1String("-")) + else if (keyName == "-"_L1) tokens << QStringLiteral("minus"); else tokens << keyName; @@ -295,7 +271,7 @@ QDebug operator<<(QDebug d, const QDBusMenuLayoutItem &item) { QDebugStateSaver saver(d); d.nospace(); - d << "QDBusMenuLayoutItem(id=" << item.m_id << ", properties=" << item.m_properties << ", " << item.m_children.count() << " children)"; + d << "QDBusMenuLayoutItem(id=" << item.m_id << ", properties=" << item.m_properties << ", " << item.m_children.size() << " children)"; return d; } #endif diff --git a/src/gui/platform/unix/dbusmenu/qdbusmenutypes_p.h b/src/gui/platform/unix/dbusmenu/qdbusmenutypes_p.h index fd6727d3be..4775c0094d 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusmenutypes_p.h +++ b/src/gui/platform/unix/dbusmenu/qdbusmenutypes_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QDBUSMENUTYPES_H #define QDBUSMENUTYPES_H @@ -57,14 +21,15 @@ #include <QDBusConnection> #include <QDBusObjectPath> #include <QPixmap> +#include <private/qglobal_p.h> QT_BEGIN_NAMESPACE class QDBusPlatformMenu; class QDBusPlatformMenuItem; class QDBusMenuItem; -typedef QVector<QDBusMenuItem> QDBusMenuItemList; -typedef QVector<QStringList> QDBusMenuShortcut; +typedef QList<QDBusMenuItem> QDBusMenuItemList; +typedef QList<QStringList> QDBusMenuShortcut; class QDBusMenuItem { @@ -82,7 +47,7 @@ public: int m_id; QVariantMap m_properties; }; -Q_DECLARE_TYPEINFO(QDBusMenuItem, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QDBusMenuItem, Q_RELOCATABLE_TYPE); const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuItem &item); const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuItem &item); @@ -94,12 +59,12 @@ public: int id; QStringList properties; }; -Q_DECLARE_TYPEINFO(QDBusMenuItemKeys, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QDBusMenuItemKeys, Q_RELOCATABLE_TYPE); const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuItemKeys &keys); const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuItemKeys &keys); -typedef QVector<QDBusMenuItemKeys> QDBusMenuItemKeysList; +typedef QList<QDBusMenuItemKeys> QDBusMenuItemKeysList; class QDBusMenuLayoutItem { @@ -110,14 +75,14 @@ public: int m_id; QVariantMap m_properties; - QVector<QDBusMenuLayoutItem> m_children; + QList<QDBusMenuLayoutItem> m_children; }; -Q_DECLARE_TYPEINFO(QDBusMenuLayoutItem, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QDBusMenuLayoutItem, Q_RELOCATABLE_TYPE); const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuLayoutItem &); const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuLayoutItem &item); -typedef QVector<QDBusMenuLayoutItem> QDBusMenuLayoutItemList; +typedef QList<QDBusMenuLayoutItem> QDBusMenuLayoutItemList; class QDBusMenuEvent { @@ -127,13 +92,13 @@ public: QDBusVariant m_data; uint m_timestamp; }; -Q_DECLARE_TYPEINFO(QDBusMenuEvent, Q_MOVABLE_TYPE); // QDBusVariant is movable, even though it cannot +Q_DECLARE_TYPEINFO(QDBusMenuEvent, Q_RELOCATABLE_TYPE); // QDBusVariant is movable, even though it cannot // be marked as such until Qt 6. const QDBusArgument &operator<<(QDBusArgument &arg, const QDBusMenuEvent &ev); const QDBusArgument &operator>>(const QDBusArgument &arg, QDBusMenuEvent &ev); -typedef QVector<QDBusMenuEvent> QDBusMenuEventList; +typedef QList<QDBusMenuEvent> QDBusMenuEventList; #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QDBusMenuItem &item); @@ -142,14 +107,14 @@ QDebug operator<<(QDebug d, const QDBusMenuLayoutItem &item); QT_END_NAMESPACE -Q_DECLARE_METATYPE(QDBusMenuItem) -Q_DECLARE_METATYPE(QDBusMenuItemList) -Q_DECLARE_METATYPE(QDBusMenuItemKeys) -Q_DECLARE_METATYPE(QDBusMenuItemKeysList) -Q_DECLARE_METATYPE(QDBusMenuLayoutItem) -Q_DECLARE_METATYPE(QDBusMenuLayoutItemList) -Q_DECLARE_METATYPE(QDBusMenuEvent) -Q_DECLARE_METATYPE(QDBusMenuEventList) -Q_DECLARE_METATYPE(QDBusMenuShortcut) +QT_DECL_METATYPE_EXTERN(QDBusMenuItem, Q_GUI_EXPORT) +QT_DECL_METATYPE_EXTERN(QDBusMenuItemList, Q_GUI_EXPORT) +QT_DECL_METATYPE_EXTERN(QDBusMenuItemKeys, Q_GUI_EXPORT) +QT_DECL_METATYPE_EXTERN(QDBusMenuItemKeysList, Q_GUI_EXPORT) +QT_DECL_METATYPE_EXTERN(QDBusMenuLayoutItem, Q_GUI_EXPORT) +QT_DECL_METATYPE_EXTERN(QDBusMenuLayoutItemList, Q_GUI_EXPORT) +QT_DECL_METATYPE_EXTERN(QDBusMenuEvent, Q_GUI_EXPORT) +QT_DECL_METATYPE_EXTERN(QDBusMenuEventList, Q_GUI_EXPORT) +QT_DECL_METATYPE_EXTERN(QDBusMenuShortcut, Q_GUI_EXPORT) #endif diff --git a/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp b/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp index fc1b37f2f2..f25fb0831b 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp +++ b/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qdbusplatformmenu_p.h" @@ -306,3 +270,5 @@ QPlatformMenu *QDBusPlatformMenu::createSubMenu() const } QT_END_NAMESPACE + +#include "moc_qdbusplatformmenu_p.cpp" diff --git a/src/gui/platform/unix/dbusmenu/qdbusplatformmenu_p.h b/src/gui/platform/unix/dbusmenu/qdbusplatformmenu_p.h index aa0f303416..cb672fee48 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusplatformmenu_p.h +++ b/src/gui/platform/unix/dbusmenu/qdbusplatformmenu_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QDBUSPLATFORMMENU_H #define QDBUSPLATFORMMENU_H diff --git a/src/gui/platform/unix/dbustray/dbustray.pri b/src/gui/platform/unix/dbustray/dbustray.pri deleted file mode 100644 index cc5b40ef42..0000000000 --- a/src/gui/platform/unix/dbustray/dbustray.pri +++ /dev/null @@ -1,11 +0,0 @@ -HEADERS += \ - platform/unix/dbustray/qdbustrayicon_p.h \ - platform/unix/dbustray/qdbustraytypes_p.h \ - platform/unix/dbustray/qstatusnotifieritemadaptor_p.h \ - platform/unix/dbustray/qxdgnotificationproxy_p.h - -SOURCES += \ - platform/unix/dbustray/qdbustrayicon.cpp \ - platform/unix/dbustray/qdbustraytypes.cpp \ - platform/unix/dbustray/qstatusnotifieritemadaptor.cpp \ - platform/unix/dbustray/qxdgnotificationproxy.cpp diff --git a/src/gui/platform/unix/dbustray/qdbustrayicon.cpp b/src/gui/platform/unix/dbustray/qdbustrayicon.cpp index e8fcb83c38..0dff9b598e 100644 --- a/src/gui/platform/unix/dbustray/qdbustrayicon.cpp +++ b/src/gui/platform/unix/dbustray/qdbustrayicon.cpp @@ -1,63 +1,33 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qdbustrayicon_p.h" #ifndef QT_NO_SYSTEMTRAYICON -#include <private/qdbusmenuconnection_p.h> -#include "qstatusnotifieritemadaptor_p.h" -#include <private/qdbusmenuadaptor_p.h> -#include <private/qdbusplatformmenu_p.h> -#include "qxdgnotificationproxy_p.h" +#include <QString> +#include <QDebug> +#include <QRect> +#include <QLoggingCategory> +#include <QStandardPaths> +#include <QFileInfo> +#include <QDir> +#include <QMetaObject> +#include <QMetaEnum> +#include <QDBusConnectionInterface> +#include <QDBusArgument> +#include <QDBusMetaType> +#include <QDBusServiceWatcher> #include <qpa/qplatformmenu.h> -#include <qstring.h> -#include <qdebug.h> -#include <qrect.h> -#include <qloggingcategory.h> -#include <qstandardpaths.h> -#include <qdir.h> -#include <qmetaobject.h> #include <qpa/qplatformintegration.h> #include <qpa/qplatformservices.h> -#include <qdbusconnectioninterface.h> + +#include <private/qdbusmenuconnection_p.h> +#include <private/qstatusnotifieritemadaptor_p.h> +#include <private/qdbusmenuadaptor_p.h> +#include <private/qdbusplatformmenu_p.h> +#include <private/qxdgnotificationproxy_p.h> #include <private/qlockfile_p.h> #include <private/qguiapplication_p.h> @@ -66,13 +36,19 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + Q_LOGGING_CATEGORY(qLcTray, "qt.qpa.tray") static QString iconTempPath() { QString tempPath = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); - if (!tempPath.isEmpty()) + if (!tempPath.isEmpty()) { + QString flatpakId = qEnvironmentVariable("FLATPAK_ID"); + if (!flatpakId.isEmpty() && QFileInfo::exists("/.flatpak-info"_L1)) + tempPath += "/app/"_L1 + flatpakId; return tempPath; + } tempPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation); @@ -100,7 +76,7 @@ static int instanceCount = 0; static inline QString tempFileTemplate() { - static const QString TempFileTemplate = iconTempPath() + QLatin1String("/qt-trayicon-XXXXXX.png"); + static const QString TempFileTemplate = iconTempPath() + "/qt-trayicon-XXXXXX.png"_L1; return TempFileTemplate; } @@ -202,7 +178,15 @@ QTemporaryFile *QDBusTrayIcon::tempIcon(const QIcon &icon) QDBusConnection session = QDBusConnection::sessionBus(); uint pid = session.interface()->servicePid(KDEWatcherService).value(); QString processName = QLockFilePrivate::processNameByPid(pid); - necessary = processName.endsWith(QLatin1String("indicator-application-service")); + necessary = processName.endsWith("indicator-application-service"_L1); + if (!necessary) { + necessary = session.interface()->isServiceRegistered( + QStringLiteral("com.canonical.indicator.application")); + } + if (!necessary) { + necessary = session.interface()->isServiceRegistered( + QStringLiteral("org.ayatana.indicator.application")); + } if (!necessary && QGuiApplication::desktopSettingsAware()) { // Accessing to process name might be not allowed if the application // is confined, thus we can just rely on the current desktop in use @@ -213,10 +197,12 @@ QTemporaryFile *QDBusTrayIcon::tempIcon(const QIcon &icon) } if (!necessary) return nullptr; - qreal dpr = qGuiApp->devicePixelRatio(); QTemporaryFile *ret = new QTemporaryFile(tempFileTemplate(), this); - ret->open(); - icon.pixmap(QSize(22 * dpr, 22 * dpr)).save(ret); + if (!ret->open()) { + delete ret; + return nullptr; + } + icon.pixmap(QSize(22, 22)).save(ret); ret->close(); return ret; } @@ -329,7 +315,7 @@ void QDBusTrayIcon::showMessage(const QString &title, const QString &msg, const int urgency = static_cast<int>(iconType) - 1; if (urgency < 0) // no icon urgency = 0; - hints.insert(QLatin1String("urgency"), QVariant(urgency)); + hints.insert("urgency"_L1, QVariant(urgency)); m_notifier->notify(QCoreApplication::applicationName(), 0, m_attentionIconName, title, msg, notificationActions, hints, msecs); } @@ -348,9 +334,14 @@ void QDBusTrayIcon::notificationClosed(uint id, uint reason) bool QDBusTrayIcon::isSystemTrayAvailable() const { QDBusMenuConnection * conn = const_cast<QDBusTrayIcon *>(this)->dBusConnection(); - qCDebug(qLcTray) << conn->isStatusNotifierHostRegistered(); - return conn->isStatusNotifierHostRegistered(); + + // If the KDE watcher service is registered, we must be on a desktop + // where a StatusNotifier-conforming system tray exists. + qCDebug(qLcTray) << conn->isWatcherRegistered(); + return conn->isWatcherRegistered(); } QT_END_NAMESPACE + +#include "moc_qdbustrayicon_p.cpp" #endif //QT_NO_SYSTEMTRAYICON diff --git a/src/gui/platform/unix/dbustray/qdbustrayicon_p.h b/src/gui/platform/unix/dbustray/qdbustrayicon_p.h index 04eefe3154..3041e132fd 100644 --- a/src/gui/platform/unix/dbustray/qdbustrayicon_p.h +++ b/src/gui/platform/unix/dbustray/qdbustrayicon_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QDBUSTRAYICON_H diff --git a/src/gui/platform/unix/dbustray/qdbustraytypes.cpp b/src/gui/platform/unix/dbustray/qdbustraytypes.cpp index 97cc8b7f36..accbd87e7e 100644 --- a/src/gui/platform/unix/dbustray/qdbustraytypes.cpp +++ b/src/gui/platform/unix/dbustray/qdbustraytypes.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Marco Martin <notmart@gmail.com> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2009 Marco Martin <notmart@gmail.com> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QT_NO_SYSTEMTRAYICON @@ -46,6 +10,7 @@ #include <QDBusMetaType> #include <QImage> #include <QIcon> +#include <QIconEngine> #include <QImage> #include <QPixmap> #include <QDebug> @@ -54,9 +19,14 @@ #include <QGuiApplication> #include <qpa/qplatformmenu.h> #include <private/qdbusplatformmenu_p.h> +#include <private/qicon_p.h> QT_BEGIN_NAMESPACE +QT_IMPL_METATYPE_EXTERN(QXdgDBusImageStruct) +QT_IMPL_METATYPE_EXTERN(QXdgDBusImageVector) +QT_IMPL_METATYPE_EXTERN(QXdgDBusToolTipStruct) + static const int IconSizeLimit = 64; static const int IconNormalSmallSize = 22; static const int IconNormalMediumSize = 64; @@ -64,35 +34,37 @@ static const int IconNormalMediumSize = 64; QXdgDBusImageVector iconToQXdgDBusImageVector(const QIcon &icon) { QXdgDBusImageVector ret; - QList<QSize> sizes = icon.availableSizes(); + if (icon.isNull()) + return ret; + QIconEngine *engine = const_cast<QIcon &>(icon).data_ptr()->engine; + QList<QSize> sizes = engine->availableSizes(QIcon::Normal, QIcon::Off); // Omit any size larger than 64 px, to save D-Bus bandwidth; // ensure that 22px or smaller exists, because it's a common size; // and ensure that something between 22px and 64px exists, for better scaling to other sizes. bool hasSmallIcon = false; bool hasMediumIcon = false; - qreal dpr = qGuiApp->devicePixelRatio(); QList<QSize> toRemove; - for (const QSize &size : qAsConst(sizes)) { + for (const QSize &size : std::as_const(sizes)) { int maxSize = qMax(size.width(), size.height()); - if (maxSize <= IconNormalSmallSize * dpr) + if (maxSize <= IconNormalSmallSize) hasSmallIcon = true; - else if (maxSize <= IconNormalMediumSize * dpr) + else if (maxSize <= IconNormalMediumSize) hasMediumIcon = true; - else if (maxSize > IconSizeLimit * dpr) + else if (maxSize > IconSizeLimit) toRemove << size; } - for (const QSize &size : qAsConst(toRemove)) + for (const QSize &size : std::as_const(toRemove)) sizes.removeOne(size); if (!hasSmallIcon) - sizes.append(QSize(IconNormalSmallSize * dpr, IconNormalSmallSize * dpr)); + sizes.append(QSize(IconNormalSmallSize, IconNormalSmallSize)); if (!hasMediumIcon) - sizes.append(QSize(IconNormalMediumSize * dpr, IconNormalMediumSize * dpr)); + sizes.append(QSize(IconNormalMediumSize, IconNormalMediumSize)); ret.reserve(sizes.size()); - for (const QSize &size : qAsConst(sizes)) { + for (const QSize &size : std::as_const(sizes)) { // Protocol specifies ARGB32 format in network byte order - QImage im = icon.pixmap(size).toImage().convertToFormat(QImage::Format_ARGB32); + QImage im = engine->pixmap(size, QIcon::Normal, QIcon::Off).toImage().convertToFormat(QImage::Format_ARGB32); // letterbox if necessary to make it square if (im.height() != im.width()) { int maxSize = qMax(im.width(), im.height()); @@ -104,10 +76,7 @@ QXdgDBusImageVector iconToQXdgDBusImageVector(const QIcon &icon) } // copy and endian-convert QXdgDBusImageStruct kim(im.width(), im.height()); - const uchar *end = im.constBits() + im.sizeInBytes(); - uchar *dest = reinterpret_cast<uchar *>(kim.data.data()); - for (const uchar *src = im.constBits(); src < end; src += 4, dest += 4) - qToUnaligned(qToBigEndian<quint32>(qFromUnaligned<quint32>(src)), dest); + qToBigEndian<quint32>(im.constBits(), im.width() * im.height(), kim.data.data()); ret << kim; } diff --git a/src/gui/platform/unix/dbustray/qdbustraytypes_p.h b/src/gui/platform/unix/dbustray/qdbustraytypes_p.h index 3f75555579..08294d486c 100644 --- a/src/gui/platform/unix/dbustray/qdbustraytypes_p.h +++ b/src/gui/platform/unix/dbustray/qdbustraytypes_p.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Marco Martin <notmart@gmail.com> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2009 Marco Martin <notmart@gmail.com> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QDBUSTRAYTYPES_P_H #define QDBUSTRAYTYPES_P_H @@ -75,9 +39,9 @@ struct QXdgDBusImageStruct int height; QByteArray data; }; -Q_DECLARE_TYPEINFO(QXdgDBusImageStruct, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QXdgDBusImageStruct, Q_RELOCATABLE_TYPE); -typedef QVector<QXdgDBusImageStruct> QXdgDBusImageVector; +using QXdgDBusImageVector = QList<QXdgDBusImageStruct>; QXdgDBusImageVector iconToQXdgDBusImageVector(const QIcon &icon); @@ -89,7 +53,7 @@ struct QXdgDBusToolTipStruct QString title; QString subTitle; }; -Q_DECLARE_TYPEINFO(QXdgDBusToolTipStruct, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QXdgDBusToolTipStruct, Q_RELOCATABLE_TYPE); const QDBusArgument &operator<<(QDBusArgument &argument, const QXdgDBusImageStruct &icon); const QDBusArgument &operator>>(const QDBusArgument &argument, QXdgDBusImageStruct &icon); @@ -102,8 +66,8 @@ const QDBusArgument &operator>>(const QDBusArgument &argument, QXdgDBusToolTipSt QT_END_NAMESPACE -Q_DECLARE_METATYPE(QXdgDBusImageStruct) -Q_DECLARE_METATYPE(QXdgDBusImageVector) -Q_DECLARE_METATYPE(QXdgDBusToolTipStruct) +QT_DECL_METATYPE_EXTERN(QXdgDBusImageStruct, Q_GUI_EXPORT) +QT_DECL_METATYPE_EXTERN(QXdgDBusImageVector, Q_GUI_EXPORT) +QT_DECL_METATYPE_EXTERN(QXdgDBusToolTipStruct, Q_GUI_EXPORT) #endif // QDBUSTRAYTYPES_P_H diff --git a/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor.cpp b/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor.cpp index ef2d330959..2f6c13b6cf 100644 --- a/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor.cpp +++ b/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /* This file was originally created by qdbusxml2cpp version 0.8 @@ -172,6 +136,12 @@ void QStatusNotifierItemAdaptor::ContextMenu(int x, int y) emit m_trayIcon->activated(QPlatformSystemTrayIcon::Context); } +void QStatusNotifierItemAdaptor::ProvideXdgActivationToken(const QString &token) +{ + qCDebug(qLcTray) << token; + qputenv("XDG_ACTIVATION_TOKEN", token.toUtf8()); +} + void QStatusNotifierItemAdaptor::Scroll(int w, const QString &s) { qCDebug(qLcTray) << w << s; @@ -186,4 +156,6 @@ void QStatusNotifierItemAdaptor::SecondaryActivate(int x, int y) QT_END_NAMESPACE +#include "moc_qstatusnotifieritemadaptor_p.cpp" + #endif // QT_NO_SYSTEMTRAYICON diff --git a/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor_p.h b/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor_p.h index f2bb156b1d..103fc974dd 100644 --- a/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor_p.h +++ b/src/gui/platform/unix/dbustray/qstatusnotifieritemadaptor_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /* This file was originally created by qdbusxml2cpp version 0.8 @@ -62,14 +26,14 @@ // We mean it. // -#include <QtGui/private/qtguiglobal_p.h> +#include <private/qtguiglobal_p.h> QT_REQUIRE_CONFIG(systemtrayicon); -#include <QtCore/QObject> -#include <QtDBus/QtDBus> +#include <QObject> +#include <QDBusAbstractAdaptor> -#include "qdbustraytypes_p.h" +#include <private/qdbustraytypes_p.h> QT_BEGIN_NAMESPACE class QDBusTrayIcon; @@ -109,6 +73,9 @@ class QStatusNotifierItemAdaptor: public QDBusAbstractAdaptor " <property access=\"read\" type=\"(sa(iiay)ss)\" name=\"ToolTip\">\n" " <annotation value=\"QXdgDBusToolTipStruct\" name=\"org.qtproject.QtDBus.QtTypeName\"/>\n" " </property>\n" +" <method name=\"ProvideXdgActivationToken\">\n" +" <arg name=\"token\" type=\"s\" direction=\"in\"/>\n" +" </method>\n" " <method name=\"ContextMenu\">\n" " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n" " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n" @@ -186,6 +153,7 @@ public: // PROPERTIES public Q_SLOTS: // METHODS void Activate(int x, int y); void ContextMenu(int x, int y); + void ProvideXdgActivationToken(const QString &token); void Scroll(int delta, const QString &orientation); void SecondaryActivate(int x, int y); Q_SIGNALS: // SIGNALS diff --git a/src/gui/platform/unix/dbustray/qxdgnotificationproxy.cpp b/src/gui/platform/unix/dbustray/qxdgnotificationproxy.cpp index ef2aa799c8..97b6697e6d 100644 --- a/src/gui/platform/unix/dbustray/qxdgnotificationproxy.cpp +++ b/src/gui/platform/unix/dbustray/qxdgnotificationproxy.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qxdgnotificationproxy_p.h" @@ -51,3 +15,5 @@ QXdgNotificationInterface::~QXdgNotificationInterface() } QT_END_NAMESPACE + +#include "moc_qxdgnotificationproxy_p.cpp" diff --git a/src/gui/platform/unix/dbustray/qxdgnotificationproxy_p.h b/src/gui/platform/unix/dbustray/qxdgnotificationproxy_p.h index 495208f873..dfbc64f33b 100644 --- a/src/gui/platform/unix/dbustray/qxdgnotificationproxy_p.h +++ b/src/gui/platform/unix/dbustray/qxdgnotificationproxy_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only /* This file was originally created by qdbusxml2cpp version 0.8 @@ -62,15 +26,18 @@ // We mean it. // -#include <QtCore/QObject> -#include <QtCore/QByteArray> -#include <QtCore/QList> -#include <QtCore/QLoggingCategory> -#include <QtCore/QMap> -#include <QtCore/QString> -#include <QtCore/QStringList> -#include <QtCore/QVariant> -#include <QtDBus/QtDBus> +#include <QObject> +#include <QByteArray> +#include <QList> +#include <QLoggingCategory> +#include <QMap> +#include <QString> +#include <QStringList> +#include <QVariant> +#include <QDBusAbstractInterface> +#include <QDBusPendingReply> +#include <QDBusReply> +#include <private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -110,7 +77,7 @@ public Q_SLOTS: // METHODS inline QDBusReply<QString> getServerInformation(QString &vendor, QString &version, QString &specVersion) { QDBusMessage reply = call(QDBus::Block, QStringLiteral("GetServerInformation")); - if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == 4) { + if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().size() == 4) { vendor = qdbus_cast<QString>(reply.arguments().at(1)); version = qdbus_cast<QString>(reply.arguments().at(2)); specVersion = qdbus_cast<QString>(reply.arguments().at(3)); diff --git a/src/gui/platform/unix/qeventdispatcher_glib.cpp b/src/gui/platform/unix/qeventdispatcher_glib.cpp index 0ccbf01e80..368006f302 100644 --- a/src/gui/platform/unix/qeventdispatcher_glib.cpp +++ b/src/gui/platform/unix/qeventdispatcher_glib.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qeventdispatcher_glib_p.h" @@ -57,14 +21,14 @@ struct GUserEventSource static gboolean userEventSourcePrepare(GSource *source, gint *timeout) { - Q_UNUSED(timeout) + Q_UNUSED(timeout); GUserEventSource *userEventSource = reinterpret_cast<GUserEventSource *>(source); return userEventSource->d->wakeUpCalled; } static gboolean userEventSourceCheck(GSource *source) { - return userEventSourcePrepare(source, 0); + return userEventSourcePrepare(source, nullptr); } static gboolean userEventSourceDispatch(GSource *source, GSourceFunc, gpointer) @@ -88,8 +52,11 @@ QPAEventDispatcherGlibPrivate::QPAEventDispatcherGlibPrivate(GMainContext *conte : QEventDispatcherGlibPrivate(context) { Q_Q(QPAEventDispatcherGlib); - userEventSource = reinterpret_cast<GUserEventSource *>(g_source_new(&userEventSourceFuncs, - sizeof(GUserEventSource))); + + GSource *source = g_source_new(&userEventSourceFuncs, sizeof(GUserEventSource)); + g_source_set_name(source, "[Qt] GUserEventSource"); + userEventSource = reinterpret_cast<GUserEventSource *>(source); + userEventSource->q = q; userEventSource->d = this; g_source_set_can_recurse(&userEventSource->source, true); @@ -111,7 +78,7 @@ QPAEventDispatcherGlib::~QPAEventDispatcherGlib() g_source_destroy(&d->userEventSource->source); g_source_unref(&d->userEventSource->source); - d->userEventSource = 0; + d->userEventSource = nullptr; } bool QPAEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags) @@ -121,3 +88,5 @@ bool QPAEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags) } QT_END_NAMESPACE + +#include "moc_qeventdispatcher_glib_p.cpp" diff --git a/src/gui/platform/unix/qeventdispatcher_glib_p.h b/src/gui/platform/unix/qeventdispatcher_glib_p.h index 91be66a81f..95fac16237 100644 --- a/src/gui/platform/unix/qeventdispatcher_glib_p.h +++ b/src/gui/platform/unix/qeventdispatcher_glib_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QEVENTDISPATCHER_GLIB_QPA_P_H #define QEVENTDISPATCHER_GLIB_QPA_P_H diff --git a/src/gui/platform/unix/qgenericunixeventdispatcher.cpp b/src/gui/platform/unix/qgenericunixeventdispatcher.cpp index 2e1d81a181..b551aefe99 100644 --- a/src/gui/platform/unix/qgenericunixeventdispatcher.cpp +++ b/src/gui/platform/unix/qgenericunixeventdispatcher.cpp @@ -1,46 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qgenericunixeventdispatcher_p.h" #include "qunixeventdispatcher_qpa_p.h" -#include "qeventdispatcher_glib_p.h" - +#if QT_CONFIG(glib) +# include "qeventdispatcher_glib_p.h" +#endif QT_BEGIN_NAMESPACE class QAbstractEventDispatcher *QtGenericUnixDispatcher::createUnixEventDispatcher() diff --git a/src/gui/platform/unix/qgenericunixeventdispatcher_p.h b/src/gui/platform/unix/qgenericunixeventdispatcher_p.h index c567463772..82dc35da0b 100644 --- a/src/gui/platform/unix/qgenericunixeventdispatcher_p.h +++ b/src/gui/platform/unix/qgenericunixeventdispatcher_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // // W A R N I N G @@ -52,6 +16,7 @@ #define QGENERICUNIXEVENTDISPATCHER_P_H #include <QtGui/qtguiglobal.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE diff --git a/src/gui/platform/unix/qgenericunixservices.cpp b/src/gui/platform/unix/qgenericunixservices.cpp index 10d5468b9a..bfd2556b1e 100644 --- a/src/gui/platform/unix/qgenericunixservices.cpp +++ b/src/gui/platform/unix/qgenericunixservices.cpp @@ -1,44 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qgenericunixservices_p.h" #include <QtGui/private/qtguiglobal_p.h> +#include "qguiapplication.h" +#include "qwindow.h" +#include <QtGui/qpa/qplatformwindow_p.h> +#include <QtGui/qpa/qplatformwindow.h> +#include <QtGui/qpa/qplatformnativeinterface.h> #include <QtCore/QDebug> #include <QtCore/QFile> @@ -73,6 +42,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + #if QT_CONFIG(multiprocess) enum { debug = 0 }; @@ -142,9 +113,11 @@ static inline bool detectWebBrowser(const QByteArray &desktop, } if (desktop == QByteArray("KDE")) { + if (checkExecutable(QStringLiteral("kde-open5"), browser)) + return true; // Konqueror launcher if (checkExecutable(QStringLiteral("kfmclient"), browser)) { - browser->append(QLatin1String(" exec")); + browser->append(" exec"_L1); return true; } } else if (desktop == QByteArray("GNOME")) { @@ -153,18 +126,23 @@ static inline bool detectWebBrowser(const QByteArray &desktop, } for (size_t i = 0; i < sizeof(browsers)/sizeof(char *); ++i) - if (checkExecutable(QLatin1String(browsers[i]), browser)) + if (checkExecutable(QLatin1StringView(browsers[i]), browser)) return true; return false; } -static inline bool launch(const QString &launcher, const QUrl &url) +static inline bool launch(const QString &launcher, const QUrl &url, + const QString &xdgActivationToken) { - const QString command = launcher + QLatin1Char(' ') + QLatin1String(url.toEncoded()); + if (!xdgActivationToken.isEmpty()) { + qputenv("XDG_ACTIVATION_TOKEN", xdgActivationToken.toUtf8()); + } + + const QString command = launcher + u' ' + QLatin1StringView(url.toEncoded()); if (debug) qDebug("Launching %s", qPrintable(command)); #if !QT_CONFIG(process) - const bool ok = ::system(qPrintable(command + QLatin1String(" &"))); + const bool ok = ::system(qPrintable(command + " &"_L1)); #else QStringList args = QProcess::splitCommand(command); bool ok = false; @@ -175,24 +153,20 @@ static inline bool launch(const QString &launcher, const QUrl &url) #endif if (!ok) qWarning("Launch failed (%s)", qPrintable(command)); + + qunsetenv("XDG_ACTIVATION_TOKEN"); + return ok; } #if QT_CONFIG(dbus) static inline bool checkNeedPortalSupport() { - return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, QLatin1String("flatpak-info")).isEmpty() || qEnvironmentVariableIsSet("SNAP"); -} - -static inline bool isPortalReturnPermanent(const QDBusError &error) -{ - // A service unknown error isn't permanent, it just indicates that we - // should fall back to the regular way. This check includes - // QDBusError::NoError. - return error.type() != QDBusError::ServiceUnknown && error.type() != QDBusError::AccessDenied; + return QFileInfo::exists("/.flatpak-info"_L1) || qEnvironmentVariableIsSet("SNAP"); } -static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url) +static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url, const QString &parentWindow, + const QString &xdgActivationToken) { // DBus signature: // OpenFile (IN s parent_window, @@ -203,30 +177,32 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url) // handle_token (s) - A string that will be used as the last element of the @handle. // writable (b) - Whether to allow the chosen application to write to the file. -#ifdef O_PATH - const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_PATH); + const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_RDONLY); if (fd != -1) { - QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), - QLatin1String("/org/freedesktop/portal/desktop"), - QLatin1String("org.freedesktop.portal.OpenURI"), - QLatin1String("OpenFile")); + QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1, + "/org/freedesktop/portal/desktop"_L1, + "org.freedesktop.portal.OpenURI"_L1, + "OpenFile"_L1); QDBusUnixFileDescriptor descriptor; descriptor.giveFileDescriptor(fd); - // FIXME parent_window_id and handle writable option - message << QString() << QVariant::fromValue(descriptor) << QVariantMap(); + QVariantMap options = {}; + + if (!xdgActivationToken.isEmpty()) { + options.insert("activation_token"_L1, xdgActivationToken); + } + + message << parentWindow << QVariant::fromValue(descriptor) << options; return QDBusConnection::sessionBus().call(message); } -#else - Q_UNUSED(url) -#endif return QDBusMessage::createError(QDBusError::InternalError, qt_error_string()); } -static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url) +static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url, const QString &parentWindow, + const QString &xdgActivationToken) { // DBus signature: // OpenURI (IN s parent_window, @@ -239,17 +215,24 @@ static inline QDBusMessage xdgDesktopPortalOpenUrl(const QUrl &url) // This key only takes effect the uri points to a local file that is exported in the document portal, // and the chosen application is sandboxed itself. - QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), - QLatin1String("/org/freedesktop/portal/desktop"), - QLatin1String("org.freedesktop.portal.OpenURI"), - QLatin1String("OpenURI")); + QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1, + "/org/freedesktop/portal/desktop"_L1, + "org.freedesktop.portal.OpenURI"_L1, + "OpenURI"_L1); // FIXME parent_window_id and handle writable option - message << QString() << url.toString() << QVariantMap(); + QVariantMap options; + + if (!xdgActivationToken.isEmpty()) { + options.insert("activation_token"_L1, xdgActivationToken); + } + + message << parentWindow << url.toString() << options; return QDBusConnection::sessionBus().call(message); } -static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url) +static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url, const QString &parentWindow, + const QString &xdgActivationToken) { // DBus signature: // ComposeEmail (IN s parent_window, @@ -263,14 +246,14 @@ static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url) QUrlQuery urlQuery(url); QVariantMap options; - options.insert(QLatin1String("address"), url.path()); - options.insert(QLatin1String("subject"), urlQuery.queryItemValue(QLatin1String("subject"))); - options.insert(QLatin1String("body"), urlQuery.queryItemValue(QLatin1String("body"))); + options.insert("address"_L1, url.path()); + options.insert("subject"_L1, urlQuery.queryItemValue("subject"_L1)); + options.insert("body"_L1, urlQuery.queryItemValue("body"_L1)); // O_PATH seems to be present since Linux 2.6.39, which is not case of RHEL 6 #ifdef O_PATH QList<QDBusUnixFileDescriptor> attachments; - const QStringList attachmentUris = urlQuery.allQueryItemValues(QLatin1String("attachment")); + const QStringList attachmentUris = urlQuery.allQueryItemValues("attachment"_L1); for (const QString &attachmentUri : attachmentUris) { const int fd = qt_safe_open(QFile::encodeName(attachmentUri), O_PATH); @@ -281,75 +264,277 @@ static inline QDBusMessage xdgDesktopPortalSendEmail(const QUrl &url) } } - options.insert(QLatin1String("attachment_fds"), QVariant::fromValue(attachments)); + options.insert("attachment_fds"_L1, QVariant::fromValue(attachments)); #endif - QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), - QLatin1String("/org/freedesktop/portal/desktop"), - QLatin1String("org.freedesktop.portal.Email"), - QLatin1String("ComposeEmail")); + if (!xdgActivationToken.isEmpty()) { + options.insert("activation_token"_L1, xdgActivationToken); + } + + QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1, + "/org/freedesktop/portal/desktop"_L1, + "org.freedesktop.portal.Email"_L1, + "ComposeEmail"_L1); - // FIXME parent_window_id - message << QString() << options; + message << parentWindow << options; return QDBusConnection::sessionBus().call(message); } + +namespace { +struct XDGDesktopColor +{ + double r = 0; + double g = 0; + double b = 0; + + QColor toQColor() const + { + constexpr auto rgbMax = 255; + return { static_cast<int>(r * rgbMax), static_cast<int>(g * rgbMax), + static_cast<int>(b * rgbMax) }; + } +}; + +const QDBusArgument &operator>>(const QDBusArgument &argument, XDGDesktopColor &myStruct) +{ + argument.beginStructure(); + argument >> myStruct.r >> myStruct.g >> myStruct.b; + argument.endStructure(); + return argument; +} + +class XdgDesktopPortalColorPicker : public QPlatformServiceColorPicker +{ + Q_OBJECT +public: + XdgDesktopPortalColorPicker(const QString &parentWindowId, QWindow *parent) + : QPlatformServiceColorPicker(parent), m_parentWindowId(parentWindowId) + { + } + + void pickColor() override + { + // DBus signature: + // PickColor (IN s parent_window, + // IN a{sv} options + // OUT o handle) + // Options: + // handle_token (s) - A string that will be used as the last element of the @handle. + + QDBusMessage message = QDBusMessage::createMethodCall( + "org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1, + "org.freedesktop.portal.Screenshot"_L1, "PickColor"_L1); + message << m_parentWindowId << QVariantMap(); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply<QDBusObjectPath> reply = *watcher; + if (reply.isError()) { + qWarning("DBus call to pick color failed: %s", + qPrintable(reply.error().message())); + Q_EMIT colorPicked({}); + } else { + QDBusConnection::sessionBus().connect( + "org.freedesktop.portal.Desktop"_L1, reply.value().path(), + "org.freedesktop.portal.Request"_L1, "Response"_L1, this, + // clang-format off + SLOT(gotColorResponse(uint,QVariantMap)) + // clang-format on + ); + } + }); + } + +private Q_SLOTS: + void gotColorResponse(uint result, const QVariantMap &map) + { + if (result != 0) + return; + if (map.contains(u"color"_s)) { + XDGDesktopColor color{}; + map.value(u"color"_s).value<QDBusArgument>() >> color; + Q_EMIT colorPicked(color.toQColor()); + } else { + Q_EMIT colorPicked({}); + } + deleteLater(); + } + +private: + const QString m_parentWindowId; +}; +} // namespace + #endif // QT_CONFIG(dbus) +QGenericUnixServices::QGenericUnixServices() +{ +#if QT_CONFIG(dbus) + if (qEnvironmentVariableIntValue("QT_NO_XDG_DESKTOP_PORTAL") > 0) { + return; + } + QDBusMessage message = QDBusMessage::createMethodCall( + "org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1, + "org.freedesktop.DBus.Properties"_L1, "Get"_L1); + message << "org.freedesktop.portal.Screenshot"_L1 + << "version"_L1; + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply<QVariant> reply = *watcher; + if (!reply.isError() && reply.value().toUInt() >= 2) + m_hasScreenshotPortalWithColorPicking = true; + }); + +#endif +} + +QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent) +{ +#if QT_CONFIG(dbus) + // Make double sure that we are in a wayland environment. In particular check + // WAYLAND_DISPLAY so also XWayland apps benefit from portal-based color picking. + // Outside wayland we'll rather rely on other means than the XDG desktop portal. + if (!qEnvironmentVariableIsEmpty("WAYLAND_DISPLAY") + || QGuiApplication::platformName().startsWith("wayland"_L1)) { + return new XdgDesktopPortalColorPicker(portalWindowIdentifier(parent), parent); + } + return nullptr; +#else + Q_UNUSED(parent); + return nullptr; +#endif +} + QByteArray QGenericUnixServices::desktopEnvironment() const { static const QByteArray result = detectDesktopEnvironment(); return result; } -bool QGenericUnixServices::openUrl(const QUrl &url) +template<typename F> +void runWithXdgActivationToken(F &&functionToCall) { - if (url.scheme() == QLatin1String("mailto")) { -#if QT_CONFIG(dbus) - if (checkNeedPortalSupport()) { - QDBusError error = xdgDesktopPortalSendEmail(url); - if (isPortalReturnPermanent(error)) - return !error.isValid(); +#if QT_CONFIG(wayland) + QWindow *window = qGuiApp->focusWindow(); - // service not running, fall back - } -#endif - return openDocument(url); + if (!window) { + functionToCall({}); + return; } -#if QT_CONFIG(dbus) - if (checkNeedPortalSupport()) { - QDBusError error = xdgDesktopPortalOpenUrl(url); - if (isPortalReturnPermanent(error)) - return !error.isValid(); + auto waylandApp = dynamic_cast<QNativeInterface::QWaylandApplication *>( + qGuiApp->platformNativeInterface()); + auto waylandWindow = + dynamic_cast<QNativeInterface::Private::QWaylandWindow *>(window->handle()); + + if (!waylandWindow || !waylandApp) { + functionToCall({}); + return; } + + QObject::connect(waylandWindow, + &QNativeInterface::Private::QWaylandWindow::xdgActivationTokenCreated, + waylandWindow, functionToCall, Qt::SingleShotConnection); + waylandWindow->requestXdgActivationToken(waylandApp->lastInputSerial()); +#else + functionToCall({}); #endif +} + +bool QGenericUnixServices::openUrl(const QUrl &url) +{ + auto openUrlInternal = [this](const QUrl &url, const QString &xdgActivationToken) { + if (url.scheme() == "mailto"_L1) { +# if QT_CONFIG(dbus) + if (checkNeedPortalSupport()) { + const QString parentWindow = QGuiApplication::focusWindow() + ? portalWindowIdentifier(QGuiApplication::focusWindow()) + : QString(); + QDBusError error = xdgDesktopPortalSendEmail(url, parentWindow, xdgActivationToken); + if (!error.isValid()) + return true; + + // service not running, fall back + } +# endif + return openDocument(url); + } + +# if QT_CONFIG(dbus) + if (checkNeedPortalSupport()) { + const QString parentWindow = QGuiApplication::focusWindow() + ? portalWindowIdentifier(QGuiApplication::focusWindow()) + : QString(); + QDBusError error = xdgDesktopPortalOpenUrl(url, parentWindow, xdgActivationToken); + if (!error.isValid()) + return true; + } +# endif + + if (m_webBrowser.isEmpty() + && !detectWebBrowser(desktopEnvironment(), true, &m_webBrowser)) { + qWarning("Unable to detect a web browser to launch '%s'", qPrintable(url.toString())); + return false; + } + return launch(m_webBrowser, url, xdgActivationToken); + }; + + if (QGuiApplication::platformName().startsWith("wayland"_L1)) { + runWithXdgActivationToken( + [openUrlInternal, url](const QString &token) { openUrlInternal(url, token); }); + + return true; - if (m_webBrowser.isEmpty() && !detectWebBrowser(desktopEnvironment(), true, &m_webBrowser)) { - qWarning("Unable to detect a web browser to launch '%s'", qPrintable(url.toString())); - return false; + } else { + return openUrlInternal(url, QString()); } - return launch(m_webBrowser, url); } bool QGenericUnixServices::openDocument(const QUrl &url) { -#if QT_CONFIG(dbus) - if (checkNeedPortalSupport()) { - QDBusError error = xdgDesktopPortalOpenFile(url); - if (isPortalReturnPermanent(error)) - return !error.isValid(); - } -#endif + auto openDocumentInternal = [this](const QUrl &url, const QString &xdgActivationToken) { - if (m_documentLauncher.isEmpty() && !detectWebBrowser(desktopEnvironment(), false, &m_documentLauncher)) { - qWarning("Unable to detect a launcher for '%s'", qPrintable(url.toString())); - return false; +# if QT_CONFIG(dbus) + if (checkNeedPortalSupport()) { + const QString parentWindow = QGuiApplication::focusWindow() + ? portalWindowIdentifier(QGuiApplication::focusWindow()) + : QString(); + QDBusError error = xdgDesktopPortalOpenFile(url, parentWindow, xdgActivationToken); + if (!error.isValid()) + return true; + } +# endif + + if (m_documentLauncher.isEmpty() + && !detectWebBrowser(desktopEnvironment(), false, &m_documentLauncher)) { + qWarning("Unable to detect a launcher for '%s'", qPrintable(url.toString())); + return false; + } + return launch(m_documentLauncher, url, xdgActivationToken); + }; + + if (QGuiApplication::platformName().startsWith("wayland"_L1)) { + runWithXdgActivationToken([openDocumentInternal, url](const QString &token) { + openDocumentInternal(url, token); + }); + + return true; + } else { + return openDocumentInternal(url, QString()); } - return launch(m_documentLauncher, url); } #else +QGenericUnixServices::QGenericUnixServices() = default; + QByteArray QGenericUnixServices::desktopEnvironment() const { return QByteArrayLiteral("UNKNOWN"); @@ -357,18 +542,72 @@ QByteArray QGenericUnixServices::desktopEnvironment() const bool QGenericUnixServices::openUrl(const QUrl &url) { - Q_UNUSED(url) + Q_UNUSED(url); qWarning("openUrl() not supported on this platform"); return false; } bool QGenericUnixServices::openDocument(const QUrl &url) { - Q_UNUSED(url) + Q_UNUSED(url); qWarning("openDocument() not supported on this platform"); return false; } +QPlatformServiceColorPicker *QGenericUnixServices::colorPicker(QWindow *parent) +{ + Q_UNUSED(parent); + return nullptr; +} + #endif // QT_NO_MULTIPROCESS +QString QGenericUnixServices::portalWindowIdentifier(QWindow *window) +{ + Q_UNUSED(window); + return QString(); +} + +bool QGenericUnixServices::hasCapability(Capability capability) const +{ + switch (capability) { + case Capability::ColorPicking: + return m_hasScreenshotPortalWithColorPicking; + } + return false; +} + +void QGenericUnixServices::setApplicationBadge(qint64 number) +{ +#if QT_CONFIG(dbus) + if (qGuiApp->desktopFileName().isEmpty()) { + qWarning("QGuiApplication::desktopFileName() is empty"); + return; + } + + + const QString launcherUrl = QStringLiteral("application://") + qGuiApp->desktopFileName() + QStringLiteral(".desktop"); + const qint64 count = qBound(0, number, 9999); + QVariantMap dbusUnityProperties; + + if (count > 0) { + dbusUnityProperties[QStringLiteral("count")] = count; + dbusUnityProperties[QStringLiteral("count-visible")] = true; + } else { + dbusUnityProperties[QStringLiteral("count-visible")] = false; + } + + auto signal = QDBusMessage::createSignal(QStringLiteral("/com/canonical/unity/launcherentry/") + + qGuiApp->applicationName(), QStringLiteral("com.canonical.Unity.LauncherEntry"), QStringLiteral("Update")); + + signal.setArguments({launcherUrl, dbusUnityProperties}); + + QDBusConnection::sessionBus().send(signal); +#else + Q_UNUSED(number) +#endif +} + QT_END_NAMESPACE + +#include "qgenericunixservices.moc" diff --git a/src/gui/platform/unix/qgenericunixservices_p.h b/src/gui/platform/unix/qgenericunixservices_p.h index 0634360c41..56e15103f7 100644 --- a/src/gui/platform/unix/qgenericunixservices_p.h +++ b/src/gui/platform/unix/qgenericunixservices_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QGENERICUNIXDESKTOPSERVICES_H #define QGENERICUNIXDESKTOPSERVICES_H @@ -53,22 +17,31 @@ #include <qpa/qplatformservices.h> #include <QtCore/QString> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE +class QWindow; + class Q_GUI_EXPORT QGenericUnixServices : public QPlatformServices { public: - QGenericUnixServices() {} + QGenericUnixServices(); QByteArray desktopEnvironment() const override; + bool hasCapability(Capability capability) const override; bool openUrl(const QUrl &url) override; bool openDocument(const QUrl &url) override; + QPlatformServiceColorPicker *colorPicker(QWindow *parent = nullptr) override; + + void setApplicationBadge(qint64 number); + virtual QString portalWindowIdentifier(QWindow *window); private: QString m_webBrowser; QString m_documentLauncher; + bool m_hasScreenshotPortalWithColorPicking = false; }; QT_END_NAMESPACE diff --git a/src/gui/platform/unix/qgenericunixthemes.cpp b/src/gui/platform/unix/qgenericunixthemes.cpp index 352c975400..90971ba94c 100644 --- a/src/gui/platform/unix/qgenericunixthemes.cpp +++ b/src/gui/platform/unix/qgenericunixthemes.cpp @@ -1,72 +1,44 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qgenericunixthemes_p.h" -#include "qpa/qplatformtheme_p.h" -#include "qpa/qplatformfontdatabase.h" // lcQpaFonts - -#include <QtGui/QPalette> -#include <QtGui/QFont> -#include <QtGui/QGuiApplication> -#include <QtCore/QDir> -#include <QtCore/QFileInfo> -#include <QtCore/QFile> -#include <QtCore/QDebug> -#include <QtCore/QHash> +#include <QPalette> +#include <QFont> +#include <QGuiApplication> +#include <QDir> +#include <QFileInfo> +#include <QFile> +#include <QDebug> +#include <QHash> +#include <QLoggingCategory> +#include <QVariant> +#include <QStandardPaths> +#include <QStringList> #if QT_CONFIG(mimetype) -#include <QtCore/QMimeDatabase> +#include <QMimeDatabase> #endif -#include <QtCore/QLoggingCategory> #if QT_CONFIG(settings) -#include <QtCore/QSettings> +#include <QSettings> #endif -#include <QtCore/QVariant> -#include <QtCore/QStandardPaths> -#include <QtCore/QStringList> -#include <private/qguiapplication_p.h> + +#include <qpa/qplatformfontdatabase.h> // lcQpaFonts #include <qpa/qplatformintegration.h> #include <qpa/qplatformservices.h> #include <qpa/qplatformdialoghelper.h> +#include <qpa/qplatformtheme_p.h> + +#include <private/qguiapplication_p.h> #ifndef QT_NO_DBUS +#include <QDBusConnectionInterface> #include <private/qdbusplatformmenu_p.h> #include <private/qdbusmenubar_p.h> +#include <private/qflatmap_p.h> +#include <QJsonDocument> +#include <QJsonArray> +#include <QJsonObject> +#include <QJsonValue> +#include <QJsonParseError> #endif #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON) #include <private/qdbustrayicon_p.h> @@ -75,21 +47,26 @@ #include <algorithm> QT_BEGIN_NAMESPACE +#ifndef QT_NO_DBUS +Q_LOGGING_CATEGORY(lcQpaThemeDBus, "qt.qpa.theme.dbus") +#endif + +using namespace Qt::StringLiterals; Q_DECLARE_LOGGING_CATEGORY(qLcTray) ResourceHelper::ResourceHelper() { - std::fill(palettes, palettes + QPlatformTheme::NPalettes, static_cast<QPalette *>(0)); - std::fill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(0)); + std::fill(palettes, palettes + QPlatformTheme::NPalettes, static_cast<QPalette *>(nullptr)); + std::fill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(nullptr)); } void ResourceHelper::clear() { qDeleteAll(palettes, palettes + QPlatformTheme::NPalettes); qDeleteAll(fonts, fonts + QPlatformTheme::NFonts); - std::fill(palettes, palettes + QPlatformTheme::NPalettes, static_cast<QPalette *>(0)); - std::fill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(0)); + std::fill(palettes, palettes + QPlatformTheme::NPalettes, static_cast<QPalette *>(nullptr)); + std::fill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(nullptr)); } const char *QGenericUnixTheme::name = "generic"; @@ -101,20 +78,31 @@ static const char defaultFixedFontNameC[] = "monospace"; enum { defaultSystemFontSize = 9 }; #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON) -static bool isDBusTrayAvailable() { - static bool dbusTrayAvailable = false; - static bool dbusTrayAvailableKnown = false; - if (!dbusTrayAvailableKnown) { - QDBusMenuConnection conn; - if (conn.isStatusNotifierHostRegistered()) - dbusTrayAvailable = true; - dbusTrayAvailableKnown = true; - qCDebug(qLcTray) << "D-Bus tray available:" << dbusTrayAvailable; - } - return dbusTrayAvailable; +static bool shouldUseDBusTray() { + // There's no other tray implementation to fallback to on non-X11 + // and QDBusTrayIcon can register the icon on the fly after creation + if (QGuiApplication::platformName() != "xcb"_L1) + return true; + const bool result = QDBusMenuConnection().isWatcherRegistered(); + qCDebug(qLcTray) << "D-Bus tray available:" << result; + return result; } #endif +static QString mouseCursorTheme() +{ + static QString themeName = qEnvironmentVariable("XCURSOR_THEME"); + return themeName; +} + +static QSize mouseCursorSize() +{ + constexpr int defaultCursorSize = 24; + static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE"); + static const int s = xCursorSize > 0 ? xCursorSize : defaultCursorSize; + return QSize(s, s); +} + #ifndef QT_NO_DBUS static bool checkDBusGlobalMenuAvailable() { @@ -130,15 +118,296 @@ static bool isDBusGlobalMenuAvailable() static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable(); return dbusGlobalMenuAvailable; } + +/*! + * \internal + * The QGenericUnixThemeDBusListener class listens to the SettingChanged DBus signal + * and translates it into combinations of the enums \c Provider and \c Setting. + * Upon construction, it logs success/failure of the DBus connection. + * + * The signal settingChanged delivers the normalized setting type and the new value as a string. + * It is emitted on known setting types only. + */ + +class QGenericUnixThemeDBusListener : public QObject +{ + Q_OBJECT + +public: + + enum class Provider { + Kde, + Gtk, + Gnome, + }; + Q_ENUM(Provider) + + enum class Setting { + Theme, + ApplicationStyle, + ColorScheme, + }; + Q_ENUM(Setting) + + QGenericUnixThemeDBusListener(); + QGenericUnixThemeDBusListener(const QString &service, const QString &path, + const QString &interface, const QString &signal); + +private Q_SLOTS: + void onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value); + +Q_SIGNALS: + void settingChanged(QGenericUnixThemeDBusListener::Provider provider, + QGenericUnixThemeDBusListener::Setting setting, + const QString &value); + +private: + struct DBusKey + { + QString location; + QString key; + DBusKey(const QString &loc, const QString &k) : location(loc), key(k) {}; + bool operator<(const DBusKey &other) const + { + return location + key < other.location + other.key; + } + }; + + struct ChangeSignal + { + Provider provider; + Setting setting; + ChangeSignal(Provider p, Setting s) : provider(p), setting(s) {} + ChangeSignal() {} + }; + + // Json keys + static constexpr QLatin1StringView s_dbusLocation = QLatin1StringView("DBusLocation"); + static constexpr QLatin1StringView s_dbusKey = QLatin1StringView("DBusKey"); + static constexpr QLatin1StringView s_provider = QLatin1StringView("Provider"); + static constexpr QLatin1StringView s_setting = QLatin1StringView("Setting"); + static constexpr QLatin1StringView s_signals = QLatin1StringView("DbusSignals"); + static constexpr QLatin1StringView s_root = QLatin1StringView("Qt.qpa.DBusSignals"); + + QFlatMap <DBusKey, ChangeSignal> m_signalMap; + + void init(const QString &service, const QString &path, + const QString &interface, const QString &signal); + + std::optional<ChangeSignal> findSignal(const QString &location, const QString &key) const; + void populateSignalMap(); + void loadJson(const QString &fileName); + void saveJson(const QString &fileName) const; +}; + +QGenericUnixThemeDBusListener::QGenericUnixThemeDBusListener(const QString &service, + const QString &path, const QString &interface, const QString &signal) +{ + init (service, path, interface, signal); +} + +QGenericUnixThemeDBusListener::QGenericUnixThemeDBusListener() +{ + static constexpr QLatin1StringView service(""); + static constexpr QLatin1StringView path("/org/freedesktop/portal/desktop"); + static constexpr QLatin1StringView interface("org.freedesktop.portal.Settings"); + static constexpr QLatin1StringView signal("SettingChanged"); + + init (service, path, interface, signal); +} + +void QGenericUnixThemeDBusListener::init(const QString &service, const QString &path, + const QString &interface, const QString &signal) +{ + QDBusConnection dbus = QDBusConnection::sessionBus(); + const bool dBusRunning = dbus.isConnected(); + bool dBusSignalConnected = false; +#define LOG service << path << interface << signal; + + if (dBusRunning) { + populateSignalMap(); + qRegisterMetaType<QDBusVariant>(); + dBusSignalConnected = dbus.connect(service, path, interface, signal, this, + SLOT(onSettingChanged(QString,QString,QDBusVariant))); + } + + if (dBusSignalConnected) { + // Connection successful + qCDebug(lcQpaThemeDBus) << LOG; + } else { + if (dBusRunning) { + // DBus running, but connection failed + qCWarning(lcQpaThemeDBus) << "DBus connection failed:" << LOG; + } else { + // DBus not running + qCWarning(lcQpaThemeDBus) << "Session DBus not running."; + } + qCWarning(lcQpaThemeDBus) << "Application will not react to setting changes.\n" + << "Check your DBus installation."; + } +#undef LOG +} + +void QGenericUnixThemeDBusListener::loadJson(const QString &fileName) +{ + Q_ASSERT(!fileName.isEmpty()); +#define CHECK(cond, warning)\ + if (!cond) {\ + qCWarning(lcQpaThemeDBus) << fileName << warning << "Falling back to default.";\ + return;\ + } + +#define PARSE(var, enumeration, string)\ + enumeration var;\ + {\ + bool success;\ + const int val = QMetaEnum::fromType<enumeration>().keyToValue(string.toLatin1(), &success);\ + CHECK(success, "Parse Error: Invalid value" << string << "for" << #var);\ + var = static_cast<enumeration>(val);\ + } + + QFile file(fileName); + CHECK(file.exists(), fileName << "doesn't exist."); + CHECK(file.open(QIODevice::ReadOnly), "could not be opened for reading."); + + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); + CHECK((error.error == QJsonParseError::NoError), error.errorString()); + CHECK(doc.isObject(), "Parse Error: Expected root object" << s_root); + + const QJsonObject &root = doc.object(); + CHECK(root.contains(s_root), "Parse Error: Expected root object" << s_root); + CHECK(root[s_root][s_signals].isArray(), "Parse Error: Expected array" << s_signals); + + const QJsonArray &sigs = root[s_root][s_signals].toArray(); + CHECK((sigs.count() > 0), "Parse Error: Found empty array" << s_signals); + + for (auto sig = sigs.constBegin(); sig != sigs.constEnd(); ++sig) { + CHECK(sig->isObject(), "Parse Error: Expected object array" << s_signals); + const QJsonObject &obj = sig->toObject(); + CHECK(obj.contains(s_dbusLocation), "Parse Error: Expected key" << s_dbusLocation); + CHECK(obj.contains(s_dbusKey), "Parse Error: Expected key" << s_dbusKey); + CHECK(obj.contains(s_provider), "Parse Error: Expected key" << s_provider); + CHECK(obj.contains(s_setting), "Parse Error: Expected key" << s_setting); + const QString &location = obj[s_dbusLocation].toString(); + const QString &key = obj[s_dbusKey].toString(); + const QString &providerString = obj[s_provider].toString(); + const QString &settingString = obj[s_setting].toString(); + PARSE(provider, Provider, providerString); + PARSE(setting, Setting, settingString); + const DBusKey dkey(location, key); + CHECK (!m_signalMap.contains(dkey), "Duplicate key" << location << key); + m_signalMap.insert(dkey, ChangeSignal(provider, setting)); + } +#undef PARSE +#undef CHECK + + if (m_signalMap.count() > 0) + qCInfo(lcQpaThemeDBus) << "Successfully imported" << fileName; + else + qCWarning(lcQpaThemeDBus) << "No data imported from" << fileName << "falling back to default."; + +#ifdef QT_DEBUG + const int count = m_signalMap.count(); + if (count == 0) + return; + + qCDebug(lcQpaThemeDBus) << "Listening to" << count << "signals:"; + for (auto it = m_signalMap.constBegin(); it != m_signalMap.constEnd(); ++it) { + qDebug() << it.key().key << it.key().location << "mapped to" + << it.value().provider << it.value().setting; + } + #endif +} + +void QGenericUnixThemeDBusListener::saveJson(const QString &fileName) const +{ + Q_ASSERT(!m_signalMap.isEmpty()); + Q_ASSERT(!fileName.isEmpty()); + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) { + qCWarning(lcQpaThemeDBus) << fileName << "could not be opened for writing."; + return; + } + + QJsonArray sigs; + for (auto sig = m_signalMap.constBegin(); sig != m_signalMap.constEnd(); ++sig) { + const DBusKey &dkey = sig.key(); + const ChangeSignal &csig = sig.value(); + QJsonObject obj; + obj[s_dbusLocation] = dkey.location; + obj[s_dbusKey] = dkey.key; + obj[s_provider] = QLatin1StringView(QMetaEnum::fromType<Provider>() + .valueToKey(static_cast<int>(csig.provider))); + obj[s_setting] = QLatin1StringView(QMetaEnum::fromType<Setting>() + .valueToKey(static_cast<int>(csig.setting))); + sigs.append(obj); + } + QJsonObject obj; + obj[s_signals] = sigs; + QJsonObject root; + root[s_root] = obj; + QJsonDocument doc(root); + file.write(doc.toJson()); + file.close(); +} + +void QGenericUnixThemeDBusListener::populateSignalMap() +{ + m_signalMap.clear(); + const QString &loadJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS"); + if (!loadJsonFile.isEmpty()) + loadJson(loadJsonFile); + if (!m_signalMap.isEmpty()) + return; + + m_signalMap.insert(DBusKey("org.kde.kdeglobals.KDE"_L1, "widgetStyle"_L1), + ChangeSignal(Provider::Kde, Setting::ApplicationStyle)); + + m_signalMap.insert(DBusKey("org.kde.kdeglobals.General"_L1, "ColorScheme"_L1), + ChangeSignal(Provider::Kde, Setting::Theme)); + + m_signalMap.insert(DBusKey("org.gnome.desktop.interface"_L1, "gtk-theme"_L1), + ChangeSignal(Provider::Gtk, Setting::Theme)); + + m_signalMap.insert(DBusKey("org.freedesktop.appearance"_L1, "color-scheme"_L1), + ChangeSignal(Provider::Gnome, Setting::ColorScheme)); + + const QString &saveJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS_SAVE"); + if (!saveJsonFile.isEmpty()) + saveJson(saveJsonFile); +} + +std::optional<QGenericUnixThemeDBusListener::ChangeSignal> + QGenericUnixThemeDBusListener::findSignal(const QString &location, const QString &key) const +{ + const DBusKey dkey(location, key); + std::optional<QGenericUnixThemeDBusListener::ChangeSignal> ret; + if (m_signalMap.contains(dkey)) + ret.emplace(m_signalMap.value(dkey)); + + return ret; +} + +void QGenericUnixThemeDBusListener::onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value) +{ + auto sig = findSignal(location, key); + if (!sig.has_value()) + return; + + emit settingChanged(sig.value().provider, sig.value().setting, value.variant().toString()); +} + +#endif //QT_NO_DBUS class QGenericUnixThemePrivate : public QPlatformThemePrivate { public: QGenericUnixThemePrivate() : QPlatformThemePrivate() - , systemFont(QLatin1String(defaultSystemFontNameC), defaultSystemFontSize) - , fixedFont(QLatin1String(defaultFixedFontNameC), systemFont.pointSize()) + , systemFont(QLatin1StringView(defaultSystemFontNameC), defaultSystemFontSize) + , fixedFont(QLatin1StringView(defaultFixedFontNameC), systemFont.pointSize()) { fixedFont.setStyleHint(QFont::TypeWriter); qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont; @@ -162,7 +431,7 @@ const QFont *QGenericUnixTheme::font(Font type) const case QPlatformTheme::FixedFont: return &d->fixedFont; default: - return 0; + return nullptr; } } @@ -171,7 +440,7 @@ QStringList QGenericUnixTheme::xdgIconThemePaths() { QStringList paths; // Add home directory first in search path - const QFileInfo homeIconDir(QDir::homePath() + QLatin1String("/.icons")); + const QFileInfo homeIconDir(QDir::homePath() + "/.icons"_L1); if (homeIconDir.isDir()) paths.prepend(homeIconDir.absoluteFilePath()); @@ -204,7 +473,7 @@ QPlatformMenuBar *QGenericUnixTheme::createPlatformMenuBar() const #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON) QPlatformSystemTrayIcon *QGenericUnixTheme::createPlatformSystemTrayIcon() const { - if (isDBusTrayAvailable()) + if (shouldUseDBusTray()) return new QDBusTrayIcon(); return nullptr; } @@ -230,6 +499,12 @@ QVariant QGenericUnixTheme::themeHint(ThemeHint hint) const return QVariant(int(X11KeyboardScheme)); case QPlatformTheme::UiEffects: return QVariant(int(HoverEffect)); + case QPlatformTheme::MouseCursorTheme: + return QVariant(mouseCursorTheme()); + case QPlatformTheme::MouseCursorSize: + return QVariant(mouseCursorSize()); + case QPlatformTheme::PreferFileIconFromTheme: + return true; default: break; } @@ -263,21 +538,63 @@ static QIcon xdgFileIcon(const QFileInfo &fileInfo) #if QT_CONFIG(settings) class QKdeThemePrivate : public QPlatformThemePrivate { + public: - QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion) - : kdeDirs(kdeDirs) - , kdeVersion(kdeVersion) - { } + enum class KdeSettingType { + Root, + KDE, + Icons, + ToolBarIcons, + ToolBarStyle, + Fonts, + Colors, + }; + + enum class KdeSetting { + WidgetStyle, + ColorScheme, + SingleClick, + ShowIconsOnPushButtons, + IconTheme, + ToolBarIconSize, + ToolButtonStyle, + WheelScrollLines, + DoubleClickInterval, + StartDragDistance, + StartDragTime, + CursorBlinkRate, + Font, + Fixed, + MenuFont, + ToolBarFont, + ButtonBackground, + WindowBackground, + ViewForeground, + WindowForeground, + ViewBackground, + SelectionBackground, + SelectionForeground, + ViewBackgroundAlternate, + ButtonForeground, + ViewForegroundLink, + ViewForegroundVisited, + TooltipBackground, + TooltipForeground, + }; + + QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion); static QString kdeGlobals(const QString &kdeDir, int kdeVersion) { if (kdeVersion > 4) - return kdeDir + QLatin1String("/kdeglobals"); - return kdeDir + QLatin1String("/share/config/kdeglobals"); + return kdeDir + "/kdeglobals"_L1; + return kdeDir + "/share/config/kdeglobals"_L1; } void refresh(); - static QVariant readKdeSetting(const QString &key, const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings); + static QVariant readKdeSetting(KdeSetting s, const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &settings); + QVariant readKdeSetting(KdeSetting s) const; + void clearKdeSettings() const; static void readKdeSystemPalette(const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings, QPalette *pal); static QFont *kdeFont(const QVariant &fontValue); static QStringList kdeIconThemeSearchPaths(const QStringList &kdeDirs); @@ -298,114 +615,304 @@ public: int startDragDist = 10; int startDragTime = 500; int cursorBlinkRate = 1000; + Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown; + void updateColorScheme(const QString &themeName); + +private: + mutable QHash<QString, QSettings *> kdeSettings; +#ifndef QT_NO_DBUS + std::unique_ptr<QGenericUnixThemeDBusListener> dbus; + bool initDbus(); + void settingChangedHandler(QGenericUnixThemeDBusListener::Provider provider, + QGenericUnixThemeDBusListener::Setting setting, + const QString &value); +#endif // QT_NO_DBUS }; +#ifndef QT_NO_DBUS +void QKdeThemePrivate::settingChangedHandler(QGenericUnixThemeDBusListener::Provider provider, + QGenericUnixThemeDBusListener::Setting setting, + const QString &value) +{ + if (provider != QGenericUnixThemeDBusListener::Provider::Kde) + return; + + switch (setting) { + case QGenericUnixThemeDBusListener::Setting::ColorScheme: + qCDebug(lcQpaThemeDBus) << "KDE color theme changed to:" << value; + break; + case QGenericUnixThemeDBusListener::Setting::Theme: + qCDebug(lcQpaThemeDBus) << "KDE global theme changed to:" << value; + break; + case QGenericUnixThemeDBusListener::Setting::ApplicationStyle: + qCDebug(lcQpaThemeDBus) << "KDE application style changed to:" << value; + break; + } + + refresh(); +} + +bool QKdeThemePrivate::initDbus() +{ + dbus.reset(new QGenericUnixThemeDBusListener()); + Q_ASSERT(dbus); + + // Wrap slot in a lambda to avoid inheriting QKdeThemePrivate from QObject + auto wrapper = [this](QGenericUnixThemeDBusListener::Provider provider, + QGenericUnixThemeDBusListener::Setting setting, + const QString &value) { + settingChangedHandler(provider, setting, value); + }; + + return QObject::connect(dbus.get(), &QGenericUnixThemeDBusListener::settingChanged, dbus.get(), wrapper); +} +#endif // QT_NO_DBUS + +QKdeThemePrivate::QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion) + : kdeDirs(kdeDirs), kdeVersion(kdeVersion) +{ +#ifndef QT_NO_DBUS + initDbus(); +#endif // QT_NO_DBUS +} + +static constexpr QLatin1StringView settingsPrefix(QKdeThemePrivate::KdeSettingType type) +{ + switch (type) { + case QKdeThemePrivate::KdeSettingType::Root: + return QLatin1StringView(); + case QKdeThemePrivate::KdeSettingType::KDE: + return QLatin1StringView("KDE/"); + case QKdeThemePrivate::KdeSettingType::Fonts: + return QLatin1StringView(); + case QKdeThemePrivate::KdeSettingType::Colors: + return QLatin1StringView("Colors:"); + case QKdeThemePrivate::KdeSettingType::Icons: + return QLatin1StringView("Icons/"); + case QKdeThemePrivate::KdeSettingType::ToolBarIcons: + return QLatin1StringView("ToolbarIcons/"); + case QKdeThemePrivate::KdeSettingType::ToolBarStyle: + return QLatin1StringView("Toolbar style/"); + } + Q_UNREACHABLE_RETURN(QLatin1StringView()); +} + +static constexpr QKdeThemePrivate::KdeSettingType settingsType(QKdeThemePrivate::KdeSetting setting) +{ +#define CASE(s, type) case QKdeThemePrivate::KdeSetting::s:\ + return QKdeThemePrivate::KdeSettingType::type + + switch (setting) { + CASE(WidgetStyle, Root); + CASE(ColorScheme, Root); + CASE(SingleClick, KDE); + CASE(ShowIconsOnPushButtons, KDE); + CASE(IconTheme, Icons); + CASE(ToolBarIconSize, ToolBarIcons); + CASE(ToolButtonStyle, ToolBarStyle); + CASE(WheelScrollLines, KDE); + CASE(DoubleClickInterval, KDE); + CASE(StartDragDistance, KDE); + CASE(StartDragTime, KDE); + CASE(CursorBlinkRate, KDE); + CASE(Font, Root); + CASE(Fixed, Root); + CASE(MenuFont, Root); + CASE(ToolBarFont, Root); + CASE(ButtonBackground, Colors); + CASE(WindowBackground, Colors); + CASE(ViewForeground, Colors); + CASE(WindowForeground, Colors); + CASE(ViewBackground, Colors); + CASE(SelectionBackground, Colors); + CASE(SelectionForeground, Colors); + CASE(ViewBackgroundAlternate, Colors); + CASE(ButtonForeground, Colors); + CASE(ViewForegroundLink, Colors); + CASE(ViewForegroundVisited, Colors); + CASE(TooltipBackground, Colors); + CASE(TooltipForeground, Colors); + }; + Q_UNREACHABLE_RETURN(QKdeThemePrivate::KdeSettingType::Root); +} +#undef CASE + +static constexpr QLatin1StringView settingsKey(QKdeThemePrivate::KdeSetting setting) +{ + switch (setting) { + case QKdeThemePrivate::KdeSetting::WidgetStyle: + return QLatin1StringView("widgetStyle"); + case QKdeThemePrivate::KdeSetting::ColorScheme: + return QLatin1StringView("ColorScheme"); + case QKdeThemePrivate::KdeSetting::SingleClick: + return QLatin1StringView("SingleClick"); + case QKdeThemePrivate::KdeSetting::ShowIconsOnPushButtons: + return QLatin1StringView("ShowIconsOnPushButtons"); + case QKdeThemePrivate::KdeSetting::IconTheme: + return QLatin1StringView("Theme"); + case QKdeThemePrivate::KdeSetting::ToolBarIconSize: + return QLatin1StringView("Size"); + case QKdeThemePrivate::KdeSetting::ToolButtonStyle: + return QLatin1StringView("ToolButtonStyle"); + case QKdeThemePrivate::KdeSetting::WheelScrollLines: + return QLatin1StringView("WheelScrollLines"); + case QKdeThemePrivate::KdeSetting::DoubleClickInterval: + return QLatin1StringView("DoubleClickInterval"); + case QKdeThemePrivate::KdeSetting::StartDragDistance: + return QLatin1StringView("StartDragDist"); + case QKdeThemePrivate::KdeSetting::StartDragTime: + return QLatin1StringView("StartDragTime"); + case QKdeThemePrivate::KdeSetting::CursorBlinkRate: + return QLatin1StringView("CursorBlinkRate"); + case QKdeThemePrivate::KdeSetting::Font: + return QLatin1StringView("font"); + case QKdeThemePrivate::KdeSetting::Fixed: + return QLatin1StringView("fixed"); + case QKdeThemePrivate::KdeSetting::MenuFont: + return QLatin1StringView("menuFont"); + case QKdeThemePrivate::KdeSetting::ToolBarFont: + return QLatin1StringView("toolBarFont"); + case QKdeThemePrivate::KdeSetting::ButtonBackground: + return QLatin1StringView("Button/BackgroundNormal"); + case QKdeThemePrivate::KdeSetting::WindowBackground: + return QLatin1StringView("Window/BackgroundNormal"); + case QKdeThemePrivate::KdeSetting::ViewForeground: + return QLatin1StringView("View/ForegroundNormal"); + case QKdeThemePrivate::KdeSetting::WindowForeground: + return QLatin1StringView("Window/ForegroundNormal"); + case QKdeThemePrivate::KdeSetting::ViewBackground: + return QLatin1StringView("View/BackgroundNormal"); + case QKdeThemePrivate::KdeSetting::SelectionBackground: + return QLatin1StringView("Selection/BackgroundNormal"); + case QKdeThemePrivate::KdeSetting::SelectionForeground: + return QLatin1StringView("Selection/ForegroundNormal"); + case QKdeThemePrivate::KdeSetting::ViewBackgroundAlternate: + return QLatin1StringView("View/BackgroundAlternate"); + case QKdeThemePrivate::KdeSetting::ButtonForeground: + return QLatin1StringView("Button/ForegroundNormal"); + case QKdeThemePrivate::KdeSetting::ViewForegroundLink: + return QLatin1StringView("View/ForegroundLink"); + case QKdeThemePrivate::KdeSetting::ViewForegroundVisited: + return QLatin1StringView("View/ForegroundVisited"); + case QKdeThemePrivate::KdeSetting::TooltipBackground: + return QLatin1StringView("Tooltip/BackgroundNormal"); + case QKdeThemePrivate::KdeSetting::TooltipForeground: + return QLatin1StringView("Tooltip/ForegroundNormal"); + }; + Q_UNREACHABLE_RETURN(QLatin1StringView()); +} + void QKdeThemePrivate::refresh() { resources.clear(); + clearKdeSettings(); toolButtonStyle = Qt::ToolButtonTextBesideIcon; toolBarIconSize = 0; styleNames.clear(); if (kdeVersion >= 5) styleNames << QStringLiteral("breeze"); - styleNames << QStringLiteral("Oxygen") << QStringLiteral("fusion") << QStringLiteral("windows"); + styleNames << QStringLiteral("Oxygen") << QStringLiteral("Fusion") << QStringLiteral("windows"); if (kdeVersion >= 5) iconFallbackThemeName = iconThemeName = QStringLiteral("breeze"); else iconFallbackThemeName = iconThemeName = QStringLiteral("oxygen"); - QHash<QString, QSettings*> kdeSettings; - QPalette systemPalette = QPalette(); readKdeSystemPalette(kdeDirs, kdeVersion, kdeSettings, &systemPalette); resources.palettes[QPlatformTheme::SystemPalette] = new QPalette(systemPalette); //## TODO tooltip color - const QVariant styleValue = readKdeSetting(QStringLiteral("widgetStyle"), kdeDirs, kdeVersion, kdeSettings); + const QVariant styleValue = readKdeSetting(KdeSetting::WidgetStyle); if (styleValue.isValid()) { const QString style = styleValue.toString(); if (style != styleNames.front()) styleNames.push_front(style); } - const QVariant singleClickValue = readKdeSetting(QStringLiteral("KDE/SingleClick"), kdeDirs, kdeVersion, kdeSettings); + const QVariant colorScheme = readKdeSetting(KdeSetting::ColorScheme); + + updateColorScheme(colorScheme.toString()); + + const QVariant singleClickValue = readKdeSetting(KdeSetting::SingleClick); if (singleClickValue.isValid()) singleClick = singleClickValue.toBool(); - const QVariant showIconsOnPushButtonsValue = readKdeSetting(QStringLiteral("KDE/ShowIconsOnPushButtons"), kdeDirs, kdeVersion, kdeSettings); + const QVariant showIconsOnPushButtonsValue = readKdeSetting(KdeSetting::ShowIconsOnPushButtons); if (showIconsOnPushButtonsValue.isValid()) showIconsOnPushButtons = showIconsOnPushButtonsValue.toBool(); - const QVariant themeValue = readKdeSetting(QStringLiteral("Icons/Theme"), kdeDirs, kdeVersion, kdeSettings); + const QVariant themeValue = readKdeSetting(KdeSetting::IconTheme); if (themeValue.isValid()) iconThemeName = themeValue.toString(); - const QVariant toolBarIconSizeValue = readKdeSetting(QStringLiteral("ToolbarIcons/Size"), kdeDirs, kdeVersion, kdeSettings); + const QVariant toolBarIconSizeValue = readKdeSetting(KdeSetting::ToolBarIconSize); if (toolBarIconSizeValue.isValid()) toolBarIconSize = toolBarIconSizeValue.toInt(); - const QVariant toolbarStyleValue = readKdeSetting(QStringLiteral("Toolbar style/ToolButtonStyle"), kdeDirs, kdeVersion, kdeSettings); + const QVariant toolbarStyleValue = readKdeSetting(KdeSetting::ToolButtonStyle); if (toolbarStyleValue.isValid()) { const QString toolBarStyle = toolbarStyleValue.toString(); - if (toolBarStyle == QLatin1String("TextBesideIcon")) + if (toolBarStyle == "TextBesideIcon"_L1) toolButtonStyle = Qt::ToolButtonTextBesideIcon; - else if (toolBarStyle == QLatin1String("TextOnly")) + else if (toolBarStyle == "TextOnly"_L1) toolButtonStyle = Qt::ToolButtonTextOnly; - else if (toolBarStyle == QLatin1String("TextUnderIcon")) + else if (toolBarStyle == "TextUnderIcon"_L1) toolButtonStyle = Qt::ToolButtonTextUnderIcon; } - const QVariant wheelScrollLinesValue = readKdeSetting(QStringLiteral("KDE/WheelScrollLines"), kdeDirs, kdeVersion, kdeSettings); + const QVariant wheelScrollLinesValue = readKdeSetting(KdeSetting::WheelScrollLines); if (wheelScrollLinesValue.isValid()) wheelScrollLines = wheelScrollLinesValue.toInt(); - const QVariant doubleClickIntervalValue = readKdeSetting(QStringLiteral("KDE/DoubleClickInterval"), kdeDirs, kdeVersion, kdeSettings); + const QVariant doubleClickIntervalValue = readKdeSetting(KdeSetting::DoubleClickInterval); if (doubleClickIntervalValue.isValid()) doubleClickInterval = doubleClickIntervalValue.toInt(); - const QVariant startDragDistValue = readKdeSetting(QStringLiteral("KDE/StartDragDist"), kdeDirs, kdeVersion, kdeSettings); + const QVariant startDragDistValue = readKdeSetting(KdeSetting::StartDragDistance); if (startDragDistValue.isValid()) startDragDist = startDragDistValue.toInt(); - const QVariant startDragTimeValue = readKdeSetting(QStringLiteral("KDE/StartDragTime"), kdeDirs, kdeVersion, kdeSettings); + const QVariant startDragTimeValue = readKdeSetting(KdeSetting::StartDragTime); if (startDragTimeValue.isValid()) startDragTime = startDragTimeValue.toInt(); - const QVariant cursorBlinkRateValue = readKdeSetting(QStringLiteral("KDE/CursorBlinkRate"), kdeDirs, kdeVersion, kdeSettings); + const QVariant cursorBlinkRateValue = readKdeSetting(KdeSetting::CursorBlinkRate); if (cursorBlinkRateValue.isValid()) { cursorBlinkRate = cursorBlinkRateValue.toInt(); cursorBlinkRate = cursorBlinkRate > 0 ? qBound(200, cursorBlinkRate, 2000) : 0; } // Read system font, ignore 'smallestReadableFont' - if (QFont *systemFont = kdeFont(readKdeSetting(QStringLiteral("font"), kdeDirs, kdeVersion, kdeSettings))) + if (QFont *systemFont = kdeFont(readKdeSetting(KdeSetting::Font))) resources.fonts[QPlatformTheme::SystemFont] = systemFont; else - resources.fonts[QPlatformTheme::SystemFont] = new QFont(QLatin1String(defaultSystemFontNameC), defaultSystemFontSize); + resources.fonts[QPlatformTheme::SystemFont] = new QFont(QLatin1StringView(defaultSystemFontNameC), defaultSystemFontSize); - if (QFont *fixedFont = kdeFont(readKdeSetting(QStringLiteral("fixed"), kdeDirs, kdeVersion, kdeSettings))) { + if (QFont *fixedFont = kdeFont(readKdeSetting(KdeSetting::Fixed))) { resources.fonts[QPlatformTheme::FixedFont] = fixedFont; } else { - fixedFont = new QFont(QLatin1String(defaultFixedFontNameC), defaultSystemFontSize); + fixedFont = new QFont(QLatin1StringView(defaultFixedFontNameC), defaultSystemFontSize); fixedFont->setStyleHint(QFont::TypeWriter); resources.fonts[QPlatformTheme::FixedFont] = fixedFont; } - if (QFont *menuFont = kdeFont(readKdeSetting(QStringLiteral("menuFont"), kdeDirs, kdeVersion, kdeSettings))) { + if (QFont *menuFont = kdeFont(readKdeSetting(KdeSetting::MenuFont))) { resources.fonts[QPlatformTheme::MenuFont] = menuFont; resources.fonts[QPlatformTheme::MenuBarFont] = new QFont(*menuFont); } - if (QFont *toolBarFont = kdeFont(readKdeSetting(QStringLiteral("toolBarFont"), kdeDirs, kdeVersion, kdeSettings))) + if (QFont *toolBarFont = kdeFont(readKdeSetting(KdeSetting::ToolBarFont))) resources.fonts[QPlatformTheme::ToolButtonFont] = toolBarFont; + QWindowSystemInterface::handleThemeChange(); + qCDebug(lcQpaFonts) << "default fonts: system" << resources.fonts[QPlatformTheme::SystemFont] << "fixed" << resources.fonts[QPlatformTheme::FixedFont]; qDeleteAll(kdeSettings); } -QVariant QKdeThemePrivate::readKdeSetting(const QString &key, const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings) +QVariant QKdeThemePrivate::readKdeSetting(KdeSetting s, const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings) { for (const QString &kdeDir : kdeDirs) { QSettings *settings = kdeSettings.value(kdeDir); @@ -417,6 +924,7 @@ QVariant QKdeThemePrivate::readKdeSetting(const QString &key, const QStringList } } if (settings) { + const QString key = settingsPrefix(settingsType(s)) + settingsKey(s); const QVariant value = settings->value(key); if (value.isValid()) return value; @@ -425,6 +933,16 @@ QVariant QKdeThemePrivate::readKdeSetting(const QString &key, const QStringList return QVariant(); } +QVariant QKdeThemePrivate::readKdeSetting(KdeSetting s) const +{ + return readKdeSetting(s, kdeDirs, kdeVersion, kdeSettings); +} + +void QKdeThemePrivate::clearKdeSettings() const +{ + kdeSettings.clear(); +} + // Reads the color from the KDE configuration, and store it in the // palette with the given color role if found. static inline bool kdeColor(QPalette *pal, QPalette::ColorRole role, const QVariant &value) @@ -440,7 +958,7 @@ static inline bool kdeColor(QPalette *pal, QPalette::ColorRole role, const QVari void QKdeThemePrivate::readKdeSystemPalette(const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings, QPalette *pal) { - if (!kdeColor(pal, QPalette::Button, readKdeSetting(QStringLiteral("Colors:Button/BackgroundNormal"), kdeDirs, kdeVersion, kdeSettings))) { + if (!kdeColor(pal, QPalette::Button, readKdeSetting(KdeSetting::ButtonBackground, kdeDirs, kdeVersion, kdeSettings))) { // kcolorscheme.cpp: SetDefaultColors const QColor defaultWindowBackground(214, 210, 208); const QColor defaultButtonBackground(223, 220, 217); @@ -448,18 +966,18 @@ void QKdeThemePrivate::readKdeSystemPalette(const QStringList &kdeDirs, int kdeV return; } - kdeColor(pal, QPalette::Window, readKdeSetting(QStringLiteral("Colors:Window/BackgroundNormal"), kdeDirs, kdeVersion, kdeSettings)); - kdeColor(pal, QPalette::Text, readKdeSetting(QStringLiteral("Colors:View/ForegroundNormal"), kdeDirs, kdeVersion, kdeSettings)); - kdeColor(pal, QPalette::WindowText, readKdeSetting(QStringLiteral("Colors:Window/ForegroundNormal"), kdeDirs, kdeVersion, kdeSettings)); - kdeColor(pal, QPalette::Base, readKdeSetting(QStringLiteral("Colors:View/BackgroundNormal"), kdeDirs, kdeVersion, kdeSettings)); - kdeColor(pal, QPalette::Highlight, readKdeSetting(QStringLiteral("Colors:Selection/BackgroundNormal"), kdeDirs, kdeVersion, kdeSettings)); - kdeColor(pal, QPalette::HighlightedText, readKdeSetting(QStringLiteral("Colors:Selection/ForegroundNormal"), kdeDirs, kdeVersion, kdeSettings)); - kdeColor(pal, QPalette::AlternateBase, readKdeSetting(QStringLiteral("Colors:View/BackgroundAlternate"), kdeDirs, kdeVersion, kdeSettings)); - kdeColor(pal, QPalette::ButtonText, readKdeSetting(QStringLiteral("Colors:Button/ForegroundNormal"), kdeDirs, kdeVersion, kdeSettings)); - kdeColor(pal, QPalette::Link, readKdeSetting(QStringLiteral("Colors:View/ForegroundLink"), kdeDirs, kdeVersion, kdeSettings)); - kdeColor(pal, QPalette::LinkVisited, readKdeSetting(QStringLiteral("Colors:View/ForegroundVisited"), kdeDirs, kdeVersion, kdeSettings)); - kdeColor(pal, QPalette::ToolTipBase, readKdeSetting(QStringLiteral("Colors:Tooltip/BackgroundNormal"), kdeDirs, kdeVersion, kdeSettings)); - kdeColor(pal, QPalette::ToolTipText, readKdeSetting(QStringLiteral("Colors:Tooltip/ForegroundNormal"), kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::Window, readKdeSetting(KdeSetting::WindowBackground, kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::Text, readKdeSetting(KdeSetting::ViewForeground, kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::WindowText, readKdeSetting(KdeSetting::WindowForeground, kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::Base, readKdeSetting(KdeSetting::ViewBackground, kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::Highlight, readKdeSetting(KdeSetting::SelectionBackground, kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::HighlightedText, readKdeSetting(KdeSetting::SelectionForeground, kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::AlternateBase, readKdeSetting(KdeSetting::ViewBackgroundAlternate, kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::ButtonText, readKdeSetting(KdeSetting::ButtonForeground, kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::Link, readKdeSetting(KdeSetting::ViewForegroundLink, kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::LinkVisited, readKdeSetting(KdeSetting::ViewForegroundVisited, kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::ToolTipBase, readKdeSetting(KdeSetting::TooltipBackground, kdeDirs, kdeVersion, kdeSettings)); + kdeColor(pal, QPalette::ToolTipText, readKdeSetting(KdeSetting::TooltipForeground, kdeDirs, kdeVersion, kdeSettings)); // The above code sets _all_ color roles to "normal" colors. In KDE, the disabled // color roles are calculated by applying various effects described in kdeglobals. @@ -521,7 +1039,7 @@ QFont *QKdeThemePrivate::kdeFont(const QVariant &fontValue) const QStringList list = fontValue.toStringList(); if (!list.isEmpty()) { fontFamily = list.first(); - fontDescription = list.join(QLatin1Char(',')); + fontDescription = list.join(u','); } } else { fontDescription = fontFamily = fontValue.toString(); @@ -532,7 +1050,7 @@ QFont *QKdeThemePrivate::kdeFont(const QVariant &fontValue) return new QFont(font); } } - return 0; + return nullptr; } @@ -588,6 +1106,12 @@ QVariant QKdeTheme::themeHint(QPlatformTheme::ThemeHint hint) const return QVariant(d->cursorBlinkRate); case QPlatformTheme::UiEffects: return QVariant(int(HoverEffect)); + case QPlatformTheme::MouseCursorTheme: + return QVariant(mouseCursorTheme()); + case QPlatformTheme::MouseCursorSize: + return QVariant(mouseCursorSize()); + case QPlatformTheme::PreferFileIconFromTheme: + return true; default: break; } @@ -604,6 +1128,47 @@ QIcon QKdeTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions #endif } +Qt::ColorScheme QKdeTheme::colorScheme() const +{ + return d_func()->m_colorScheme; +} + +/*! + \internal + \brief QKdeTheme::updateColorScheme - guess and set a color scheme for unix themes. + KDE themes do not have a color scheme property. + The key words "dark" or "light" are usually part of the theme name. + This is, however, not a mandatory convention. + + If \param themeName contains a valid key word, the respective color scheme is set. + If it doesn't, the color scheme is heuristically determined by comparing text and base color + of the system palette. + */ +void QKdeThemePrivate::updateColorScheme(const QString &themeName) +{ + if (themeName.contains(QLatin1StringView("light"), Qt::CaseInsensitive)) { + m_colorScheme = Qt::ColorScheme::Light; + return; + } + if (themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive)) { + m_colorScheme = Qt::ColorScheme::Dark; + return; + } + + if (systemPalette) { + if (systemPalette->text().color().lightness() < systemPalette->base().color().lightness()) { + m_colorScheme = Qt::ColorScheme::Light; + return; + } + if (systemPalette->text().color().lightness() > systemPalette->base().color().lightness()) { + m_colorScheme = Qt::ColorScheme::Dark; + return; + } + } + + m_colorScheme = Qt::ColorScheme::Unknown; +} + const QPalette *QKdeTheme::palette(Palette type) const { Q_D(const QKdeTheme); @@ -621,7 +1186,7 @@ QPlatformTheme *QKdeTheme::createKdeTheme() const QByteArray kdeVersionBA = qgetenv("KDE_SESSION_VERSION"); const int kdeVersion = kdeVersionBA.toInt(); if (kdeVersion < 4) - return 0; + return nullptr; if (kdeVersion > 4) // Plasma 5 follows XDG spec @@ -641,31 +1206,31 @@ QPlatformTheme *QKdeTheme::createKdeTheme() const QString kdeDirsVar = QFile::decodeName(qgetenv("KDEDIRS")); if (!kdeDirsVar.isEmpty()) - kdeDirs += kdeDirsVar.split(QLatin1Char(':'), Qt::SkipEmptyParts); + kdeDirs += kdeDirsVar.split(u':', Qt::SkipEmptyParts); - const QString kdeVersionHomePath = QDir::homePath() + QLatin1String("/.kde") + QLatin1String(kdeVersionBA); + const QString kdeVersionHomePath = QDir::homePath() + "/.kde"_L1 + QLatin1StringView(kdeVersionBA); if (QFileInfo(kdeVersionHomePath).isDir()) kdeDirs += kdeVersionHomePath; - const QString kdeHomePath = QDir::homePath() + QLatin1String("/.kde"); + const QString kdeHomePath = QDir::homePath() + "/.kde"_L1; if (QFileInfo(kdeHomePath).isDir()) kdeDirs += kdeHomePath; - const QString kdeRcPath = QLatin1String("/etc/kde") + QLatin1String(kdeVersionBA) + QLatin1String("rc"); + const QString kdeRcPath = "/etc/kde"_L1 + QLatin1StringView(kdeVersionBA) + "rc"_L1; if (QFileInfo(kdeRcPath).isReadable()) { QSettings kdeSettings(kdeRcPath, QSettings::IniFormat); kdeSettings.beginGroup(QStringLiteral("Directories-default")); kdeDirs += kdeSettings.value(QStringLiteral("prefixes")).toStringList(); } - const QString kdeVersionPrefix = QLatin1String("/etc/kde") + QLatin1String(kdeVersionBA); + const QString kdeVersionPrefix = "/etc/kde"_L1 + QLatin1StringView(kdeVersionBA); if (QFileInfo(kdeVersionPrefix).isDir()) kdeDirs += kdeVersionPrefix; kdeDirs.removeDuplicates(); if (kdeDirs.isEmpty()) { qWarning("Unable to determine KDE dirs"); - return 0; + return nullptr; } return new QKdeTheme(kdeDirs, kdeVersion); @@ -683,7 +1248,7 @@ QPlatformMenuBar *QKdeTheme::createPlatformMenuBar() const #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON) QPlatformSystemTrayIcon *QKdeTheme::createPlatformSystemTrayIcon() const { - if (isDBusTrayAvailable()) + if (shouldUseDBusTray()) return new QDBusTrayIcon(); return nullptr; } @@ -704,8 +1269,8 @@ const char *QGnomeTheme::name = "gnome"; class QGnomeThemePrivate : public QPlatformThemePrivate { public: - QGnomeThemePrivate() : systemFont(nullptr), fixedFont(nullptr) {} - ~QGnomeThemePrivate() { delete systemFont; delete fixedFont; } + QGnomeThemePrivate(); + ~QGnomeThemePrivate(); void configureFonts(const QString >kFontName) const { @@ -715,15 +1280,75 @@ public: QString fontName = gtkFontName.left(split); systemFont = new QFont(fontName, size); - fixedFont = new QFont(QLatin1String(defaultFixedFontNameC), systemFont->pointSize()); + fixedFont = new QFont(QLatin1StringView(defaultFixedFontNameC), systemFont->pointSize()); fixedFont->setStyleHint(QFont::TypeWriter); qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont; } - mutable QFont *systemFont; - mutable QFont *fixedFont; + mutable QFont *systemFont = nullptr; + mutable QFont *fixedFont = nullptr; + +#ifndef QT_NO_DBUS + Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown; +private: + std::unique_ptr<QGenericUnixThemeDBusListener> dbus; + bool initDbus(); + void updateColorScheme(const QString &themeName); +#endif // QT_NO_DBUS }; +QGnomeThemePrivate::QGnomeThemePrivate() +{ +#ifndef QT_NO_DBUS + initDbus(); +#endif // QT_NO_DBUS +} +QGnomeThemePrivate::~QGnomeThemePrivate() +{ + if (systemFont) + delete systemFont; + if (fixedFont) + delete fixedFont; +} + +#ifndef QT_NO_DBUS +bool QGnomeThemePrivate::initDbus() +{ + dbus.reset(new QGenericUnixThemeDBusListener()); + Q_ASSERT(dbus); + + // Wrap slot in a lambda to avoid inheriting QGnomeThemePrivate from QObject + auto wrapper = [this](QGenericUnixThemeDBusListener::Provider provider, + QGenericUnixThemeDBusListener::Setting setting, + const QString &value) { + if (provider != QGenericUnixThemeDBusListener::Provider::Gnome + && provider != QGenericUnixThemeDBusListener::Provider::Gtk) { + return; + } + + if (setting == QGenericUnixThemeDBusListener::Setting::Theme) + updateColorScheme(value); + }; + + return QObject::connect(dbus.get(), &QGenericUnixThemeDBusListener::settingChanged, dbus.get(), wrapper); +} + +void QGnomeThemePrivate::updateColorScheme(const QString &themeName) +{ + const auto oldColorScheme = m_colorScheme; + if (themeName.contains(QLatin1StringView("light"), Qt::CaseInsensitive)) { + m_colorScheme = Qt::ColorScheme::Light; + } else if (themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive)) { + m_colorScheme = Qt::ColorScheme::Dark; + } else { + m_colorScheme = Qt::ColorScheme::Unknown; + } + + if (oldColorScheme != m_colorScheme) + QWindowSystemInterface::handleThemeChange(); +} +#endif // QT_NO_DBUS + QGnomeTheme::QGnomeTheme() : QPlatformTheme(new QGnomeThemePrivate()) { @@ -746,7 +1371,7 @@ QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const return QVariant::fromValue(availableXdgFileIconSizes()); case QPlatformTheme::StyleNames: { QStringList styleNames; - styleNames << QStringLiteral("fusion") << QStringLiteral("windows"); + styleNames << QStringLiteral("Fusion") << QStringLiteral("windows"); return QVariant(styleNames); } case QPlatformTheme::KeyboardScheme: @@ -755,6 +1380,17 @@ QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const return QVariant(QChar(0x2022)); case QPlatformTheme::UiEffects: return QVariant(int(HoverEffect)); + case QPlatformTheme::ButtonPressKeys: + return QVariant::fromValue( + QList<Qt::Key>({ Qt::Key_Space, Qt::Key_Return, Qt::Key_Enter, Qt::Key_Select })); + case QPlatformTheme::PreselectFirstFileInDirectory: + return true; + case QPlatformTheme::MouseCursorTheme: + return QVariant(mouseCursorTheme()); + case QPlatformTheme::MouseCursorSize: + return QVariant(mouseCursorSize()); + case QPlatformTheme::PreferFileIconFromTheme: + return true; default: break; } @@ -782,13 +1418,13 @@ const QFont *QGnomeTheme::font(Font type) const case QPlatformTheme::FixedFont: return d->fixedFont; default: - return 0; + return nullptr; } } QString QGnomeTheme::gtkFontName() const { - return QStringLiteral("%1 %2").arg(QLatin1String(defaultSystemFontNameC)).arg(defaultSystemFontSize); + return QStringLiteral("%1 %2").arg(QLatin1StringView(defaultSystemFontNameC)).arg(defaultSystemFontSize); } #ifndef QT_NO_DBUS @@ -798,12 +1434,18 @@ QPlatformMenuBar *QGnomeTheme::createPlatformMenuBar() const return new QDBusMenuBar(); return nullptr; } + +Qt::ColorScheme QGnomeTheme::colorScheme() const +{ + return d_func()->m_colorScheme; +} + #endif #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON) QPlatformSystemTrayIcon *QGnomeTheme::createPlatformSystemTrayIcon() const { - if (isDBusTrayAvailable()) + if (shouldUseDBusTray()) return new QDBusTrayIcon(); return nullptr; } @@ -834,14 +1476,14 @@ QString QGnomeTheme::standardButtonText(int button) const QPlatformTheme *QGenericUnixTheme::createUnixTheme(const QString &name) { - if (name == QLatin1String(QGenericUnixTheme::name)) + if (name == QLatin1StringView(QGenericUnixTheme::name)) return new QGenericUnixTheme; #if QT_CONFIG(settings) - if (name == QLatin1String(QKdeTheme::name)) + if (name == QLatin1StringView(QKdeTheme::name)) if (QPlatformTheme *kdeTheme = QKdeTheme::createKdeTheme()) return kdeTheme; #endif - if (name == QLatin1String(QGnomeTheme::name)) + if (name == QLatin1StringView(QGnomeTheme::name)) return new QGnomeTheme; return nullptr; } @@ -862,23 +1504,27 @@ QStringList QGenericUnixTheme::themeNames() for (const QByteArray &desktopName : desktopNames) { if (desktopEnvironment == "KDE") { #if QT_CONFIG(settings) - result.push_back(QLatin1String(QKdeTheme::name)); + result.push_back(QLatin1StringView(QKdeTheme::name)); #endif } else if (gtkBasedEnvironments.contains(desktopName)) { // prefer the GTK3 theme implementation with native dialogs etc. result.push_back(QStringLiteral("gtk3")); // fallback to the generic Gnome theme if loading the GTK3 theme fails - result.push_back(QLatin1String(QGnomeTheme::name)); + result.push_back(QLatin1StringView(QGnomeTheme::name)); } else { // unknown, but lowercase the name (our standard practice) and // remove any "x-" prefix QString s = QString::fromLatin1(desktopName.toLower()); - result.push_back(s.startsWith(QLatin1String("x-")) ? s.mid(2) : s); + result.push_back(s.startsWith("x-"_L1) ? s.mid(2) : s); } } } // desktopSettingsAware - result.append(QLatin1String(QGenericUnixTheme::name)); + result.append(QLatin1StringView(QGenericUnixTheme::name)); return result; } QT_END_NAMESPACE + +#ifndef QT_NO_DBUS +#include "qgenericunixthemes.moc" +#endif // QT_NO_DBUS diff --git a/src/gui/platform/unix/qgenericunixthemes_p.h b/src/gui/platform/unix/qgenericunixthemes_p.h index 0870275888..63b20651e6 100644 --- a/src/gui/platform/unix/qgenericunixthemes_p.h +++ b/src/gui/platform/unix/qgenericunixthemes_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QGENERICUNIXTHEMES_H #define QGENERICUNIXTHEMES_H @@ -55,6 +19,7 @@ #include <QtCore/QString> #include <QtCore/QStringList> #include <QtGui/QFont> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -112,6 +77,7 @@ public: QPlatformTheme::IconOptions iconOptions = { }) const override; const QPalette *palette(Palette type = SystemPalette) const override; + Qt::ColorScheme colorScheme() const override; const QFont *font(Font type) const override; #ifndef QT_NO_DBUS @@ -141,6 +107,7 @@ public: virtual QString gtkFontName() const; #ifndef QT_NO_DBUS QPlatformMenuBar *createPlatformMenuBar() const override; + Qt::ColorScheme colorScheme() const override; #endif #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON) QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override; diff --git a/src/gui/platform/unix/qtx11extras.cpp b/src/gui/platform/unix/qtx11extras.cpp new file mode 100644 index 0000000000..ef37518d95 --- /dev/null +++ b/src/gui/platform/unix/qtx11extras.cpp @@ -0,0 +1,514 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2016 Richard Moore <rich@kde.org> +// Copyright (C) 2016 David Faure <david.faure@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qtx11extras_p.h" + +#include <qpa/qplatformnativeinterface.h> +#include <qpa/qplatformwindow.h> +#include <qpa/qplatformscreen_p.h> +#include <qpa/qplatformscreen.h> +#include <qscreen.h> +#include <qwindow.h> +#include <qguiapplication.h> +#include <xcb/xcb.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +static QScreen *findScreenForVirtualDesktop(int virtualDesktopNumber) +{ + const auto screens = QGuiApplication::screens(); + for (QScreen *screen : screens) { + auto *qxcbScreen = dynamic_cast<QNativeInterface::Private::QXcbScreen *>(screen->handle()); + if (qxcbScreen && qxcbScreen->virtualDesktopNumber() == virtualDesktopNumber) + return screen; + } + return nullptr; +} + +/*! + \class QX11Info + \inmodule QtGui + \since 6.2 + \internal + + \brief Provides information about the X display configuration. + + The class provides two APIs: a set of non-static functions that + provide information about a specific widget or pixmap, and a set + of static functions that provide the default information for the + application. + + \warning This class is only available on X11. For querying + per-screen information in a portable way, use QScreen. +*/ + +/*! + Constructs an empty QX11Info object. +*/ +QX11Info::QX11Info() +{ +} + +/*! + Returns true if the application is currently running on X11. + + \since 6.2 + */ +bool QX11Info::isPlatformX11() +{ + return QGuiApplication::platformName() == "xcb"_L1; +} + +/*! + Returns the horizontal resolution of the given \a screen in terms of the + number of dots per inch. + + The \a screen argument is an X screen number. Be aware that if + the user's system uses Xinerama (as opposed to traditional X11 + multiscreen), there is only one X screen. Use QScreen to + query for information about Xinerama screens. + + \sa appDpiY() +*/ +int QX11Info::appDpiX(int screen) +{ + if (screen == -1) { + const QScreen *scr = QGuiApplication::primaryScreen(); + if (!scr) + return 75; + return qRound(scr->logicalDotsPerInchX()); + } + + QScreen *scr = findScreenForVirtualDesktop(screen); + if (!scr) + return 0; + + return scr->logicalDotsPerInchX(); +} + +/*! + Returns the vertical resolution of the given \a screen in terms of the + number of dots per inch. + + The \a screen argument is an X screen number. Be aware that if + the user's system uses Xinerama (as opposed to traditional X11 + multiscreen), there is only one X screen. Use QScreen to + query for information about Xinerama screens. + + \sa appDpiX() +*/ +int QX11Info::appDpiY(int screen) +{ + if (screen == -1) { + const QScreen *scr = QGuiApplication::primaryScreen(); + if (!scr) + return 75; + return qRound(scr->logicalDotsPerInchY()); + } + + QScreen *scr = findScreenForVirtualDesktop(screen); + if (!scr) + return 0; + + return scr->logicalDotsPerInchY(); +} + +/*! + Returns a handle for the applications root window on the given \a screen. + + The \a screen argument is an X screen number. Be aware that if + the user's system uses Xinerama (as opposed to traditional X11 + multiscreen), there is only one X screen. Use QScreen to + query for information about Xinerama screens. +*/ +quint32 QX11Info::appRootWindow(int screen) +{ + if (!qApp) + return 0; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return 0; + QScreen *scr = screen == -1 ? QGuiApplication::primaryScreen() : findScreenForVirtualDesktop(screen); + if (!scr) + return 0; + return static_cast<xcb_window_t>(reinterpret_cast<quintptr>(native->nativeResourceForScreen(QByteArrayLiteral("rootwindow"), scr))); +} + +/*! + Returns the number of the screen where the application is being + displayed. + + This method refers to screens in the original X11 meaning with a + different DISPLAY environment variable per screen. + This information is only useful if your application needs to know + on which X screen it is running. + + In a typical multi-head configuration, multiple physical monitors + are combined in one X11 screen. This means this method returns the + same number for each of the physical monitors. In such a setup you + are interested in the monitor information as provided by the X11 + RandR extension. This is available through QScreen. + + \sa display() +*/ +int QX11Info::appScreen() +{ + if (!qApp) + return 0; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return 0; + return reinterpret_cast<qintptr>(native->nativeResourceForIntegration(QByteArrayLiteral("x11screen"))); +} + +/*! + Returns the X11 time. + + \sa setAppTime(), appUserTime() +*/ +quint32 QX11Info::appTime() +{ + if (!qApp) + return 0; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return 0; + QScreen* screen = QGuiApplication::primaryScreen(); + return static_cast<xcb_timestamp_t>(reinterpret_cast<quintptr>(native->nativeResourceForScreen("apptime", screen))); +} + +/*! + Returns the X11 user time. + + \sa setAppUserTime(), appTime() +*/ +quint32 QX11Info::appUserTime() +{ + if (!qApp) + return 0; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return 0; + QScreen* screen = QGuiApplication::primaryScreen(); + return static_cast<xcb_timestamp_t>(reinterpret_cast<quintptr>(native->nativeResourceForScreen("appusertime", screen))); +} + +/*! + Sets the X11 time to the value specified by \a time. + + \sa appTime(), setAppUserTime() +*/ +void QX11Info::setAppTime(quint32 time) +{ + if (!qApp) + return; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return; + typedef void (*SetAppTimeFunc)(QScreen *, xcb_timestamp_t); + QScreen* screen = QGuiApplication::primaryScreen(); + SetAppTimeFunc func = reinterpret_cast<SetAppTimeFunc>(reinterpret_cast<void *>(native->nativeResourceFunctionForScreen("setapptime"))); + if (func) + func(screen, time); + else + qWarning("Internal error: QPA plugin doesn't implement setAppTime"); +} + +/*! + Sets the X11 user time as specified by \a time. + + \sa appUserTime(), setAppTime() +*/ +void QX11Info::setAppUserTime(quint32 time) +{ + if (!qApp) + return; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return; + typedef void (*SetAppUserTimeFunc)(QScreen *, xcb_timestamp_t); + QScreen* screen = QGuiApplication::primaryScreen(); + SetAppUserTimeFunc func = reinterpret_cast<SetAppUserTimeFunc>(reinterpret_cast<void *>(native->nativeResourceFunctionForScreen("setappusertime"))); + if (func) + func(screen, time); + else + qWarning("Internal error: QPA plugin doesn't implement setAppUserTime"); +} + +/*! + Fetches the current X11 time stamp from the X Server. + + This method creates a property notify event and blocks till it is + received back from the X Server. +*/ +quint32 QX11Info::getTimestamp() +{ + if (!qApp) + return 0; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return 0; + QScreen* screen = QGuiApplication::primaryScreen(); + return static_cast<xcb_timestamp_t>(reinterpret_cast<quintptr>(native->nativeResourceForScreen("gettimestamp", screen))); +} + +/*! + Returns the startup ID that will be used for the next window to be shown by this process. + + After the next window is shown, the next startup ID will be empty. + + http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt + + \sa setNextStartupId() +*/ +QByteArray QX11Info::nextStartupId() +{ + if (!qApp) + return QByteArray(); + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return QByteArray(); + return static_cast<char *>(native->nativeResourceForIntegration("startupid")); +} + +/*! + Sets the next startup ID to \a id. + + This is the startup ID that will be used for the next window to be shown by this process. + + The startup ID of the first window comes from the environment variable DESKTOP_STARTUP_ID. + This method is useful for subsequent windows, when the request comes from another process + (e.g. via DBus). + + \sa nextStartupId() +*/ +void QX11Info::setNextStartupId(const QByteArray &id) +{ + if (!qApp) + return; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return; + typedef void (*SetStartupIdFunc)(const char*); + SetStartupIdFunc func = reinterpret_cast<SetStartupIdFunc>(reinterpret_cast<void *>(native->nativeResourceFunctionForIntegration("setstartupid"))); + if (func) + func(id.constData()); + else + qWarning("Internal error: QPA plugin doesn't implement setStartupId"); +} + +/*! + Returns the default display for the application. + + \sa appScreen() +*/ +Display *QX11Info::display() +{ + if (!qApp) + return nullptr; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return nullptr; + + void *display = native->nativeResourceForIntegration(QByteArray("display")); + return reinterpret_cast<Display *>(display); +} + +/*! + Returns the default XCB connection for the application. + + \sa display() +*/ +xcb_connection_t *QX11Info::connection() +{ + if (!qApp) + return nullptr; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return nullptr; + + void *connection = native->nativeResourceForIntegration(QByteArray("connection")); + return reinterpret_cast<xcb_connection_t *>(connection); +} + +/*! + Returns true if there is a compositing manager running for the connection + attached to \a screen. + + If \a screen equals -1, the application's primary screen is used. +*/ +bool QX11Info::isCompositingManagerRunning(int screen) +{ + if (!qApp) + return false; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return false; + + QScreen *scr = screen == -1 ? QGuiApplication::primaryScreen() : findScreenForVirtualDesktop(screen); + if (!scr) { + qWarning() << "isCompositingManagerRunning: Could not find screen number" << screen; + return false; + } + + return native->nativeResourceForScreen(QByteArray("compositingEnabled"), scr); +} + +/*! + Returns a new peeker id or -1 if some internal error has occurred. + Each peeker id is associated with an index in the buffered native + event queue. + + For more details see QX11Info::PeekOption and peekEventQueue(). + + \sa peekEventQueue(), removePeekerId() +*/ +qint32 QX11Info::generatePeekerId() +{ + if (!qApp) + return -1; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return -1; + + typedef qint32 (*GeneratePeekerIdFunc)(void); + GeneratePeekerIdFunc generatepeekerid = reinterpret_cast<GeneratePeekerIdFunc>( + reinterpret_cast<void *>(native->nativeResourceFunctionForIntegration("generatepeekerid"))); + if (!generatepeekerid) { + qWarning("Internal error: QPA plugin doesn't implement generatePeekerId"); + return -1; + } + + return generatepeekerid(); +} + +/*! + Removes \a peekerId, which was earlier obtained via generatePeekerId(). + + Returns \c true on success or \c false if unknown peeker id was + provided or some internal error has occurred. + + \sa generatePeekerId() +*/ +bool QX11Info::removePeekerId(qint32 peekerId) +{ + if (!qApp) + return false; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return false; + + typedef bool (*RemovePeekerIdFunc)(qint32); + RemovePeekerIdFunc removePeekerId = reinterpret_cast<RemovePeekerIdFunc>( + reinterpret_cast<void *>(native->nativeResourceFunctionForIntegration("removepeekerid"))); + if (!removePeekerId) { + qWarning("Internal error: QPA plugin doesn't implement removePeekerId"); + return false; + } + + return removePeekerId(peekerId); +} + +/*! + \enum QX11Info::PeekOption + \brief An enum to tune the behavior of QX11Info::peekEventQueue(). + + \value PeekDefault + Peek from the beginning of the buffered native event queue. A peeker + id is optional with PeekDefault. If a peeker id is provided to + peekEventQueue() when using PeekDefault, then peeking starts from + the beginning of the queue, not from the cached index; thus, this + can be used to manually reset a cached index to peek from the start + of the queue. When this operation completes, the associated index + will be updated to the new position in the queue. + + \value PeekFromCachedIndex + QX11Info::peekEventQueue() can optimize the peeking algorithm by + skipping events that it already has seen in earlier calls to + peekEventQueue(). When control returns to the main event loop, + which causes the buffered native event queue to be flushed to Qt's + event queue, the cached indices are marked invalid and will be + reset on the next access. The same is true if the program + explicitly flushes the buffered native event queue by + QCoreApplication::processEvents(). +*/ + +/*! + \typedef QX11Info::PeekerCallback + Typedef for a pointer to a function with the following signature: + + \code + bool (*PeekerCallback)(xcb_generic_event_t *event, void *peekerData); + \endcode + + The \a event is a native XCB event. + The \a peekerData is a pointer to data, passed in via peekEventQueue(). + + Return \c true from this function to stop examining the buffered + native event queue or \c false to continue. + + \note A non-capturing lambda can serve as a PeekerCallback. +*/ + +/*! + \brief Peek into the buffered XCB event queue. + + You can call peekEventQueue() periodically, when your program is busy + performing a long-running operation, to peek into the buffered native + event queue. The more time the long-running operation blocks the + program from returning control to the main event loop, the more + events will accumulate in the buffered XCB event queue. Once control + returns to the main event loop these events will be flushed to Qt's + event queue, which is a separate event queue from the queue this + function is peeking into. + + \note It is usually better to run CPU-intensive operations in a + non-GUI thread, instead of blocking the main event loop. + + The buffered XCB event queue is populated from a non-GUI thread and + therefore might be ahead of the current GUI state. To handle native + events as they are processed by the GUI thread, see + QAbstractNativeEventFilter::nativeEventFilter(). + + The \a peeker is a callback function as documented in PeekerCallback. + The \a peekerData can be used to pass in arbitrary data to the \a + peeker callback. + The \a option is an enum that tunes the behavior of peekEventQueue(). + The \a peekerId is used to track an index in the queue, for more + details see QX11Info::PeekOption. There can be several indices, + each tracked individually by a peeker id obtained via generatePeekerId(). + + This function returns \c true when the peeker has stopped the event + proccesing by returning \c true from the callback. If there were no + events in the buffered native event queue to peek at or all the + events have been processed by the peeker, this function returns \c + false. + + \sa generatePeekerId(), QAbstractNativeEventFilter::nativeEventFilter() +*/ +bool QX11Info::peekEventQueue(PeekerCallback peeker, void *peekerData, PeekOptions option, + qint32 peekerId) +{ + if (!peeker || !qApp) + return false; + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + if (!native) + return false; + + typedef bool (*PeekEventQueueFunc)(PeekerCallback, void *, PeekOptions, qint32); + PeekEventQueueFunc peekeventqueue = reinterpret_cast<PeekEventQueueFunc>( + reinterpret_cast<void *>(native->nativeResourceFunctionForIntegration("peekeventqueue"))); + if (!peekeventqueue) { + qWarning("Internal error: QPA plugin doesn't implement peekEventQueue"); + return false; + } + + return peekeventqueue(peeker, peekerData, option, peekerId); +} + +QT_END_NAMESPACE diff --git a/src/gui/platform/unix/qtx11extras_p.h b/src/gui/platform/unix/qtx11extras_p.h new file mode 100644 index 0000000000..253162f83a --- /dev/null +++ b/src/gui/platform/unix/qtx11extras_p.h @@ -0,0 +1,75 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QTX11EXTRAS_P_H +#define QTX11EXTRAS_P_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. +// + +#include <QtGui/qtguiglobal.h> +#include <QtCore/private/qglobal_p.h> + +#include <xcb/xcb.h> + +typedef struct _XDisplay Display; + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QX11Info +{ +public: + enum PeekOption { + PeekDefault = 0, + PeekFromCachedIndex = 1 + }; + Q_DECLARE_FLAGS(PeekOptions, PeekOption) + + static bool isPlatformX11(); + + static int appDpiX(int screen=-1); + static int appDpiY(int screen=-1); + + static quint32 appRootWindow(int screen=-1); + static int appScreen(); + + static quint32 appTime(); + static quint32 appUserTime(); + + static void setAppTime(quint32 time); + static void setAppUserTime(quint32 time); + + static quint32 getTimestamp(); + + static QByteArray nextStartupId(); + static void setNextStartupId(const QByteArray &id); + + static Display *display(); + static xcb_connection_t *connection(); + + static bool isCompositingManagerRunning(int screen = -1); + + static qint32 generatePeekerId(); + static bool removePeekerId(qint32 peekerId); + typedef bool (*PeekerCallback)(xcb_generic_event_t *event, void *peekerData); + static bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr, + PeekOptions option = PeekDefault, qint32 peekerId = -1); + +private: + QX11Info(); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QX11Info::PeekOptions) + +QT_END_NAMESPACE + +#endif // QTX11EXTRAS_P_H + diff --git a/src/gui/platform/unix/qunixeventdispatcher.cpp b/src/gui/platform/unix/qunixeventdispatcher.cpp index 44258538ec..0178b7544e 100644 --- a/src/gui/platform/unix/qunixeventdispatcher.cpp +++ b/src/gui/platform/unix/qunixeventdispatcher.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qplatformdefs.h" #include "qcoreapplication.h" @@ -64,16 +28,4 @@ bool QUnixEventDispatcherQPA::processEvents(QEventLoop::ProcessEventsFlags flags return QWindowSystemInterface::sendWindowSystemEvents(flags) || didSendEvents; } -bool QUnixEventDispatcherQPA::hasPendingEvents() -{ - extern uint qGlobalPostedEventsCount(); // from qapplication.cpp - return qGlobalPostedEventsCount() || QWindowSystemInterface::windowSystemEventsQueued(); -} - -void QUnixEventDispatcherQPA::flush() -{ - if(qApp) - qApp->sendPostedEvents(); -} - QT_END_NAMESPACE diff --git a/src/gui/platform/unix/qunixeventdispatcher_qpa_p.h b/src/gui/platform/unix/qunixeventdispatcher_qpa_p.h index 9cd16be22a..bcae9b5a1c 100644 --- a/src/gui/platform/unix/qunixeventdispatcher_qpa_p.h +++ b/src/gui/platform/unix/qunixeventdispatcher_qpa_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QUNIXEVENTDISPATCHER_QPA_H #define QUNIXEVENTDISPATCHER_QPA_H @@ -65,9 +29,6 @@ public: ~QUnixEventDispatcherQPA(); bool processEvents(QEventLoop::ProcessEventsFlags flags) override; - bool hasPendingEvents() override; - - void flush() override; }; QT_END_NAMESPACE diff --git a/src/gui/platform/unix/qunixnativeinterface.cpp b/src/gui/platform/unix/qunixnativeinterface.cpp new file mode 100644 index 0000000000..09561d9ada --- /dev/null +++ b/src/gui/platform/unix/qunixnativeinterface.cpp @@ -0,0 +1,311 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtGui/private/qtguiglobal_p.h> + +#if QT_CONFIG(opengl) +# include <QtGui/private/qopenglcontext_p.h> +#endif +#include <QtGui/private/qguiapplication_p.h> + +#include <qpa/qplatformopenglcontext.h> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformscreen_p.h> +#include <qpa/qplatformwindow_p.h> + +#include <QtGui/private/qkeymapper_p.h> + +QT_BEGIN_NAMESPACE + +using namespace QNativeInterface::Private; + +#ifndef QT_NO_OPENGL + +#if QT_CONFIG(xcb_glx_plugin) + +/*! + \class QNativeInterface::QGLXContext + \since 6.0 + \brief Native interface to a GLX context. + + Accessed through QOpenGLContext::nativeInterface(). + + \inmodule QtGui + \inheaderfile QOpenGLContext + \ingroup native-interfaces + \ingroup native-interfaces-qopenglcontext +*/ + +/*! + \fn QOpenGLContext *QNativeInterface::QGLXContext::fromNative(GLXContext configBasedContext, QOpenGLContext *shareContext = nullptr) + + \brief Adopts a GLXContext \a configBasedContext created from an FBConfig. + + The context must be created from a framebuffer configuration, using the \c glXCreateNewContext function. + + Ownership of the created QOpenGLContext \a shareContext is transferred to the caller. +*/ + +/*! + \fn QOpenGLContext *QNativeInterface::QGLXContext::fromNative(GLXContext visualBasedContext, void *visualInfo, QOpenGLContext *shareContext = nullptr) + + \brief Adopts a GLXContext created from an X visual. + + The context must be created from a visual, using the \c glXCreateContext function. + The same visual must be passed as a pointer to an \c XVisualInfo struct, in the \a visualInfo argument. + + Ownership of the created QOpenGLContext is transferred to the caller. +*/ + +/*! + \fn GLXContext QNativeInterface::QGLXContext::nativeContext() const + + \return the underlying GLXContext. +*/ + +QT_DEFINE_NATIVE_INTERFACE(QGLXContext); +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QGLXIntegration); + +QOpenGLContext *QNativeInterface::QGLXContext::fromNative(GLXContext configBasedContext, QOpenGLContext *shareContext) +{ + return QGuiApplicationPrivate::platformIntegration()->call< + &QGLXIntegration::createOpenGLContext>(configBasedContext, nullptr, shareContext); +} + +QOpenGLContext *QNativeInterface::QGLXContext::fromNative(GLXContext visualBasedContext, void *visualInfo, QOpenGLContext *shareContext) +{ + return QGuiApplicationPrivate::platformIntegration()->call< + &QGLXIntegration::createOpenGLContext>(visualBasedContext, visualInfo, shareContext); +} +#endif // QT_CONFIG(xcb_glx_plugin) + +#if QT_CONFIG(egl) + +/*! + \class QNativeInterface::QEGLContext + \since 6.0 + \brief Native interface to an EGL context. + + Accessed through QOpenGLContext::nativeInterface(). + + \inmodule QtGui + \inheaderfile QOpenGLContext + \ingroup native-interfaces + \ingroup native-interfaces-qopenglcontext +*/ + +/*! + \fn QOpenGLContext *QNativeInterface::QEGLContext::fromNative(EGLContext context, EGLDisplay display, QOpenGLContext *shareContext = nullptr) + + \brief Adopts an EGLContext \a context. + + The same \c EGLDisplay passed to \c eglCreateContext must be passed as the \a display argument. + + Ownership of the created QOpenGLContext \a shareContext is transferred + to the caller. +*/ + +/*! + \fn EGLContext QNativeInterface::QEGLContext::nativeContext() const + + \return the underlying EGLContext. +*/ + +/*! + \fn EGLConfig QNativeInterface::QEGLContext::config() const + \since 6.3 + \return the EGLConfig associated with the underlying EGLContext. +*/ + +/*! + \fn EGLDisplay QNativeInterface::QEGLContext::display() const + \since 6.3 + \return the EGLDisplay associated with the underlying EGLContext. +*/ + + +/*! + \fn void QNativeInterface::QEGLContext::invalidateContext() + \since 6.5 + \brief Marks the context as invalid + + If this context is used by the Qt Quick scenegraph, this will trigger the + SceneGraph to destroy this context and create a new one. + + Similarly to QPlatformWindow::invalidateSurface(), + this function can only be expected to have an effect on certain platforms, + such as eglfs. + + \sa QOpenGLContext::isValid(), QPlatformWindow::invalidateSurface() +*/ + +QT_DEFINE_NATIVE_INTERFACE(QEGLContext); +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QEGLIntegration); + +QOpenGLContext *QNativeInterface::QEGLContext::fromNative(EGLContext context, EGLDisplay display, QOpenGLContext *shareContext) +{ + return QGuiApplicationPrivate::platformIntegration()->call< + &QEGLIntegration::createOpenGLContext>(context, display, shareContext); +} +#endif // QT_CONFIG(egl) + +#endif // QT_NO_OPENGL + +#if QT_CONFIG(xcb) + +/*! + \class QNativeInterface::Private::QXcbScreen + \since 6.0 + \internal + \brief Native interface to QPlatformScreen. + \inmodule QtGui + \ingroup native-interfaces +*/ + +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QXcbScreen); + +/*! + \class QNativeInterface::Private::QXcbWindow + \since 6.0 + \internal + \brief Native interface to QPlatformWindow. + \inmodule QtGui + \ingroup native-interfaces +*/ + +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QXcbWindow); + +/*! + \class QNativeInterface::QX11Application + \since 6.2 + \brief Native interface to an X11 application. + + Accessed through QGuiApplication::nativeInterface(). + + \inmodule QtGui + \inheaderfile QGuiApplication + \ingroup native-interfaces + \ingroup native-interfaces-qguiapplication +*/ + +/*! + \fn Display *QNativeInterface::QX11Application::display() const + + \return the X display of the application, for use with Xlib. + + \sa connection() +*/ + +/*! + \fn xcb_connection_t *QNativeInterface::QX11Application::connection() const + + \return the X connection of the application, for use with XCB. + + \sa display() +*/ + +QT_DEFINE_NATIVE_INTERFACE(QX11Application); + +#endif // QT_CONFIG(xcb) + +#if QT_CONFIG(vsp2) +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QVsp2Screen); +#endif + +#ifdef Q_OS_WEBOS +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWebOSScreen); +#endif + +#if QT_CONFIG(evdev) + +/*! + \class QNativeInterface::Private::QEvdevKeyMapper + \since 6.0 + \internal + \brief Native interface to QKeyMapper. + \inmodule QtGui + \ingroup native-interfaces +*/ + +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QEvdevKeyMapper); + +#endif // QT_CONFIG(evdev) + +#if QT_CONFIG(wayland) + +/*! + \class QNativeInterface::QWaylandApplication + \inheaderfile QGuiApplication + \since 6.5 + \brief Native interface to a Wayland application. + + Accessed through QGuiApplication::nativeInterface(). + \inmodule QtGui + \ingroup native-interfaces + \ingroup native-interfaces-qguiapplication +*/ +/*! + \fn wl_display *QNativeInterface::QWaylandApplication::display() const + \return the wl_display that the application is using. +*/ +/*! + \fn wl_compositor *QNativeInterface::QWaylandApplication::compositor() const + \return the wl_compositor that the application is using. +*/ +/*! + \fn wl_keyboard *QNativeInterface::QWaylandApplication::keyboard() const + \return the wl_keyboard belonging to seat() if available. +*/ +/*! + \fn wl_pointer *QNativeInterface::QWaylandApplication::pointer() const + \return the wl_pointer belonging to seat() if available. +*/ +/*! + \fn wl_touch *QNativeInterface::QWaylandApplication::touch() const + \return the wl_touch belonging to seat() if available. +*/ +/*! + \fn uint *QNativeInterface::QWaylandApplication::lastInputSerial() const + \return the serial of the last input event on any seat. +*/ +/*! + \fn wl_seat *QNativeInterface::QWaylandApplication::lastInputSeat() const + \return the seat on which the last input event happened. +*/ +/*! + \fn wl_seat *QNativeInterface::QWaylandApplication::seat() const + \return the seat associated with the default input device. +*/ + +QT_DEFINE_NATIVE_INTERFACE(QWaylandApplication); + +/*! + \class QNativeInterface::QWaylandScreen + \since 6.7 + \brief Native interface to a screen on Wayland. + + Accessed through QScreen::nativeInterface(). + \inmodule QtGui + \ingroup native-interfaces + \ingroup native-interfaces-qscreen +*/ +/*! + \fn wl_output *QNativeInterface::QWaylandScreen::output() const + \return the underlying wl_output of this QScreen. +*/ +QT_DEFINE_NATIVE_INTERFACE(QWaylandScreen); + +/*! + \class QNativeInterface::QWaylandWindow + \since 6.5 + \internal + \brief Native interface to a Wayland window. + \inmodule QtGui + \ingroup native-interfaces +*/ + +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWaylandWindow); + +#endif // QT_CONFIG(wayland) + +QT_END_NAMESPACE diff --git a/src/gui/platform/unix/qunixplatforminterface.cpp b/src/gui/platform/unix/qunixplatforminterface.cpp deleted file mode 100644 index 2584a9b91b..0000000000 --- a/src/gui/platform/unix/qunixplatforminterface.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtGui/private/qtguiglobal_p.h> - -#include <QtGui/private/qopenglcontext_p.h> -#include <QtGui/private/qguiapplication_p.h> - -#include <qpa/qplatformopenglcontext.h> -#include <qpa/qplatformintegration.h> - -QT_BEGIN_NAMESPACE - -using namespace QPlatformInterface::Private; - -#ifndef QT_NO_OPENGL - -#if defined(Q_OS_LINUX) -QT_DEFINE_PLATFORM_INTERFACE(QGLXContext, QOpenGLContext); -QT_DEFINE_PRIVATE_PLATFORM_INTERFACE(QGLXIntegration); - -QOpenGLContext *QPlatformInterface::QGLXContext::fromNative(GLXContext configBasedContext, QOpenGLContext *shareContext) -{ - return QGuiApplicationPrivate::platformIntegration()->call< - &QGLXIntegration::createOpenGLContext>(configBasedContext, nullptr, shareContext); -} - -QOpenGLContext *QPlatformInterface::QGLXContext::fromNative(GLXContext visualBasedContext, void *visualInfo, QOpenGLContext *shareContext) -{ - return QGuiApplicationPrivate::platformIntegration()->call< - &QGLXIntegration::createOpenGLContext>(visualBasedContext, visualInfo, shareContext); -} -#endif - -#if QT_CONFIG(egl) -QT_DEFINE_PLATFORM_INTERFACE(QEGLContext, QOpenGLContext); -QT_DEFINE_PRIVATE_PLATFORM_INTERFACE(QEGLIntegration); - -QOpenGLContext *QPlatformInterface::QEGLContext::fromNative(EGLContext context, EGLDisplay display, QOpenGLContext *shareContext) -{ - return QGuiApplicationPrivate::platformIntegration()->call< - &QEGLIntegration::createOpenGLContext>(context, display, shareContext); -} -#endif - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE diff --git a/src/gui/platform/unix/qxkbcommon.cpp b/src/gui/platform/unix/qxkbcommon.cpp new file mode 100644 index 0000000000..ed29db3005 --- /dev/null +++ b/src/gui/platform/unix/qxkbcommon.cpp @@ -0,0 +1,831 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qxkbcommon_p.h" + +#include <private/qmakearray_p.h> + +#include <QtCore/private/qstringiterator_p.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/QMetaMethod> + +#include <QtGui/QKeyEvent> +#include <QtGui/private/qguiapplication_p.h> + +#include <qpa/qplatforminputcontext.h> +#include <qpa/qplatformintegration.h> + +QT_BEGIN_NAMESPACE + +static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, + xkb_state *state, xkb_keycode_t code, + bool superAsMeta, bool hyperAsMeta); + +typedef struct xkb2qt +{ + unsigned int xkb; + unsigned int qt; + + constexpr bool operator <=(const xkb2qt &that) const noexcept + { + return xkb <= that.xkb; + } + + constexpr bool operator <(const xkb2qt &that) const noexcept + { + return xkb < that.xkb; + } +} xkb2qt_t; + +template<std::size_t Xkb, std::size_t Qt> +struct Xkb2Qt +{ + using Type = xkb2qt_t; + static constexpr Type data() noexcept { return Type{Xkb, Qt}; } +}; + +static constexpr const auto KeyTbl = qMakeArray( + QSortedData< + // misc keys + + Xkb2Qt<XKB_KEY_Escape, Qt::Key_Escape>, + Xkb2Qt<XKB_KEY_Tab, Qt::Key_Tab>, + Xkb2Qt<XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab>, + Xkb2Qt<XKB_KEY_BackSpace, Qt::Key_Backspace>, + Xkb2Qt<XKB_KEY_Return, Qt::Key_Return>, + Xkb2Qt<XKB_KEY_Insert, Qt::Key_Insert>, + Xkb2Qt<XKB_KEY_Delete, Qt::Key_Delete>, + Xkb2Qt<XKB_KEY_Clear, Qt::Key_Delete>, + Xkb2Qt<XKB_KEY_Pause, Qt::Key_Pause>, + Xkb2Qt<XKB_KEY_Print, Qt::Key_Print>, + Xkb2Qt<XKB_KEY_Sys_Req, Qt::Key_SysReq>, + Xkb2Qt<0x1005FF60, Qt::Key_SysReq>, // hardcoded Sun SysReq + Xkb2Qt<0x1007ff00, Qt::Key_SysReq>, // hardcoded X386 SysReq + + // cursor movement + + Xkb2Qt<XKB_KEY_Home, Qt::Key_Home>, + Xkb2Qt<XKB_KEY_End, Qt::Key_End>, + Xkb2Qt<XKB_KEY_Left, Qt::Key_Left>, + Xkb2Qt<XKB_KEY_Up, Qt::Key_Up>, + Xkb2Qt<XKB_KEY_Right, Qt::Key_Right>, + Xkb2Qt<XKB_KEY_Down, Qt::Key_Down>, + Xkb2Qt<XKB_KEY_Prior, Qt::Key_PageUp>, + Xkb2Qt<XKB_KEY_Next, Qt::Key_PageDown>, + + // modifiers + + Xkb2Qt<XKB_KEY_Shift_L, Qt::Key_Shift>, + Xkb2Qt<XKB_KEY_Shift_R, Qt::Key_Shift>, + Xkb2Qt<XKB_KEY_Shift_Lock, Qt::Key_Shift>, + Xkb2Qt<XKB_KEY_Control_L, Qt::Key_Control>, + Xkb2Qt<XKB_KEY_Control_R, Qt::Key_Control>, + Xkb2Qt<XKB_KEY_Meta_L, Qt::Key_Meta>, + Xkb2Qt<XKB_KEY_Meta_R, Qt::Key_Meta>, + Xkb2Qt<XKB_KEY_Alt_L, Qt::Key_Alt>, + Xkb2Qt<XKB_KEY_Alt_R, Qt::Key_Alt>, + Xkb2Qt<XKB_KEY_Caps_Lock, Qt::Key_CapsLock>, + Xkb2Qt<XKB_KEY_Num_Lock, Qt::Key_NumLock>, + Xkb2Qt<XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock>, + Xkb2Qt<XKB_KEY_Super_L, Qt::Key_Super_L>, + Xkb2Qt<XKB_KEY_Super_R, Qt::Key_Super_R>, + Xkb2Qt<XKB_KEY_Menu, Qt::Key_Menu>, + Xkb2Qt<XKB_KEY_Hyper_L, Qt::Key_Hyper_L>, + Xkb2Qt<XKB_KEY_Hyper_R, Qt::Key_Hyper_R>, + Xkb2Qt<XKB_KEY_Help, Qt::Key_Help>, + Xkb2Qt<0x1000FF74, Qt::Key_Backtab>, // hardcoded HP backtab + Xkb2Qt<0x1005FF10, Qt::Key_F11>, // hardcoded Sun F36 (labeled F11) + Xkb2Qt<0x1005FF11, Qt::Key_F12>, // hardcoded Sun F37 (labeled F12) + + // numeric and function keypad keys + + Xkb2Qt<XKB_KEY_KP_Space, Qt::Key_Space>, + Xkb2Qt<XKB_KEY_KP_Tab, Qt::Key_Tab>, + Xkb2Qt<XKB_KEY_KP_Enter, Qt::Key_Enter>, + Xkb2Qt<XKB_KEY_KP_Home, Qt::Key_Home>, + Xkb2Qt<XKB_KEY_KP_Left, Qt::Key_Left>, + Xkb2Qt<XKB_KEY_KP_Up, Qt::Key_Up>, + Xkb2Qt<XKB_KEY_KP_Right, Qt::Key_Right>, + Xkb2Qt<XKB_KEY_KP_Down, Qt::Key_Down>, + Xkb2Qt<XKB_KEY_KP_Prior, Qt::Key_PageUp>, + Xkb2Qt<XKB_KEY_KP_Next, Qt::Key_PageDown>, + Xkb2Qt<XKB_KEY_KP_End, Qt::Key_End>, + Xkb2Qt<XKB_KEY_KP_Begin, Qt::Key_Clear>, + Xkb2Qt<XKB_KEY_KP_Insert, Qt::Key_Insert>, + Xkb2Qt<XKB_KEY_KP_Delete, Qt::Key_Delete>, + Xkb2Qt<XKB_KEY_KP_Equal, Qt::Key_Equal>, + Xkb2Qt<XKB_KEY_KP_Multiply, Qt::Key_Asterisk>, + Xkb2Qt<XKB_KEY_KP_Add, Qt::Key_Plus>, + Xkb2Qt<XKB_KEY_KP_Separator, Qt::Key_Comma>, + Xkb2Qt<XKB_KEY_KP_Subtract, Qt::Key_Minus>, + Xkb2Qt<XKB_KEY_KP_Decimal, Qt::Key_Period>, + Xkb2Qt<XKB_KEY_KP_Divide, Qt::Key_Slash>, + + // special non-XF86 function keys + + Xkb2Qt<XKB_KEY_Undo, Qt::Key_Undo>, + Xkb2Qt<XKB_KEY_Redo, Qt::Key_Redo>, + Xkb2Qt<XKB_KEY_Find, Qt::Key_Find>, + Xkb2Qt<XKB_KEY_Cancel, Qt::Key_Cancel>, + + // International input method support keys + + // International & multi-key character composition + Xkb2Qt<XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr>, + Xkb2Qt<XKB_KEY_Multi_key, Qt::Key_Multi_key>, + Xkb2Qt<XKB_KEY_Codeinput, Qt::Key_Codeinput>, + Xkb2Qt<XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate>, + Xkb2Qt<XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate>, + Xkb2Qt<XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate>, + + // Misc Functions + Xkb2Qt<XKB_KEY_Mode_switch, Qt::Key_Mode_switch>, + Xkb2Qt<XKB_KEY_script_switch, Qt::Key_Mode_switch>, + + // Japanese keyboard support + Xkb2Qt<XKB_KEY_Kanji, Qt::Key_Kanji>, + Xkb2Qt<XKB_KEY_Muhenkan, Qt::Key_Muhenkan>, + //Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan_Mode>, + Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan>, + Xkb2Qt<XKB_KEY_Henkan, Qt::Key_Henkan>, + Xkb2Qt<XKB_KEY_Romaji, Qt::Key_Romaji>, + Xkb2Qt<XKB_KEY_Hiragana, Qt::Key_Hiragana>, + Xkb2Qt<XKB_KEY_Katakana, Qt::Key_Katakana>, + Xkb2Qt<XKB_KEY_Hiragana_Katakana, Qt::Key_Hiragana_Katakana>, + Xkb2Qt<XKB_KEY_Zenkaku, Qt::Key_Zenkaku>, + Xkb2Qt<XKB_KEY_Hankaku, Qt::Key_Hankaku>, + Xkb2Qt<XKB_KEY_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku>, + Xkb2Qt<XKB_KEY_Touroku, Qt::Key_Touroku>, + Xkb2Qt<XKB_KEY_Massyo, Qt::Key_Massyo>, + Xkb2Qt<XKB_KEY_Kana_Lock, Qt::Key_Kana_Lock>, + Xkb2Qt<XKB_KEY_Kana_Shift, Qt::Key_Kana_Shift>, + Xkb2Qt<XKB_KEY_Eisu_Shift, Qt::Key_Eisu_Shift>, + Xkb2Qt<XKB_KEY_Eisu_toggle, Qt::Key_Eisu_toggle>, + //Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Kanji_Bangou>, + //Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_Zen_Koho>, + //Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_Mae_Koho>, + Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Codeinput>, + Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_MultipleCandidate>, + Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_PreviousCandidate>, + + // Korean keyboard support + Xkb2Qt<XKB_KEY_Hangul, Qt::Key_Hangul>, + Xkb2Qt<XKB_KEY_Hangul_Start, Qt::Key_Hangul_Start>, + Xkb2Qt<XKB_KEY_Hangul_End, Qt::Key_Hangul_End>, + Xkb2Qt<XKB_KEY_Hangul_Hanja, Qt::Key_Hangul_Hanja>, + Xkb2Qt<XKB_KEY_Hangul_Jamo, Qt::Key_Hangul_Jamo>, + Xkb2Qt<XKB_KEY_Hangul_Romaja, Qt::Key_Hangul_Romaja>, + //Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Hangul_Codeinput>, + Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Codeinput>, + Xkb2Qt<XKB_KEY_Hangul_Jeonja, Qt::Key_Hangul_Jeonja>, + Xkb2Qt<XKB_KEY_Hangul_Banja, Qt::Key_Hangul_Banja>, + Xkb2Qt<XKB_KEY_Hangul_PreHanja, Qt::Key_Hangul_PreHanja>, + Xkb2Qt<XKB_KEY_Hangul_PostHanja, Qt::Key_Hangul_PostHanja>, + //Xkb2Qt<XKB_KEY_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate>, + //Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate>, + //Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate>, + Xkb2Qt<XKB_KEY_Hangul_SingleCandidate, Qt::Key_SingleCandidate>, + Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate>, + Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate>, + Xkb2Qt<XKB_KEY_Hangul_Special, Qt::Key_Hangul_Special>, + //Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Hangul_switch>, + Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Mode_switch>, + + // dead keys + Xkb2Qt<XKB_KEY_dead_grave, Qt::Key_Dead_Grave>, + Xkb2Qt<XKB_KEY_dead_acute, Qt::Key_Dead_Acute>, + Xkb2Qt<XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex>, + Xkb2Qt<XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde>, + Xkb2Qt<XKB_KEY_dead_macron, Qt::Key_Dead_Macron>, + Xkb2Qt<XKB_KEY_dead_breve, Qt::Key_Dead_Breve>, + Xkb2Qt<XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot>, + Xkb2Qt<XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis>, + Xkb2Qt<XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering>, + Xkb2Qt<XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute>, + Xkb2Qt<XKB_KEY_dead_caron, Qt::Key_Dead_Caron>, + Xkb2Qt<XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla>, + Xkb2Qt<XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek>, + Xkb2Qt<XKB_KEY_dead_iota, Qt::Key_Dead_Iota>, + Xkb2Qt<XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound>, + Xkb2Qt<XKB_KEY_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound>, + Xkb2Qt<XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot>, + Xkb2Qt<XKB_KEY_dead_hook, Qt::Key_Dead_Hook>, + Xkb2Qt<XKB_KEY_dead_horn, Qt::Key_Dead_Horn>, + Xkb2Qt<XKB_KEY_dead_stroke, Qt::Key_Dead_Stroke>, + Xkb2Qt<XKB_KEY_dead_abovecomma, Qt::Key_Dead_Abovecomma>, + Xkb2Qt<XKB_KEY_dead_abovereversedcomma, Qt::Key_Dead_Abovereversedcomma>, + Xkb2Qt<XKB_KEY_dead_doublegrave, Qt::Key_Dead_Doublegrave>, + Xkb2Qt<XKB_KEY_dead_belowring, Qt::Key_Dead_Belowring>, + Xkb2Qt<XKB_KEY_dead_belowmacron, Qt::Key_Dead_Belowmacron>, + Xkb2Qt<XKB_KEY_dead_belowcircumflex, Qt::Key_Dead_Belowcircumflex>, + Xkb2Qt<XKB_KEY_dead_belowtilde, Qt::Key_Dead_Belowtilde>, + Xkb2Qt<XKB_KEY_dead_belowbreve, Qt::Key_Dead_Belowbreve>, + Xkb2Qt<XKB_KEY_dead_belowdiaeresis, Qt::Key_Dead_Belowdiaeresis>, + Xkb2Qt<XKB_KEY_dead_invertedbreve, Qt::Key_Dead_Invertedbreve>, + Xkb2Qt<XKB_KEY_dead_belowcomma, Qt::Key_Dead_Belowcomma>, + Xkb2Qt<XKB_KEY_dead_currency, Qt::Key_Dead_Currency>, + Xkb2Qt<XKB_KEY_dead_a, Qt::Key_Dead_a>, + Xkb2Qt<XKB_KEY_dead_A, Qt::Key_Dead_A>, + Xkb2Qt<XKB_KEY_dead_e, Qt::Key_Dead_e>, + Xkb2Qt<XKB_KEY_dead_E, Qt::Key_Dead_E>, + Xkb2Qt<XKB_KEY_dead_i, Qt::Key_Dead_i>, + Xkb2Qt<XKB_KEY_dead_I, Qt::Key_Dead_I>, + Xkb2Qt<XKB_KEY_dead_o, Qt::Key_Dead_o>, + Xkb2Qt<XKB_KEY_dead_O, Qt::Key_Dead_O>, + Xkb2Qt<XKB_KEY_dead_u, Qt::Key_Dead_u>, + Xkb2Qt<XKB_KEY_dead_U, Qt::Key_Dead_U>, + Xkb2Qt<XKB_KEY_dead_small_schwa, Qt::Key_Dead_Small_Schwa>, + Xkb2Qt<XKB_KEY_dead_capital_schwa, Qt::Key_Dead_Capital_Schwa>, + Xkb2Qt<XKB_KEY_dead_greek, Qt::Key_Dead_Greek>, +/* The following four XKB_KEY_dead keys got removed in libxkbcommon 1.6.0 + The define check is kind of version check here. */ +#ifdef XKB_KEY_dead_lowline + Xkb2Qt<XKB_KEY_dead_lowline, Qt::Key_Dead_Lowline>, + Xkb2Qt<XKB_KEY_dead_aboveverticalline, Qt::Key_Dead_Aboveverticalline>, + Xkb2Qt<XKB_KEY_dead_belowverticalline, Qt::Key_Dead_Belowverticalline>, + Xkb2Qt<XKB_KEY_dead_longsolidusoverlay, Qt::Key_Dead_Longsolidusoverlay>, +#endif + + // Special keys from X.org - This include multimedia keys, + // wireless/bluetooth/uwb keys, special launcher keys, etc. + Xkb2Qt<XKB_KEY_XF86Back, Qt::Key_Back>, + Xkb2Qt<XKB_KEY_XF86Forward, Qt::Key_Forward>, + Xkb2Qt<XKB_KEY_XF86Stop, Qt::Key_Stop>, + Xkb2Qt<XKB_KEY_XF86Refresh, Qt::Key_Refresh>, + Xkb2Qt<XKB_KEY_XF86Favorites, Qt::Key_Favorites>, + Xkb2Qt<XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia>, + Xkb2Qt<XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl>, + Xkb2Qt<XKB_KEY_XF86HomePage, Qt::Key_HomePage>, + Xkb2Qt<XKB_KEY_XF86Search, Qt::Key_Search>, + Xkb2Qt<XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown>, + Xkb2Qt<XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute>, + Xkb2Qt<XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp>, + Xkb2Qt<XKB_KEY_XF86AudioPlay, Qt::Key_MediaPlay>, + Xkb2Qt<XKB_KEY_XF86AudioStop, Qt::Key_MediaStop>, + Xkb2Qt<XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious>, + Xkb2Qt<XKB_KEY_XF86AudioNext, Qt::Key_MediaNext>, + Xkb2Qt<XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord>, + Xkb2Qt<XKB_KEY_XF86AudioPause, Qt::Key_MediaPause>, + Xkb2Qt<XKB_KEY_XF86Mail, Qt::Key_LaunchMail>, + Xkb2Qt<XKB_KEY_XF86MyComputer, Qt::Key_LaunchMedia>, + Xkb2Qt<XKB_KEY_XF86Memo, Qt::Key_Memo>, + Xkb2Qt<XKB_KEY_XF86ToDoList, Qt::Key_ToDoList>, + Xkb2Qt<XKB_KEY_XF86Calendar, Qt::Key_Calendar>, + Xkb2Qt<XKB_KEY_XF86PowerDown, Qt::Key_PowerDown>, + Xkb2Qt<XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust>, + Xkb2Qt<XKB_KEY_XF86Standby, Qt::Key_Standby>, + Xkb2Qt<XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp>, + Xkb2Qt<XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown>, + Xkb2Qt<XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff>, + Xkb2Qt<XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp>, + Xkb2Qt<XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown>, + Xkb2Qt<XKB_KEY_XF86PowerOff, Qt::Key_PowerOff>, + Xkb2Qt<XKB_KEY_XF86WakeUp, Qt::Key_WakeUp>, + Xkb2Qt<XKB_KEY_XF86Eject, Qt::Key_Eject>, + Xkb2Qt<XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver>, + Xkb2Qt<XKB_KEY_XF86WWW, Qt::Key_WWW>, + Xkb2Qt<XKB_KEY_XF86Sleep, Qt::Key_Sleep>, + Xkb2Qt<XKB_KEY_XF86LightBulb, Qt::Key_LightBulb>, + Xkb2Qt<XKB_KEY_XF86Shop, Qt::Key_Shop>, + Xkb2Qt<XKB_KEY_XF86History, Qt::Key_History>, + Xkb2Qt<XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite>, + Xkb2Qt<XKB_KEY_XF86HotLinks, Qt::Key_HotLinks>, + Xkb2Qt<XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust>, + Xkb2Qt<XKB_KEY_XF86Finance, Qt::Key_Finance>, + Xkb2Qt<XKB_KEY_XF86Community, Qt::Key_Community>, + Xkb2Qt<XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind>, + Xkb2Qt<XKB_KEY_XF86BackForward, Qt::Key_BackForward>, + Xkb2Qt<XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft>, + Xkb2Qt<XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight>, + Xkb2Qt<XKB_KEY_XF86Book, Qt::Key_Book>, + Xkb2Qt<XKB_KEY_XF86CD, Qt::Key_CD>, + Xkb2Qt<XKB_KEY_XF86Calculater, Qt::Key_Calculator>, + Xkb2Qt<XKB_KEY_XF86Calculator, Qt::Key_Calculator>, + Xkb2Qt<XKB_KEY_XF86Clear, Qt::Key_Clear>, + Xkb2Qt<XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab>, + Xkb2Qt<XKB_KEY_XF86Close, Qt::Key_Close>, + Xkb2Qt<XKB_KEY_XF86Copy, Qt::Key_Copy>, + Xkb2Qt<XKB_KEY_XF86Cut, Qt::Key_Cut>, + Xkb2Qt<XKB_KEY_XF86Display, Qt::Key_Display>, + Xkb2Qt<XKB_KEY_XF86DOS, Qt::Key_DOS>, + Xkb2Qt<XKB_KEY_XF86Documents, Qt::Key_Documents>, + Xkb2Qt<XKB_KEY_XF86Excel, Qt::Key_Excel>, + Xkb2Qt<XKB_KEY_XF86Explorer, Qt::Key_Explorer>, + Xkb2Qt<XKB_KEY_XF86Game, Qt::Key_Game>, + Xkb2Qt<XKB_KEY_XF86Go, Qt::Key_Go>, + Xkb2Qt<XKB_KEY_XF86iTouch, Qt::Key_iTouch>, + Xkb2Qt<XKB_KEY_XF86LogOff, Qt::Key_LogOff>, + Xkb2Qt<XKB_KEY_XF86Market, Qt::Key_Market>, + Xkb2Qt<XKB_KEY_XF86Meeting, Qt::Key_Meeting>, + Xkb2Qt<XKB_KEY_XF86MenuKB, Qt::Key_MenuKB>, + Xkb2Qt<XKB_KEY_XF86MenuPB, Qt::Key_MenuPB>, + Xkb2Qt<XKB_KEY_XF86MySites, Qt::Key_MySites>, + Xkb2Qt<XKB_KEY_XF86New, Qt::Key_New>, + Xkb2Qt<XKB_KEY_XF86News, Qt::Key_News>, + Xkb2Qt<XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome>, + Xkb2Qt<XKB_KEY_XF86Open, Qt::Key_Open>, + Xkb2Qt<XKB_KEY_XF86Option, Qt::Key_Option>, + Xkb2Qt<XKB_KEY_XF86Paste, Qt::Key_Paste>, + Xkb2Qt<XKB_KEY_XF86Phone, Qt::Key_Phone>, + Xkb2Qt<XKB_KEY_XF86Reply, Qt::Key_Reply>, + Xkb2Qt<XKB_KEY_XF86Reload, Qt::Key_Reload>, + Xkb2Qt<XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows>, + Xkb2Qt<XKB_KEY_XF86RotationPB, Qt::Key_RotationPB>, + Xkb2Qt<XKB_KEY_XF86RotationKB, Qt::Key_RotationKB>, + Xkb2Qt<XKB_KEY_XF86Save, Qt::Key_Save>, + Xkb2Qt<XKB_KEY_XF86Send, Qt::Key_Send>, + Xkb2Qt<XKB_KEY_XF86Spell, Qt::Key_Spell>, + Xkb2Qt<XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen>, + Xkb2Qt<XKB_KEY_XF86Support, Qt::Key_Support>, + Xkb2Qt<XKB_KEY_XF86TaskPane, Qt::Key_TaskPane>, + Xkb2Qt<XKB_KEY_XF86Terminal, Qt::Key_Terminal>, + Xkb2Qt<XKB_KEY_XF86Tools, Qt::Key_Tools>, + Xkb2Qt<XKB_KEY_XF86Travel, Qt::Key_Travel>, + Xkb2Qt<XKB_KEY_XF86Video, Qt::Key_Video>, + Xkb2Qt<XKB_KEY_XF86Word, Qt::Key_Word>, + Xkb2Qt<XKB_KEY_XF86Xfer, Qt::Key_Xfer>, + Xkb2Qt<XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn>, + Xkb2Qt<XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut>, + Xkb2Qt<XKB_KEY_XF86Away, Qt::Key_Away>, + Xkb2Qt<XKB_KEY_XF86Messenger, Qt::Key_Messenger>, + Xkb2Qt<XKB_KEY_XF86WebCam, Qt::Key_WebCam>, + Xkb2Qt<XKB_KEY_XF86MailForward, Qt::Key_MailForward>, + Xkb2Qt<XKB_KEY_XF86Pictures, Qt::Key_Pictures>, + Xkb2Qt<XKB_KEY_XF86Music, Qt::Key_Music>, + Xkb2Qt<XKB_KEY_XF86Battery, Qt::Key_Battery>, + Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>, + Xkb2Qt<XKB_KEY_XF86WLAN, Qt::Key_WLAN>, + Xkb2Qt<XKB_KEY_XF86UWB, Qt::Key_UWB>, + Xkb2Qt<XKB_KEY_XF86AudioForward, Qt::Key_AudioForward>, + Xkb2Qt<XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat>, + Xkb2Qt<XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay>, + Xkb2Qt<XKB_KEY_XF86Subtitle, Qt::Key_Subtitle>, + Xkb2Qt<XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack>, + Xkb2Qt<XKB_KEY_XF86Time, Qt::Key_Time>, + Xkb2Qt<XKB_KEY_XF86Select, Qt::Key_Select>, + Xkb2Qt<XKB_KEY_XF86View, Qt::Key_View>, + Xkb2Qt<XKB_KEY_XF86TopMenu, Qt::Key_TopMenu>, + Xkb2Qt<XKB_KEY_XF86Red, Qt::Key_Red>, + Xkb2Qt<XKB_KEY_XF86Green, Qt::Key_Green>, + Xkb2Qt<XKB_KEY_XF86Yellow, Qt::Key_Yellow>, + Xkb2Qt<XKB_KEY_XF86Blue, Qt::Key_Blue>, + Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>, + Xkb2Qt<XKB_KEY_XF86Suspend, Qt::Key_Suspend>, + Xkb2Qt<XKB_KEY_XF86Hibernate, Qt::Key_Hibernate>, + Xkb2Qt<XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle>, + Xkb2Qt<XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn>, + Xkb2Qt<XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff>, + Xkb2Qt<XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute>, + Xkb2Qt<XKB_KEY_XF86Launch0, Qt::Key_Launch0>, + Xkb2Qt<XKB_KEY_XF86Launch1, Qt::Key_Launch1>, + Xkb2Qt<XKB_KEY_XF86Launch2, Qt::Key_Launch2>, + Xkb2Qt<XKB_KEY_XF86Launch3, Qt::Key_Launch3>, + Xkb2Qt<XKB_KEY_XF86Launch4, Qt::Key_Launch4>, + Xkb2Qt<XKB_KEY_XF86Launch5, Qt::Key_Launch5>, + Xkb2Qt<XKB_KEY_XF86Launch6, Qt::Key_Launch6>, + Xkb2Qt<XKB_KEY_XF86Launch7, Qt::Key_Launch7>, + Xkb2Qt<XKB_KEY_XF86Launch8, Qt::Key_Launch8>, + Xkb2Qt<XKB_KEY_XF86Launch9, Qt::Key_Launch9>, + Xkb2Qt<XKB_KEY_XF86LaunchA, Qt::Key_LaunchA>, + Xkb2Qt<XKB_KEY_XF86LaunchB, Qt::Key_LaunchB>, + Xkb2Qt<XKB_KEY_XF86LaunchC, Qt::Key_LaunchC>, + Xkb2Qt<XKB_KEY_XF86LaunchD, Qt::Key_LaunchD>, + Xkb2Qt<XKB_KEY_XF86LaunchE, Qt::Key_LaunchE>, + Xkb2Qt<XKB_KEY_XF86LaunchF, Qt::Key_LaunchF> + >::Data{} +); + +xkb_keysym_t QXkbCommon::qxkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks) +{ + xkb_keysym_t lower, upper; + + xkbcommon_XConvertCase(ks, &lower, &upper); + + return upper; +} + +QString QXkbCommon::lookupString(struct xkb_state *state, xkb_keycode_t code) +{ + QVarLengthArray<char, 32> chars(32); + const int size = xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); + if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL + chars.resize(size + 1); + xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); + } + return QString::fromUtf8(chars.constData(), size); +} + +QString QXkbCommon::lookupStringNoKeysymTransformations(xkb_keysym_t keysym) +{ + QVarLengthArray<char, 32> chars(32); + const int size = xkb_keysym_to_utf8(keysym, chars.data(), chars.size()); + if (size == 0) + return QString(); // the keysym does not have a Unicode representation + + if (Q_UNLIKELY(size > chars.size())) { + chars.resize(size); + xkb_keysym_to_utf8(keysym, chars.data(), chars.size()); + } + return QString::fromUtf8(chars.constData(), size - 1); +} + +QList<xkb_keysym_t> QXkbCommon::toKeysym(QKeyEvent *event) +{ + QList<xkb_keysym_t> keysyms; + int qtKey = event->key(); + + if (qtKey >= Qt::Key_F1 && qtKey <= Qt::Key_F35) { + keysyms.append(XKB_KEY_F1 + (qtKey - Qt::Key_F1)); + } else if (event->modifiers() & Qt::KeypadModifier) { + if (qtKey >= Qt::Key_0 && qtKey <= Qt::Key_9) + keysyms.append(XKB_KEY_KP_0 + (qtKey - Qt::Key_0)); + } else if (isLatin1(qtKey) && event->text().isUpper()) { + keysyms.append(qtKey); + } + + if (!keysyms.isEmpty()) + return keysyms; + + // check if we have a direct mapping + auto it = std::find_if(KeyTbl.cbegin(), KeyTbl.cend(), [&qtKey](xkb2qt_t elem) { + return elem.qt == static_cast<uint>(qtKey); + }); + if (it != KeyTbl.end()) { + keysyms.append(it->xkb); + return keysyms; + } + + QList<uint> ucs4; + if (event->text().isEmpty()) + ucs4.append(qtKey); + else + ucs4 = event->text().toUcs4(); + + // From libxkbcommon keysym-utf.c: + // "We allow to represent any UCS character in the range U-00000000 to + // U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff." + for (uint utf32 : std::as_const(ucs4)) + keysyms.append(utf32 | 0x01000000); + + return keysyms; +} + +int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers) +{ + return keysymToQtKey(keysym, modifiers, nullptr, 0); +} + +int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, + xkb_state *state, xkb_keycode_t code, + bool superAsMeta, bool hyperAsMeta) +{ + // Note 1: All standard key sequences on linux (as defined in platform theme) + // that use a latin character also contain a control modifier, which is why + // checking for Qt::ControlModifier is sufficient here. It is possible to + // override QPlatformTheme::keyBindings() and provide custom sequences for + // QKeySequence::StandardKey. Custom sequences probably should respect this + // convention (alternatively, we could test against other modifiers here). + // Note 2: The possibleKeys() shorcut mechanism is not affected by this value + // adjustment and does its own thing. + if (modifiers & Qt::ControlModifier) { + // With standard shortcuts we should prefer a latin character, this is + // for checks like "some qkeyevent == QKeySequence::Copy" to work even + // when using for example 'russian' keyboard layout. + if (!QXkbCommon::isLatin1(keysym)) { + xkb_keysym_t latinKeysym = QXkbCommon::lookupLatinKeysym(state, code); + if (latinKeysym != XKB_KEY_NoSymbol) + keysym = latinKeysym; + } + } + + return keysymToQtKey_internal(keysym, modifiers, state, code, superAsMeta, hyperAsMeta); +} + +static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, + xkb_state *state, xkb_keycode_t code, + bool superAsMeta, bool hyperAsMeta) +{ + int qtKey = 0; + + // lookup from direct mapping + if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) { + // function keys + qtKey = Qt::Key_F1 + (keysym - XKB_KEY_F1); + } else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) { + // numeric keypad keys + qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0); + } else if (QXkbCommon::isLatin1(keysym)) { + // Most Qt::Key values are determined by their upper-case version, + // where this is in the Latin-1 repertoire. So start with that: + qtKey = QXkbCommon::qxkbcommon_xkb_keysym_to_upper(keysym); + // However, Key_mu and Key_ydiaeresis are U+00B5 MICRO SIGN and + // U+00FF LATIN SMALL LETTER Y WITH DIAERESIS, both lower-case, + // with upper-case forms outside Latin-1, so use them as they are + // since they're the Qt::Key values. + if (!QXkbCommon::isLatin1(qtKey)) + qtKey = keysym; + } else { + // check if we have a direct mapping + xkb2qt_t searchKey{keysym, 0}; + auto it = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey); + if (it != KeyTbl.end() && !(searchKey < *it)) + qtKey = it->qt; + + // translate Super/Hyper keys to Meta if we're using them as the MetaModifier + if (superAsMeta && (qtKey == Qt::Key_Super_L || qtKey == Qt::Key_Super_R)) + qtKey = Qt::Key_Meta; + if (hyperAsMeta && (qtKey == Qt::Key_Hyper_L || qtKey == Qt::Key_Hyper_R)) + qtKey = Qt::Key_Meta; + } + + if (qtKey) + return qtKey; + + // lookup from unicode + QString text; + if (!state || modifiers & Qt::ControlModifier) { + // Control modifier changes the text to ASCII control character, therefore we + // can't use this text to map keysym to a qt key. We can use the same keysym + // (it is not affectd by transformation) to obtain untransformed text. For details + // see "Appendix A. Default Symbol Transformations" in the XKB specification. + text = QXkbCommon::lookupStringNoKeysymTransformations(keysym); + } else { + text = QXkbCommon::lookupString(state, code); + } + if (!text.isEmpty()) { + if (text.unicode()->isDigit()) { + // Ensures that also non-latin digits are mapped to corresponding qt keys, + // e.g CTRL + ۲ (arabic two), is mapped to CTRL + Qt::Key_2. + qtKey = Qt::Key_0 + text.unicode()->digitValue(); + } else { + text = text.toUpper(); + QStringIterator i(text); + qtKey = i.next(0); + } + } + + return qtKey; +} + +Qt::KeyboardModifiers QXkbCommon::modifiers(struct xkb_state *state, xkb_keysym_t keysym) +{ + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) + modifiers |= Qt::ControlModifier; + if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0) + modifiers |= Qt::AltModifier; + if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0) + modifiers |= Qt::ShiftModifier; + if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0) + modifiers |= Qt::MetaModifier; + + if (isKeypad(keysym)) + modifiers |= Qt::KeypadModifier; + + return modifiers; +} + +// Possible modifier states. +static const Qt::KeyboardModifiers ModsTbl[] = { + Qt::NoModifier, // 0 + Qt::ShiftModifier, // 1 + Qt::ControlModifier, // 2 + Qt::ControlModifier | Qt::ShiftModifier, // 3 + Qt::AltModifier, // 4 + Qt::AltModifier | Qt::ShiftModifier, // 5 + Qt::AltModifier | Qt::ControlModifier, // 6 + Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7 + Qt::NoModifier // Fall-back to raw Key_*, for non-latin1 kb layouts +}; + +/* + Compatibility until all sub modules have transitioned to new API below +*/ +QList<int> QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event, + bool superAsMeta, bool hyperAsMeta) +{ + QList<int> result; + auto keyCombinations = possibleKeyCombinations(state, event, superAsMeta, hyperAsMeta); + for (auto keyCombination : keyCombinations) + result << keyCombination.toCombined(); + + return result; +} + +QList<QKeyCombination> QXkbCommon::possibleKeyCombinations(xkb_state *state, const QKeyEvent *event, + bool superAsMeta, bool hyperAsMeta) +{ + QList<QKeyCombination> result; + quint32 keycode = event->nativeScanCode(); + if (!keycode) + return result; + + Qt::KeyboardModifiers modifiers = event->modifiers(); + xkb_keymap *keymap = xkb_state_get_keymap(state); + // turn off the modifier bits which doesn't participate in shortcuts + Qt::KeyboardModifiers notNeeded = Qt::KeypadModifier | Qt::GroupSwitchModifier; + modifiers &= ~notNeeded; + // create a fresh kb state and test against the relevant modifier combinations + ScopedXKBState scopedXkbQueryState(xkb_state_new(keymap)); + xkb_state *queryState = scopedXkbQueryState.get(); + if (!queryState) { + qCWarning(lcQpaKeyMapper) << Q_FUNC_INFO << "failed to compile xkb keymap"; + return result; + } + // get kb state from the master state and update the temporary state + xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(state, XKB_STATE_LAYOUT_LOCKED); + xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LATCHED); + xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LOCKED); + xkb_mod_mask_t depressedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_DEPRESSED); + xkb_state_update_mask(queryState, depressedMods, latchedMods, lockedMods, 0, 0, lockedLayout); + // handle shortcuts for level three and above + xkb_layout_index_t layoutIndex = xkb_state_key_get_layout(queryState, keycode); + xkb_level_index_t levelIndex = 0; + if (layoutIndex != XKB_LAYOUT_INVALID) { + levelIndex = xkb_state_key_get_level(queryState, keycode, layoutIndex); + if (levelIndex == XKB_LEVEL_INVALID) + levelIndex = 0; + } + if (levelIndex <= 1) + xkb_state_update_mask(queryState, 0, latchedMods, lockedMods, 0, 0, lockedLayout); + + xkb_keysym_t sym = xkb_state_key_get_one_sym(queryState, keycode); + if (sym == XKB_KEY_NoSymbol) + return result; + + int baseQtKey = keysymToQtKey_internal(sym, modifiers, queryState, keycode, superAsMeta, hyperAsMeta); + if (baseQtKey) + result += QKeyCombination::fromCombined(baseQtKey + int(modifiers)); + + xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(keymap, "Shift"); + xkb_mod_index_t altMod = xkb_keymap_mod_get_index(keymap, "Alt"); + xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(keymap, "Control"); + xkb_mod_index_t metaMod = xkb_keymap_mod_get_index(keymap, "Meta"); + + Q_ASSERT(shiftMod < 32); + Q_ASSERT(altMod < 32); + Q_ASSERT(controlMod < 32); + + xkb_mod_mask_t depressed; + int qtKey = 0; + // obtain a list of possible shortcuts for the given key event + for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl) ; ++i) { + Qt::KeyboardModifiers neededMods = ModsTbl[i]; + if ((modifiers & neededMods) == neededMods) { + if (i == 8) { + if (isLatin1(baseQtKey)) + continue; + // add a latin key as a fall back key + sym = lookupLatinKeysym(state, keycode); + } else { + depressed = 0; + if (neededMods & Qt::AltModifier) + depressed |= (1 << altMod); + if (neededMods & Qt::ShiftModifier) + depressed |= (1 << shiftMod); + if (neededMods & Qt::ControlModifier) + depressed |= (1 << controlMod); + if (metaMod < 32 && neededMods & Qt::MetaModifier) + depressed |= (1 << metaMod); + xkb_state_update_mask(queryState, depressed, latchedMods, lockedMods, 0, 0, lockedLayout); + sym = xkb_state_key_get_one_sym(queryState, keycode); + } + if (sym == XKB_KEY_NoSymbol) + continue; + + Qt::KeyboardModifiers mods = modifiers & ~neededMods; + qtKey = keysymToQtKey_internal(sym, mods, queryState, keycode, superAsMeta, hyperAsMeta); + if (!qtKey || qtKey == baseQtKey) + continue; + + // catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +, + // but Ctrl++ is more specific than +, so we should skip the last one + bool ambiguous = false; + for (auto keyCombination : std::as_const(result)) { + if (keyCombination.key() == qtKey + && (keyCombination.keyboardModifiers() & mods) == mods) { + ambiguous = true; + break; + } + } + if (ambiguous) + continue; + + result += QKeyCombination::fromCombined(qtKey + int(mods)); + } + } + + return result; +} + +void QXkbCommon::verifyHasLatinLayout(xkb_keymap *keymap) +{ + const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(keymap); + const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap); + const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap); + + const xkb_keysym_t *keysyms = nullptr; + int nrLatinKeys = 0; + for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) { + for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) { + xkb_keymap_key_get_syms_by_level(keymap, code, layout, 0, &keysyms); + if (keysyms && isLatin1(keysyms[0])) + nrLatinKeys++; + if (nrLatinKeys > 10) // arbitrarily chosen threshold + return; + } + } + // This means that lookupLatinKeysym() will not find anything and latin + // key shortcuts might not work. This is a bug in the affected desktop + // environment. Usually can be solved via system settings by adding e.g. 'us' + // layout to the list of selected layouts, or by using command line, "setxkbmap + // -layout rus,en". The position of latin key based layout in the list of the + // selected layouts is irrelevant. Properly functioning desktop environments + // handle this behind the scenes, even if no latin key based layout has been + // explicitly listed in the selected layouts. + qCDebug(lcQpaKeyMapper, "no keyboard layouts with latin keys present"); +} + +xkb_keysym_t QXkbCommon::lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode) +{ + xkb_layout_index_t layout; + xkb_keysym_t sym = XKB_KEY_NoSymbol; + if (!state) + return sym; + xkb_keymap *keymap = xkb_state_get_keymap(state); + const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(keymap, keycode); + const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(state, keycode); + // Look at user layouts in the order in which they are defined in system + // settings to find a latin keysym. + for (layout = 0; layout < layoutCount; ++layout) { + if (layout == currentLayout) + continue; + const xkb_keysym_t *syms = nullptr; + xkb_level_index_t level = xkb_state_key_get_level(state, keycode, layout); + if (xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, level, &syms) != 1) + continue; + if (isLatin1(syms[0])) { + sym = syms[0]; + break; + } + } + + if (sym == XKB_KEY_NoSymbol) + return sym; + + xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LATCHED); + xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LOCKED); + + // Check for uniqueness, consider the following setup: + // setxkbmap -layout us,ru,us -variant dvorak,, -option 'grp:ctrl_alt_toggle' (set 'ru' as active). + // In this setup, the user would expect to trigger a ctrl+q shortcut by pressing ctrl+<physical x key>, + // because "US dvorak" is higher up in the layout settings list. This check verifies that an obtained + // 'sym' can not be acquired by any other layout higher up in the user's layout list. If it can be acquired + // then the obtained key is not unique. This prevents ctrl+<physical q key> from generating a ctrl+q + // shortcut in the above described setup. We don't want ctrl+<physical x key> and ctrl+<physical q key> to + // generate the same shortcut event in this case. + const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap); + const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap); + ScopedXKBState queryState(xkb_state_new(keymap)); + for (xkb_layout_index_t prevLayout = 0; prevLayout < layout; ++prevLayout) { + xkb_state_update_mask(queryState.get(), 0, latchedMods, lockedMods, 0, 0, prevLayout); + for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) { + xkb_keysym_t prevSym = xkb_state_key_get_one_sym(queryState.get(), code); + if (prevSym == sym) { + sym = XKB_KEY_NoSymbol; + break; + } + } + } + + return sym; +} + +void QXkbCommon::setXkbContext(QPlatformInputContext *inputContext, struct xkb_context *context) +{ + if (!inputContext || !context) + return; + + const char *const inputContextClassName = "QComposeInputContext"; + const char *const normalizedSignature = "setXkbContext(xkb_context*)"; + + if (inputContext->objectName() != QLatin1StringView(inputContextClassName)) + return; + + static const QMetaMethod setXkbContext = [&]() { + int methodIndex = inputContext->metaObject()->indexOfMethod(normalizedSignature); + QMetaMethod method = inputContext->metaObject()->method(methodIndex); + Q_ASSERT(method.isValid()); + if (!method.isValid()) + qCWarning(lcQpaKeyMapper) << normalizedSignature << "not found on" << inputContextClassName; + return method; + }(); + + if (!setXkbContext.isValid()) + return; + + setXkbContext.invoke(inputContext, Qt::DirectConnection, Q_ARG(struct xkb_context*, context)); +} + +QT_END_NAMESPACE diff --git a/src/gui/platform/unix/qxkbcommon_3rdparty.cpp b/src/gui/platform/unix/qxkbcommon_3rdparty.cpp new file mode 100644 index 0000000000..207c103235 --- /dev/null +++ b/src/gui/platform/unix/qxkbcommon_3rdparty.cpp @@ -0,0 +1,187 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +/* Copyright 1985, 1987, 1990, 1998 The Open Group + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the authors or their + institutions shall not be used in advertising or otherwise to promote the + sale, use or other dealings in this Software without prior written + authorization from the authors. + + + + Copyright © 2009 Dan Nicholson + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/* + XConvertCase was copied from src/3rdparty/xkbcommon/src/keysym.c + The following code modifications were applied: + + XConvertCase() was renamed to xkbcommon_XConvertCase(), to not confuse it + with Xlib's XConvertCase(). + + UCSConvertCase() was renamed to qt_UCSConvertCase() and function's body was + replaced to use Qt APIs for doing case conversion, which should give us better + results instead of using the less complete version from keysym.c +*/ + +#include "qxkbcommon_p.h" + +#include <QtCore/QChar> + +QT_BEGIN_NAMESPACE + +static void qt_UCSConvertCase(uint32_t code, xkb_keysym_t *lower, xkb_keysym_t *upper) +{ + *lower = QChar::toLower(code); + *upper = QChar::toUpper(code); +} + +void QXkbCommon::xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper) +{ + /* Latin 1 keysym */ + if (sym < 0x100) { + qt_UCSConvertCase(sym, lower, upper); + return; + } + + /* Unicode keysym */ + if ((sym & 0xff000000) == 0x01000000) { + qt_UCSConvertCase((sym & 0x00ffffff), lower, upper); + *upper |= 0x01000000; + *lower |= 0x01000000; + return; + } + + /* Legacy keysym */ + + *lower = sym; + *upper = sym; + + switch (sym >> 8) { + case 1: /* Latin 2 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym == XKB_KEY_Aogonek) + *lower = XKB_KEY_aogonek; + else if (sym >= XKB_KEY_Lstroke && sym <= XKB_KEY_Sacute) + *lower += (XKB_KEY_lstroke - XKB_KEY_Lstroke); + else if (sym >= XKB_KEY_Scaron && sym <= XKB_KEY_Zacute) + *lower += (XKB_KEY_scaron - XKB_KEY_Scaron); + else if (sym >= XKB_KEY_Zcaron && sym <= XKB_KEY_Zabovedot) + *lower += (XKB_KEY_zcaron - XKB_KEY_Zcaron); + else if (sym == XKB_KEY_aogonek) + *upper = XKB_KEY_Aogonek; + else if (sym >= XKB_KEY_lstroke && sym <= XKB_KEY_sacute) + *upper -= (XKB_KEY_lstroke - XKB_KEY_Lstroke); + else if (sym >= XKB_KEY_scaron && sym <= XKB_KEY_zacute) + *upper -= (XKB_KEY_scaron - XKB_KEY_Scaron); + else if (sym >= XKB_KEY_zcaron && sym <= XKB_KEY_zabovedot) + *upper -= (XKB_KEY_zcaron - XKB_KEY_Zcaron); + else if (sym >= XKB_KEY_Racute && sym <= XKB_KEY_Tcedilla) + *lower += (XKB_KEY_racute - XKB_KEY_Racute); + else if (sym >= XKB_KEY_racute && sym <= XKB_KEY_tcedilla) + *upper -= (XKB_KEY_racute - XKB_KEY_Racute); + break; + case 2: /* Latin 3 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Hstroke && sym <= XKB_KEY_Hcircumflex) + *lower += (XKB_KEY_hstroke - XKB_KEY_Hstroke); + else if (sym >= XKB_KEY_Gbreve && sym <= XKB_KEY_Jcircumflex) + *lower += (XKB_KEY_gbreve - XKB_KEY_Gbreve); + else if (sym >= XKB_KEY_hstroke && sym <= XKB_KEY_hcircumflex) + *upper -= (XKB_KEY_hstroke - XKB_KEY_Hstroke); + else if (sym >= XKB_KEY_gbreve && sym <= XKB_KEY_jcircumflex) + *upper -= (XKB_KEY_gbreve - XKB_KEY_Gbreve); + else if (sym >= XKB_KEY_Cabovedot && sym <= XKB_KEY_Scircumflex) + *lower += (XKB_KEY_cabovedot - XKB_KEY_Cabovedot); + else if (sym >= XKB_KEY_cabovedot && sym <= XKB_KEY_scircumflex) + *upper -= (XKB_KEY_cabovedot - XKB_KEY_Cabovedot); + break; + case 3: /* Latin 4 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Rcedilla && sym <= XKB_KEY_Tslash) + *lower += (XKB_KEY_rcedilla - XKB_KEY_Rcedilla); + else if (sym >= XKB_KEY_rcedilla && sym <= XKB_KEY_tslash) + *upper -= (XKB_KEY_rcedilla - XKB_KEY_Rcedilla); + else if (sym == XKB_KEY_ENG) + *lower = XKB_KEY_eng; + else if (sym == XKB_KEY_eng) + *upper = XKB_KEY_ENG; + else if (sym >= XKB_KEY_Amacron && sym <= XKB_KEY_Umacron) + *lower += (XKB_KEY_amacron - XKB_KEY_Amacron); + else if (sym >= XKB_KEY_amacron && sym <= XKB_KEY_umacron) + *upper -= (XKB_KEY_amacron - XKB_KEY_Amacron); + break; + case 6: /* Cyrillic */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Serbian_DJE && sym <= XKB_KEY_Serbian_DZE) + *lower -= (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje); + else if (sym >= XKB_KEY_Serbian_dje && sym <= XKB_KEY_Serbian_dze) + *upper += (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje); + else if (sym >= XKB_KEY_Cyrillic_YU && sym <= XKB_KEY_Cyrillic_HARDSIGN) + *lower -= (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu); + else if (sym >= XKB_KEY_Cyrillic_yu && sym <= XKB_KEY_Cyrillic_hardsign) + *upper += (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu); + break; + case 7: /* Greek */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Greek_ALPHAaccent && sym <= XKB_KEY_Greek_OMEGAaccent) + *lower += (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent); + else if (sym >= XKB_KEY_Greek_alphaaccent && sym <= XKB_KEY_Greek_omegaaccent && + sym != XKB_KEY_Greek_iotaaccentdieresis && + sym != XKB_KEY_Greek_upsilonaccentdieresis) + *upper -= (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent); + else if (sym >= XKB_KEY_Greek_ALPHA && sym <= XKB_KEY_Greek_OMEGA) + *lower += (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA); + else if (sym >= XKB_KEY_Greek_alpha && sym <= XKB_KEY_Greek_omega && + sym != XKB_KEY_Greek_finalsmallsigma) + *upper -= (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA); + break; + case 0x13: /* Latin 9 */ + if (sym == XKB_KEY_OE) + *lower = XKB_KEY_oe; + else if (sym == XKB_KEY_oe) + *upper = XKB_KEY_OE; + else if (sym == XKB_KEY_Ydiaeresis) + *lower = XKB_KEY_ydiaeresis; + break; + } +} + +QT_END_NAMESPACE diff --git a/src/gui/platform/unix/qxkbcommon_p.h b/src/gui/platform/unix/qxkbcommon_p.h new file mode 100644 index 0000000000..a40d794451 --- /dev/null +++ b/src/gui/platform/unix/qxkbcommon_p.h @@ -0,0 +1,128 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QXKBCOMMON_P_H +#define QXKBCOMMON_P_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. +// + +#include <QtGui/qtguiglobal.h> +#include <QtCore/qstring.h> +#include <QtCore/qloggingcategory.h> +#include <QtCore/qlist.h> +#include <QtCore/private/qglobal_p.h> + +#include <xkbcommon/xkbcommon.h> + +#include <qpa/qplatformkeymapper.h> + +#include <memory> + +QT_BEGIN_NAMESPACE + +class QEvent; +class QKeyEvent; +class QPlatformInputContext; + +class Q_GUI_EXPORT QXkbCommon +{ +public: + static QString lookupString(struct xkb_state *state, xkb_keycode_t code); + static QString lookupStringNoKeysymTransformations(xkb_keysym_t keysym); + + static QList<xkb_keysym_t> toKeysym(QKeyEvent *event); + + static int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers); + static int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, + xkb_state *state, xkb_keycode_t code, + bool superAsMeta = true, bool hyperAsMeta = true); + + // xkbcommon_* API is part of libxkbcommon internals, with modifications as + // described in the header of the implementation file. + static void xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper); + static xkb_keysym_t qxkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks); + + static Qt::KeyboardModifiers modifiers(struct xkb_state *state, xkb_keysym_t keysym = XKB_KEY_VoidSymbol); + + static QList<int> possibleKeys(xkb_state *state, + const QKeyEvent *event, bool superAsMeta = false, bool hyperAsMeta = false); + static QList<QKeyCombination> possibleKeyCombinations(xkb_state *state, + const QKeyEvent *event, bool superAsMeta = false, bool hyperAsMeta = false); + + static void verifyHasLatinLayout(xkb_keymap *keymap); + static xkb_keysym_t lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode); + + static bool isLatin1(xkb_keysym_t sym) { + return sym >= 0x20 && sym <= 0xff; + } + static bool isKeypad(xkb_keysym_t sym) { + switch (sym) { + case XKB_KEY_KP_Space: + case XKB_KEY_KP_Tab: + case XKB_KEY_KP_Enter: + case XKB_KEY_KP_F1: + case XKB_KEY_KP_F2: + case XKB_KEY_KP_F3: + case XKB_KEY_KP_F4: + case XKB_KEY_KP_Home: + case XKB_KEY_KP_Left: + case XKB_KEY_KP_Up: + case XKB_KEY_KP_Right: + case XKB_KEY_KP_Down: + case XKB_KEY_KP_Prior: + case XKB_KEY_KP_Next: + case XKB_KEY_KP_End: + case XKB_KEY_KP_Begin: + case XKB_KEY_KP_Insert: + case XKB_KEY_KP_Delete: + case XKB_KEY_KP_Equal: + case XKB_KEY_KP_Multiply: + case XKB_KEY_KP_Add: + case XKB_KEY_KP_Separator: + case XKB_KEY_KP_Subtract: + case XKB_KEY_KP_Decimal: + case XKB_KEY_KP_Divide: + case XKB_KEY_KP_0: + case XKB_KEY_KP_1: + case XKB_KEY_KP_2: + case XKB_KEY_KP_3: + case XKB_KEY_KP_4: + case XKB_KEY_KP_5: + case XKB_KEY_KP_6: + case XKB_KEY_KP_7: + case XKB_KEY_KP_8: + case XKB_KEY_KP_9: + return true; + default: + return false; + } + } + + static void setXkbContext(QPlatformInputContext *inputContext, struct xkb_context *context); + + struct XKBStateDeleter { + void operator()(struct xkb_state *state) const { return xkb_state_unref(state); } + }; + struct XKBKeymapDeleter { + void operator()(struct xkb_keymap *keymap) const { return xkb_keymap_unref(keymap); } + }; + struct XKBContextDeleter { + void operator()(struct xkb_context *context) const { return xkb_context_unref(context); } + }; + using ScopedXKBState = std::unique_ptr<struct xkb_state, XKBStateDeleter>; + using ScopedXKBKeymap = std::unique_ptr<struct xkb_keymap, XKBKeymapDeleter>; + using ScopedXKBContext = std::unique_ptr<struct xkb_context, XKBContextDeleter>; +}; + +QT_END_NAMESPACE + +#endif // QXKBCOMMON_P_H diff --git a/src/gui/platform/unix/unix.pri b/src/gui/platform/unix/unix.pri deleted file mode 100644 index b12f3147a3..0000000000 --- a/src/gui/platform/unix/unix.pri +++ /dev/null @@ -1,36 +0,0 @@ -SOURCES += \ - platform/unix/qunixeventdispatcher.cpp \ - platform/unix/qgenericunixeventdispatcher.cpp \ - platform/unix/qunixplatforminterface.cpp - -HEADERS += \ - platform/unix/qunixeventdispatcher_qpa_p.h \ - platform/unix/qgenericunixeventdispatcher_p.h - - -qtConfig(glib) { - SOURCES += platform/unix/qeventdispatcher_glib.cpp - HEADERS += platform/unix/qeventdispatcher_glib_p.h - QMAKE_USE_PRIVATE += glib -} - -if(unix:!uikit)|qtConfig(xcb) { - SOURCES += \ - platform/unix/qgenericunixservices.cpp - HEADERS += \ - platform/unix/qgenericunixservices_p.h - - qtHaveModule(dbus): QT_PRIVATE += dbus -} - -if(unix:!uikit:!macos)|qtConfig(xcb) { - SOURCES += \ - platform/unix/qgenericunixthemes.cpp - HEADERS += \ - platform/unix/qgenericunixthemes_p.h - - qtHaveModule(dbus) { - include(dbusmenu/dbusmenu.pri) - qtConfig(systemtrayicon): include(dbustray/dbustray.pri) - } -} diff --git a/src/gui/platform/wasm/qlocalfileapi.cpp b/src/gui/platform/wasm/qlocalfileapi.cpp new file mode 100644 index 0000000000..76b99361c4 --- /dev/null +++ b/src/gui/platform/wasm/qlocalfileapi.cpp @@ -0,0 +1,211 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qlocalfileapi_p.h" +#include <private/qstdweb_p.h> +#include <QtCore/QRegularExpression> + +QT_BEGIN_NAMESPACE +namespace LocalFileApi { +namespace { +std::string qtFilterListToFileInputAccept(const QStringList &filterList) +{ + QStringList transformed; + for (const auto &filter : filterList) { + const auto type = Type::fromQt(filter); + if (type && type->accept()) { + const auto &extensions = type->accept()->mimeType().extensions(); + std::transform(extensions.begin(), extensions.end(), std::back_inserter(transformed), + [](const Type::Accept::MimeType::Extension &extension) { + return extension.value().toString(); + }); + } + } + return transformed.join(QStringLiteral(",")).toStdString(); +} + +std::optional<emscripten::val> qtFilterListToTypes(const QStringList &filterList) +{ + using namespace qstdweb; + using namespace emscripten; + auto types = emscripten::val::array(); + + for (const auto &fileFilter : filterList) { + auto type = Type::fromQt(fileFilter); + if (type) { + auto jsType = emscripten::val::object(); + jsType.set("description", type->description().toString().toStdString()); + if (type->accept()) { + jsType.set("accept", ([&mimeType = type->accept()->mimeType()]() { + val acceptDict = val::object(); + + QList<emscripten::val> extensions; + extensions.reserve(mimeType.extensions().size()); + std::transform( + mimeType.extensions().begin(), mimeType.extensions().end(), + std::back_inserter(extensions), + [](const Type::Accept::MimeType::Extension &extension) { + return val(extension.value().toString().toStdString()); + }); + acceptDict.set("application/octet-stream", + emscripten::val::array(extensions.begin(), + extensions.end())); + return acceptDict; + })()); + } + types.call<void>("push", std::move(jsType)); + } + } + + return types["length"].as<int>() == 0 ? std::optional<emscripten::val>() : types; +} +} // namespace + +Type::Type(QStringView description, std::optional<Accept> accept) + : m_description(description.trimmed()), m_accept(std::move(accept)) +{ +} + +Type::~Type() = default; + +std::optional<Type> Type::fromQt(QStringView type) +{ + using namespace emscripten; + + // Accepts either a string in format: + // GROUP3 + // or in this format: + // GROUP1 (GROUP2) + // Group 1 is treated as the description, whereas group 2 or 3 are treated as the filter list. + static QRegularExpression regex( + QString(QStringLiteral("(?:(?:([^(]*)\\(([^()]+)\\)[^)]*)|([^()]+))"))); + const auto match = regex.matchView(type); + + if (!match.hasMatch()) + return std::nullopt; + + constexpr size_t DescriptionIndex = 1; + constexpr size_t FilterListFromParensIndex = 2; + constexpr size_t PlainFilterListIndex = 3; + + const auto description = match.hasCaptured(DescriptionIndex) + ? match.capturedView(DescriptionIndex) + : QStringView(); + const auto filterList = match.capturedView(match.hasCaptured(FilterListFromParensIndex) + ? FilterListFromParensIndex + : PlainFilterListIndex); + + auto accept = Type::Accept::fromQt(filterList); + if (!accept) + return std::nullopt; + + return Type(description, std::move(*accept)); +} + +Type::Accept::Accept() = default; + +Type::Accept::~Accept() = default; + +std::optional<Type::Accept> Type::Accept::fromQt(QStringView qtRepresentation) +{ + Accept accept; + + // Used for accepting multiple extension specifications on a filter list. + // The next group of non-empty characters. + static QRegularExpression internalRegex(QString(QStringLiteral("([^\\s]+)\\s*"))); + int offset = 0; + auto internalMatch = internalRegex.matchView(qtRepresentation, offset); + MimeType mimeType; + + while (internalMatch.hasMatch()) { + auto webExtension = MimeType::Extension::fromQt(internalMatch.capturedView(1)); + + if (!webExtension) + return std::nullopt; + + mimeType.addExtension(*webExtension); + + internalMatch = internalRegex.matchView(qtRepresentation, internalMatch.capturedEnd()); + } + + accept.setMimeType(mimeType); + return accept; +} + +void Type::Accept::setMimeType(MimeType mimeType) +{ + m_mimeType = std::move(mimeType); +} + +Type::Accept::MimeType::MimeType() = default; + +Type::Accept::MimeType::~MimeType() = default; + +void Type::Accept::MimeType::addExtension(Extension extension) +{ + m_extensions.push_back(std::move(extension)); +} + +Type::Accept::MimeType::Extension::Extension(QStringView extension) : m_value(extension) { } + +Type::Accept::MimeType::Extension::~Extension() = default; + +std::optional<Type::Accept::MimeType::Extension> +Type::Accept::MimeType::Extension::fromQt(QStringView qtRepresentation) +{ + // Checks for a filter that matches everything: + // Any number of asterisks or any number of asterisks with a '.' between them. + // The web filter does not support wildcards. + static QRegularExpression qtAcceptAllRegex( + QRegularExpression::anchoredPattern(QString(QStringLiteral("[*]+|[*]+\\.[*]+")))); + if (qtAcceptAllRegex.matchView(qtRepresentation).hasMatch()) + return std::nullopt; + + // Checks for correctness. The web filter only allows filename extensions and does not filter + // the actual filenames, therefore we check whether the filter provided only filters for the + // extension. + static QRegularExpression qtFilenameMatcherRegex( + QRegularExpression::anchoredPattern(QString(QStringLiteral("(\\*?)(\\.[^*]+)")))); + + auto extensionMatch = qtFilenameMatcherRegex.matchView(qtRepresentation); + if (extensionMatch.hasMatch()) + return Extension(extensionMatch.capturedView(2)); + + // Mapping impossible. + return std::nullopt; +} + +emscripten::val makeOpenFileOptions(const QStringList &filterList, bool acceptMultiple) +{ + auto options = emscripten::val::object(); + if (auto typeList = qtFilterListToTypes(filterList); typeList) { + options.set("types", std::move(*typeList)); + options.set("excludeAcceptAllOption", true); + } + + options.set("multiple", acceptMultiple); + + return options; +} + +emscripten::val makeSaveFileOptions(const QStringList &filterList, const std::string& suggestedName) +{ + auto options = emscripten::val::object(); + + if (!suggestedName.empty()) + options.set("suggestedName", emscripten::val(suggestedName)); + + if (auto typeList = qtFilterListToTypes(filterList)) + options.set("types", emscripten::val(std::move(*typeList))); + + return options; +} + +std::string makeFileInputAccept(const QStringList &filterList) +{ + return qtFilterListToFileInputAccept(filterList); +} + +} // namespace LocalFileApi + +QT_END_NAMESPACE diff --git a/src/gui/platform/wasm/qlocalfileapi_p.h b/src/gui/platform/wasm/qlocalfileapi_p.h new file mode 100644 index 0000000000..1398d674d8 --- /dev/null +++ b/src/gui/platform/wasm/qlocalfileapi_p.h @@ -0,0 +1,94 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QLOCALFILEAPI_P_H +#define QLOCALFILEAPI_P_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. +// + +#include <private/qglobal_p.h> +#include <qstringview.h> +#include <emscripten/val.h> +#include <cstdint> +#include <functional> + +QT_BEGIN_NAMESPACE + +namespace LocalFileApi { +class Q_AUTOTEST_EXPORT Type +{ +public: + class Accept { + public: + class MimeType { + public: + class Extension { + public: + static std::optional<Extension> fromQt(QStringView extension); + + ~Extension(); + + const QStringView &value() const { return m_value; } + + private: + explicit Extension(QStringView extension); + + QStringView m_value; + }; + + MimeType(); + ~MimeType(); + + void addExtension(Extension type); + + const std::vector<Extension> &extensions() const { return m_extensions; } + + private: + std::vector<Extension> m_extensions; + }; + + static std::optional<Accept> fromQt(QStringView type); + + ~Accept(); + + void setMimeType(MimeType mimeType); + + const MimeType &mimeType() const { return m_mimeType; } + + private: + Accept(); + MimeType m_mimeType; + }; + + Type(QStringView description, std::optional<Accept> accept); + ~Type(); + + static std::optional<Type> fromQt(QStringView type); + const QStringView &description() const { return m_description; } + const std::optional<Accept> &accept() const { return m_accept; } + +private: + QStringView m_description; + std::optional<Accept> m_accept; +}; + +Q_AUTOTEST_EXPORT emscripten::val makeOpenFileOptions(const QStringList &filterList, + bool acceptMultiple); +Q_AUTOTEST_EXPORT emscripten::val makeSaveFileOptions(const QStringList &filterList, + const std::string &suggestedName); + +Q_AUTOTEST_EXPORT std::string makeFileInputAccept(const QStringList &filterList); + +} // namespace LocalFileApi +QT_END_NAMESPACE + +#endif // QLOCALFILEAPI_P_H diff --git a/src/gui/platform/wasm/qwasmlocalfileaccess.cpp b/src/gui/platform/wasm/qwasmlocalfileaccess.cpp index 85c894a74a..051e2b1a04 100644 --- a/src/gui/platform/wasm/qwasmlocalfileaccess.cpp +++ b/src/gui/platform/wasm/qwasmlocalfileaccess.cpp @@ -1,94 +1,111 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwasmlocalfileaccess_p.h" +#include "qlocalfileapi_p.h" #include <private/qstdweb_p.h> #include <emscripten.h> #include <emscripten/bind.h> #include <emscripten/html5.h> #include <emscripten/val.h> +#include <QtCore/qregularexpression.h> + QT_BEGIN_NAMESPACE namespace QWasmLocalFileAccess { +namespace FileDialog { +namespace { +bool hasLocalFilesApi() +{ + return !qstdweb::window()["showOpenFilePicker"].isUndefined(); +} -void streamFile(const qstdweb::File &file, uint32_t offset, uint32_t length, char *buffer, const std::function<void ()> &completed) +void showOpenViaHTMLPolyfill(const QStringList &accept, FileSelectMode fileSelectMode, + qstdweb::PromiseCallbacks onFilesSelected) { - // Read file in chunks in order to avoid holding two copies in memory at the same time - const uint32_t chunkSize = 256 * 1024; - const uint32_t end = offset + length; - // assert end < file.size - auto fileReader = std::make_shared<qstdweb::FileReader>(); - - auto chunkCompleted = std::make_shared<std::function<void (uint32_t, char *buffer)>>(); - *chunkCompleted = [=](uint32_t chunkBegin, char *chunkBuffer) mutable { - - // Copy current chunk from JS memory to Wasm memory - qstdweb::ArrayBuffer result = fileReader->result(); - qstdweb::Uint8Array(result).copyTo(chunkBuffer); - - // Read next chunk if not at buffer end - uint32_t nextChunkBegin = std::min(chunkBegin + result.byteLength(), end); - uint32_t nextChunkEnd = std::min(nextChunkBegin + chunkSize, end); - if (nextChunkBegin == end) { - completed(); - chunkCompleted.reset(); - return; - } - char *nextChunkBuffer = chunkBuffer + result.byteLength(); - fileReader->onLoad([=]() { (*chunkCompleted)(nextChunkBegin, nextChunkBuffer); }); - qstdweb::Blob blob = file.slice(nextChunkBegin, nextChunkEnd); - fileReader->readAsArrayBuffer(blob); - }; + // Create file input html element which will display a native file dialog + // and call back to our onchange handler once the user has selected + // one or more files. + emscripten::val document = emscripten::val::global("document"); + emscripten::val input = document.call<emscripten::val>("createElement", std::string("input")); + input.set("type", "file"); + input.set("style", "display:none"); + input.set("accept", LocalFileApi::makeFileInputAccept(accept)); + Q_UNUSED(accept); + input.set("multiple", emscripten::val(fileSelectMode == FileSelectMode::MultipleFiles)); + + // Note: there is no event in case the user cancels the file dialog. + static std::unique_ptr<qstdweb::EventCallback> changeEvent; + auto callback = [=](emscripten::val) { onFilesSelected.thenFunc(input["files"]); }; + changeEvent = std::make_unique<qstdweb::EventCallback>(input, "change", callback); + + // Activate file input + emscripten::val body = document["body"]; + body.call<void>("appendChild", input); + input.call<void>("click"); + body.call<void>("removeChild", input); +} + +void showOpenViaLocalFileApi(const QStringList &accept, FileSelectMode fileSelectMode, + qstdweb::PromiseCallbacks callbacks) +{ + using namespace qstdweb; + + auto options = LocalFileApi::makeOpenFileOptions(accept, fileSelectMode == FileSelectMode::MultipleFiles); + + Promise::make( + window(), QStringLiteral("showOpenFilePicker"), + { + .thenFunc = [=](emscripten::val fileHandles) mutable { + std::vector<emscripten::val> filePromises; + filePromises.reserve(fileHandles["length"].as<int>()); + for (int i = 0; i < fileHandles["length"].as<int>(); ++i) + filePromises.push_back(fileHandles[i].call<emscripten::val>("getFile")); + Promise::all(std::move(filePromises), callbacks); + }, + .catchFunc = callbacks.catchFunc, + .finallyFunc = callbacks.finallyFunc, + }, std::move(options)); +} + +void showSaveViaLocalFileApi(const std::string &fileNameHint, qstdweb::PromiseCallbacks callbacks) +{ + using namespace qstdweb; + using namespace emscripten; + + auto options = LocalFileApi::makeSaveFileOptions(QStringList(), fileNameHint); + + Promise::make( + window(), QStringLiteral("showSaveFilePicker"), + std::move(callbacks), std::move(options)); +} +} // namespace - // Read first chunk. First iteration is a dummy iteration with no available data. - (*chunkCompleted)(offset, buffer); +void showOpen(const QStringList &accept, FileSelectMode fileSelectMode, + qstdweb::PromiseCallbacks callbacks) +{ + hasLocalFilesApi() ? + showOpenViaLocalFileApi(accept, fileSelectMode, std::move(callbacks)) : + showOpenViaHTMLPolyfill(accept, fileSelectMode, std::move(callbacks)); +} + +bool canShowSave() +{ + return hasLocalFilesApi(); } -void streamFile(const qstdweb::File &file, char *buffer, const std::function<void ()> &completed) +void showSave(const std::string &fileNameHint, qstdweb::PromiseCallbacks callbacks) { - streamFile(file, 0, file.size(), buffer, completed); + Q_ASSERT(canShowSave()); + showSaveViaLocalFileApi(fileNameHint, std::move(callbacks)); } +} // namespace FileDialog +namespace { void readFiles(const qstdweb::FileList &fileList, - const std::function<char *(uint64_t size, const std::string name)> &acceptFile, - const std::function<void ()> &fileDataReady) + const std::function<char *(uint64_t size, const std::string name)> &acceptFile, + const std::function<void ()> &fileDataReady) { auto readFile = std::make_shared<std::function<void(int)>>(); @@ -99,7 +116,7 @@ void readFiles(const qstdweb::FileList &fileList, return; } - const qstdweb::File file = fileList[fileIndex]; + const qstdweb::File file = qstdweb::File(fileList[fileIndex]); // Ask caller if the file should be accepted char *buffer = acceptFile(file.size(), file.name()); @@ -109,7 +126,7 @@ void readFiles(const qstdweb::FileList &fileList, } // Read file data into caller-provided buffer - streamFile(file, buffer, [=]() { + file.stream(buffer, [readFile = readFile.get(), fileIndex, fileDataReady]() { fileDataReady(); (*readFile)(fileIndex + 1); }); @@ -118,87 +135,154 @@ void readFiles(const qstdweb::FileList &fileList, (*readFile)(0); } -typedef std::function<void (const qstdweb::FileList &fileList)> OpenFileDialogCallback; -void openFileDialog(const std::string &accept, FileSelectMode fileSelectMode, - const OpenFileDialogCallback &filesSelected) +QStringList makeFilterList(const std::string &qtAcceptList) { - // Create file input html element which will display a native file dialog - // and call back to our onchange handler once the user has selected - // one or more files. - emscripten::val document = emscripten::val::global("document"); - emscripten::val input = document.call<emscripten::val>("createElement", std::string("input")); - input.set("type", "file"); - input.set("style", "display:none"); - input.set("accept", emscripten::val(accept)); - input.set("multiple", emscripten::val(fileSelectMode == MultipleFiles)); + // copy of qt_make_filter_list() from qfiledialog.cpp + auto filter = QString::fromStdString(qtAcceptList); + if (filter.isEmpty()) + return QStringList(); + QString sep(";;"); + if (!filter.contains(sep) && filter.contains(u'\n')) + sep = u'\n'; - // Note: there is no event in case the user cancels the file dialog. - static std::unique_ptr<qstdweb::EventCallback> changeEvent; - auto callback = [=]() { filesSelected(qstdweb::FileList(input["files"])); }; - changeEvent.reset(new qstdweb::EventCallback(input, "change", callback)); + return filter.split(sep); +} +} + +void downloadDataAsFile(const char *content, size_t size, const std::string &fileNameHint) +{ + // Save a file by creating programmatically clicking a download + // link to an object url to a Blob containing a copy of the file + // content. The copy is made so that the passed in content buffer + // can be released as soon as this function returns. + qstdweb::Blob contentBlob = qstdweb::Blob::copyFrom(content, size); + emscripten::val document = emscripten::val::global("document"); + emscripten::val window = qstdweb::window(); + emscripten::val contentUrl = window["URL"].call<emscripten::val>("createObjectURL", contentBlob.val()); + emscripten::val contentLink = document.call<emscripten::val>("createElement", std::string("a")); + contentLink.set("href", contentUrl); + contentLink.set("download", fileNameHint); + contentLink.set("style", "display:none"); - // Activate file input emscripten::val body = document["body"]; - body.call<void>("appendChild", input); - input.call<void>("click"); - body.call<void>("removeChild", input); + body.call<void>("appendChild", contentLink); + contentLink.call<void>("click"); + body.call<void>("removeChild", contentLink); + + window["URL"].call<emscripten::val>("revokeObjectURL", contentUrl); } void openFiles(const std::string &accept, FileSelectMode fileSelectMode, const std::function<void (int fileCount)> &fileDialogClosed, - const std::function<char *(uint64_t size, const std::string name)> &acceptFile, + const std::function<char *(uint64_t size, const std::string& name)> &acceptFile, const std::function<void()> &fileDataReady) { - openFileDialog(accept, fileSelectMode, [=](const qstdweb::FileList &files) { - fileDialogClosed(files.length()); - readFiles(files, acceptFile, fileDataReady); + FileDialog::showOpen(makeFilterList(accept), fileSelectMode, { + .thenFunc = [=](emscripten::val result) { + auto files = qstdweb::FileList(result); + fileDialogClosed(files.length()); + readFiles(files, acceptFile, fileDataReady); + }, + .catchFunc = [=](emscripten::val) { + fileDialogClosed(0); + } }); } void openFile(const std::string &accept, const std::function<void (bool fileSelected)> &fileDialogClosed, - const std::function<char *(uint64_t size, const std::string name)> &acceptFile, + const std::function<char *(uint64_t size, const std::string& name)> &acceptFile, const std::function<void()> &fileDataReady) { auto fileDialogClosedWithInt = [=](int fileCount) { fileDialogClosed(fileCount != 0); }; openFiles(accept, FileSelectMode::SingleFile, fileDialogClosedWithInt, acceptFile, fileDataReady); } -void saveFile(const char *content, size_t size, const std::string &fileNameHint) +void saveDataToFileInChunks(emscripten::val fileHandle, const QByteArray &data) { - // Save a file by creating programatically clicking a download - // link to an object url to a Blob containing the file content. - // File content is copied once, so that the passed in content - // buffer can be released as soon as this function returns - we - // don't know for how long the browser will retain the TypedArray - // view used to create the Blob. + using namespace emscripten; + using namespace qstdweb; - emscripten::val document = emscripten::val::global("document"); - emscripten::val window = emscripten::val::global("window"); + Promise::make(fileHandle, QStringLiteral("createWritable"), { + .thenFunc = [=](val writable) { + struct State { + size_t written; + std::function<void(val result)> continuation; + }; - emscripten::val fileContentView = emscripten::val(emscripten::typed_memory_view(size, content)); - emscripten::val fileContentCopy = emscripten::val::global("ArrayBuffer").new_(size); - emscripten::val fileContentCopyView = emscripten::val::global("Uint8Array").new_(fileContentCopy); - fileContentCopyView.call<void>("set", fileContentView); + static constexpr size_t desiredChunkSize = 1024u; +#if defined(__EMSCRIPTEN_SHARED_MEMORY__) + qstdweb::Uint8Array chunkArray(desiredChunkSize); +#endif - emscripten::val contentArray = emscripten::val::array(); - contentArray.call<void>("push", fileContentCopyView); - emscripten::val type = emscripten::val::object(); - type.set("type","application/octet-stream"); - emscripten::val contentBlob = emscripten::val::global("Blob").new_(contentArray, type); + auto state = std::make_shared<State>(); + state->written = 0u; + state->continuation = [=](val) mutable { + const size_t remaining = data.size() - state->written; + if (remaining == 0) { + Promise::make(writable, QStringLiteral("close"), { .thenFunc = [=](val) {} }); + state.reset(); + return; + } - emscripten::val contentUrl = window["URL"].call<emscripten::val>("createObjectURL", contentBlob); - emscripten::val contentLink = document.call<emscripten::val>("createElement", std::string("a")); - contentLink.set("href", contentUrl); - contentLink.set("download", fileNameHint); - contentLink.set("style", "display:none"); + const auto currentChunkSize = std::min(remaining, desiredChunkSize); - emscripten::val body = document["body"]; - body.call<void>("appendChild", contentLink); - contentLink.call<void>("click"); - body.call<void>("removeChild", contentLink); +#if defined(__EMSCRIPTEN_SHARED_MEMORY__) + // If shared memory is used, WebAssembly.Memory is instantiated with the 'shared' + // option on. Passing a typed_memory_view to SharedArrayBuffer to + // FileSystemWritableFileStream.write is disallowed by security policies, so we + // need to make a copy of the data to a chunk array buffer. + Promise::make( + writable, QStringLiteral("write"), + { + .thenFunc = state->continuation, + }, + chunkArray.copyFrom(data.constData() + state->written, currentChunkSize) + .val() + .call<emscripten::val>("subarray", emscripten::val(0), + emscripten::val(currentChunkSize))); +#else + Promise::make(writable, QStringLiteral("write"), + { + .thenFunc = state->continuation, + }, + val(typed_memory_view(currentChunkSize, data.constData() + state->written))); +#endif + state->written += currentChunkSize; + }; - window["URL"].call<emscripten::val>("revokeObjectURL", contentUrl); + state->continuation(val::undefined()); + }, + }); +} + +void saveFile(const QByteArray &data, const std::string &fileNameHint) +{ + if (!FileDialog::canShowSave()) { + downloadDataAsFile(data.constData(), data.size(), fileNameHint); + return; + } + + FileDialog::showSave(fileNameHint, { + .thenFunc = [=](emscripten::val result) { + saveDataToFileInChunks(result, data); + }, + }); +} + +void saveFile(const char *content, size_t size, const std::string &fileNameHint) +{ + if (!FileDialog::canShowSave()) { + downloadDataAsFile(content, size, fileNameHint); + return; + } + + QByteArray data(content, size); + FileDialog::showSave(fileNameHint, { + .thenFunc = [=](emscripten::val result) { + saveDataToFileInChunks(result, data); + }, + }); } } // namespace QWasmLocalFileAccess diff --git a/src/gui/platform/wasm/qwasmlocalfileaccess_p.h b/src/gui/platform/wasm/qwasmlocalfileaccess_p.h index ccd88570c8..77b14577f7 100644 --- a/src/gui/platform/wasm/qwasmlocalfileaccess_p.h +++ b/src/gui/platform/wasm/qwasmlocalfileaccess_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWASMLOCALFILEACCESS_P_H #define QWASMLOCALFILEACCESS_P_H @@ -51,7 +15,7 @@ // We mean it. // -#include <qglobal.h> +#include <private/qglobal_p.h> #include <cstdint> #include <functional> @@ -59,19 +23,20 @@ QT_BEGIN_NAMESPACE namespace QWasmLocalFileAccess { -enum FileSelectMode { SingleFile, MultipleFiles }; +enum class FileSelectMode { SingleFile, MultipleFiles }; -void openFiles(const std::string &accept, FileSelectMode fileSelectMode, +Q_CORE_EXPORT void openFiles(const std::string &accept, FileSelectMode fileSelectMode, const std::function<void (int fileCount)> &fileDialogClosed, - const std::function<char *(uint64_t size, const std::string name)> &acceptFile, + const std::function<char *(uint64_t size, const std::string& name)> &acceptFile, const std::function<void()> &fileDataReady); -void openFile(const std::string &accept, +Q_CORE_EXPORT void openFile(const std::string &accept, const std::function<void (bool fileSelected)> &fileDialogClosed, - const std::function<char *(uint64_t size, const std::string name)> &acceptFile, + const std::function<char *(uint64_t size, const std::string& name)> &acceptFile, const std::function<void()> &fileDataReady); -void saveFile(const char *content, size_t size, const std::string &fileNameHint); +Q_CORE_EXPORT void saveFile(const QByteArray &data, const std::string &fileNameHint); +Q_CORE_EXPORT void saveFile(const char *content, size_t size, const std::string &fileNameHint); } // namespace QWasmLocalFileAccess diff --git a/src/gui/platform/wasm/qwasmnativeinterface.cpp b/src/gui/platform/wasm/qwasmnativeinterface.cpp new file mode 100644 index 0000000000..7313629a8d --- /dev/null +++ b/src/gui/platform/wasm/qwasmnativeinterface.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + + + +#include <QtGui/private/qguiapplication_p.h> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformwindow_p.h> + + +QT_BEGIN_NAMESPACE + +using namespace QNativeInterface::Private; + +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWasmWindow); + +QT_END_NAMESPACE diff --git a/src/gui/platform/wasm/wasm.pri b/src/gui/platform/wasm/wasm.pri deleted file mode 100644 index 1b5d7eadb5..0000000000 --- a/src/gui/platform/wasm/wasm.pri +++ /dev/null @@ -1,3 +0,0 @@ -INCLUDEDIR += $$PWD -HEADERS += $$PWD/qwasmlocalfileaccess_p.h -SOURCES += $$PWD/qwasmlocalfileaccess.cpp diff --git a/src/gui/platform/windows/qwindowsguieventdispatcher.cpp b/src/gui/platform/windows/qwindowsguieventdispatcher.cpp index ca4bc4091b..c2f0efe96e 100644 --- a/src/gui/platform/windows/qwindowsguieventdispatcher.cpp +++ b/src/gui/platform/windows/qwindowsguieventdispatcher.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowsguieventdispatcher_p.h" @@ -63,7 +27,6 @@ QWindowsGuiEventDispatcher::QWindowsGuiEventDispatcher(QObject *parent) : QEventDispatcherWin32(parent) { setObjectName(QStringLiteral("QWindowsGuiEventDispatcher")); - createInternalHwnd(); // QTBUG-40881: Do not delay registering timers, etc. for QtMfc. } bool QWindowsGuiEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) @@ -210,6 +173,12 @@ messageDebugEntries[] = { {WM_POINTERROUTEDAWAY, "WM_POINTERROUTEDAWAY", true}, {WM_POINTERROUTEDRELEASED, "WM_POINTERROUTEDRELEASED", true}, #endif // WM_POINTERROUTEDTO +#ifdef WM_GETDPISCALEDSIZE + {WM_GETDPISCALEDSIZE, "WM_GETDPISCALEDSIZE", true}, +#endif +#ifdef WM_DPICHANGED + {WM_DPICHANGED, "WM_DPICHANGED", true}, +#endif }; static inline const MessageDebugEntry *messageDebugEntry(UINT msg) @@ -228,3 +197,5 @@ const char *QWindowsGuiEventDispatcher::windowsMessageName(UINT msg) } QT_END_NAMESPACE + +#include "moc_qwindowsguieventdispatcher_p.cpp" diff --git a/src/gui/platform/windows/qwindowsguieventdispatcher_p.h b/src/gui/platform/windows/qwindowsguieventdispatcher_p.h index c6506f2dc9..7d326c0780 100644 --- a/src/gui/platform/windows/qwindowsguieventdispatcher_p.h +++ b/src/gui/platform/windows/qwindowsguieventdispatcher_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QWINDOWSGUIEVENTDISPATCHER_H #define QWINDOWSGUIEVENTDISPATCHER_H @@ -60,7 +24,7 @@ class Q_GUI_EXPORT QWindowsGuiEventDispatcher : public QEventDispatcherWin32 { Q_OBJECT public: - explicit QWindowsGuiEventDispatcher(QObject *parent = 0); + explicit QWindowsGuiEventDispatcher(QObject *parent = nullptr); static const char *windowsMessageName(UINT msg); diff --git a/src/gui/platform/windows/qwindowsmimeconverter.cpp b/src/gui/platform/windows/qwindowsmimeconverter.cpp new file mode 100644 index 0000000000..49d524cb99 --- /dev/null +++ b/src/gui/platform/windows/qwindowsmimeconverter.cpp @@ -0,0 +1,170 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwindowsmimeconverter.h" + +#include <QtCore/qt_windows.h> + +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatformintegration.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QWindowsMimeConverter + \brief The QWindowsMimeConverter class maps open-standard MIME to Window Clipboard formats. + \inmodule QtGui + + 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 QWindowsMimeConverter that provide + conversions between Windows Clipboard and MIME formats, you can convert + proprietary clipboard formats to MIME formats. + + Construct an instance of your converter implementation after instantiating + QGuiApplication: + + \code + int main(int argc, char **argv) + { + QGuiApplication app(argc, argv); + JsonMimeConverter jsonConverter; + } + \endcode + + Destroying the instance will unregister the converter and remove support + for the conversion. It is also valid to heap-allocate the converter + instance; Qt takes ownership and will delete the converter object during + QGuiApplication shut-down. + + Qt has predefined support for the following Windows Clipboard formats: + + \table + \header \li Windows Format \li Equivalent MIME type + \row \li \c CF_UNICODETEXT \li \c text/plain + \row \li \c CF_TEXT \li \c text/plain + \row \li \c CF_DIB \li \c{image/xyz}, where \c xyz is + a \l{QImageWriter::supportedImageFormats()}{Qt image format} + \row \li \c CF_HDROP \li \c text/uri-list + \row \li \c CF_INETURL \li \c text/uri-list + \row \li \c CF_HTML \li \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(). +*/ + + +/*! + \fn bool QWindowsMimeConverter::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const + + Returns \c 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 QWindowsMimeConverter::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const + + Returns \c 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 QWindowsMimeConverter::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 QList<FORMATETC> QWindowsMimeConverter::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const + + Returns a QList 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 QWindowsMimeConverter::convertToMime(const QString &mimeType, IDataObject *pDataObj, + QMetaType 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 QWindowsMimeConverter::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. +*/ + +/*! + Constructs a QWindowsMimeConverter instance. + + The instance is automatically registered, and will be called to convert data during + clipboard or drag'n'drop operations. + + Call this constructor after QGuiApplication has been created. +*/ +QWindowsMimeConverter::QWindowsMimeConverter() +{ + using QWindowsApplication = QNativeInterface::Private::QWindowsApplication; + auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration()); + Q_ASSERT(nativeWindowsApp); + nativeWindowsApp->registerMime(this); +} + +/*! + Constructs a QWindowsMimeConverter instance. + + The instance is automatically unregistered. +*/ +QWindowsMimeConverter::~QWindowsMimeConverter() +{ + using QWindowsApplication = QNativeInterface::Private::QWindowsApplication; + auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration()); + Q_ASSERT(nativeWindowsApp); + nativeWindowsApp->unregisterMime(this); +} + +/*! + Registers the MIME type \a mimeType, and returns an ID number + identifying the format on Windows. + + A mime type \c {application/x-qt-windows-mime;value="WindowsType"} will be + registered as the clipboard format for \c WindowsType. +*/ +int QWindowsMimeConverter::registerMimeType(const QString &mimeType) +{ + using QWindowsApplication = QNativeInterface::Private::QWindowsApplication; + auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration()); + Q_ASSERT(nativeWindowsApp); + return nativeWindowsApp->registerMimeType(mimeType); +} + +QT_END_NAMESPACE diff --git a/src/gui/platform/windows/qwindowsmimeconverter.h b/src/gui/platform/windows/qwindowsmimeconverter.h new file mode 100644 index 0000000000..145355fe15 --- /dev/null +++ b/src/gui/platform/windows/qwindowsmimeconverter.h @@ -0,0 +1,43 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWINDOWSMIMECONVERTER_P_H +#define QWINDOWSMIMECONVERTER_P_H + +#include <QtGui/qtguiglobal.h> + +struct tagFORMATETC; +using FORMATETC = tagFORMATETC; +struct tagSTGMEDIUM; +using STGMEDIUM = tagSTGMEDIUM; +struct IDataObject; + +QT_BEGIN_NAMESPACE + +class QMetaType; +class QMimeData; +class QVariant; + +class Q_GUI_EXPORT QWindowsMimeConverter +{ + Q_DISABLE_COPY(QWindowsMimeConverter) +public: + QWindowsMimeConverter(); + virtual ~QWindowsMimeConverter(); + + static int registerMimeType(const QString &mimeType); + + // 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 QList<FORMATETC> 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, QMetaType preferredType) const = 0; + virtual QString mimeForFormat(const FORMATETC &formatetc) const = 0; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSMIMECONVERTER_H diff --git a/src/gui/platform/windows/qwindowsnativeinterface.cpp b/src/gui/platform/windows/qwindowsnativeinterface.cpp new file mode 100644 index 0000000000..44f230e1d3 --- /dev/null +++ b/src/gui/platform/windows/qwindowsnativeinterface.cpp @@ -0,0 +1,312 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtGui/qopenglcontext.h> +#include <QtGui/private/qguiapplication_p.h> +#include <qpa/qplatformopenglcontext.h> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformwindow.h> +#include <qpa/qplatformwindow_p.h> +#include <qpa/qplatformscreen_p.h> + +QT_BEGIN_NAMESPACE + +using namespace QNativeInterface::Private; + +#ifndef QT_NO_OPENGL + +/*! + \class QNativeInterface::QWGLContext + \inheaderfile QOpenGLContext + \since 6.0 + \brief Native interface to a WGL context on Windows. + + Accessed through QOpenGLContext::nativeInterface(). + + \inmodule QtGui + \inheaderfile QOpenGLContext + \ingroup native-interfaces + \ingroup native-interfaces-qopenglcontext +*/ + +/*! + \fn QOpenGLContext *QNativeInterface::QWGLContext::fromNative(HGLRC context, HWND window, QOpenGLContext *shareContext = nullptr) + + \brief Adopts an WGL \a context handle. + + The \a window is needed because the its pixel format will be queried. When the + adoption is successful, QOpenGLContext::format() will return a QSurfaceFormat + describing this pixel format. + + \note The window specified by \a window must have its pixel format set to a + format compatible with the context's. If no SetPixelFormat() call was made on + any device context belonging to the window, adopting the context will fail. + + Ownership of the created QOpenGLContext \a shareContext is transferred to the + caller. +*/ + +/*! + \fn HGLRC QNativeInterface::QWGLContext::nativeContext() const + + \return the underlying context handle. +*/ + +/*! + \fn HMODULE QNativeInterface::QWGLContext::openGLModuleHandle() + + \return the handle for the OpenGL implementation that is currently in use. + + \note This function requires that the QGuiApplication instance is already created. +*/ + +QT_DEFINE_NATIVE_INTERFACE(QWGLContext); +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWindowsGLIntegration); + +HMODULE QNativeInterface::QWGLContext::openGLModuleHandle() +{ + return QGuiApplicationPrivate::platformIntegration()->call< + &QWindowsGLIntegration::openGLModuleHandle>(); +} + +QOpenGLContext *QNativeInterface::QWGLContext::fromNative(HGLRC context, HWND window, QOpenGLContext *shareContext) +{ + return QGuiApplicationPrivate::platformIntegration()->call< + &QWindowsGLIntegration::createOpenGLContext>(context, window, shareContext); +} + +#endif // QT_NO_OPENGL + +/*! + \class QNativeInterface::Private::QWindowsApplication + \since 6.0 + \internal + \brief Native interface to QGuiApplication, to be retrieved from QPlatformIntegration. + \inmodule QtGui + \ingroup native-interfaces +*/ + +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWindowsApplication); + +/*! + \class QNativeInterface::QWindowsScreen + \since 6.7 + \brief Native interface to a screen. + + Accessed through QScreen::nativeInterface(). + \inmodule QtGui + \ingroup native-interfaces + \ingroup native-interfaces-qscreen +*/ +/*! + * \fn HWMONITOR QNativeInterface::QWindowsScreen::handle() const; + * \return The underlying HWMONITOR of the screen. + */ +QT_DEFINE_NATIVE_INTERFACE(QWindowsScreen); +/*! + \enum QNativeInterface::Private::QWindowsApplication::TouchWindowTouchType + + This enum represents the supported TouchWindow touch flags for registerTouchWindow(). + + \value NormalTouch + \value FineTouch + \value WantPalmTouch +*/ + +/*! + \fn void QNativeInterface::Private::QWindowsApplication::setTouchWindowTouchType(QNativeInterface::Private::QWindowsApplication::TouchWindowTouchTypes type) + \internal + + Sets the touch window type for all windows to \a type. +*/ + +/*! + \fn QNativeInterface::Private::QWindowsApplication::TouchWindowTouchTypes QNativeInterface::Private::QWindowsApplication::touchWindowTouchType() const + \internal + + Returns the currently set the touch window type. +*/ + +/*! + \enum QNativeInterface::Private::QWindowsApplication::WindowActivationBehavior + + This enum specifies the behavior of QWidget::activateWindow() and + QWindow::requestActivate(). + + \value DefaultActivateWindow The window is activated according to the default + behavior of the Windows operating system. This means the window will not + be activated in some circumstances (most notably when the calling process + is not the active process); only the taskbar entry will be flashed. + \value AlwaysActivateWindow The window is always activated, even when the + calling process is not the active process. + + \sa QWidget::activateWindow(), QWindow::requestActivate() +*/ + +/*! + \fn void QNativeInterface::Private::QWindowsApplication::setWindowActivationBehavior(QNativeInterface::Private::QWindowsApplication::WindowActivationBehavior behavior) + \internal + + Sets the window activation behavior to \a behavior. + + \sa QWidget::activateWindow(), QWindow::requestActivate() +*/ + +/*! + \fn QNativeInterface::Private::QWindowsApplication::WindowActivationBehavior QNativeInterface::Private::QWindowsApplication::windowActivationBehavior() const + \internal + + Returns the currently set the window activation behavior. +*/ + +/*! + \fn bool QNativeInterface::Private::QWindowsApplication::isTabletMode() const + \internal + + Returns \c true if Windows 10 operates in \e{Tablet Mode}. + In this mode, Windows forces all application main windows to open in maximized + state. Applications should then avoid resizing windows or restoring geometries + to non-maximized states. + + \sa QWidget::showMaximized(), QWidget::saveGeometry(), QWidget::restoreGeometry() +*/ + +/*! + \enum QNativeInterface::Private::QWindowsApplication::DarkModeHandlingFlag + + This enum specifies the behavior of the application when Windows + is configured to use dark mode for applications. + + \value DarkModeWindowFrames The window frames will be switched to dark. + \value DarkModeStyle The Windows Vista style will be turned off and + a simple dark style will be used. + + \sa setDarkModeHandling() +*/ + +/*! + \fn void QNativeInterface::Private::QWindowsApplication::setDarkModeHandling(DarkModeHandling handling) = 0 + \internal + + Sets the dark mode handling to \a handling. +*/ + +/*! + \fn QNativeInterface::Private::QWindowsApplication::DarkModeHandling QNativeInterface::Private::QWindowsApplication::darkModeHandling() const + \internal + + Returns the currently set dark mode handling. +*/ + +/*! + \fn bool QNativeInterface::Private::QWindowsApplication::isWinTabEnabled() const = 0 + \internal + + Returns whether the \e{Tablet WinTab Driver} (\c Wintab32.dll) is used. +*/ + +/*! + \fn bool QNativeInterface::Private::QWindowsApplication::setWinTabEnabled(bool enabled) + \internal + + Sets whether the \e{Tablet WinTab Driver} (\c Wintab32.dll) should be used to \a enabled. + + Returns \c true on success, \c false otherwise. +*/ + +/*! + \fn bool QNativeInterface::Private::QWindowsApplication::registerMime(QWindowsMimeConverter *mime) + \internal + + Registers the converter \a mime to the system. + + \sa QWindowsMimeConverter, unregisterMime() +*/ + +/*! + \fn void QNativeInterface::Private::QWindowsApplication::unregisterMime(QWindowsMimeConverter *mime) + \internal + + Unregisters the converter \a mime from the system. + + \sa QWindowsMimeConverter, registerMime() +*/ + +/*! + \fn int QNativeInterface::Private::QWindowsApplication::registerMimeType(const QString &mime) + \internal + + Registers the MIME type \a mime, and returns an ID number + identifying the format on Windows. +*/ + +/*! + \fn HWND QNativeInterface::Private::QWindowsApplication::createMessageWindow(const QString &, const QString &, QFunctionPointer) const + \internal +*/ + +/*! + \fn bool QNativeInterface::Private::QWindowsApplication::asyncExpose() const + \internal +*/ + +/*! + \fn void QNativeInterface::Private::QWindowsApplication::setAsyncExpose(bool) + \internal +*/ + +/*! + \fn QVariant QNativeInterface::Private::QWindowsApplication::gpu() + \internal +*/ + +/*! + \fn QVariant QNativeInterface::Private::QWindowsApplication::gpuList() + \internal +*/ + +/*! + \class QNativeInterface::Private::QWindowsWindow + \since 6.0 + \internal + \brief Native interface to QPlatformWindow. + \inmodule QtGui + \ingroup native-interfaces +*/ + +QT_DEFINE_PRIVATE_NATIVE_INTERFACE(QWindowsWindow); + +/*! + \fn void QNativeInterface::Private::QWindowsWindow::setHasBorderInFullScreen(bool border) + \internal + + Sets whether the WS_BORDER flag will be set for the window in full screen mode + to \a border. + + See also \l [QtDoc] {Fullscreen OpenGL Based Windows} +*/ + +/*! + \fn bool QNativeInterface::Private::QWindowsWindow::hasBorderInFullScreen() const + \internal + + Returns whether the WS_BORDER flag will be set for the window in full screen + mode. +*/ + +/*! + \fn QMargins QNativeInterface::Private::QWindowsWindow::customMargins() const + \internal + + Returns the margin to be used when handling the \c WM_NCCALCSIZE message. +*/ + +/*! + \fn void QNativeInterface::Private::QWindowsWindow::setCustomMargins(const QMargins &margins) + \internal + + Sets the\a margins to be used when handling the \c WM_NCCALCSIZE message. It is + possible to remove a frame border by specifying a negative value. +*/ + +QT_END_NAMESPACE diff --git a/src/gui/platform/windows/qwindowsplatforminterface.cpp b/src/gui/platform/windows/qwindowsplatforminterface.cpp deleted file mode 100644 index 6ace981475..0000000000 --- a/src/gui/platform/windows/qwindowsplatforminterface.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtGui/qopenglcontext.h> -#include <QtGui/private/qguiapplication_p.h> -#include <qpa/qplatformopenglcontext.h> -#include <qpa/qplatformintegration.h> - -QT_BEGIN_NAMESPACE - -using namespace QPlatformInterface::Private; - -#ifndef QT_NO_OPENGL - -QT_DEFINE_PLATFORM_INTERFACE(QWGLContext, QOpenGLContext); -QT_DEFINE_PRIVATE_PLATFORM_INTERFACE(QWindowsGLIntegration); - -HMODULE QPlatformInterface::QWGLContext::openGLModuleHandle() -{ - return QGuiApplicationPrivate::platformIntegration()->call< - &QWindowsGLIntegration::openGLModuleHandle>(); -} - -QOpenGLContext *QPlatformInterface::QWGLContext::fromNative(HGLRC context, HWND window, QOpenGLContext *shareContext) -{ - return QGuiApplicationPrivate::platformIntegration()->call< - &QWindowsGLIntegration::createOpenGLContext>(context, window, shareContext); -} - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE diff --git a/src/gui/platform/windows/qwindowsthemecache.cpp b/src/gui/platform/windows/qwindowsthemecache.cpp new file mode 100644 index 0000000000..3bb92e67ca --- /dev/null +++ b/src/gui/platform/windows/qwindowsthemecache.cpp @@ -0,0 +1,79 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwindowsthemecache_p.h" +#include <QtCore/qdebug.h> +#include <QtCore/qhash.h> + +QT_BEGIN_NAMESPACE + +// Theme names matching the QWindowsVistaStylePrivate::Theme enumeration. +constexpr const wchar_t *themeNames[] = { + L"BUTTON", L"COMBOBOX", L"EDIT", L"HEADER", L"LISTVIEW", + L"MENU", L"PROGRESS", L"REBAR", L"SCROLLBAR", L"SPIN", + L"TAB", L"TASKDIALOG", L"TOOLBAR", L"TOOLTIP", L"TRACKBAR", + L"WINDOW", L"STATUS", L"TREEVIEW" +}; + +typedef std::array<HTHEME, std::size(themeNames)> ThemeArray; +typedef QHash<HWND, ThemeArray> ThemesCache; +Q_GLOBAL_STATIC(ThemesCache, themesCache); + +QString QWindowsThemeCache::themeName(int theme) +{ + return theme >= 0 && theme < int(std::size(themeNames)) + ? QString::fromWCharArray(themeNames[theme]) : QString(); +} + +HTHEME QWindowsThemeCache::createTheme(int theme, HWND hwnd) +{ + if (Q_UNLIKELY(theme < 0 || theme >= int(std::size(themeNames)) || !hwnd)) { + qWarning("Invalid parameters #%d, %p", theme, hwnd); + return nullptr; + } + + // Get or create themes array for this window. + ThemesCache *cache = themesCache(); + auto it = cache->find(hwnd); + if (it == cache->end()) + it = cache->insert(hwnd, ThemeArray {}); + + // Get or create theme data + ThemeArray &themes = *it; + if (!themes[theme]) { + const wchar_t *name = themeNames[theme]; + themes[theme] = OpenThemeData(hwnd, name); + if (Q_UNLIKELY(!themes[theme])) + qErrnoWarning("OpenThemeData() failed for theme %d (%s).", + theme, qPrintable(themeName(theme))); + } + return themes[theme]; +} + +static void clearThemes(ThemeArray &themes) +{ + for (auto &theme : themes) { + if (theme) { + CloseThemeData(theme); + theme = nullptr; + } + } +} + +void QWindowsThemeCache::clearThemeCache(HWND hwnd) +{ + ThemesCache *cache = themesCache(); + auto it = cache->find(hwnd); + if (it == cache->end()) + return; + clearThemes(*it); +} + +void QWindowsThemeCache::clearAllThemeCaches() +{ + ThemesCache *cache = themesCache(); + for (auto &themeArray : *cache) + clearThemes(themeArray); +} + +QT_END_NAMESPACE diff --git a/src/gui/platform/windows/qwindowsthemecache_p.h b/src/gui/platform/windows/qwindowsthemecache_p.h new file mode 100644 index 0000000000..beb724dc5c --- /dev/null +++ b/src/gui/platform/windows/qwindowsthemecache_p.h @@ -0,0 +1,35 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWINDOWSTHEME_CACHE_P_H +#define QWINDOWSTHEME_CACHE_P_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. +// + +#include "QtGui/private/qtguiglobal_p.h" + +#include <QtCore/qt_windows.h> +#include <uxtheme.h> + +QT_BEGIN_NAMESPACE + +namespace QWindowsThemeCache +{ + Q_GUI_EXPORT QString themeName(int theme); + Q_GUI_EXPORT HTHEME createTheme(int theme, HWND hwnd); + Q_GUI_EXPORT void clearThemeCache(HWND hwnd); + Q_GUI_EXPORT void clearAllThemeCaches(); +} + +QT_END_NAMESPACE + +#endif // QWINDOWSTHEME_CACHE_P_H diff --git a/src/gui/platform/windows/windows.pri b/src/gui/platform/windows/windows.pri deleted file mode 100644 index b1e80f6538..0000000000 --- a/src/gui/platform/windows/windows.pri +++ /dev/null @@ -1,4 +0,0 @@ -HEADERS += platform/windows/qwindowsguieventdispatcher_p.h -SOURCES += \ - platform/windows/qwindowsguieventdispatcher.cpp \ - platform/windows/qwindowsplatforminterface.cpp |