mirror of
https://github.com/crystalidea/qt-build-tools.git
synced 2024-11-22 19:00:04 +08:00
5.13.2 уходит
This commit is contained in:
parent
4d982edc93
commit
4e79ef8198
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,23 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export PATH=$PATH:/usr/local/Qt-5.13.2/bin
|
|
||||||
|
|
||||||
cd qtbase
|
|
||||||
|
|
||||||
./configure -opensource -confirm-license -nomake examples -nomake tests -no-openssl -securetransport
|
|
||||||
|
|
||||||
make -j 12
|
|
||||||
echo maki | sudo -S sudo make install
|
|
||||||
|
|
||||||
cd ../qttools
|
|
||||||
qmake
|
|
||||||
make -j 12
|
|
||||||
echo maki | sudo -S sudo make install
|
|
||||||
|
|
||||||
cd ../qtmacextras
|
|
||||||
qmake
|
|
||||||
make -j 12
|
|
||||||
echo maki | sudo -S sudo make install
|
|
||||||
|
|
||||||
cd /usr/local
|
|
||||||
zip -r ~/Desktop/qt5.13.2_mac.zip Qt-5.13.2/*
|
|
@ -1,23 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export PATH=$PATH:/usr/local/Qt-5.13.2/bin
|
|
||||||
|
|
||||||
cd qtbase
|
|
||||||
|
|
||||||
./configure $OPTIONS QMAKE_APPLE_DEVICE_ARCHS=arm64 -opensource -confirm-license -nomake examples -nomake tests -no-openssl -securetransport
|
|
||||||
|
|
||||||
make -j 8
|
|
||||||
echo maki | sudo -S sudo make install
|
|
||||||
|
|
||||||
cd ../qttools
|
|
||||||
qmake
|
|
||||||
make -j 8
|
|
||||||
echo maki | sudo -S sudo make install
|
|
||||||
|
|
||||||
cd ../qtmacextras
|
|
||||||
qmake
|
|
||||||
make -j 8
|
|
||||||
echo maki | sudo -S sudo make install
|
|
||||||
|
|
||||||
cd /usr/local
|
|
||||||
zip -r ~/Desktop/qt5.13.2_mac_arm.zip Qt-5.13.2/*
|
|
@ -1,17 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export PATH=$PATH:/usr/local/Qt-5.13.2/bin
|
|
||||||
|
|
||||||
cd qtbase
|
|
||||||
|
|
||||||
./configure -developer-build -opensource -confirm-license -nomake examples -nomake tests -no-openssl -securetransport
|
|
||||||
|
|
||||||
make -j 12
|
|
||||||
|
|
||||||
cd ../qttools
|
|
||||||
qmake
|
|
||||||
make -j 12
|
|
||||||
|
|
||||||
cd ../qtmacextras
|
|
||||||
qmake
|
|
||||||
make -j 12
|
|
@ -1,86 +0,0 @@
|
|||||||
use strict;
|
|
||||||
|
|
||||||
die "Cannot proceed without the 'bin' folder'" if (!-e "bin");
|
|
||||||
|
|
||||||
my $arch = $ARGV[0];
|
|
||||||
my $openssl_v_major = "1.1.1"; # The 1.1.1 series is Long Term Support (LTS) release, supported until 11th September 2023
|
|
||||||
my $openssl_v_minor = "a";
|
|
||||||
my $openssl_version = "$openssl_v_major$openssl_v_minor";
|
|
||||||
my $openssl_dir = "openssl-$openssl_version";
|
|
||||||
my $openssl_download = "https://www.openssl.org/source/openssl-$openssl_version.tar.gz";
|
|
||||||
my $openssl_arch = $arch eq "amd64" ? "WIN64A" : "WIN32";
|
|
||||||
|
|
||||||
$arch = "x86" if ($arch eq ''); # specify x86 is nothing is specified
|
|
||||||
die "Please specify architecture (x86 or amd64)" if ($arch ne "x86" && $arch ne "amd64"); # die if user specified anything except x86 or amd64
|
|
||||||
|
|
||||||
# will create a batch file
|
|
||||||
|
|
||||||
my $batfile = 'compile_win.bat';
|
|
||||||
|
|
||||||
open BAT, '>', $batfile;
|
|
||||||
|
|
||||||
printLineToBat ("SET PATH=%PATH%;%cd%\\bin"); # add bin folder to the path for 7z and wget
|
|
||||||
printLineToBat ("CALL \"C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Auxiliary\\Build\\vcvarsall.bat\" $arch");
|
|
||||||
printLineToBat ("SET _ROOT=%cd%");
|
|
||||||
printLineToBat ("SET PATH=%_ROOT%\\qtbase\\bin;%_ROOT%\\gnuwin32\\bin;%PATH%"); # http://doc.qt.io/qt-5/windows-building.html
|
|
||||||
|
|
||||||
printLineToBat ("cd qtbase");
|
|
||||||
printLineToBat ("if \"%~1\"==\"step2\" goto step2");
|
|
||||||
|
|
||||||
# step1: compile openssl and do configure. For some reason, can't continue script execution after configure, have to make step2
|
|
||||||
printLineToBat ("IF EXIST $openssl_dir\\build GOTO OPENSSL_ALREAD_COMPILED");
|
|
||||||
printLineToBat ("wget --no-check-certificate $openssl_download");
|
|
||||||
printLineToBat ("7z x openssl-$openssl_version.tar.gz");
|
|
||||||
printLineToBat ("7z x openssl-$openssl_version.tar");
|
|
||||||
printLineToBat ("rm openssl-$openssl_version.tar.gz");
|
|
||||||
printLineToBat ("rm openssl-$openssl_version.tar");
|
|
||||||
printLineToBat ("cd $openssl_dir");
|
|
||||||
printLineToBat ("perl Configure VC-$openssl_arch no-asm no-shared no-tests --prefix=%cd%\\build --openssldir=%cd%\\build");
|
|
||||||
printLineToBat ("nmake");
|
|
||||||
printLineToBat ("nmake install");
|
|
||||||
# do little clean up
|
|
||||||
printLineToBat ("rm test\\*.exe");
|
|
||||||
printLineToBat ("rm test\\*.pdb");
|
|
||||||
printLineToBat ("rm test\\*.obj");
|
|
||||||
printLineToBat (":OPENSSL_ALREAD_COMPILED");
|
|
||||||
# go back to qtbase
|
|
||||||
printLineToBat ("cd ..");
|
|
||||||
|
|
||||||
# -developer-build creates an in-source build for developer usage.
|
|
||||||
# openssl: see https://bugreports.qt.io/browse/QTBUG-65501
|
|
||||||
printLineToBat ("configure -opensource -developer-build -confirm-license -opengl desktop -mp -nomake tests -nomake examples -I \"%cd%\\$openssl_dir\\build\\include\" -openssl-linked OPENSSL_LIBS=\"%cd%\\$openssl_dir\\build\\lib\\libssl.lib %cd%\\$openssl_dir\\build\\lib\\libcrypto.lib -lcrypt32 -lws2_32 -lAdvapi32 -luser32\"");
|
|
||||||
printLineToBat ("goto :EOF");
|
|
||||||
|
|
||||||
# step 2:
|
|
||||||
printLineToBat (":step2");
|
|
||||||
|
|
||||||
printLineToBat ("nmake");
|
|
||||||
printLineToBat ("cd ..\\qttools");
|
|
||||||
printLineToBat ("..\\qtbase\\bin\\qmake");
|
|
||||||
printLineToBat ("nmake");
|
|
||||||
printLineToBat ("cd ..\\qtbase");
|
|
||||||
printLineToBat ("cd .."); # go up to qt dir
|
|
||||||
# openssl clean up
|
|
||||||
printLineToBat ("cd qtbase");
|
|
||||||
printLineToBat ("cd $openssl_dir");
|
|
||||||
printLineToBat ("del /s /f /q out32");
|
|
||||||
printLineToBat ("del /s /f /q out32.dbg");
|
|
||||||
printLineToBat ("cd ..");
|
|
||||||
printLineToBat ("cd ..");
|
|
||||||
# the rest
|
|
||||||
printLineToBat ("del *.obj /s /f");
|
|
||||||
printLineToBat ("del *.ilk /s /f");
|
|
||||||
printLineToBat ("del *.pch /s /f");
|
|
||||||
printLineToBat ("del Makefile* /s /f");
|
|
||||||
|
|
||||||
close BAT;
|
|
||||||
|
|
||||||
system ($batfile);
|
|
||||||
system ("$batfile step2");
|
|
||||||
|
|
||||||
system ("pause");
|
|
||||||
|
|
||||||
sub printLineToBat
|
|
||||||
{
|
|
||||||
print BAT "$_[0]\n";
|
|
||||||
}
|
|
@ -1,119 +0,0 @@
|
|||||||
#
|
|
||||||
# This file is used as a basis for the following compilers:
|
|
||||||
#
|
|
||||||
# - Microsoft C/C++ Optimizing Compiler (all desktop versions)
|
|
||||||
# - Intel C++ Compiler on Windows
|
|
||||||
# - Clang-cl
|
|
||||||
#
|
|
||||||
# Baseline:
|
|
||||||
#
|
|
||||||
# - Visual Studio 2005 (8.0), VC++ 14.0
|
|
||||||
#
|
|
||||||
# Version-specific settings go in msvc-version.conf (loaded by default_pre)
|
|
||||||
#
|
|
||||||
|
|
||||||
MAKEFILE_GENERATOR = MSVC.NET
|
|
||||||
QMAKE_PLATFORM = win32
|
|
||||||
QMAKE_COMPILER = msvc
|
|
||||||
CONFIG += flat debug_and_release debug_and_release_target precompile_header autogen_precompile_source embed_manifest_dll embed_manifest_exe
|
|
||||||
# MSVC 2017 15.8+ fixed std::aligned_storage but compilation fails without
|
|
||||||
# _ENABLE_EXTENDED_ALIGNED_STORAGE flag since the fix breaks binary compatibility.
|
|
||||||
DEFINES += UNICODE _UNICODE WIN32 _ENABLE_EXTENDED_ALIGNED_STORAGE
|
|
||||||
QMAKE_COMPILER_DEFINES += _WIN32
|
|
||||||
contains(QMAKE_TARGET.arch, x86_64) {
|
|
||||||
DEFINES += WIN64
|
|
||||||
QMAKE_COMPILER_DEFINES += _WIN64
|
|
||||||
}
|
|
||||||
|
|
||||||
QMAKE_CFLAGS_OPTIMIZE_DEBUG = -Od
|
|
||||||
QMAKE_CFLAGS_OPTIMIZE = -O2
|
|
||||||
QMAKE_CFLAGS_OPTIMIZE_SIZE = -O1
|
|
||||||
|
|
||||||
QMAKE_CC = cl
|
|
||||||
QMAKE_LEX = flex
|
|
||||||
QMAKE_LEXFLAGS =
|
|
||||||
QMAKE_YACC = bison -y
|
|
||||||
QMAKE_YACCFLAGS = -d
|
|
||||||
QMAKE_CFLAGS = -nologo -Zc:wchar_t
|
|
||||||
QMAKE_CFLAGS_WARN_ON = -W3
|
|
||||||
QMAKE_CFLAGS_WARN_OFF = -W0
|
|
||||||
QMAKE_CFLAGS_RELEASE = $$QMAKE_CFLAGS_OPTIMIZE -MD -Zi
|
|
||||||
QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += $$QMAKE_CFLAGS_OPTIMIZE -Zi -MD
|
|
||||||
QMAKE_CFLAGS_DEBUG = -Zi -MDd
|
|
||||||
QMAKE_CFLAGS_YACC =
|
|
||||||
QMAKE_CFLAGS_LTCG = -GL
|
|
||||||
|
|
||||||
contains(QMAKE_TARGET.arch, x86_64) {
|
|
||||||
# SSE2 is mandatory on 64-bit mode, so skip the option. It triggers:
|
|
||||||
# cl : Command line warning D9002 : ignoring unknown option '-arch:SSE2'
|
|
||||||
QMAKE_CFLAGS_SSE2 =
|
|
||||||
} else {
|
|
||||||
QMAKE_CFLAGS_SSE2 = -arch:SSE2
|
|
||||||
}
|
|
||||||
QMAKE_CFLAGS_SSE3 = $$QMAKE_CFLAGS_SSE2
|
|
||||||
QMAKE_CFLAGS_SSSE3 = $$QMAKE_CFLAGS_SSE2
|
|
||||||
QMAKE_CFLAGS_SSE4_1 = $$QMAKE_CFLAGS_SSE2
|
|
||||||
QMAKE_CFLAGS_SSE4_2 = $$QMAKE_CFLAGS_SSE2
|
|
||||||
QMAKE_CFLAGS_AESNI = $$QMAKE_CFLAGS_SSE2
|
|
||||||
QMAKE_CFLAGS_SHANI = $$QMAKE_CFLAGS_SSE2
|
|
||||||
|
|
||||||
QMAKE_CXX = $$QMAKE_CC
|
|
||||||
QMAKE_CXXFLAGS = $$QMAKE_CFLAGS
|
|
||||||
QMAKE_CXXFLAGS_WARN_ON = $$QMAKE_CFLAGS_WARN_ON -w34100 -w34189 -w44996
|
|
||||||
QMAKE_CXXFLAGS_WARN_OFF = $$QMAKE_CFLAGS_WARN_OFF
|
|
||||||
QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE
|
|
||||||
QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO += $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
|
|
||||||
QMAKE_CXXFLAGS_DEBUG = $$QMAKE_CFLAGS_DEBUG
|
|
||||||
QMAKE_CXXFLAGS_YACC = $$QMAKE_CFLAGS_YACC
|
|
||||||
QMAKE_CXXFLAGS_LTCG = $$QMAKE_CFLAGS_LTCG
|
|
||||||
QMAKE_CXXFLAGS_STL_ON = -EHsc
|
|
||||||
QMAKE_CXXFLAGS_STL_OFF =
|
|
||||||
QMAKE_CXXFLAGS_RTTI_ON = -GR
|
|
||||||
QMAKE_CXXFLAGS_RTTI_OFF =
|
|
||||||
QMAKE_CXXFLAGS_EXCEPTIONS_ON = -EHsc
|
|
||||||
QMAKE_CXXFLAGS_EXCEPTIONS_OFF =
|
|
||||||
|
|
||||||
QMAKE_INCDIR =
|
|
||||||
|
|
||||||
QMAKE_RUN_CC = $(CC) -c $(CFLAGS) $(INCPATH) -Fo$obj $src
|
|
||||||
QMAKE_RUN_CC_IMP = $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ $<
|
|
||||||
QMAKE_RUN_CC_IMP_BATCH = $(CC) -c $(CFLAGS) $(INCPATH) -Fo$@ @<<
|
|
||||||
QMAKE_RUN_CXX = $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$obj $src
|
|
||||||
QMAKE_RUN_CXX_IMP = $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ $<
|
|
||||||
QMAKE_RUN_CXX_IMP_BATCH = $(CXX) -c $(CXXFLAGS) $(INCPATH) -Fo$@ @<<
|
|
||||||
|
|
||||||
QMAKE_LINK = link
|
|
||||||
QMAKE_LFLAGS = /NOLOGO /DYNAMICBASE /NXCOMPAT
|
|
||||||
QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO
|
|
||||||
QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO = /DEBUG /OPT:REF /INCREMENTAL:NO
|
|
||||||
QMAKE_LFLAGS_DEBUG = /DEBUG
|
|
||||||
QMAKE_LFLAGS_CONSOLE = /SUBSYSTEM:CONSOLE
|
|
||||||
QMAKE_LFLAGS_WINDOWS = /SUBSYSTEM:WINDOWS
|
|
||||||
QMAKE_LFLAGS_EXE = \"/MANIFESTDEPENDENCY:type=\'win32\' name=\'Microsoft.Windows.Common-Controls\' version=\'6.0.0.0\' publicKeyToken=\'6595b64144ccf1df\' language=\'*\' processorArchitecture=\'*\'\"
|
|
||||||
QMAKE_LFLAGS_DLL = /DLL
|
|
||||||
QMAKE_LFLAGS_LTCG = /LTCG
|
|
||||||
QMAKE_PREFIX_SHLIB =
|
|
||||||
QMAKE_EXTENSION_SHLIB = dll
|
|
||||||
QMAKE_PREFIX_STATICLIB =
|
|
||||||
QMAKE_EXTENSION_STATICLIB = lib
|
|
||||||
|
|
||||||
QMAKE_LIBS =
|
|
||||||
QMAKE_LIBS_GUI = gdi32.lib comdlg32.lib oleaut32.lib imm32.lib winmm.lib ws2_32.lib ole32.lib uuid.lib user32.lib advapi32.lib
|
|
||||||
QMAKE_LIBS_NETWORK = ws2_32.lib user32.lib gdi32.lib
|
|
||||||
QMAKE_LIBS_OPENGL = glu32.lib opengl32.lib gdi32.lib user32.lib
|
|
||||||
QMAKE_LIBS_OPENGL_ES2 = gdi32.lib user32.lib
|
|
||||||
QMAKE_LIBS_OPENGL_ES2_DEBUG = gdi32.lib user32.lib
|
|
||||||
QMAKE_LIBS_COMPAT = advapi32.lib shell32.lib comdlg32.lib user32.lib gdi32.lib ws2_32.lib
|
|
||||||
QMAKE_LIBS_QT_ENTRY = -lqtmain
|
|
||||||
|
|
||||||
QMAKE_IDL = midl
|
|
||||||
QMAKE_LIB = lib /NOLOGO
|
|
||||||
QMAKE_RC = rc /NOLOGO
|
|
||||||
|
|
||||||
VCPROJ_EXTENSION = .vcproj
|
|
||||||
VCSOLUTION_EXTENSION = .sln
|
|
||||||
VCPROJ_KEYWORD = Qt4VSv1.0
|
|
||||||
|
|
||||||
include(angle.conf)
|
|
||||||
include(windows-desktop.conf)
|
|
||||||
include(windows-vulkan.conf)
|
|
56
5.13.2/qtbase/src/3rdparty/pcre2/pcre2.pro
vendored
56
5.13.2/qtbase/src/3rdparty/pcre2/pcre2.pro
vendored
@ -1,56 +0,0 @@
|
|||||||
TARGET = qtpcre2
|
|
||||||
|
|
||||||
CONFIG += \
|
|
||||||
static \
|
|
||||||
hide_symbols \
|
|
||||||
exceptions_off rtti_off warn_off
|
|
||||||
|
|
||||||
|
|
||||||
MODULE_DEFINES += PCRE2_CODE_UNIT_WIDTH=16
|
|
||||||
win32: MODULE_DEFINES += PCRE2_STATIC
|
|
||||||
MODULE_INCLUDEPATH += $$PWD/src
|
|
||||||
|
|
||||||
load(qt_helper_lib)
|
|
||||||
|
|
||||||
DEFINES += HAVE_CONFIG_H
|
|
||||||
|
|
||||||
# platform/compiler specific definitions
|
|
||||||
uikit|qnx|winrt: DEFINES += PCRE2_DISABLE_JIT
|
|
||||||
win32:contains(QT_ARCH, "arm"): DEFINES += PCRE2_DISABLE_JIT
|
|
||||||
win32:contains(QT_ARCH, "arm64"): DEFINES += PCRE2_DISABLE_JIT
|
|
||||||
macos:contains(QT_ARCH, "arm64"): DEFINES += PCRE2_DISABLE_JIT
|
|
||||||
|
|
||||||
SOURCES += \
|
|
||||||
$$PWD/src/pcre2_auto_possess.c \
|
|
||||||
$$PWD/src/pcre2_chartables.c \
|
|
||||||
$$PWD/src/pcre2_compile.c \
|
|
||||||
$$PWD/src/pcre2_config.c \
|
|
||||||
$$PWD/src/pcre2_context.c \
|
|
||||||
$$PWD/src/pcre2_dfa_match.c \
|
|
||||||
$$PWD/src/pcre2_error.c \
|
|
||||||
$$PWD/src/pcre2_extuni.c \
|
|
||||||
$$PWD/src/pcre2_find_bracket.c \
|
|
||||||
$$PWD/src/pcre2_jit_compile.c \
|
|
||||||
$$PWD/src/pcre2_maketables.c \
|
|
||||||
$$PWD/src/pcre2_match.c \
|
|
||||||
$$PWD/src/pcre2_match_data.c \
|
|
||||||
$$PWD/src/pcre2_newline.c \
|
|
||||||
$$PWD/src/pcre2_ord2utf.c \
|
|
||||||
$$PWD/src/pcre2_pattern_info.c \
|
|
||||||
$$PWD/src/pcre2_script_run.c \
|
|
||||||
$$PWD/src/pcre2_serialize.c \
|
|
||||||
$$PWD/src/pcre2_string_utils.c \
|
|
||||||
$$PWD/src/pcre2_study.c \
|
|
||||||
$$PWD/src/pcre2_substitute.c \
|
|
||||||
$$PWD/src/pcre2_substring.c \
|
|
||||||
$$PWD/src/pcre2_tables.c \
|
|
||||||
$$PWD/src/pcre2_ucd.c \
|
|
||||||
$$PWD/src/pcre2_valid_utf.c \
|
|
||||||
$$PWD/src/pcre2_xclass.c
|
|
||||||
|
|
||||||
HEADERS += \
|
|
||||||
$$PWD/src/config.h \
|
|
||||||
$$PWD/src/pcre2.h \
|
|
||||||
$$PWD/src/pcre2_internal.h \
|
|
||||||
$$PWD/src/pcre2_intmodedep.h \
|
|
||||||
$$PWD/src/pcre2_ucp.h
|
|
File diff suppressed because it is too large
Load Diff
@ -1,569 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
** Contact: https://www.qt.io/licensing/
|
|
||||||
**
|
|
||||||
** This file is part of the QtCore 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 "qoperatingsystemversion.h"
|
|
||||||
#if !defined(Q_OS_DARWIN) && !defined(Q_OS_WIN)
|
|
||||||
#include "qoperatingsystemversion_p.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_DARWIN)
|
|
||||||
#include <QtCore/private/qcore_mac_p.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <qversionnumber.h>
|
|
||||||
#include <qdebug.h>
|
|
||||||
|
|
||||||
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
|
|
||||||
#include <private/qjni_p.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\class QOperatingSystemVersion
|
|
||||||
\inmodule QtCore
|
|
||||||
\since 5.9
|
|
||||||
\brief The QOperatingSystemVersion class provides information about the
|
|
||||||
operating system version.
|
|
||||||
|
|
||||||
Unlike other version functions in QSysInfo, QOperatingSystemVersion provides
|
|
||||||
access to the full version number that \a developers typically use to vary
|
|
||||||
behavior or determine whether to enable APIs or features based on the
|
|
||||||
operating system version (as opposed to the kernel version number or
|
|
||||||
marketing version).
|
|
||||||
|
|
||||||
This class is also a complete replacement for QSysInfo::macVersion and
|
|
||||||
QSysInfo::windowsVersion, additionally providing access to the third (micro)
|
|
||||||
version number component.
|
|
||||||
|
|
||||||
Presently, Android, Apple Platforms (iOS, macOS, tvOS, and watchOS),
|
|
||||||
and Windows are supported.
|
|
||||||
|
|
||||||
The \a majorVersion(), \a minorVersion(), and \a microVersion() functions
|
|
||||||
return the parts of the operating system version number based on:
|
|
||||||
|
|
||||||
\table
|
|
||||||
\header
|
|
||||||
\li Platforms
|
|
||||||
\li Value
|
|
||||||
\row
|
|
||||||
\li Android
|
|
||||||
\li result of parsing
|
|
||||||
\l{https://developer.android.com/reference/android/os/Build.VERSION.html#RELEASE}{android.os.Build.VERSION.RELEASE}
|
|
||||||
using QVersionNumber, with a fallback to
|
|
||||||
\l{https://developer.android.com/reference/android/os/Build.VERSION.html#SDK_INT}{android.os.Build.VERSION.SDK_INT}
|
|
||||||
to determine the major and minor version component if the former
|
|
||||||
fails
|
|
||||||
\row
|
|
||||||
\li Apple Platforms
|
|
||||||
\li majorVersion, minorVersion, and patchVersion from
|
|
||||||
\l{https://developer.apple.com/reference/foundation/nsprocessinfo/1410906-operatingsystemversion?language=objc}{NSProcessInfo.operatingSystemVersion}
|
|
||||||
\row
|
|
||||||
\li Windows
|
|
||||||
\li dwMajorVersion, dwMinorVersion, and dwBuildNumber from
|
|
||||||
\l{https://msdn.microsoft.com/en-us/library/mt723418.aspx}{RtlGetVersion} -
|
|
||||||
note that this function ALWAYS return the version number of the
|
|
||||||
underlying operating system, as opposed to the shim underneath
|
|
||||||
GetVersionEx that hides the real version number if the
|
|
||||||
application is not manifested for that version of the OS
|
|
||||||
\endtable
|
|
||||||
|
|
||||||
Because QOperatingSystemVersion stores both a version number and an OS type, the OS type
|
|
||||||
can be taken into account when performing comparisons. For example, on a macOS system running
|
|
||||||
macOS Sierra (v10.12), the following expression will return \c false even though the
|
|
||||||
major version number component of the object on the left hand side of the expression (10) is
|
|
||||||
greater than that of the object on the right (9):
|
|
||||||
|
|
||||||
\snippet code/src_corelib_global_qoperatingsystemversion.cpp 0
|
|
||||||
|
|
||||||
This allows expressions for multiple operating systems to be joined with a logical OR operator
|
|
||||||
and still work as expected. For example:
|
|
||||||
|
|
||||||
\snippet code/src_corelib_global_qoperatingsystemversion.cpp 1
|
|
||||||
|
|
||||||
A more naive comparison algorithm might incorrectly return true on all versions of macOS,
|
|
||||||
including Mac OS 9. This behavior is achieved by overloading the comparison operators to return
|
|
||||||
\c false whenever the OS types of the QOperatingSystemVersion instances being compared do not
|
|
||||||
match. Be aware that due to this it can be the case \c x >= y and \c x < y are BOTH \c false
|
|
||||||
for the same instances of \c x and \c y.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\enum QOperatingSystemVersion::OSType
|
|
||||||
|
|
||||||
This enum provides symbolic names for the various operating
|
|
||||||
system families supported by QOperatingSystemVersion.
|
|
||||||
|
|
||||||
\value Android The Google Android operating system.
|
|
||||||
\value IOS The Apple iOS operating system.
|
|
||||||
\value MacOS The Apple macOS operating system.
|
|
||||||
\value TvOS The Apple tvOS operating system.
|
|
||||||
\value WatchOS The Apple watchOS operating system.
|
|
||||||
\value Windows The Microsoft Windows operating system.
|
|
||||||
|
|
||||||
\value Unknown An unknown or unsupported operating system.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn QOperatingSystemVersion::QOperatingSystemVersion(OSType osType, int vmajor, int vminor = -1, int vmicro = -1)
|
|
||||||
|
|
||||||
Constructs a QOperatingSystemVersion consisting of the OS type \a osType, and
|
|
||||||
major, minor, and micro version numbers \a vmajor, \a vminor and \a vmicro, respectively.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn QOperatingSystemVersion QOperatingSystemVersion::current()
|
|
||||||
|
|
||||||
Returns a QOperatingSystemVersion indicating the current OS and its version number.
|
|
||||||
|
|
||||||
\sa currentType()
|
|
||||||
*/
|
|
||||||
#if !defined(Q_OS_DARWIN) && !defined(Q_OS_WIN)
|
|
||||||
QOperatingSystemVersion QOperatingSystemVersion::current()
|
|
||||||
{
|
|
||||||
QOperatingSystemVersion version;
|
|
||||||
version.m_os = currentType();
|
|
||||||
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
|
|
||||||
#ifndef QT_BOOTSTRAPPED
|
|
||||||
const QVersionNumber v = QVersionNumber::fromString(QJNIObjectPrivate::getStaticObjectField(
|
|
||||||
"android/os/Build$VERSION", "RELEASE", "Ljava/lang/String;").toString());
|
|
||||||
if (!v.isNull()) {
|
|
||||||
version.m_major = v.majorVersion();
|
|
||||||
version.m_minor = v.minorVersion();
|
|
||||||
version.m_micro = v.microVersion();
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
version.m_major = -1;
|
|
||||||
version.m_minor = -1;
|
|
||||||
|
|
||||||
static const struct {
|
|
||||||
uint major : 4;
|
|
||||||
uint minor : 4;
|
|
||||||
} versions[] = {
|
|
||||||
{ 1, 0 }, // API level 1
|
|
||||||
{ 1, 1 }, // API level 2
|
|
||||||
{ 1, 5 }, // API level 3
|
|
||||||
{ 1, 6 }, // API level 4
|
|
||||||
{ 2, 0 }, // API level 5
|
|
||||||
{ 2, 0 }, // API level 6
|
|
||||||
{ 2, 1 }, // API level 7
|
|
||||||
{ 2, 2 }, // API level 8
|
|
||||||
{ 2, 3 }, // API level 9
|
|
||||||
{ 2, 3 }, // API level 10
|
|
||||||
{ 3, 0 }, // API level 11
|
|
||||||
{ 3, 1 }, // API level 12
|
|
||||||
{ 3, 2 }, // API level 13
|
|
||||||
{ 4, 0 }, // API level 14
|
|
||||||
{ 4, 0 }, // API level 15
|
|
||||||
{ 4, 1 }, // API level 16
|
|
||||||
{ 4, 2 }, // API level 17
|
|
||||||
{ 4, 3 }, // API level 18
|
|
||||||
{ 4, 4 }, // API level 19
|
|
||||||
{ 4, 4 }, // API level 20
|
|
||||||
{ 5, 0 }, // API level 21
|
|
||||||
{ 5, 1 }, // API level 22
|
|
||||||
{ 6, 0 }, // API level 23
|
|
||||||
{ 7, 0 }, // API level 24
|
|
||||||
{ 7, 1 }, // API level 25
|
|
||||||
{ 8, 0 }, // API level 26
|
|
||||||
};
|
|
||||||
|
|
||||||
// This will give us at least the first 2 version components
|
|
||||||
const size_t versionIdx = size_t(QJNIObjectPrivate::getStaticField<jint>(
|
|
||||||
"android/os/Build$VERSION", "SDK_INT")) - 1;
|
|
||||||
if (versionIdx < sizeof(versions) / sizeof(versions[0])) {
|
|
||||||
version.m_major = versions[versionIdx].major;
|
|
||||||
version.m_minor = versions[versionIdx].minor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// API level 6 was exactly version 2.0.1
|
|
||||||
version.m_micro = versionIdx == 5 ? 1 : -1;
|
|
||||||
#else
|
|
||||||
version.m_major = -1;
|
|
||||||
version.m_minor = -1;
|
|
||||||
version.m_micro = -1;
|
|
||||||
#endif
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline int compareVersionComponents(int lhs, int rhs)
|
|
||||||
{
|
|
||||||
return lhs >= 0 && rhs >= 0 ? lhs - rhs : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QOperatingSystemVersion::compare(const QOperatingSystemVersion &v1,
|
|
||||||
const QOperatingSystemVersion &v2)
|
|
||||||
{
|
|
||||||
if (v1.m_major == v2.m_major) {
|
|
||||||
if (v1.m_minor == v2.m_minor) {
|
|
||||||
return compareVersionComponents(v1.m_micro, v2.m_micro);
|
|
||||||
}
|
|
||||||
return compareVersionComponents(v1.m_minor, v2.m_minor);
|
|
||||||
}
|
|
||||||
return compareVersionComponents(v1.m_major, v2.m_major);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn int QOperatingSystemVersion::majorVersion() const
|
|
||||||
|
|
||||||
Returns the major version number, that is, the first segment of the
|
|
||||||
operating system's version number.
|
|
||||||
|
|
||||||
See the main class documentation for what the major version number is on a given
|
|
||||||
operating system.
|
|
||||||
|
|
||||||
-1 indicates an unknown or absent version number component.
|
|
||||||
|
|
||||||
\sa minorVersion(), microVersion()
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn int QOperatingSystemVersion::minorVersion() const
|
|
||||||
|
|
||||||
Returns the minor version number, that is, the second segment of the
|
|
||||||
operating system's version number.
|
|
||||||
|
|
||||||
See the main class documentation for what the minor version number is on a given
|
|
||||||
operating system.
|
|
||||||
|
|
||||||
-1 indicates an unknown or absent version number component.
|
|
||||||
|
|
||||||
\sa majorVersion(), microVersion()
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn int QOperatingSystemVersion::microVersion() const
|
|
||||||
|
|
||||||
Returns the micro version number, that is, the third segment of the
|
|
||||||
operating system's version number.
|
|
||||||
|
|
||||||
See the main class documentation for what the micro version number is on a given
|
|
||||||
operating system.
|
|
||||||
|
|
||||||
-1 indicates an unknown or absent version number component.
|
|
||||||
|
|
||||||
\sa majorVersion(), minorVersion()
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn int QOperatingSystemVersion::segmentCount() const
|
|
||||||
|
|
||||||
Returns the number of integers stored in the version number.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn QOperatingSystemVersion::OSType QOperatingSystemVersion::type() const
|
|
||||||
|
|
||||||
Returns the OS type identified by the QOperatingSystemVersion.
|
|
||||||
|
|
||||||
\sa name()
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn QOperatingSystemVersion::OSType QOperatingSystemVersion::currentType()
|
|
||||||
|
|
||||||
Returns the current OS type without constructing a QOperatingSystemVersion instance.
|
|
||||||
|
|
||||||
\sa current()
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn QString QOperatingSystemVersion::name() const
|
|
||||||
|
|
||||||
Returns a string representation of the OS type identified by the QOperatingSystemVersion.
|
|
||||||
|
|
||||||
\sa type()
|
|
||||||
*/
|
|
||||||
QString QOperatingSystemVersion::name() const
|
|
||||||
{
|
|
||||||
switch (type()) {
|
|
||||||
case QOperatingSystemVersion::Windows:
|
|
||||||
return QStringLiteral("Windows");
|
|
||||||
case QOperatingSystemVersion::MacOS: {
|
|
||||||
if (majorVersion() < 10)
|
|
||||||
return QStringLiteral("Mac OS");
|
|
||||||
if (majorVersion() == 10 && minorVersion() < 8)
|
|
||||||
return QStringLiteral("Mac OS X");
|
|
||||||
if (majorVersion() == 10 && minorVersion() < 12)
|
|
||||||
return QStringLiteral("OS X");
|
|
||||||
return QStringLiteral("macOS");
|
|
||||||
}
|
|
||||||
case QOperatingSystemVersion::IOS: {
|
|
||||||
if (majorVersion() < 4)
|
|
||||||
return QStringLiteral("iPhone OS");
|
|
||||||
return QStringLiteral("iOS");
|
|
||||||
}
|
|
||||||
case QOperatingSystemVersion::TvOS:
|
|
||||||
return QStringLiteral("tvOS");
|
|
||||||
case QOperatingSystemVersion::WatchOS:
|
|
||||||
return QStringLiteral("watchOS");
|
|
||||||
case QOperatingSystemVersion::Android:
|
|
||||||
return QStringLiteral("Android");
|
|
||||||
case QOperatingSystemVersion::Unknown:
|
|
||||||
default:
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef Q_COMPILER_INITIALIZER_LISTS
|
|
||||||
/*!
|
|
||||||
\fn bool QOperatingSystemVersion::isAnyOfType(std::initializer_list<OSType> types) const
|
|
||||||
|
|
||||||
Returns whether the OS type identified by the QOperatingSystemVersion
|
|
||||||
matches any of the OS types in \a types.
|
|
||||||
*/
|
|
||||||
bool QOperatingSystemVersion::isAnyOfType(std::initializer_list<OSType> types) const
|
|
||||||
{
|
|
||||||
for (const auto &t : qAsConst(types)) {
|
|
||||||
if (type() == t)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::Windows7
|
|
||||||
\brief a version corresponding to Windows 7 (version 6.1).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::Windows7 =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Windows, 6, 1);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::Windows8
|
|
||||||
\brief a version corresponding to Windows 8 (version 6.2).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::Windows8 =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Windows, 6, 2);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::Windows8_1
|
|
||||||
\brief a version corresponding to Windows 8.1 (version 6.3).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::Windows8_1 =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Windows, 6, 3);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::Windows10
|
|
||||||
\brief a version corresponding to Windows 10 (version 10.0).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::Windows10 =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::OSXMavericks
|
|
||||||
\brief a version corresponding to OS X Mavericks (version 10.9).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::OSXMavericks =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 9);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::OSXYosemite
|
|
||||||
\brief a version corresponding to OS X Yosemite (version 10.10).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::OSXYosemite =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 10);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::OSXElCapitan
|
|
||||||
\brief a version corresponding to OS X El Capitan (version 10.11).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::OSXElCapitan =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 11);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::MacOSSierra
|
|
||||||
\brief a version corresponding to macOS Sierra (version 10.12).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::MacOSSierra =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 12);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::MacOSHighSierra
|
|
||||||
\brief a version corresponding to macOS High Sierra (version 10.13).
|
|
||||||
\since 5.9.1
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::MacOSHighSierra =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 13);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::MacOSMojave
|
|
||||||
\brief a version corresponding to macOS Mojave (version 10.14).
|
|
||||||
\since 5.11.2
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::MacOSMojave =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 14);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::MacOSCatalina
|
|
||||||
\brief a version corresponding to macOS Catalina (version 10.15).
|
|
||||||
\since 5.12.5
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::MacOSCatalina =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 15);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::MacOSBigSur
|
|
||||||
\brief a version corresponding to macOS Big Sur
|
|
||||||
|
|
||||||
The actual version number depends on whether the application was built
|
|
||||||
using the Xcode 12 SDK. If it was, the version number corresponds
|
|
||||||
to macOS 11.0. If not it will correspond to macOS 10.16.
|
|
||||||
|
|
||||||
By comparing QOperatingSystemVersion::current() to this constant
|
|
||||||
you will always end up comparing to the right version number.
|
|
||||||
\since 6.0
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::MacOSBigSur = [] {
|
|
||||||
#if defined(Q_OS_DARWIN)
|
|
||||||
if (QMacVersion::buildSDK(QMacVersion::ApplicationBinary) >= QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 16))
|
|
||||||
return QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 11, 0);
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
return QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 10, 16);
|
|
||||||
}();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::AndroidJellyBean
|
|
||||||
\brief a version corresponding to Android Jelly Bean (version 4.1, API level 16).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::AndroidJellyBean =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Android, 4, 1);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::AndroidJellyBean_MR1
|
|
||||||
\brief a version corresponding to Android Jelly Bean, maintenance release 1
|
|
||||||
(version 4.2, API level 17).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::AndroidJellyBean_MR1 =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Android, 4, 2);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::AndroidJellyBean_MR2
|
|
||||||
\brief a version corresponding to Android Jelly Bean, maintenance release 2
|
|
||||||
(version 4.3, API level 18).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::AndroidJellyBean_MR2 =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Android, 4, 3);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::AndroidKitKat
|
|
||||||
\brief a version corresponding to Android KitKat (versions 4.4 & 4.4W, API levels 19 & 20).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::AndroidKitKat =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Android, 4, 4);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::AndroidLollipop
|
|
||||||
\brief a version corresponding to Android Lollipop (version 5.0, API level 21).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::AndroidLollipop =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Android, 5, 0);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::AndroidLollipop_MR1
|
|
||||||
\brief a version corresponding to Android Lollipop, maintenance release 1
|
|
||||||
(version 5.1, API level 22).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::AndroidLollipop_MR1 =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Android, 5, 1);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::AndroidMarshmallow
|
|
||||||
\brief a version corresponding to Android Marshmallow (version 6.0, API level 23).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::AndroidMarshmallow =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Android, 6, 0);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::AndroidNougat
|
|
||||||
\brief a version corresponding to Android Nougat (version 7.0, API level 24).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::AndroidNougat =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Android, 7, 0);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::AndroidNougat_MR1
|
|
||||||
\brief a version corresponding to Android Nougat, maintenance release 1
|
|
||||||
(version 7.0, API level 25).
|
|
||||||
\since 5.9
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::AndroidNougat_MR1 =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Android, 7, 1);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\variable QOperatingSystemVersion::AndroidOreo
|
|
||||||
\brief a version corresponding to Android Oreo (version 8.0, API level 26).
|
|
||||||
\since 5.9.2
|
|
||||||
*/
|
|
||||||
const QOperatingSystemVersion QOperatingSystemVersion::AndroidOreo =
|
|
||||||
QOperatingSystemVersion(QOperatingSystemVersion::Android, 8, 0);
|
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG_STREAM
|
|
||||||
QDebug operator<<(QDebug debug, const QOperatingSystemVersion &ov)
|
|
||||||
{
|
|
||||||
QDebugStateSaver saver(debug);
|
|
||||||
debug.nospace();
|
|
||||||
debug << "QOperatingSystemVersion(" << ov.name()
|
|
||||||
<< ", " << ov.majorVersion() << '.' << ov.minorVersion()
|
|
||||||
<< '.' << ov.microVersion() << ')';
|
|
||||||
return debug;
|
|
||||||
}
|
|
||||||
#endif // !QT_NO_DEBUG_STREAM
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
@ -1,160 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
** Contact: https://www.qt.io/licensing/
|
|
||||||
**
|
|
||||||
** This file is part of the QtCore 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 <QtCore/qglobal.h>
|
|
||||||
|
|
||||||
#ifndef QOPERATINGSYSTEMVERSION_H
|
|
||||||
#define QOPERATINGSYSTEMVERSION_H
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
class QString;
|
|
||||||
class QVersionNumber;
|
|
||||||
|
|
||||||
class Q_CORE_EXPORT QOperatingSystemVersion
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum OSType {
|
|
||||||
Unknown = 0,
|
|
||||||
Windows,
|
|
||||||
MacOS,
|
|
||||||
IOS,
|
|
||||||
TvOS,
|
|
||||||
WatchOS,
|
|
||||||
Android
|
|
||||||
};
|
|
||||||
|
|
||||||
static const QOperatingSystemVersion Windows7;
|
|
||||||
static const QOperatingSystemVersion Windows8;
|
|
||||||
static const QOperatingSystemVersion Windows8_1;
|
|
||||||
static const QOperatingSystemVersion Windows10;
|
|
||||||
|
|
||||||
static const QOperatingSystemVersion OSXMavericks;
|
|
||||||
static const QOperatingSystemVersion OSXYosemite;
|
|
||||||
static const QOperatingSystemVersion OSXElCapitan;
|
|
||||||
static const QOperatingSystemVersion MacOSSierra;
|
|
||||||
static const QOperatingSystemVersion MacOSHighSierra;
|
|
||||||
static const QOperatingSystemVersion MacOSMojave;
|
|
||||||
static const QOperatingSystemVersion MacOSCatalina;
|
|
||||||
static const QOperatingSystemVersion MacOSBigSur;
|
|
||||||
|
|
||||||
static const QOperatingSystemVersion AndroidJellyBean;
|
|
||||||
static const QOperatingSystemVersion AndroidJellyBean_MR1;
|
|
||||||
static const QOperatingSystemVersion AndroidJellyBean_MR2;
|
|
||||||
static const QOperatingSystemVersion AndroidKitKat;
|
|
||||||
static const QOperatingSystemVersion AndroidLollipop;
|
|
||||||
static const QOperatingSystemVersion AndroidLollipop_MR1;
|
|
||||||
static const QOperatingSystemVersion AndroidMarshmallow;
|
|
||||||
static const QOperatingSystemVersion AndroidNougat;
|
|
||||||
static const QOperatingSystemVersion AndroidNougat_MR1;
|
|
||||||
static const QOperatingSystemVersion AndroidOreo;
|
|
||||||
|
|
||||||
Q_DECL_CONSTEXPR QOperatingSystemVersion(OSType osType,
|
|
||||||
int vmajor, int vminor = -1, int vmicro = -1)
|
|
||||||
: m_os(osType),
|
|
||||||
m_major(qMax(-1, vmajor)),
|
|
||||||
m_minor(vmajor < 0 ? -1 : qMax(-1, vminor)),
|
|
||||||
m_micro(vmajor < 0 || vminor < 0 ? -1 : qMax(-1, vmicro))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
static QOperatingSystemVersion current();
|
|
||||||
|
|
||||||
static Q_DECL_CONSTEXPR OSType currentType()
|
|
||||||
{
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
return Windows;
|
|
||||||
#elif defined(Q_OS_MACOS)
|
|
||||||
return MacOS;
|
|
||||||
#elif defined(Q_OS_IOS)
|
|
||||||
return IOS;
|
|
||||||
#elif defined(Q_OS_TVOS)
|
|
||||||
return TvOS;
|
|
||||||
#elif defined(Q_OS_WATCHOS)
|
|
||||||
return WatchOS;
|
|
||||||
#elif defined(Q_OS_ANDROID)
|
|
||||||
return Android;
|
|
||||||
#else
|
|
||||||
return Unknown;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_DECL_CONSTEXPR int majorVersion() const { return m_major; }
|
|
||||||
Q_DECL_CONSTEXPR int minorVersion() const { return m_minor; }
|
|
||||||
Q_DECL_CONSTEXPR int microVersion() const { return m_micro; }
|
|
||||||
|
|
||||||
Q_DECL_CONSTEXPR int segmentCount() const
|
|
||||||
{ return m_micro >= 0 ? 3 : m_minor >= 0 ? 2 : m_major >= 0 ? 1 : 0; }
|
|
||||||
|
|
||||||
#ifdef Q_COMPILER_INITIALIZER_LISTS
|
|
||||||
bool isAnyOfType(std::initializer_list<OSType> types) const;
|
|
||||||
#endif
|
|
||||||
Q_DECL_CONSTEXPR OSType type() const { return m_os; }
|
|
||||||
QString name() const;
|
|
||||||
|
|
||||||
friend bool operator>(const QOperatingSystemVersion &lhs, const QOperatingSystemVersion &rhs)
|
|
||||||
{ return lhs.type() == rhs.type() && QOperatingSystemVersion::compare(lhs, rhs) > 0; }
|
|
||||||
|
|
||||||
friend bool operator>=(const QOperatingSystemVersion &lhs, const QOperatingSystemVersion &rhs)
|
|
||||||
{ return lhs.type() == rhs.type() && QOperatingSystemVersion::compare(lhs, rhs) >= 0; }
|
|
||||||
|
|
||||||
friend bool operator<(const QOperatingSystemVersion &lhs, const QOperatingSystemVersion &rhs)
|
|
||||||
{ return lhs.type() == rhs.type() && QOperatingSystemVersion::compare(lhs, rhs) < 0; }
|
|
||||||
|
|
||||||
friend bool operator<=(const QOperatingSystemVersion &lhs, const QOperatingSystemVersion &rhs)
|
|
||||||
{ return lhs.type() == rhs.type() && QOperatingSystemVersion::compare(lhs, rhs) <= 0; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
QOperatingSystemVersion() = default;
|
|
||||||
OSType m_os;
|
|
||||||
int m_major;
|
|
||||||
int m_minor;
|
|
||||||
int m_micro;
|
|
||||||
|
|
||||||
static int compare(const QOperatingSystemVersion &v1, const QOperatingSystemVersion &v2);
|
|
||||||
};
|
|
||||||
Q_DECLARE_TYPEINFO(QOperatingSystemVersion, QT_VERSION < QT_VERSION_CHECK(6, 0, 0) ? Q_RELOCATABLE_TYPE : Q_PRIMITIVE_TYPE);
|
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG_STREAM
|
|
||||||
class QDebug;
|
|
||||||
Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QOperatingSystemVersion &ov);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // QOPERATINGSYSTEMVERSION_H
|
|
@ -1,288 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
** Contact: https://www.qt.io/licensing/
|
|
||||||
**
|
|
||||||
** This file is part of the QtCore 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$
|
|
||||||
**
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef QGLOBAL_H
|
|
||||||
# include <QtCore/qglobal.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef QSYSTEMDETECTION_H
|
|
||||||
#define QSYSTEMDETECTION_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
The operating system, must be one of: (Q_OS_x)
|
|
||||||
|
|
||||||
DARWIN - Any Darwin system (macOS, iOS, watchOS, tvOS)
|
|
||||||
MACOS - macOS
|
|
||||||
IOS - iOS
|
|
||||||
WATCHOS - watchOS
|
|
||||||
TVOS - tvOS
|
|
||||||
WIN32 - Win32 (Windows 2000/XP/Vista/7 and Windows Server 2003/2008)
|
|
||||||
WINRT - WinRT (Windows Runtime)
|
|
||||||
CYGWIN - Cygwin
|
|
||||||
SOLARIS - Sun Solaris
|
|
||||||
HPUX - HP-UX
|
|
||||||
LINUX - Linux [has variants]
|
|
||||||
FREEBSD - FreeBSD [has variants]
|
|
||||||
NETBSD - NetBSD
|
|
||||||
OPENBSD - OpenBSD
|
|
||||||
INTERIX - Interix
|
|
||||||
AIX - AIX
|
|
||||||
HURD - GNU Hurd
|
|
||||||
QNX - QNX [has variants]
|
|
||||||
QNX6 - QNX RTP 6.1
|
|
||||||
LYNX - LynxOS
|
|
||||||
BSD4 - Any BSD 4.4 system
|
|
||||||
UNIX - Any UNIX BSD/SYSV system
|
|
||||||
ANDROID - Android platform
|
|
||||||
HAIKU - Haiku
|
|
||||||
|
|
||||||
The following operating systems have variants:
|
|
||||||
LINUX - both Q_OS_LINUX and Q_OS_ANDROID are defined when building for Android
|
|
||||||
- only Q_OS_LINUX is defined if building for other Linux systems
|
|
||||||
FREEBSD - Q_OS_FREEBSD is defined only when building for FreeBSD with a BSD userland
|
|
||||||
- Q_OS_FREEBSD_KERNEL is always defined on FreeBSD, even if the userland is from GNU
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__))
|
|
||||||
# include <TargetConditionals.h>
|
|
||||||
# if defined(TARGET_OS_MAC) && TARGET_OS_MAC
|
|
||||||
# define Q_OS_DARWIN
|
|
||||||
# define Q_OS_BSD4
|
|
||||||
# ifdef __LP64__
|
|
||||||
# define Q_OS_DARWIN64
|
|
||||||
# else
|
|
||||||
# define Q_OS_DARWIN32
|
|
||||||
# endif
|
|
||||||
# if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
|
|
||||||
# define QT_PLATFORM_UIKIT
|
|
||||||
# if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH
|
|
||||||
# define Q_OS_WATCHOS
|
|
||||||
# elif defined(TARGET_OS_TV) && TARGET_OS_TV
|
|
||||||
# define Q_OS_TVOS
|
|
||||||
# else
|
|
||||||
# // TARGET_OS_IOS is only available in newer SDKs,
|
|
||||||
# // so assume any other iOS-based platform is iOS for now
|
|
||||||
# define Q_OS_IOS
|
|
||||||
# endif
|
|
||||||
# else
|
|
||||||
# // TARGET_OS_OSX is only available in newer SDKs,
|
|
||||||
# // so assume any non iOS-based platform is macOS for now
|
|
||||||
# define Q_OS_MACOS
|
|
||||||
# endif
|
|
||||||
# else
|
|
||||||
# error "Qt has not been ported to this Apple platform - see http://www.qt.io/developers"
|
|
||||||
# endif
|
|
||||||
#elif defined(__ANDROID__) || defined(ANDROID)
|
|
||||||
# define Q_OS_ANDROID
|
|
||||||
# define Q_OS_LINUX
|
|
||||||
#elif defined(__CYGWIN__)
|
|
||||||
# define Q_OS_CYGWIN
|
|
||||||
#elif !defined(SAG_COM) && (!defined(WINAPI_FAMILY) || WINAPI_FAMILY==WINAPI_FAMILY_DESKTOP_APP) && (defined(WIN64) || defined(_WIN64) || defined(__WIN64__))
|
|
||||||
# define Q_OS_WIN32
|
|
||||||
# define Q_OS_WIN64
|
|
||||||
#elif !defined(SAG_COM) && (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__))
|
|
||||||
# if defined(WINAPI_FAMILY)
|
|
||||||
# ifndef WINAPI_FAMILY_PC_APP
|
|
||||||
# define WINAPI_FAMILY_PC_APP WINAPI_FAMILY_APP
|
|
||||||
# endif
|
|
||||||
# if defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
|
|
||||||
# define Q_OS_WINRT
|
|
||||||
# elif WINAPI_FAMILY==WINAPI_FAMILY_PC_APP
|
|
||||||
# define Q_OS_WINRT
|
|
||||||
# else
|
|
||||||
# define Q_OS_WIN32
|
|
||||||
# endif
|
|
||||||
# else
|
|
||||||
# define Q_OS_WIN32
|
|
||||||
# endif
|
|
||||||
#elif defined(__sun) || defined(sun)
|
|
||||||
# define Q_OS_SOLARIS
|
|
||||||
#elif defined(hpux) || defined(__hpux)
|
|
||||||
# define Q_OS_HPUX
|
|
||||||
#elif defined(__native_client__)
|
|
||||||
# define Q_OS_NACL
|
|
||||||
#elif defined(__EMSCRIPTEN__)
|
|
||||||
# define Q_OS_WASM
|
|
||||||
#elif defined(__linux__) || defined(__linux)
|
|
||||||
# define Q_OS_LINUX
|
|
||||||
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
|
|
||||||
# ifndef __FreeBSD_kernel__
|
|
||||||
# define Q_OS_FREEBSD
|
|
||||||
# endif
|
|
||||||
# define Q_OS_FREEBSD_KERNEL
|
|
||||||
# define Q_OS_BSD4
|
|
||||||
#elif defined(__NetBSD__)
|
|
||||||
# define Q_OS_NETBSD
|
|
||||||
# define Q_OS_BSD4
|
|
||||||
#elif defined(__OpenBSD__)
|
|
||||||
# define Q_OS_OPENBSD
|
|
||||||
# define Q_OS_BSD4
|
|
||||||
#elif defined(__INTERIX)
|
|
||||||
# define Q_OS_INTERIX
|
|
||||||
# define Q_OS_BSD4
|
|
||||||
#elif defined(_AIX)
|
|
||||||
# define Q_OS_AIX
|
|
||||||
#elif defined(__Lynx__)
|
|
||||||
# define Q_OS_LYNX
|
|
||||||
#elif defined(__GNU__)
|
|
||||||
# define Q_OS_HURD
|
|
||||||
#elif defined(__QNXNTO__)
|
|
||||||
# define Q_OS_QNX
|
|
||||||
#elif defined(__INTEGRITY)
|
|
||||||
# define Q_OS_INTEGRITY
|
|
||||||
#elif defined(VXWORKS) /* there is no "real" VxWorks define - this has to be set in the mkspec! */
|
|
||||||
# define Q_OS_VXWORKS
|
|
||||||
#elif defined(__HAIKU__)
|
|
||||||
# define Q_OS_HAIKU
|
|
||||||
#elif defined(__MAKEDEPEND__)
|
|
||||||
#else
|
|
||||||
# error "Qt has not been ported to this OS - see http://www.qt-project.org/"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64) || defined(Q_OS_WINRT)
|
|
||||||
# define Q_OS_WINDOWS
|
|
||||||
# define Q_OS_WIN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
# undef Q_OS_UNIX
|
|
||||||
#elif !defined(Q_OS_UNIX)
|
|
||||||
# define Q_OS_UNIX
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Compatibility synonyms
|
|
||||||
#ifdef Q_OS_DARWIN
|
|
||||||
#define Q_OS_MAC
|
|
||||||
#endif
|
|
||||||
#ifdef Q_OS_DARWIN32
|
|
||||||
#define Q_OS_MAC32
|
|
||||||
#endif
|
|
||||||
#ifdef Q_OS_DARWIN64
|
|
||||||
#define Q_OS_MAC64
|
|
||||||
#endif
|
|
||||||
#ifdef Q_OS_MACOS
|
|
||||||
#define Q_OS_MACX
|
|
||||||
#define Q_OS_OSX
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_DARWIN
|
|
||||||
# include <Availability.h>
|
|
||||||
# include <AvailabilityMacros.h>
|
|
||||||
#
|
|
||||||
# ifdef Q_OS_MACOS
|
|
||||||
# if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_6
|
|
||||||
# undef __MAC_OS_X_VERSION_MIN_REQUIRED
|
|
||||||
# define __MAC_OS_X_VERSION_MIN_REQUIRED __MAC_10_6
|
|
||||||
# endif
|
|
||||||
# if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
|
|
||||||
# undef MAC_OS_X_VERSION_MIN_REQUIRED
|
|
||||||
# define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#
|
|
||||||
# // Numerical checks are preferred to named checks, but to be safe
|
|
||||||
# // we define the missing version names in case Qt uses them.
|
|
||||||
#
|
|
||||||
# if !defined(__MAC_10_11)
|
|
||||||
# define __MAC_10_11 101100
|
|
||||||
# endif
|
|
||||||
# if !defined(__MAC_10_12)
|
|
||||||
# define __MAC_10_12 101200
|
|
||||||
# endif
|
|
||||||
# if !defined(__MAC_10_13)
|
|
||||||
# define __MAC_10_13 101300
|
|
||||||
# endif
|
|
||||||
# if !defined(__MAC_10_14)
|
|
||||||
# define __MAC_10_14 101400
|
|
||||||
# endif
|
|
||||||
# if !defined(__MAC_10_15)
|
|
||||||
# define __MAC_10_15 101500
|
|
||||||
# endif
|
|
||||||
# if !defined(__MAC_10_16)
|
|
||||||
# define __MAC_10_16 101600
|
|
||||||
# endif
|
|
||||||
# if !defined(MAC_OS_X_VERSION_10_11)
|
|
||||||
# define MAC_OS_X_VERSION_10_11 __MAC_10_11
|
|
||||||
# endif
|
|
||||||
# if !defined(MAC_OS_X_VERSION_10_12)
|
|
||||||
# define MAC_OS_X_VERSION_10_12 __MAC_10_12
|
|
||||||
# endif
|
|
||||||
# if !defined(MAC_OS_X_VERSION_10_13)
|
|
||||||
# define MAC_OS_X_VERSION_10_13 __MAC_10_13
|
|
||||||
# endif
|
|
||||||
# if !defined(MAC_OS_X_VERSION_10_14)
|
|
||||||
# define MAC_OS_X_VERSION_10_14 __MAC_10_14
|
|
||||||
# endif
|
|
||||||
# if !defined(MAC_OS_X_VERSION_10_15)
|
|
||||||
# define MAC_OS_X_VERSION_10_15 __MAC_10_15
|
|
||||||
# endif
|
|
||||||
# if !defined(MAC_OS_X_VERSION_10_16)
|
|
||||||
# define MAC_OS_X_VERSION_10_16 __MAC_10_16
|
|
||||||
# endif
|
|
||||||
#
|
|
||||||
# if !defined(__IPHONE_10_0)
|
|
||||||
# define __IPHONE_10_0 100000
|
|
||||||
# endif
|
|
||||||
# if !defined(__IPHONE_10_1)
|
|
||||||
# define __IPHONE_10_1 100100
|
|
||||||
# endif
|
|
||||||
# if !defined(__IPHONE_10_2)
|
|
||||||
# define __IPHONE_10_2 100200
|
|
||||||
# endif
|
|
||||||
# if !defined(__IPHONE_10_3)
|
|
||||||
# define __IPHONE_10_3 100300
|
|
||||||
# endif
|
|
||||||
# if !defined(__IPHONE_11_0)
|
|
||||||
# define __IPHONE_11_0 110000
|
|
||||||
# endif
|
|
||||||
# if !defined(__IPHONE_12_0)
|
|
||||||
# define __IPHONE_12_0 120000
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __LSB_VERSION__
|
|
||||||
# if __LSB_VERSION__ < 40
|
|
||||||
# error "This version of the Linux Standard Base is unsupported"
|
|
||||||
# endif
|
|
||||||
#ifndef QT_LINUXBASE
|
|
||||||
# define QT_LINUXBASE
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // QSYSTEMDETECTION_H
|
|
@ -1,289 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
** Contact: https://www.qt.io/licensing/
|
|
||||||
**
|
|
||||||
** This file is part of the QtCore 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 <private/qcore_mac_p.h>
|
|
||||||
#include <new>
|
|
||||||
|
|
||||||
#include "qhash.h"
|
|
||||||
#include "qpair.h"
|
|
||||||
#include "qmutex.h"
|
|
||||||
#include "qvarlengtharray.h"
|
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <mach-o/dyld.h>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
QCFString::operator QString() const
|
|
||||||
{
|
|
||||||
if (string.isEmpty() && value)
|
|
||||||
const_cast<QCFString*>(this)->string = QString::fromCFString(value);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
QCFString::operator CFStringRef() const
|
|
||||||
{
|
|
||||||
if (!value)
|
|
||||||
const_cast<QCFString*>(this)->value = string.toCFString();
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
|
|
||||||
|
|
||||||
bool AppleUnifiedLogger::willMirrorToStderr()
|
|
||||||
{
|
|
||||||
// When running under Xcode or LLDB, one or more of these variables will
|
|
||||||
// be set, which triggers libsystem_trace.dyld to log messages to stderr
|
|
||||||
// as well, via_os_log_impl_mirror_to_stderr. Un-setting these variables
|
|
||||||
// is not an option, as that would silence normal NSLog or os_log calls,
|
|
||||||
// so instead we skip our own stderr output. See rdar://36919139.
|
|
||||||
static bool willMirror = qEnvironmentVariableIsSet("OS_ACTIVITY_DT_MODE")
|
|
||||||
|| qEnvironmentVariableIsSet("ACTIVITY_LOG_STDERR")
|
|
||||||
|| qEnvironmentVariableIsSet("CFLOG_FORCE_STDERR");
|
|
||||||
return willMirror;
|
|
||||||
}
|
|
||||||
|
|
||||||
QT_MAC_WEAK_IMPORT(_os_log_default);
|
|
||||||
bool AppleUnifiedLogger::messageHandler(QtMsgType msgType, const QMessageLogContext &context,
|
|
||||||
const QString &message, const QString &optionalSubsystem)
|
|
||||||
{
|
|
||||||
QString subsystem = optionalSubsystem;
|
|
||||||
if (subsystem.isNull()) {
|
|
||||||
static QString bundleIdentifier = []() {
|
|
||||||
if (CFBundleRef bundle = CFBundleGetMainBundle()) {
|
|
||||||
if (CFStringRef identifier = CFBundleGetIdentifier(bundle))
|
|
||||||
return QString::fromCFString(identifier);
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}();
|
|
||||||
subsystem = bundleIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool isDefault = !context.category || !strcmp(context.category, "default");
|
|
||||||
os_log_t log = isDefault ? OS_LOG_DEFAULT :
|
|
||||||
cachedLog(subsystem, QString::fromLatin1(context.category));
|
|
||||||
os_log_type_t logType = logTypeForMessageType(msgType);
|
|
||||||
|
|
||||||
if (!os_log_type_enabled(log, logType))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Logging best practices says we should not include symbolication
|
|
||||||
// information or source file line numbers in messages, as the system
|
|
||||||
// will automatically captures this information. In our case, what
|
|
||||||
// the system captures is the call to os_log_with_type below, which
|
|
||||||
// isn't really useful, but we still don't want to include the context's
|
|
||||||
// info, as that would clutter the logging output. See rdar://35958308.
|
|
||||||
|
|
||||||
// The format must be a string constant, so we can't pass on the
|
|
||||||
// message. This means we won't be able to take advantage of the
|
|
||||||
// unified logging's custom format specifiers such as %{BOOL}d.
|
|
||||||
// We use the 'public' format specifier to prevent the logging
|
|
||||||
// system from redacting our log message.
|
|
||||||
os_log_with_type(log, logType, "%{public}s", qPrintable(message));
|
|
||||||
|
|
||||||
return willMirrorToStderr();
|
|
||||||
}
|
|
||||||
|
|
||||||
os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType)
|
|
||||||
{
|
|
||||||
switch (msgType) {
|
|
||||||
case QtDebugMsg: return OS_LOG_TYPE_DEBUG;
|
|
||||||
case QtInfoMsg: return OS_LOG_TYPE_INFO;
|
|
||||||
case QtWarningMsg: return OS_LOG_TYPE_DEFAULT;
|
|
||||||
case QtCriticalMsg: return OS_LOG_TYPE_ERROR;
|
|
||||||
case QtFatalMsg: return OS_LOG_TYPE_FAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OS_LOG_TYPE_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
os_log_t AppleUnifiedLogger::cachedLog(const QString &subsystem, const QString &category)
|
|
||||||
{
|
|
||||||
static QBasicMutex mutex;
|
|
||||||
QMutexLocker locker(&mutex);
|
|
||||||
|
|
||||||
static QHash<QPair<QString, QString>, os_log_t> logs;
|
|
||||||
const auto cacheKey = qMakePair(subsystem, category);
|
|
||||||
os_log_t log = logs.value(cacheKey);
|
|
||||||
|
|
||||||
if (!log) {
|
|
||||||
log = os_log_create(subsystem.toLatin1().constData(),
|
|
||||||
category.toLatin1().constData());
|
|
||||||
logs.insert(cacheKey, log);
|
|
||||||
|
|
||||||
// Technically we should release the os_log_t resource when done
|
|
||||||
// with it, but since we don't know when a category is disabled
|
|
||||||
// we keep all cached os_log_t instances until shutdown, where
|
|
||||||
// the OS will clean them up for us.
|
|
||||||
}
|
|
||||||
|
|
||||||
return log;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // QT_USE_APPLE_UNIFIED_LOGGING
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
QOperatingSystemVersion QMacVersion::buildSDK(VersionTarget target)
|
|
||||||
{
|
|
||||||
switch (target) {
|
|
||||||
case ApplicationBinary: return applicationVersion().second;
|
|
||||||
case QtLibraries: return libraryVersion().second;
|
|
||||||
}
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
QOperatingSystemVersion QMacVersion::deploymentTarget(VersionTarget target)
|
|
||||||
{
|
|
||||||
switch (target) {
|
|
||||||
case ApplicationBinary: return applicationVersion().first;
|
|
||||||
case QtLibraries: return libraryVersion().first;
|
|
||||||
}
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
QOperatingSystemVersion QMacVersion::currentRuntime()
|
|
||||||
{
|
|
||||||
return QOperatingSystemVersion::current();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mach-O platforms
|
|
||||||
enum Platform {
|
|
||||||
macOS = 1,
|
|
||||||
iOS = 2,
|
|
||||||
tvOS = 3,
|
|
||||||
watchOS = 4,
|
|
||||||
bridgeOS = 5,
|
|
||||||
macCatalyst = 6,
|
|
||||||
iOSSimulator = 7,
|
|
||||||
tvOSSimulator = 8,
|
|
||||||
watchOSSimulator = 9
|
|
||||||
};
|
|
||||||
|
|
||||||
QMacVersion::VersionTuple QMacVersion::versionsForImage(const mach_header *machHeader)
|
|
||||||
{
|
|
||||||
static auto osForLoadCommand = [](uint32_t cmd) {
|
|
||||||
switch (cmd) {
|
|
||||||
case LC_VERSION_MIN_MACOSX: return QOperatingSystemVersion::MacOS;
|
|
||||||
case LC_VERSION_MIN_IPHONEOS: return QOperatingSystemVersion::IOS;
|
|
||||||
case LC_VERSION_MIN_TVOS: return QOperatingSystemVersion::TvOS;
|
|
||||||
case LC_VERSION_MIN_WATCHOS: return QOperatingSystemVersion::WatchOS;
|
|
||||||
default: return QOperatingSystemVersion::Unknown;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static auto osForPlatform = [](uint32_t platform) {
|
|
||||||
switch (platform) {
|
|
||||||
case Platform::macOS:
|
|
||||||
return QOperatingSystemVersion::MacOS;
|
|
||||||
case Platform::iOS:
|
|
||||||
case Platform::iOSSimulator:
|
|
||||||
return QOperatingSystemVersion::IOS;
|
|
||||||
case Platform::tvOS:
|
|
||||||
case Platform::tvOSSimulator:
|
|
||||||
return QOperatingSystemVersion::TvOS;
|
|
||||||
case Platform::watchOS:
|
|
||||||
case Platform::watchOSSimulator:
|
|
||||||
return QOperatingSystemVersion::WatchOS;
|
|
||||||
default:
|
|
||||||
return QOperatingSystemVersion::Unknown;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static auto makeVersionTuple = [](uint32_t dt, uint32_t sdk, QOperatingSystemVersion::OSType osType) {
|
|
||||||
return qMakePair(
|
|
||||||
QOperatingSystemVersion(osType, dt >> 16 & 0xffff, dt >> 8 & 0xff, dt & 0xff),
|
|
||||||
QOperatingSystemVersion(osType, sdk >> 16 & 0xffff, sdk >> 8 & 0xff, sdk & 0xff)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const bool is64Bit = machHeader->magic == MH_MAGIC_64 || machHeader->magic == MH_CIGAM_64;
|
|
||||||
auto commandCursor = uintptr_t(machHeader) + (is64Bit ? sizeof(mach_header_64) : sizeof(mach_header));
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < machHeader->ncmds; ++i) {
|
|
||||||
load_command *loadCommand = reinterpret_cast<load_command *>(commandCursor);
|
|
||||||
if (loadCommand->cmd == LC_VERSION_MIN_MACOSX || loadCommand->cmd == LC_VERSION_MIN_IPHONEOS
|
|
||||||
|| loadCommand->cmd == LC_VERSION_MIN_TVOS || loadCommand->cmd == LC_VERSION_MIN_WATCHOS) {
|
|
||||||
auto versionCommand = reinterpret_cast<version_min_command *>(loadCommand);
|
|
||||||
return makeVersionTuple(versionCommand->version, versionCommand->sdk, osForLoadCommand(loadCommand->cmd));
|
|
||||||
#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0)
|
|
||||||
} else if (loadCommand->cmd == LC_BUILD_VERSION) {
|
|
||||||
auto versionCommand = reinterpret_cast<build_version_command *>(loadCommand);
|
|
||||||
return makeVersionTuple(versionCommand->minos, versionCommand->sdk, osForPlatform(versionCommand->platform));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
commandCursor += loadCommand->cmdsize;
|
|
||||||
}
|
|
||||||
Q_ASSERT_X(false, "QMacVersion", "Could not find any version load command");
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
QMacVersion::VersionTuple QMacVersion::applicationVersion()
|
|
||||||
{
|
|
||||||
static VersionTuple version = []() {
|
|
||||||
const mach_header *executableHeader = nullptr;
|
|
||||||
for (uint32_t i = 0; i < _dyld_image_count(); ++i) {
|
|
||||||
auto header = _dyld_get_image_header(i);
|
|
||||||
if (header->filetype == MH_EXECUTE) {
|
|
||||||
executableHeader = header;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Q_ASSERT_X(executableHeader, "QMacVersion", "Failed to resolve Mach-O header of executable");
|
|
||||||
return versionsForImage(executableHeader);
|
|
||||||
}();
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMacVersion::VersionTuple QMacVersion::libraryVersion()
|
|
||||||
{
|
|
||||||
static VersionTuple version = []() {
|
|
||||||
Dl_info qtCoreImage;
|
|
||||||
dladdr((const void *)&QMacVersion::libraryVersion, &qtCoreImage);
|
|
||||||
Q_ASSERT_X(qtCoreImage.dli_fbase, "QMacVersion", "Failed to resolve Mach-O header of QtCore");
|
|
||||||
return versionsForImage(static_cast<mach_header*>(qtCoreImage.dli_fbase));
|
|
||||||
}();
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
@ -1,428 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
|
||||||
** Contact: https://www.qt.io/licensing/
|
|
||||||
**
|
|
||||||
** This file is part of the QtCore 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$
|
|
||||||
**
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef QCORE_MAC_P_H
|
|
||||||
#define QCORE_MAC_P_H
|
|
||||||
|
|
||||||
//
|
|
||||||
// W A R N I N G
|
|
||||||
// -------------
|
|
||||||
//
|
|
||||||
// This file is not part of the Qt API. It exists for the convenience
|
|
||||||
// of other Qt classes. This header file may change from version to
|
|
||||||
// version without notice, or even be removed.
|
|
||||||
//
|
|
||||||
// We mean it.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "private/qglobal_p.h"
|
|
||||||
|
|
||||||
#include <QtCore/qoperatingsystemversion.h>
|
|
||||||
struct mach_header;
|
|
||||||
|
|
||||||
#ifndef __IMAGECAPTURE__
|
|
||||||
# define __IMAGECAPTURE__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#if defined(QT_BOOTSTRAPPED)
|
|
||||||
#include <ApplicationServices/ApplicationServices.h>
|
|
||||||
#else
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __OBJC__
|
|
||||||
#include <Foundation/Foundation.h>
|
|
||||||
#include <functional>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "qstring.h"
|
|
||||||
#include "qscopedpointer.h"
|
|
||||||
#include "qpair.h"
|
|
||||||
|
|
||||||
#if defined( __OBJC__) && defined(QT_NAMESPACE)
|
|
||||||
#define QT_NAMESPACE_ALIAS_OBJC_CLASS(__KLASS__) @compatibility_alias __KLASS__ QT_MANGLE_NAMESPACE(__KLASS__)
|
|
||||||
#else
|
|
||||||
#define QT_NAMESPACE_ALIAS_OBJC_CLASS(__KLASS__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define QT_MAC_WEAK_IMPORT(symbol) extern "C" decltype(symbol) symbol __attribute__((weak_import));
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
template <typename T, typename U, U (*RetainFunction)(U), void (*ReleaseFunction)(U)>
|
|
||||||
class QAppleRefCounted
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QAppleRefCounted(const T &t = T()) : value(t) {}
|
|
||||||
QAppleRefCounted(QAppleRefCounted &&other) : value(other.value) { other.value = T(); }
|
|
||||||
QAppleRefCounted(const QAppleRefCounted &other) : value(other.value) { if (value) RetainFunction(value); }
|
|
||||||
~QAppleRefCounted() { if (value) ReleaseFunction(value); }
|
|
||||||
operator T() const { return value; }
|
|
||||||
void swap(QAppleRefCounted &other) Q_DECL_NOEXCEPT_EXPR(noexcept(qSwap(value, other.value)))
|
|
||||||
{ qSwap(value, other.value); }
|
|
||||||
QAppleRefCounted &operator=(const QAppleRefCounted &other)
|
|
||||||
{ QAppleRefCounted copy(other); swap(copy); return *this; }
|
|
||||||
QAppleRefCounted &operator=(QAppleRefCounted &&other)
|
|
||||||
{ QAppleRefCounted moved(std::move(other)); swap(moved); return *this; }
|
|
||||||
T *operator&() { return &value; }
|
|
||||||
protected:
|
|
||||||
T value;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
|
||||||
class QMacRootLevelAutoReleasePool
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QMacRootLevelAutoReleasePool();
|
|
||||||
~QMacRootLevelAutoReleasePool();
|
|
||||||
private:
|
|
||||||
QScopedPointer<QMacAutoReleasePool> pool;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
Helper class that automates refernce counting for CFtypes.
|
|
||||||
After constructing the QCFType object, it can be copied like a
|
|
||||||
value-based type.
|
|
||||||
|
|
||||||
Note that you must own the object you are wrapping.
|
|
||||||
This is typically the case if you get the object from a Core
|
|
||||||
Foundation function with the word "Create" or "Copy" in it. If
|
|
||||||
you got the object from a "Get" function, either retain it or use
|
|
||||||
constructFromGet(). One exception to this rule is the
|
|
||||||
HIThemeGet*Shape functions, which in reality are "Copy" functions.
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
class QCFType : public QAppleRefCounted<T, CFTypeRef, CFRetain, CFRelease>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using QAppleRefCounted<T, CFTypeRef, CFRetain, CFRelease>::QAppleRefCounted;
|
|
||||||
template <typename X> X as() const { return reinterpret_cast<X>(this->value); }
|
|
||||||
static QCFType constructFromGet(const T &t)
|
|
||||||
{
|
|
||||||
if (t)
|
|
||||||
CFRetain(t);
|
|
||||||
return QCFType<T>(t);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Q_CORE_EXPORT QCFString : public QCFType<CFStringRef>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
inline QCFString(const QString &str) : QCFType<CFStringRef>(0), string(str) {}
|
|
||||||
inline QCFString(const CFStringRef cfstr = 0) : QCFType<CFStringRef>(cfstr) {}
|
|
||||||
inline QCFString(const QCFType<CFStringRef> &other) : QCFType<CFStringRef>(other) {}
|
|
||||||
operator QString() const;
|
|
||||||
operator CFStringRef() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString string;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
|
||||||
Q_CORE_EXPORT QChar qt_mac_qtKey2CocoaKey(Qt::Key key);
|
|
||||||
Q_CORE_EXPORT Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode);
|
|
||||||
Q_CORE_EXPORT bool qt_mac_applicationIsInDarkMode();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG_STREAM
|
|
||||||
QDebug operator<<(QDebug debug, const QMacAutoReleasePool *pool);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Q_CORE_EXPORT bool qt_apple_isApplicationExtension();
|
|
||||||
|
|
||||||
#if defined(Q_OS_MACOS) && !defined(QT_BOOTSTRAPPED)
|
|
||||||
Q_CORE_EXPORT bool qt_apple_isSandboxed();
|
|
||||||
# ifdef __OBJC__
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
@interface NSObject (QtSandboxHelpers)
|
|
||||||
- (id)qt_valueForPrivateKey:(NSString *)key;
|
|
||||||
@end
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS)
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
# if defined(Q_OS_MACOS)
|
|
||||||
Q_FORWARD_DECLARE_OBJC_CLASS(NSApplication);
|
|
||||||
using AppleApplication = NSApplication;
|
|
||||||
# else
|
|
||||||
Q_FORWARD_DECLARE_OBJC_CLASS(UIApplication);
|
|
||||||
using AppleApplication = UIApplication;
|
|
||||||
# endif
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
Q_CORE_EXPORT AppleApplication *qt_apple_sharedApplication();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#if !defined(QT_BOOTSTRAPPED)
|
|
||||||
#define QT_USE_APPLE_UNIFIED_LOGGING
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
#include <os/log.h>
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
class Q_CORE_EXPORT AppleUnifiedLogger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static bool messageHandler(QtMsgType msgType, const QMessageLogContext &context, const QString &message,
|
|
||||||
const QString &subsystem = QString());
|
|
||||||
static bool willMirrorToStderr();
|
|
||||||
private:
|
|
||||||
static os_log_type_t logTypeForMessageType(QtMsgType msgType);
|
|
||||||
static os_log_t cachedLog(const QString &subsystem, const QString &category);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#if !defined(QT_BOOTSTRAPPED)
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
#include <os/activity.h>
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
template <typename T> using QAppleOsType = QAppleRefCounted<T, void *, os_retain, os_release>;
|
|
||||||
|
|
||||||
class Q_CORE_EXPORT QAppleLogActivity
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QAppleLogActivity() : activity(nullptr) {}
|
|
||||||
QAppleLogActivity(os_activity_t activity) : activity(activity) {}
|
|
||||||
~QAppleLogActivity() { if (activity) leave(); }
|
|
||||||
|
|
||||||
QAppleLogActivity(const QAppleLogActivity &) = delete;
|
|
||||||
QAppleLogActivity& operator=(const QAppleLogActivity &) = delete;
|
|
||||||
|
|
||||||
QAppleLogActivity(QAppleLogActivity&& other)
|
|
||||||
: activity(other.activity), state(other.state) { other.activity = nullptr; }
|
|
||||||
|
|
||||||
QAppleLogActivity& operator=(QAppleLogActivity &&other)
|
|
||||||
{
|
|
||||||
if (this != &other) {
|
|
||||||
activity = other.activity;
|
|
||||||
state = other.state;
|
|
||||||
other.activity = nullptr;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAppleLogActivity&& enter()
|
|
||||||
{
|
|
||||||
if (activity)
|
|
||||||
os_activity_scope_enter(static_cast<os_activity_t>(*this), &state);
|
|
||||||
return std::move(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void leave() {
|
|
||||||
if (activity)
|
|
||||||
os_activity_scope_leave(&state);
|
|
||||||
}
|
|
||||||
|
|
||||||
operator os_activity_t()
|
|
||||||
{
|
|
||||||
return reinterpret_cast<os_activity_t>(static_cast<void *>(activity));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Work around API_AVAILABLE not working for templates by using void*
|
|
||||||
QAppleOsType<void *> activity;
|
|
||||||
os_activity_scope_state_s state;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define QT_APPLE_LOG_ACTIVITY_CREATE(condition, description, parent) []() { \
|
|
||||||
if (!(condition)) \
|
|
||||||
return QAppleLogActivity(); \
|
|
||||||
return QAppleLogActivity(os_activity_create(description, parent, OS_ACTIVITY_FLAG_DEFAULT)); \
|
|
||||||
}()
|
|
||||||
|
|
||||||
#define QT_VA_ARGS_CHOOSE(_1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N
|
|
||||||
#define QT_VA_ARGS_COUNT(...) QT_VA_ARGS_CHOOSE(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1)
|
|
||||||
|
|
||||||
#define QT_OVERLOADED_MACRO(MACRO, ...) _QT_OVERLOADED_MACRO(MACRO, QT_VA_ARGS_COUNT(__VA_ARGS__))(__VA_ARGS__)
|
|
||||||
#define _QT_OVERLOADED_MACRO(MACRO, ARGC) _QT_OVERLOADED_MACRO_EXPAND(MACRO, ARGC)
|
|
||||||
#define _QT_OVERLOADED_MACRO_EXPAND(MACRO, ARGC) MACRO##ARGC
|
|
||||||
|
|
||||||
#define QT_APPLE_LOG_ACTIVITY_WITH_PARENT3(condition, description, parent) QT_APPLE_LOG_ACTIVITY_CREATE(condition, description, parent)
|
|
||||||
#define QT_APPLE_LOG_ACTIVITY_WITH_PARENT2(description, parent) QT_APPLE_LOG_ACTIVITY_WITH_PARENT3(true, description, parent)
|
|
||||||
#define QT_APPLE_LOG_ACTIVITY_WITH_PARENT(...) QT_OVERLOADED_MACRO(QT_APPLE_LOG_ACTIVITY_WITH_PARENT, __VA_ARGS__)
|
|
||||||
|
|
||||||
QT_MAC_WEAK_IMPORT(_os_activity_current);
|
|
||||||
#define QT_APPLE_LOG_ACTIVITY2(condition, description) QT_APPLE_LOG_ACTIVITY_CREATE(condition, description, OS_ACTIVITY_CURRENT)
|
|
||||||
#define QT_APPLE_LOG_ACTIVITY1(description) QT_APPLE_LOG_ACTIVITY2(true, description)
|
|
||||||
#define QT_APPLE_LOG_ACTIVITY(...) QT_OVERLOADED_MACRO(QT_APPLE_LOG_ACTIVITY, __VA_ARGS__)
|
|
||||||
|
|
||||||
#define QT_APPLE_SCOPED_LOG_ACTIVITY(...) QAppleLogActivity scopedLogActivity = QT_APPLE_LOG_ACTIVITY(__VA_ARGS__).enter();
|
|
||||||
|
|
||||||
#endif // !defined(QT_BOOTSTRAPPED)
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#if defined( __OBJC__)
|
|
||||||
class QMacNotificationObserver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QMacNotificationObserver() {}
|
|
||||||
|
|
||||||
template<typename Functor>
|
|
||||||
QMacNotificationObserver(id object, NSNotificationName name, Functor callback) {
|
|
||||||
observer = [[NSNotificationCenter defaultCenter] addObserverForName:name
|
|
||||||
object:object queue:nil usingBlock:^(NSNotification *) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
QMacNotificationObserver(const QMacNotificationObserver& other) = delete;
|
|
||||||
QMacNotificationObserver(QMacNotificationObserver&& other) : observer(other.observer) {
|
|
||||||
other.observer = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMacNotificationObserver &operator=(const QMacNotificationObserver& other) = delete;
|
|
||||||
QMacNotificationObserver &operator=(QMacNotificationObserver&& other) {
|
|
||||||
if (this != &other) {
|
|
||||||
remove();
|
|
||||||
observer = other.observer;
|
|
||||||
other.observer = nil;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove() {
|
|
||||||
if (observer)
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:observer];
|
|
||||||
observer = nil;
|
|
||||||
}
|
|
||||||
~QMacNotificationObserver() { remove(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
id observer = nil;
|
|
||||||
};
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
@interface QT_MANGLE_NAMESPACE(KeyValueObserver) : NSObject
|
|
||||||
@end
|
|
||||||
QT_NAMESPACE_ALIAS_OBJC_CLASS(KeyValueObserver);
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
class Q_CORE_EXPORT QMacKeyValueObserver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using Callback = std::function<void()>;
|
|
||||||
|
|
||||||
QMacKeyValueObserver() {}
|
|
||||||
|
|
||||||
// Note: QMacKeyValueObserver must not outlive the object observed!
|
|
||||||
QMacKeyValueObserver(id object, NSString *keyPath, Callback callback,
|
|
||||||
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew)
|
|
||||||
: object(object), keyPath(keyPath), callback(new Callback(callback))
|
|
||||||
{
|
|
||||||
addObserver(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
QMacKeyValueObserver(const QMacKeyValueObserver &other)
|
|
||||||
: QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get()) {}
|
|
||||||
|
|
||||||
QMacKeyValueObserver(QMacKeyValueObserver &&other) { swap(other, *this); }
|
|
||||||
|
|
||||||
~QMacKeyValueObserver() { removeObserver(); }
|
|
||||||
|
|
||||||
QMacKeyValueObserver &operator=(const QMacKeyValueObserver &other) {
|
|
||||||
QMacKeyValueObserver tmp(other);
|
|
||||||
swap(tmp, *this);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMacKeyValueObserver &operator=(QMacKeyValueObserver &&other) {
|
|
||||||
QMacKeyValueObserver tmp(std::move(other));
|
|
||||||
swap(tmp, *this);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeObserver();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void swap(QMacKeyValueObserver &first, QMacKeyValueObserver &second) {
|
|
||||||
std::swap(first.object, second.object);
|
|
||||||
std::swap(first.keyPath, second.keyPath);
|
|
||||||
std::swap(first.callback, second.callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addObserver(NSKeyValueObservingOptions options);
|
|
||||||
|
|
||||||
id object = nil;
|
|
||||||
NSString *keyPath = nullptr;
|
|
||||||
std::unique_ptr<Callback> callback;
|
|
||||||
|
|
||||||
static KeyValueObserver *observer;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class Q_CORE_EXPORT QMacVersion
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum VersionTarget {
|
|
||||||
ApplicationBinary,
|
|
||||||
QtLibraries
|
|
||||||
};
|
|
||||||
|
|
||||||
static QOperatingSystemVersion buildSDK(VersionTarget target = ApplicationBinary);
|
|
||||||
static QOperatingSystemVersion deploymentTarget(VersionTarget target = ApplicationBinary);
|
|
||||||
static QOperatingSystemVersion currentRuntime();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QMacVersion() = default;
|
|
||||||
using VersionTuple = QPair<QOperatingSystemVersion, QOperatingSystemVersion>;
|
|
||||||
static VersionTuple versionsForImage(const mach_header *machHeader);
|
|
||||||
static VersionTuple applicationVersion();
|
|
||||||
static VersionTuple libraryVersion();
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // QCORE_MAC_P_H
|
|
@ -1,421 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2018 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) 2007-2008, Apple, Inc.
|
|
||||||
**
|
|
||||||
** All rights reserved.
|
|
||||||
**
|
|
||||||
** Redistribution and use in source and binary forms, with or without
|
|
||||||
** modification, are permitted provided that the following conditions are met:
|
|
||||||
**
|
|
||||||
** * Redistributions of source code must retain the above copyright notice,
|
|
||||||
** this list of conditions and the following disclaimer.
|
|
||||||
**
|
|
||||||
** * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
** this list of conditions and the following disclaimer in the documentation
|
|
||||||
** and/or other materials provided with the distribution.
|
|
||||||
**
|
|
||||||
** * Neither the name of Apple, Inc. nor the names of its contributors
|
|
||||||
** may be used to endorse or promote products derived from this software
|
|
||||||
** without specific prior written permission.
|
|
||||||
**
|
|
||||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
**
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
#import "qcocoaapplicationdelegate.h"
|
|
||||||
#include "qcocoaintegration.h"
|
|
||||||
#include "qcocoamenu.h"
|
|
||||||
#include "qcocoamenuloader.h"
|
|
||||||
#include "qcocoamenuitem.h"
|
|
||||||
#include "qcocoansmenu.h"
|
|
||||||
|
|
||||||
#include <qevent.h>
|
|
||||||
#include <qurl.h>
|
|
||||||
#include <qdebug.h>
|
|
||||||
#include <qguiapplication.h>
|
|
||||||
#include <private/qguiapplication_p.h>
|
|
||||||
#include "qt_mac_p.h"
|
|
||||||
#include <qpa/qwindowsysteminterface.h>
|
|
||||||
#include <qwindowdefs.h>
|
|
||||||
|
|
||||||
QT_USE_NAMESPACE
|
|
||||||
|
|
||||||
@implementation QCocoaApplicationDelegate {
|
|
||||||
bool startedQuit;
|
|
||||||
NSObject <NSApplicationDelegate> *reflectionDelegate;
|
|
||||||
bool inLaunch;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (instancetype)sharedDelegate
|
|
||||||
{
|
|
||||||
static QCocoaApplicationDelegate *shared = nil;
|
|
||||||
static dispatch_once_t onceToken;
|
|
||||||
dispatch_once(&onceToken, ^{
|
|
||||||
shared = [[self alloc] init];
|
|
||||||
atexit_b(^{
|
|
||||||
[shared release];
|
|
||||||
shared = nil;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return shared;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)init
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
if (self) {
|
|
||||||
inLaunch = true;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
[_dockMenu release];
|
|
||||||
if (reflectionDelegate) {
|
|
||||||
[[NSApplication sharedApplication] setDelegate:reflectionDelegate];
|
|
||||||
[reflectionDelegate release];
|
|
||||||
}
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
||||||
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSMenu *)applicationDockMenu:(NSApplication *)sender
|
|
||||||
{
|
|
||||||
Q_UNUSED(sender);
|
|
||||||
// Manually invoke the delegate's -menuWillOpen: method.
|
|
||||||
// See QTBUG-39604 (and its fix) for details.
|
|
||||||
[self.dockMenu.delegate menuWillOpen:self.dockMenu];
|
|
||||||
return [[self.dockMenu retain] autorelease];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canQuit
|
|
||||||
{
|
|
||||||
[[NSApp mainMenu] cancelTracking];
|
|
||||||
|
|
||||||
bool handle_quit = true;
|
|
||||||
NSMenuItem *quitMenuItem = [[QT_MANGLE_NAMESPACE(QCocoaMenuLoader) sharedMenuLoader] quitMenuItem];
|
|
||||||
if (!QGuiApplicationPrivate::instance()->modalWindowList.isEmpty()
|
|
||||||
&& [quitMenuItem isEnabled]) {
|
|
||||||
int visible = 0;
|
|
||||||
const QWindowList tlws = QGuiApplication::topLevelWindows();
|
|
||||||
for (int i = 0; i < tlws.size(); ++i) {
|
|
||||||
if (tlws.at(i)->isVisible())
|
|
||||||
++visible;
|
|
||||||
}
|
|
||||||
handle_quit = (visible <= 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle_quit) {
|
|
||||||
QCloseEvent ev;
|
|
||||||
QGuiApplication::sendEvent(qGuiApp, &ev);
|
|
||||||
if (ev.isAccepted()) {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function will only be called when NSApp is actually running.
|
|
||||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
|
||||||
{
|
|
||||||
// The reflection delegate gets precedence
|
|
||||||
if (reflectionDelegate) {
|
|
||||||
if ([reflectionDelegate respondsToSelector:@selector(applicationShouldTerminate:)])
|
|
||||||
return [reflectionDelegate applicationShouldTerminate:sender];
|
|
||||||
return NSTerminateNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([self canQuit]) {
|
|
||||||
if (!startedQuit) {
|
|
||||||
startedQuit = true;
|
|
||||||
// Close open windows. This is done in order to deliver de-expose
|
|
||||||
// events while the event loop is still running.
|
|
||||||
const QWindowList topLevels = QGuiApplication::topLevelWindows();
|
|
||||||
for (int i = 0; i < topLevels.size(); ++i) {
|
|
||||||
QWindow *topLevelWindow = topLevels.at(i);
|
|
||||||
// Already closed windows will not have a platform window, skip those
|
|
||||||
if (topLevelWindow->handle())
|
|
||||||
QWindowSystemInterface::handleCloseEvent(topLevelWindow);
|
|
||||||
}
|
|
||||||
QWindowSystemInterface::flushWindowSystemEvents();
|
|
||||||
|
|
||||||
QGuiApplication::exit(0);
|
|
||||||
startedQuit = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (QGuiApplicationPrivate::instance()->threadData->eventLoops.isEmpty()) {
|
|
||||||
// INVARIANT: No event loop is executing. This probably
|
|
||||||
// means that Qt is used as a plugin, or as a part of a native
|
|
||||||
// Cocoa application. In any case it should be fine to
|
|
||||||
// terminate now:
|
|
||||||
return NSTerminateNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NSTerminateCancel;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationWillFinishLaunching:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
Q_UNUSED(notification);
|
|
||||||
|
|
||||||
/*
|
|
||||||
From the Cocoa documentation: "A good place to install event handlers
|
|
||||||
is in the applicationWillFinishLaunching: method of the application
|
|
||||||
delegate. At that point, the Application Kit has installed its default
|
|
||||||
event handlers, so if you install a handler for one of the same events,
|
|
||||||
it will replace the Application Kit version."
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
If Qt is used as a plugin, we let the 3rd party application handle
|
|
||||||
events like quit and open file events. Otherwise, if we install our own
|
|
||||||
handlers, we easily end up breaking functionality the 3rd party
|
|
||||||
application depends on.
|
|
||||||
*/
|
|
||||||
NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
|
|
||||||
|
|
||||||
[eventManager setEventHandler:self
|
|
||||||
andSelector:@selector(getUrl:withReplyEvent:)
|
|
||||||
forEventClass:kInternetEventClass
|
|
||||||
andEventID:kAEGetURL];
|
|
||||||
}
|
|
||||||
|
|
||||||
// called by QCocoaIntegration's destructor before resetting the application delegate to nil
|
|
||||||
- (void)removeAppleEventHandlers
|
|
||||||
{
|
|
||||||
NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
|
|
||||||
|
|
||||||
[eventManager removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (bool)inLaunch
|
|
||||||
{
|
|
||||||
return inLaunch;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
|
||||||
{
|
|
||||||
Q_UNUSED(aNotification);
|
|
||||||
inLaunch = false;
|
|
||||||
|
|
||||||
if (qEnvironmentVariableIsEmpty("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM")) {
|
|
||||||
// Move the application window to front to avoid launching behind the terminal.
|
|
||||||
// Ignoring other apps is necessary (we must ignore the terminal), but makes
|
|
||||||
// Qt apps play slightly less nice with other apps when lanching from Finder
|
|
||||||
// (See the activateIgnoringOtherApps docs.)
|
|
||||||
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
|
|
||||||
{
|
|
||||||
Q_UNUSED(filenames);
|
|
||||||
Q_UNUSED(sender);
|
|
||||||
|
|
||||||
for (NSString *fileName in filenames) {
|
|
||||||
QString qtFileName = QString::fromNSString(fileName);
|
|
||||||
if (inLaunch) {
|
|
||||||
// We need to be careful because Cocoa will be nice enough to take
|
|
||||||
// command line arguments and send them to us as events. Given the history
|
|
||||||
// of Qt Applications, this will result in behavior people don't want, as
|
|
||||||
// they might be doing the opening themselves with the command line parsing.
|
|
||||||
if (qApp->arguments().contains(qtFileName))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QWindowSystemInterface::handleFileOpenEvent(qtFileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reflectionDelegate &&
|
|
||||||
[reflectionDelegate respondsToSelector:@selector(application:openFiles:)])
|
|
||||||
[reflectionDelegate application:sender openFiles:filenames];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
|
|
||||||
{
|
|
||||||
// If we have a reflection delegate, that will get to call the shots.
|
|
||||||
if (reflectionDelegate
|
|
||||||
&& [reflectionDelegate respondsToSelector:
|
|
||||||
@selector(applicationShouldTerminateAfterLastWindowClosed:)])
|
|
||||||
return [reflectionDelegate applicationShouldTerminateAfterLastWindowClosed:sender];
|
|
||||||
return NO; // Someday qApp->quitOnLastWindowClosed(); when QApp and NSApp work closer together.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationDidBecomeActive:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
if (reflectionDelegate
|
|
||||||
&& [reflectionDelegate respondsToSelector:@selector(applicationDidBecomeActive:)])
|
|
||||||
[reflectionDelegate applicationDidBecomeActive:notification];
|
|
||||||
|
|
||||||
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationDidResignActive:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
if (reflectionDelegate
|
|
||||||
&& [reflectionDelegate respondsToSelector:@selector(applicationDidResignActive:)])
|
|
||||||
[reflectionDelegate applicationDidResignActive:notification];
|
|
||||||
|
|
||||||
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
|
|
||||||
{
|
|
||||||
Q_UNUSED(theApplication);
|
|
||||||
Q_UNUSED(flag);
|
|
||||||
if (reflectionDelegate
|
|
||||||
&& [reflectionDelegate respondsToSelector:@selector(applicationShouldHandleReopen:hasVisibleWindows:)])
|
|
||||||
return [reflectionDelegate applicationShouldHandleReopen:theApplication hasVisibleWindows:flag];
|
|
||||||
|
|
||||||
/*
|
|
||||||
true to force delivery of the event even if the application state is already active,
|
|
||||||
because rapp (handle reopen) events are sent each time the dock icon is clicked regardless
|
|
||||||
of the active state of the application or number of visible windows. For example, a browser
|
|
||||||
app that has no windows opened would need the event be to delivered even if it was already
|
|
||||||
active in order to create a new window as per OS X conventions.
|
|
||||||
*/
|
|
||||||
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive, true /*forcePropagate*/);
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate
|
|
||||||
{
|
|
||||||
[oldDelegate retain];
|
|
||||||
[reflectionDelegate release];
|
|
||||||
reflectionDelegate = oldDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
|
|
||||||
{
|
|
||||||
NSMethodSignature *result = [super methodSignatureForSelector:aSelector];
|
|
||||||
if (!result && reflectionDelegate) {
|
|
||||||
result = [reflectionDelegate methodSignatureForSelector:aSelector];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)respondsToSelector:(SEL)aSelector
|
|
||||||
{
|
|
||||||
BOOL result = [super respondsToSelector:aSelector];
|
|
||||||
if (!result && reflectionDelegate)
|
|
||||||
result = [reflectionDelegate respondsToSelector:aSelector];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)forwardInvocation:(NSInvocation *)invocation
|
|
||||||
{
|
|
||||||
SEL invocationSelector = [invocation selector];
|
|
||||||
if (reflectionDelegate && [reflectionDelegate respondsToSelector:invocationSelector])
|
|
||||||
[invocation invokeWithTarget:reflectionDelegate];
|
|
||||||
else
|
|
||||||
[self doesNotRecognizeSelector:invocationSelector];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
|
|
||||||
{
|
|
||||||
Q_UNUSED(replyEvent);
|
|
||||||
NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
|
|
||||||
QWindowSystemInterface::handleFileOpenEvent(QUrl(QString::fromNSString(urlString)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation QCocoaApplicationDelegate (Menus)
|
|
||||||
|
|
||||||
- (BOOL)validateMenuItem:(NSMenuItem*)item
|
|
||||||
{
|
|
||||||
auto *nativeItem = qt_objc_cast<QCocoaNSMenuItem *>(item);
|
|
||||||
if (!nativeItem)
|
|
||||||
return item.enabled; // FIXME Test with with Qt as plugin or embedded QWindow.
|
|
||||||
|
|
||||||
auto *platformItem = nativeItem.platformMenuItem;
|
|
||||||
if (!platformItem) // Try a bit harder with orphan menu itens
|
|
||||||
return item.hasSubmenu || (item.enabled && (item.action != @selector(qt_itemFired:)));
|
|
||||||
|
|
||||||
// Menu-holding items are always enabled, as it's conventional in Cocoa
|
|
||||||
if (platformItem->menu())
|
|
||||||
return YES;
|
|
||||||
|
|
||||||
return platformItem->isEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation QCocoaApplicationDelegate (MenuAPI)
|
|
||||||
|
|
||||||
- (void)qt_itemFired:(QCocoaNSMenuItem *)item
|
|
||||||
{
|
|
||||||
if (item.hasSubmenu)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto *nativeItem = qt_objc_cast<QCocoaNSMenuItem *>(item);
|
|
||||||
Q_ASSERT_X(nativeItem, qPrintable(__FUNCTION__), "Triggered menu item is not a QCocoaNSMenuItem.");
|
|
||||||
auto *platformItem = nativeItem.platformMenuItem;
|
|
||||||
// Menu-holding items also get a target to play nicely
|
|
||||||
// with NSMenuValidation but should not trigger.
|
|
||||||
if (!platformItem || platformItem->menu())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData);
|
|
||||||
QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers:[NSEvent modifierFlags]];
|
|
||||||
|
|
||||||
static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated);
|
|
||||||
activatedSignal.invoke(platformItem, Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -1,357 +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 QCOCOAHELPERS_H
|
|
||||||
#define QCOCOAHELPERS_H
|
|
||||||
|
|
||||||
//
|
|
||||||
// W A R N I N G
|
|
||||||
// -------------
|
|
||||||
//
|
|
||||||
// This file is not part of the Qt API. It provides helper functions
|
|
||||||
// for the Cocoa lighthouse plugin. This header file may
|
|
||||||
// change from version to version without notice, or even be removed.
|
|
||||||
//
|
|
||||||
// We mean it.
|
|
||||||
//
|
|
||||||
#include "qt_mac_p.h"
|
|
||||||
#include <private/qguiapplication_p.h>
|
|
||||||
#include <QtCore/qoperatingsystemversion.h>
|
|
||||||
#include <QtGui/qpalette.h>
|
|
||||||
#include <QtGui/qscreen.h>
|
|
||||||
|
|
||||||
#include <objc/runtime.h>
|
|
||||||
#include <objc/message.h>
|
|
||||||
|
|
||||||
Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSView));
|
|
||||||
|
|
||||||
struct mach_header;
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow)
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaDrawing)
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaMouse)
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
|
|
||||||
|
|
||||||
class QPixmap;
|
|
||||||
class QString;
|
|
||||||
|
|
||||||
// Conversion functions
|
|
||||||
QStringList qt_mac_NSArrayToQStringList(NSArray<NSString *> *nsarray);
|
|
||||||
NSMutableArray<NSString *> *qt_mac_QStringListToNSMutableArray(const QStringList &list);
|
|
||||||
|
|
||||||
NSDragOperation qt_mac_mapDropAction(Qt::DropAction action);
|
|
||||||
NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions);
|
|
||||||
Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions);
|
|
||||||
Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
typename std::enable_if<std::is_pointer<T>::value, T>::type
|
|
||||||
qt_objc_cast(id object)
|
|
||||||
{
|
|
||||||
if ([object isKindOfClass:[typename std::remove_pointer<T>::type class]])
|
|
||||||
return static_cast<T>(object);
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
QT_MANGLE_NAMESPACE(QNSView) *qnsview_cast(NSView *view);
|
|
||||||
|
|
||||||
// Misc
|
|
||||||
void qt_mac_transformProccessToForegroundApplication();
|
|
||||||
QString qt_mac_applicationName();
|
|
||||||
|
|
||||||
QPointF qt_mac_flip(const QPointF &pos, const QRectF &reference);
|
|
||||||
QRectF qt_mac_flip(const QRectF &rect, const QRectF &reference);
|
|
||||||
|
|
||||||
Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum);
|
|
||||||
Qt::MouseButton cocoaButton2QtButton(NSEvent *event);
|
|
||||||
|
|
||||||
QEvent::Type cocoaEvent2QtMouseEvent(NSEvent *event);
|
|
||||||
|
|
||||||
Qt::MouseButtons cocoaMouseButtons2QtMouseButtons(NSInteger pressedMouseButtons);
|
|
||||||
Qt::MouseButtons currentlyPressedMouseButtons();
|
|
||||||
|
|
||||||
// strip out '&' characters, and convert "&&" to a single '&', in menu
|
|
||||||
// text - since menu text is sometimes decorated with these for Windows
|
|
||||||
// accelerators.
|
|
||||||
QString qt_mac_removeAmpersandEscapes(QString s);
|
|
||||||
|
|
||||||
enum {
|
|
||||||
QtCocoaEventSubTypeWakeup = SHRT_MAX,
|
|
||||||
QtCocoaEventSubTypePostMessage = SHRT_MAX-1
|
|
||||||
};
|
|
||||||
|
|
||||||
class QCocoaPostMessageArgs {
|
|
||||||
public:
|
|
||||||
id target;
|
|
||||||
SEL selector;
|
|
||||||
int argCount;
|
|
||||||
id arg1;
|
|
||||||
id arg2;
|
|
||||||
QCocoaPostMessageArgs(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0)
|
|
||||||
: target(target), selector(selector), argCount(argCount), arg1(arg1), arg2(arg2)
|
|
||||||
{
|
|
||||||
[target retain];
|
|
||||||
[arg1 retain];
|
|
||||||
[arg2 retain];
|
|
||||||
}
|
|
||||||
|
|
||||||
~QCocoaPostMessageArgs()
|
|
||||||
{
|
|
||||||
[arg2 release];
|
|
||||||
[arg1 release];
|
|
||||||
[target release];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T qt_mac_resolveOption(const T &fallback, const QByteArray &environment)
|
|
||||||
{
|
|
||||||
// check for environment variable
|
|
||||||
if (!environment.isEmpty()) {
|
|
||||||
QByteArray env = qgetenv(environment);
|
|
||||||
if (!env.isEmpty())
|
|
||||||
return T(env.toInt()); // works when T is bool, int.
|
|
||||||
}
|
|
||||||
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T qt_mac_resolveOption(const T &fallback, QWindow *window, const QByteArray &property, const QByteArray &environment)
|
|
||||||
{
|
|
||||||
// check for environment variable
|
|
||||||
if (!environment.isEmpty()) {
|
|
||||||
QByteArray env = qgetenv(environment);
|
|
||||||
if (!env.isEmpty())
|
|
||||||
return T(env.toInt()); // works when T is bool, int.
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for window property
|
|
||||||
if (window && !property.isNull()) {
|
|
||||||
QVariant windowProperty = window->property(property);
|
|
||||||
if (windowProperty.isValid())
|
|
||||||
return windowProperty.value<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// return default value.
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/52722575/2761869
|
|
||||||
template<class R>
|
|
||||||
struct backwards_t {
|
|
||||||
R r;
|
|
||||||
constexpr auto begin() const { using std::rbegin; return rbegin(r); }
|
|
||||||
constexpr auto begin() { using std::rbegin; return rbegin(r); }
|
|
||||||
constexpr auto end() const { using std::rend; return rend(r); }
|
|
||||||
constexpr auto end() { using std::rend; return rend(r); }
|
|
||||||
};
|
|
||||||
template<class R>
|
|
||||||
constexpr backwards_t<R> backwards(R&& r) { return {std::forward<R>(r)}; }
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
// @compatibility_alias doesn't work with protocols
|
|
||||||
#define QNSPanelDelegate QT_MANGLE_NAMESPACE(QNSPanelDelegate)
|
|
||||||
|
|
||||||
@protocol QNSPanelDelegate
|
|
||||||
@required
|
|
||||||
- (void)onOkClicked;
|
|
||||||
- (void)onCancelClicked;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface QT_MANGLE_NAMESPACE(QNSPanelContentsWrapper) : NSView
|
|
||||||
|
|
||||||
@property (nonatomic, readonly) NSButton *okButton;
|
|
||||||
@property (nonatomic, readonly) NSButton *cancelButton;
|
|
||||||
@property (nonatomic, readonly) NSView *panelContents; // ARC: unretained, make it weak
|
|
||||||
@property (nonatomic, assign) NSEdgeInsets panelContentsMargins;
|
|
||||||
|
|
||||||
- (instancetype)initWithPanelDelegate:(id<QNSPanelDelegate>)panelDelegate;
|
|
||||||
- (void)dealloc;
|
|
||||||
|
|
||||||
- (NSButton *)createButtonWithTitle:(const char *)title;
|
|
||||||
- (void)layout;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanelContentsWrapper);
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// QAppleRefCounted expects the retain function to return the object
|
|
||||||
io_object_t q_IOObjectRetain(io_object_t obj);
|
|
||||||
// QAppleRefCounted expects the release function to return void
|
|
||||||
void q_IOObjectRelease(io_object_t obj);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class QIOType : public QAppleRefCounted<T, io_object_t, q_IOObjectRetain, q_IOObjectRelease>
|
|
||||||
{
|
|
||||||
using QAppleRefCounted<T, io_object_t, q_IOObjectRetain, q_IOObjectRelease>::QAppleRefCounted;
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Depending on the ABI of the platform, we may need to use objc_msgSendSuper_stret:
|
|
||||||
// - http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
|
|
||||||
// - https://lists.apple.com/archives/cocoa-dev/2008/Feb/msg02338.html
|
|
||||||
template <typename T>
|
|
||||||
struct objc_msgsend_requires_stret
|
|
||||||
{ static const bool value =
|
|
||||||
#if defined(Q_PROCESSOR_X86)
|
|
||||||
#define PLATFORM_USES_SEND_SUPER_STRET 1
|
|
||||||
// Any return value larger than two registers on i386/x86_64
|
|
||||||
sizeof(T) > sizeof(void*) * 2;
|
|
||||||
#elif defined(Q_PROCESSOR_ARM_32)
|
|
||||||
#define PLATFORM_USES_SEND_SUPER_STRET 1
|
|
||||||
// Any return value larger than a single register on arm
|
|
||||||
sizeof(T) > sizeof(void*);
|
|
||||||
#elif defined(Q_PROCESSOR_ARM_64)
|
|
||||||
#define PLATFORM_USES_SEND_SUPER_STRET 0
|
|
||||||
// Stret not used on arm64
|
|
||||||
false;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct objc_msgsend_requires_stret<void>
|
|
||||||
{ static const bool value = false; };
|
|
||||||
|
|
||||||
template <typename ReturnType, typename... Args>
|
|
||||||
ReturnType qt_msgSendSuper(id receiver, SEL selector, Args... args)
|
|
||||||
{
|
|
||||||
static_assert(!objc_msgsend_requires_stret<ReturnType>::value,
|
|
||||||
"The given return type requires stret on this platform");
|
|
||||||
|
|
||||||
typedef ReturnType (*SuperFn)(objc_super *, SEL, Args...);
|
|
||||||
SuperFn superFn = reinterpret_cast<SuperFn>(objc_msgSendSuper);
|
|
||||||
objc_super sup = { receiver, [receiver superclass] };
|
|
||||||
return superFn(&sup, selector, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if PLATFORM_USES_SEND_SUPER_STRET
|
|
||||||
template <typename ReturnType, typename... Args>
|
|
||||||
ReturnType qt_msgSendSuper_stret(id receiver, SEL selector, Args... args)
|
|
||||||
{
|
|
||||||
static_assert(objc_msgsend_requires_stret<ReturnType>::value,
|
|
||||||
"The given return type does not use stret on this platform");
|
|
||||||
|
|
||||||
typedef void (*SuperStretFn)(ReturnType *, objc_super *, SEL, Args...);
|
|
||||||
SuperStretFn superStretFn = reinterpret_cast<SuperStretFn>(objc_msgSendSuper_stret);
|
|
||||||
|
|
||||||
objc_super sup = { receiver, [receiver superclass] };
|
|
||||||
ReturnType ret;
|
|
||||||
superStretFn(&ret, &sup, selector, args...);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
class QSendSuperHelper {
|
|
||||||
public:
|
|
||||||
QSendSuperHelper(id receiver, SEL sel, Args... args)
|
|
||||||
: m_receiver(receiver), m_selector(sel), m_args(std::make_tuple(args...)), m_sent(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~QSendSuperHelper()
|
|
||||||
{
|
|
||||||
if (!m_sent)
|
|
||||||
msgSendSuper<void>(m_args);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ReturnType>
|
|
||||||
operator ReturnType()
|
|
||||||
{
|
|
||||||
#if defined(QT_DEBUG)
|
|
||||||
Method method = class_getInstanceMethod(object_getClass(m_receiver), m_selector);
|
|
||||||
char returnTypeEncoding[256];
|
|
||||||
method_getReturnType(method, returnTypeEncoding, sizeof(returnTypeEncoding));
|
|
||||||
NSUInteger alignedReturnTypeSize = 0;
|
|
||||||
NSGetSizeAndAlignment(returnTypeEncoding, nullptr, &alignedReturnTypeSize);
|
|
||||||
Q_ASSERT(alignedReturnTypeSize == sizeof(ReturnType));
|
|
||||||
#endif
|
|
||||||
m_sent = true;
|
|
||||||
return msgSendSuper<ReturnType>(m_args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename ReturnType, bool V>
|
|
||||||
using if_requires_stret = typename std::enable_if<objc_msgsend_requires_stret<ReturnType>::value == V, ReturnType>::type;
|
|
||||||
|
|
||||||
template <typename ReturnType, int... Is>
|
|
||||||
if_requires_stret<ReturnType, false> msgSendSuper(std::tuple<Args...>& args, QtPrivate::IndexesList<Is...>)
|
|
||||||
{
|
|
||||||
return qt_msgSendSuper<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if PLATFORM_USES_SEND_SUPER_STRET
|
|
||||||
template <typename ReturnType, int... Is>
|
|
||||||
if_requires_stret<ReturnType, true> msgSendSuper(std::tuple<Args...>& args, QtPrivate::IndexesList<Is...>)
|
|
||||||
{
|
|
||||||
return qt_msgSendSuper_stret<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename ReturnType>
|
|
||||||
ReturnType msgSendSuper(std::tuple<Args...>& args)
|
|
||||||
{
|
|
||||||
return msgSendSuper<ReturnType>(args, QtPrivate::makeIndexSequence<sizeof...(Args)>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
id m_receiver;
|
|
||||||
SEL m_selector;
|
|
||||||
std::tuple<Args...> m_args;
|
|
||||||
bool m_sent;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
QSendSuperHelper<Args...> qt_objcDynamicSuperHelper(id receiver, SEL selector, Args... args)
|
|
||||||
{
|
|
||||||
return QSendSuperHelper<Args...>(receiver, selector, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same as calling super, but the super_class field resolved at runtime instead of compile time
|
|
||||||
#define qt_objcDynamicSuper(...) qt_objcDynamicSuperHelper(self, _cmd, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#endif //QCOCOAHELPERS_H
|
|
||||||
|
|
@ -1,512 +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 <qpa/qplatformtheme.h>
|
|
||||||
|
|
||||||
#include "qcocoahelpers.h"
|
|
||||||
#include "qnsview.h"
|
|
||||||
|
|
||||||
#include <QtCore>
|
|
||||||
#include <QtGui>
|
|
||||||
#include <qpa/qplatformscreen.h>
|
|
||||||
#include <private/qguiapplication_p.h>
|
|
||||||
#include <private/qwindow_p.h>
|
|
||||||
#include <QtGui/private/qcoregraphics_p.h>
|
|
||||||
|
|
||||||
#ifndef QT_NO_WIDGETS
|
|
||||||
#include <QtWidgets/QWidget>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
|
|
||||||
Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing");
|
|
||||||
Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg);
|
|
||||||
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen", QtCriticalMsg);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Conversion Functions
|
|
||||||
//
|
|
||||||
|
|
||||||
QStringList qt_mac_NSArrayToQStringList(NSArray<NSString *> *array)
|
|
||||||
{
|
|
||||||
QStringList result;
|
|
||||||
for (NSString *string in array)
|
|
||||||
result << QString::fromNSString(string);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableArray<NSString *> *qt_mac_QStringListToNSMutableArray(const QStringList &list)
|
|
||||||
{
|
|
||||||
NSMutableArray<NSString *> *result = [NSMutableArray<NSString *> arrayWithCapacity:list.size()];
|
|
||||||
for (const QString &string : list)
|
|
||||||
[result addObject:string.toNSString()];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dndenum_mapper
|
|
||||||
{
|
|
||||||
NSDragOperation mac_code;
|
|
||||||
Qt::DropAction qt_code;
|
|
||||||
bool Qt2Mac;
|
|
||||||
};
|
|
||||||
|
|
||||||
static dndenum_mapper dnd_enums[] = {
|
|
||||||
{ NSDragOperationLink, Qt::LinkAction, true },
|
|
||||||
{ NSDragOperationMove, Qt::MoveAction, true },
|
|
||||||
{ NSDragOperationDelete, Qt::MoveAction, true },
|
|
||||||
{ NSDragOperationCopy, Qt::CopyAction, true },
|
|
||||||
{ NSDragOperationGeneric, Qt::CopyAction, false },
|
|
||||||
{ NSDragOperationEvery, Qt::ActionMask, false },
|
|
||||||
{ NSDragOperationNone, Qt::IgnoreAction, false }
|
|
||||||
};
|
|
||||||
|
|
||||||
NSDragOperation qt_mac_mapDropAction(Qt::DropAction action)
|
|
||||||
{
|
|
||||||
for (int i=0; dnd_enums[i].qt_code; i++) {
|
|
||||||
if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) {
|
|
||||||
return dnd_enums[i].mac_code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NSDragOperationNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions)
|
|
||||||
{
|
|
||||||
NSDragOperation nsActions = NSDragOperationNone;
|
|
||||||
for (int i=0; dnd_enums[i].qt_code; i++) {
|
|
||||||
if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code))
|
|
||||||
nsActions |= dnd_enums[i].mac_code;
|
|
||||||
}
|
|
||||||
return nsActions;
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions)
|
|
||||||
{
|
|
||||||
Qt::DropAction action = Qt::IgnoreAction;
|
|
||||||
for (int i=0; dnd_enums[i].mac_code; i++) {
|
|
||||||
if (nsActions & dnd_enums[i].mac_code)
|
|
||||||
return dnd_enums[i].qt_code;
|
|
||||||
}
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
|
|
||||||
{
|
|
||||||
Qt::DropActions actions = Qt::IgnoreAction;
|
|
||||||
|
|
||||||
for (int i=0; dnd_enums[i].mac_code; i++) {
|
|
||||||
if (dnd_enums[i].mac_code == NSDragOperationEvery)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (nsActions & dnd_enums[i].mac_code)
|
|
||||||
actions |= dnd_enums[i].qt_code;
|
|
||||||
}
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Returns the view cast to a QNSview if possible.
|
|
||||||
|
|
||||||
If the view is not a QNSView, nil is returned, which is safe to
|
|
||||||
send messages to, effectivly making [qnsview_cast(view) message]
|
|
||||||
a no-op.
|
|
||||||
|
|
||||||
For extra verbosity and clearer code, please consider checking
|
|
||||||
that the platform window is not a foreign window before using
|
|
||||||
this cast, via QPlatformWindow::isForeignWindow().
|
|
||||||
|
|
||||||
Do not use this method soley to check for foreign windows, as
|
|
||||||
that will make the code harder to read for people not working
|
|
||||||
primarily on macOS, who do not know the difference between the
|
|
||||||
NSView and QNSView cases.
|
|
||||||
*/
|
|
||||||
QNSView *qnsview_cast(NSView *view)
|
|
||||||
{
|
|
||||||
return qt_objc_cast<QNSView *>(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Misc
|
|
||||||
//
|
|
||||||
|
|
||||||
// Sets the activation policy for this process to NSApplicationActivationPolicyRegular,
|
|
||||||
// unless either LSUIElement or LSBackgroundOnly is set in the Info.plist.
|
|
||||||
void qt_mac_transformProccessToForegroundApplication()
|
|
||||||
{
|
|
||||||
bool forceTransform = true;
|
|
||||||
CFTypeRef value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
|
|
||||||
CFSTR("LSUIElement"));
|
|
||||||
if (value) {
|
|
||||||
CFTypeID valueType = CFGetTypeID(value);
|
|
||||||
// Officially it's supposed to be a string, a boolean makes sense, so we'll check.
|
|
||||||
// A number less so, but OK.
|
|
||||||
if (valueType == CFStringGetTypeID())
|
|
||||||
forceTransform = !(QString::fromCFString(static_cast<CFStringRef>(value)).toInt());
|
|
||||||
else if (valueType == CFBooleanGetTypeID())
|
|
||||||
forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
|
|
||||||
else if (valueType == CFNumberGetTypeID()) {
|
|
||||||
int valueAsInt;
|
|
||||||
CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
|
|
||||||
forceTransform = !valueAsInt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceTransform) {
|
|
||||||
value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(),
|
|
||||||
CFSTR("LSBackgroundOnly"));
|
|
||||||
if (value) {
|
|
||||||
CFTypeID valueType = CFGetTypeID(value);
|
|
||||||
if (valueType == CFBooleanGetTypeID())
|
|
||||||
forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value));
|
|
||||||
else if (valueType == CFStringGetTypeID())
|
|
||||||
forceTransform = !(QString::fromCFString(static_cast<CFStringRef>(value)).toInt());
|
|
||||||
else if (valueType == CFNumberGetTypeID()) {
|
|
||||||
int valueAsInt;
|
|
||||||
CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt);
|
|
||||||
forceTransform = !valueAsInt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forceTransform) {
|
|
||||||
[[NSApplication sharedApplication] setActivationPolicy:NSApplicationActivationPolicyRegular];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString qt_mac_applicationName()
|
|
||||||
{
|
|
||||||
QString appName;
|
|
||||||
CFTypeRef string = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("CFBundleName"));
|
|
||||||
if (string)
|
|
||||||
appName = QString::fromCFString(static_cast<CFStringRef>(string));
|
|
||||||
|
|
||||||
if (appName.isEmpty()) {
|
|
||||||
QString arg0 = QGuiApplicationPrivate::instance()->appName();
|
|
||||||
if (arg0.contains("/")) {
|
|
||||||
QStringList parts = arg0.split(QLatin1Char('/'));
|
|
||||||
appName = parts.at(parts.count() - 1);
|
|
||||||
} else {
|
|
||||||
appName = arg0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return appName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn QPointF qt_mac_flip(const QPointF &pos, const QRectF &reference)
|
|
||||||
\fn QRectF qt_mac_flip(const QRectF &rect, const QRectF &reference)
|
|
||||||
|
|
||||||
Flips the Y coordinate of the point/rect between quadrant I and IV.
|
|
||||||
|
|
||||||
The native coordinate system on macOS uses quadrant I, with origin
|
|
||||||
in bottom left, and Qt uses quadrant IV, with origin in top left.
|
|
||||||
|
|
||||||
By flipping the Y coordinate, we can map the point/rect between
|
|
||||||
the two coordinate systems.
|
|
||||||
|
|
||||||
The flip is always in relation to a reference rectangle, e.g.
|
|
||||||
the frame of the parent view, or the screen geometry. In the
|
|
||||||
latter case the specialized QCocoaScreen::mapFrom/To functions
|
|
||||||
should be used instead.
|
|
||||||
*/
|
|
||||||
QPointF qt_mac_flip(const QPointF &pos, const QRectF &reference)
|
|
||||||
{
|
|
||||||
return QPointF(pos.x(), reference.height() - pos.y());
|
|
||||||
}
|
|
||||||
|
|
||||||
QRectF qt_mac_flip(const QRectF &rect, const QRectF &reference)
|
|
||||||
{
|
|
||||||
return QRectF(qt_mac_flip(rect.bottomLeft(), reference), rect.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
|
|
||||||
|
|
||||||
Returns the Qt::Button that corresponds to an NSEvent.buttonNumber.
|
|
||||||
|
|
||||||
\note AppKit will use buttonNumber 0 to indicate both "left button"
|
|
||||||
and "no button". Only NSEvents that describes mouse press/release
|
|
||||||
events (e.g NSEventTypeOtherMouseDown) will contain a valid
|
|
||||||
button number.
|
|
||||||
*/
|
|
||||||
Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
|
|
||||||
{
|
|
||||||
if (buttonNum >= 0 && buttonNum <= 31)
|
|
||||||
return Qt::MouseButton(1 << buttonNum);
|
|
||||||
return Qt::NoButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn Qt::MouseButton cocoaButton2QtButton(NSEvent *event)
|
|
||||||
|
|
||||||
Returns the Qt::Button that corresponds to an NSEvent.buttonNumber.
|
|
||||||
|
|
||||||
\note AppKit will use buttonNumber 0 to indicate both "left button"
|
|
||||||
and "no button". Only NSEvents that describes mouse press/release/dragging
|
|
||||||
events (e.g NSEventTypeOtherMouseDown) will contain a valid
|
|
||||||
button number.
|
|
||||||
|
|
||||||
\note Wacom tablet might not return the correct button number for NSEvent buttonNumber
|
|
||||||
on right clicks. Decide here that the button is the "right" button.
|
|
||||||
*/
|
|
||||||
Qt::MouseButton cocoaButton2QtButton(NSEvent *event)
|
|
||||||
{
|
|
||||||
if (cocoaEvent2QtMouseEvent(event) == QEvent::MouseMove)
|
|
||||||
return Qt::NoButton;
|
|
||||||
|
|
||||||
switch (event.type) {
|
|
||||||
case NSEventTypeRightMouseUp:
|
|
||||||
case NSEventTypeRightMouseDown:
|
|
||||||
return Qt::RightButton;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cocoaButton2QtButton(event.buttonNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn QEvent::Type cocoaEvent2QtMouseEvent(NSEvent *event)
|
|
||||||
|
|
||||||
Returns the QEvent::Type that corresponds to an NSEvent.type.
|
|
||||||
*/
|
|
||||||
QEvent::Type cocoaEvent2QtMouseEvent(NSEvent *event)
|
|
||||||
{
|
|
||||||
switch (event.type) {
|
|
||||||
case NSEventTypeLeftMouseDown:
|
|
||||||
case NSEventTypeRightMouseDown:
|
|
||||||
case NSEventTypeOtherMouseDown:
|
|
||||||
return QEvent::MouseButtonPress;
|
|
||||||
|
|
||||||
case NSEventTypeLeftMouseUp:
|
|
||||||
case NSEventTypeRightMouseUp:
|
|
||||||
case NSEventTypeOtherMouseUp:
|
|
||||||
return QEvent::MouseButtonRelease;
|
|
||||||
|
|
||||||
case NSEventTypeLeftMouseDragged:
|
|
||||||
case NSEventTypeRightMouseDragged:
|
|
||||||
case NSEventTypeOtherMouseDragged:
|
|
||||||
return QEvent::MouseMove;
|
|
||||||
|
|
||||||
case NSEventTypeMouseMoved:
|
|
||||||
return QEvent::MouseMove;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return QEvent::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn Qt::MouseButtons cocoaMouseButtons2QtMouseButtons(NSInteger pressedMouseButtons)
|
|
||||||
|
|
||||||
Returns the Qt::MouseButtons that corresponds to an NSEvent.pressedMouseButtons.
|
|
||||||
*/
|
|
||||||
Qt::MouseButtons cocoaMouseButtons2QtMouseButtons(NSInteger pressedMouseButtons)
|
|
||||||
{
|
|
||||||
return static_cast<Qt::MouseButton>(pressedMouseButtons & Qt::MouseButtonMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn Qt::MouseButtons currentlyPressedMouseButtons()
|
|
||||||
|
|
||||||
Returns the Qt::MouseButtons that corresponds to an NSEvent.pressedMouseButtons.
|
|
||||||
*/
|
|
||||||
Qt::MouseButtons currentlyPressedMouseButtons()
|
|
||||||
{
|
|
||||||
return cocoaMouseButtons2QtMouseButtons(NSEvent.pressedMouseButtons);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString qt_mac_removeAmpersandEscapes(QString s)
|
|
||||||
{
|
|
||||||
return QPlatformTheme::removeMnemonics(s).trimmed();
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
/*! \internal
|
|
||||||
|
|
||||||
This NSView derived class is used to add OK/Cancel
|
|
||||||
buttons to NSColorPanel and NSFontPanel. It replaces
|
|
||||||
the panel's content view, while reparenting the former
|
|
||||||
content view into itself. It also takes care of setting
|
|
||||||
the target-action for the OK/Cancel buttons and making
|
|
||||||
sure the layout is consistent.
|
|
||||||
*/
|
|
||||||
@implementation QNSPanelContentsWrapper {
|
|
||||||
NSButton *_okButton;
|
|
||||||
NSButton *_cancelButton;
|
|
||||||
NSView *_panelContents;
|
|
||||||
NSEdgeInsets _panelContentsMargins;
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize okButton = _okButton;
|
|
||||||
@synthesize cancelButton = _cancelButton;
|
|
||||||
@synthesize panelContents = _panelContents;
|
|
||||||
@synthesize panelContentsMargins = _panelContentsMargins;
|
|
||||||
|
|
||||||
- (instancetype)initWithPanelDelegate:(id<QNSPanelDelegate>)panelDelegate
|
|
||||||
{
|
|
||||||
if ((self = [super initWithFrame:NSZeroRect])) {
|
|
||||||
// create OK and Cancel buttons and add these as subviews
|
|
||||||
_okButton = [self createButtonWithTitle:"&OK"];
|
|
||||||
_okButton.action = @selector(onOkClicked);
|
|
||||||
_okButton.target = panelDelegate;
|
|
||||||
|
|
||||||
_cancelButton = [self createButtonWithTitle:"Cancel"];
|
|
||||||
_cancelButton.action = @selector(onCancelClicked);
|
|
||||||
_cancelButton.target = panelDelegate;
|
|
||||||
|
|
||||||
_panelContents = nil;
|
|
||||||
|
|
||||||
_panelContentsMargins = NSEdgeInsetsMake(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
[_okButton release];
|
|
||||||
_okButton = nil;
|
|
||||||
[_cancelButton release];
|
|
||||||
_cancelButton = nil;
|
|
||||||
|
|
||||||
_panelContents = nil;
|
|
||||||
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSButton *)createButtonWithTitle:(const char *)title
|
|
||||||
{
|
|
||||||
NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
|
|
||||||
button.buttonType = NSMomentaryLightButton;
|
|
||||||
button.bezelStyle = NSRoundedBezelStyle;
|
|
||||||
const QString &cleanTitle = QPlatformTheme::removeMnemonics(QCoreApplication::translate("QDialogButtonBox", title));
|
|
||||||
// FIXME: Not obvious, from Cocoa's documentation, that QString::toNSString() makes a deep copy
|
|
||||||
button.title = (NSString *)cleanTitle.toCFString();
|
|
||||||
((NSButtonCell *)button.cell).font =
|
|
||||||
[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSControlSizeRegular]];
|
|
||||||
[self addSubview:button];
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layout
|
|
||||||
{
|
|
||||||
static const CGFloat ButtonMinWidth = 78.0; // 84.0 for Carbon
|
|
||||||
static const CGFloat ButtonMinHeight = 32.0;
|
|
||||||
static const CGFloat ButtonSpacing = 0.0;
|
|
||||||
static const CGFloat ButtonTopMargin = 0.0;
|
|
||||||
static const CGFloat ButtonBottomMargin = 7.0;
|
|
||||||
static const CGFloat ButtonSideMargin = 9.0;
|
|
||||||
|
|
||||||
NSSize frameSize = self.frame.size;
|
|
||||||
|
|
||||||
[self.okButton sizeToFit];
|
|
||||||
NSSize okSizeHint = self.okButton.frame.size;
|
|
||||||
|
|
||||||
[self.cancelButton sizeToFit];
|
|
||||||
NSSize cancelSizeHint = self.cancelButton.frame.size;
|
|
||||||
|
|
||||||
const CGFloat buttonWidth = qMin(qMax(ButtonMinWidth,
|
|
||||||
qMax(okSizeHint.width, cancelSizeHint.width)),
|
|
||||||
CGFloat((frameSize.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5));
|
|
||||||
const CGFloat buttonHeight = qMax(ButtonMinHeight,
|
|
||||||
qMax(okSizeHint.height, cancelSizeHint.height));
|
|
||||||
|
|
||||||
NSRect okRect = { { frameSize.width - ButtonSideMargin - buttonWidth,
|
|
||||||
ButtonBottomMargin },
|
|
||||||
{ buttonWidth, buttonHeight } };
|
|
||||||
self.okButton.frame = okRect;
|
|
||||||
self.okButton.needsDisplay = YES;
|
|
||||||
|
|
||||||
NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - buttonWidth,
|
|
||||||
ButtonBottomMargin },
|
|
||||||
{ buttonWidth, buttonHeight } };
|
|
||||||
self.cancelButton.frame = cancelRect;
|
|
||||||
self.cancelButton.needsDisplay = YES;
|
|
||||||
|
|
||||||
// The third view should be the original panel contents. Cache it.
|
|
||||||
if (!self.panelContents)
|
|
||||||
for (NSView *view in self.subviews)
|
|
||||||
if (view != self.okButton && view != self.cancelButton) {
|
|
||||||
_panelContents = view;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CGFloat buttonBoxHeight = ButtonBottomMargin + buttonHeight + ButtonTopMargin;
|
|
||||||
const NSRect panelContentsFrame = NSMakeRect(
|
|
||||||
self.panelContentsMargins.left,
|
|
||||||
buttonBoxHeight + self.panelContentsMargins.bottom,
|
|
||||||
frameSize.width - (self.panelContentsMargins.left + self.panelContentsMargins.right),
|
|
||||||
frameSize.height - buttonBoxHeight - (self.panelContentsMargins.top + self.panelContentsMargins.bottom));
|
|
||||||
self.panelContents.frame = panelContentsFrame;
|
|
||||||
self.panelContents.needsDisplay = YES;
|
|
||||||
|
|
||||||
self.needsDisplay = YES;
|
|
||||||
[super layout];
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
io_object_t q_IOObjectRetain(io_object_t obj)
|
|
||||||
{
|
|
||||||
kern_return_t ret = IOObjectRetain(obj);
|
|
||||||
Q_ASSERT(!ret);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
void q_IOObjectRelease(io_object_t obj)
|
|
||||||
{
|
|
||||||
kern_return_t ret = IOObjectRelease(obj);
|
|
||||||
Q_ASSERT(!ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -1,142 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2017 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 QCOCOASCREEN_H
|
|
||||||
#define QCOCOASCREEN_H
|
|
||||||
|
|
||||||
#include <AppKit/AppKit.h>
|
|
||||||
|
|
||||||
#include "qcocoacursor.h"
|
|
||||||
|
|
||||||
#include <qpa/qplatformintegration.h>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
class QCocoaIntegration;
|
|
||||||
|
|
||||||
class QCocoaScreen : public QPlatformScreen
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
~QCocoaScreen();
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
|
||||||
// Virtual methods overridden from QPlatformScreen
|
|
||||||
QPixmap grabWindow(WId window, int x, int y, int width, int height) const override;
|
|
||||||
QRect geometry() const override { return m_geometry; }
|
|
||||||
QRect availableGeometry() const override { return m_availableGeometry; }
|
|
||||||
int depth() const override { return m_depth; }
|
|
||||||
QImage::Format format() const override { return m_format; }
|
|
||||||
qreal devicePixelRatio() const override { return m_devicePixelRatio; }
|
|
||||||
QSizeF physicalSize() const override { return m_physicalSize; }
|
|
||||||
QDpi logicalDpi() const override { return m_logicalDpi; }
|
|
||||||
qreal refreshRate() const override { return m_refreshRate; }
|
|
||||||
QString name() const override { return m_name; }
|
|
||||||
QPlatformCursor *cursor() const override { return m_cursor; }
|
|
||||||
QWindow *topLevelAt(const QPoint &point) const override;
|
|
||||||
QList<QPlatformScreen *> virtualSiblings() const override;
|
|
||||||
QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override;
|
|
||||||
|
|
||||||
// ----------------------------------------------------
|
|
||||||
|
|
||||||
NSScreen *nativeScreen() const;
|
|
||||||
|
|
||||||
void requestUpdate();
|
|
||||||
void deliverUpdateRequests();
|
|
||||||
bool isRunningDisplayLink() const;
|
|
||||||
|
|
||||||
static QCocoaScreen *primaryScreen();
|
|
||||||
static QCocoaScreen *get(NSScreen *nsScreen);
|
|
||||||
static QCocoaScreen *get(CGDirectDisplayID displayId);
|
|
||||||
static QCocoaScreen *get(CFUUIDRef uuid);
|
|
||||||
|
|
||||||
static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
|
|
||||||
static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
|
|
||||||
static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
|
|
||||||
static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void initializeScreens();
|
|
||||||
static void updateScreens();
|
|
||||||
static void cleanupScreens();
|
|
||||||
|
|
||||||
static bool updateScreensIfNeeded();
|
|
||||||
static NSArray *s_screenConfigurationBeforeUpdate;
|
|
||||||
|
|
||||||
static void add(CGDirectDisplayID displayId);
|
|
||||||
QCocoaScreen(CGDirectDisplayID displayId);
|
|
||||||
void update(CGDirectDisplayID displayId);
|
|
||||||
void remove();
|
|
||||||
|
|
||||||
bool isOnline() const;
|
|
||||||
bool isMirroring() const;
|
|
||||||
|
|
||||||
CGDirectDisplayID m_displayId = kCGNullDirectDisplay;
|
|
||||||
CGDirectDisplayID displayId() const { return m_displayId; }
|
|
||||||
|
|
||||||
QRect m_geometry;
|
|
||||||
QRect m_availableGeometry;
|
|
||||||
QDpi m_logicalDpi;
|
|
||||||
qreal m_refreshRate;
|
|
||||||
int m_depth;
|
|
||||||
QString m_name;
|
|
||||||
QImage::Format m_format;
|
|
||||||
QSizeF m_physicalSize;
|
|
||||||
QCocoaCursor *m_cursor;
|
|
||||||
qreal m_devicePixelRatio;
|
|
||||||
|
|
||||||
CVDisplayLinkRef m_displayLink = nullptr;
|
|
||||||
dispatch_source_t m_displayLinkSource = nullptr;
|
|
||||||
QAtomicInt m_pendingUpdates;
|
|
||||||
|
|
||||||
friend class QCocoaIntegration;
|
|
||||||
friend class QCocoaWindow;
|
|
||||||
friend QDebug operator<<(QDebug debug, const QCocoaScreen *screen);
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG_STREAM
|
|
||||||
QDebug operator<<(QDebug debug, const QCocoaScreen *screen);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
@interface NSScreen (QtExtras)
|
|
||||||
@property(readonly) CGDirectDisplayID qt_displayId;
|
|
||||||
@end
|
|
||||||
|
|
||||||
#endif // QCOCOASCREEN_H
|
|
@ -1,831 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2017 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 "qcocoascreen.h"
|
|
||||||
|
|
||||||
#include "qcocoawindow.h"
|
|
||||||
#include "qcocoahelpers.h"
|
|
||||||
#include "qcocoaintegration.h"
|
|
||||||
|
|
||||||
#include <QtCore/qcoreapplication.h>
|
|
||||||
#include <QtGui/private/qcoregraphics_p.h>
|
|
||||||
|
|
||||||
#include <IOKit/graphics/IOGraphicsLib.h>
|
|
||||||
|
|
||||||
#include <QtGui/private/qwindow_p.h>
|
|
||||||
|
|
||||||
#include <QtCore/private/qeventdispatcher_cf_p.h>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
namespace CoreGraphics {
|
|
||||||
Q_NAMESPACE
|
|
||||||
enum DisplayChange {
|
|
||||||
ReconfiguredWithFlagsMissing = 0,
|
|
||||||
Moved = kCGDisplayMovedFlag,
|
|
||||||
SetMain = kCGDisplaySetMainFlag,
|
|
||||||
SetMode = kCGDisplaySetModeFlag,
|
|
||||||
Added = kCGDisplayAddFlag,
|
|
||||||
Removed = kCGDisplayRemoveFlag,
|
|
||||||
Enabled = kCGDisplayEnabledFlag,
|
|
||||||
Disabled = kCGDisplayDisabledFlag,
|
|
||||||
Mirrored = kCGDisplayMirrorFlag,
|
|
||||||
UnMirrored = kCGDisplayUnMirrorFlag,
|
|
||||||
DesktopShapeChanged = kCGDisplayDesktopShapeChangedFlag
|
|
||||||
};
|
|
||||||
Q_ENUM_NS(DisplayChange)
|
|
||||||
}
|
|
||||||
|
|
||||||
NSArray *QCocoaScreen::s_screenConfigurationBeforeUpdate = nil;
|
|
||||||
|
|
||||||
void QCocoaScreen::initializeScreens()
|
|
||||||
{
|
|
||||||
updateScreens();
|
|
||||||
|
|
||||||
CGDisplayRegisterReconfigurationCallback([](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) {
|
|
||||||
Q_UNUSED(userInfo);
|
|
||||||
|
|
||||||
// Displays are reconfigured in batches, and we want to update our screens
|
|
||||||
// once a batch ends, so that all the states of the displays are up to date.
|
|
||||||
static int displayReconfigurationsInProgress = 0;
|
|
||||||
|
|
||||||
const bool beforeReconfigure = flags & kCGDisplayBeginConfigurationFlag;
|
|
||||||
qCDebug(lcQpaScreen).verbosity(0).nospace() << "Display " << displayId
|
|
||||||
<< (beforeReconfigure ? " about to reconfigure" : " was ")
|
|
||||||
<< QFlags<CoreGraphics::DisplayChange>(flags)
|
|
||||||
<< " with " << displayReconfigurationsInProgress
|
|
||||||
<< " display configuration(s) in progress";
|
|
||||||
|
|
||||||
if (!flags) {
|
|
||||||
// CGDisplayRegisterReconfigurationCallback has been observed to be called
|
|
||||||
// with flags unset. This seems like a bug. The callback is not paired with
|
|
||||||
// a matching "completion" callback either, so we don't know whether to treat
|
|
||||||
// it as a begin or end of reconfigure.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (beforeReconfigure) {
|
|
||||||
if (!displayReconfigurationsInProgress++) {
|
|
||||||
// There might have been a screen reconfigure before this that
|
|
||||||
// we didn't process yet, so do that now if that's the case.
|
|
||||||
updateScreensIfNeeded();
|
|
||||||
|
|
||||||
Q_ASSERT(!s_screenConfigurationBeforeUpdate);
|
|
||||||
s_screenConfigurationBeforeUpdate = NSScreen.screens;
|
|
||||||
qCDebug(lcQpaScreen, "Display reconfigure transaction started"
|
|
||||||
" with screen configuration %p", s_screenConfigurationBeforeUpdate);
|
|
||||||
|
|
||||||
static void (^tryScreenUpdate)();
|
|
||||||
tryScreenUpdate = ^void () {
|
|
||||||
qCDebug(lcQpaScreen) << "Attempting screen update from runloop block";
|
|
||||||
if (!updateScreensIfNeeded())
|
|
||||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, tryScreenUpdate);
|
|
||||||
};
|
|
||||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, tryScreenUpdate);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Q_ASSERT_X(displayReconfigurationsInProgress, "QCococaScreen",
|
|
||||||
"Display configuration transactions are expected to be balanced");
|
|
||||||
|
|
||||||
if (!--displayReconfigurationsInProgress) {
|
|
||||||
qCDebug(lcQpaScreen) << "Display reconfigure transaction completed";
|
|
||||||
// We optimistically update now, in case the NSScreens have changed
|
|
||||||
updateScreensIfNeeded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, nullptr);
|
|
||||||
|
|
||||||
static QMacNotificationObserver screenParameterObserver(NSApplication.sharedApplication,
|
|
||||||
NSApplicationDidChangeScreenParametersNotification, [&]() {
|
|
||||||
qCDebug(lcQpaScreen) << "Received screen parameter change notification";
|
|
||||||
updateScreensIfNeeded(); // As a last resort we update screens here
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QCocoaScreen::updateScreensIfNeeded()
|
|
||||||
{
|
|
||||||
if (!s_screenConfigurationBeforeUpdate) {
|
|
||||||
qCDebug(lcQpaScreen) << "QScreens have already been updated, all good";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s_screenConfigurationBeforeUpdate == NSScreen.screens) {
|
|
||||||
qCDebug(lcQpaScreen) << "Still waiting for NSScreen configuration change";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(lcQpaScreen, "NSScreen configuration changed to %p", NSScreen.screens);
|
|
||||||
updateScreens();
|
|
||||||
|
|
||||||
s_screenConfigurationBeforeUpdate = nil;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Update the list of available QScreens, and the properties of existing screens.
|
|
||||||
|
|
||||||
At this point we rely on the NSScreen.screens to be up to date.
|
|
||||||
*/
|
|
||||||
void QCocoaScreen::updateScreens()
|
|
||||||
{
|
|
||||||
uint32_t displayCount = 0;
|
|
||||||
if (CGGetOnlineDisplayList(0, nullptr, &displayCount) != kCGErrorSuccess)
|
|
||||||
qFatal("Failed to get number of online displays");
|
|
||||||
|
|
||||||
QVector<CGDirectDisplayID> onlineDisplays(displayCount);
|
|
||||||
if (CGGetOnlineDisplayList(displayCount, onlineDisplays.data(), &displayCount) != kCGErrorSuccess)
|
|
||||||
qFatal("Failed to get online displays");
|
|
||||||
|
|
||||||
qCInfo(lcQpaScreen) << "Updating screens with" << displayCount
|
|
||||||
<< "online displays:" << onlineDisplays;
|
|
||||||
|
|
||||||
// TODO: Verify whether we can always assume the main display is first
|
|
||||||
int mainDisplayIndex = onlineDisplays.indexOf(CGMainDisplayID());
|
|
||||||
if (mainDisplayIndex < 0) {
|
|
||||||
qCWarning(lcQpaScreen) << "Main display not in list of online displays!";
|
|
||||||
} else if (mainDisplayIndex > 0) {
|
|
||||||
qCWarning(lcQpaScreen) << "Main display not first display, making sure it is";
|
|
||||||
onlineDisplays.move(mainDisplayIndex, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (CGDirectDisplayID displayId : onlineDisplays) {
|
|
||||||
Q_ASSERT(CGDisplayIsOnline(displayId));
|
|
||||||
|
|
||||||
if (CGDisplayMirrorsDisplay(displayId))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// A single physical screen can map to multiple displays IDs,
|
|
||||||
// depending on which GPU is in use or which physical port the
|
|
||||||
// screen is connected to. By mapping the display ID to a UUID,
|
|
||||||
// which are shared between displays that target the same screen,
|
|
||||||
// we can pick an existing QScreen to update instead of needlessly
|
|
||||||
// adding and removing QScreens.
|
|
||||||
QCFType<CFUUIDRef> uuid = CGDisplayCreateUUIDFromDisplayID(displayId);
|
|
||||||
Q_ASSERT(uuid);
|
|
||||||
|
|
||||||
if (QCocoaScreen *existingScreen = QCocoaScreen::get(uuid)) {
|
|
||||||
existingScreen->update(displayId);
|
|
||||||
qCInfo(lcQpaScreen) << "Updated" << existingScreen;
|
|
||||||
if (CGDisplayIsMain(displayId) && existingScreen != qGuiApp->primaryScreen()->handle()) {
|
|
||||||
qCInfo(lcQpaScreen) << "Primary screen changed to" << existingScreen;
|
|
||||||
QWindowSystemInterface::handlePrimaryScreenChanged(existingScreen);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
QCocoaScreen::add(displayId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (QScreen *screen : QGuiApplication::screens()) {
|
|
||||||
QCocoaScreen *platformScreen = static_cast<QCocoaScreen*>(screen->handle());
|
|
||||||
if (!platformScreen->isOnline() || platformScreen->isMirroring())
|
|
||||||
platformScreen->remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QCocoaScreen::add(CGDirectDisplayID displayId)
|
|
||||||
{
|
|
||||||
const bool isPrimary = CGDisplayIsMain(displayId);
|
|
||||||
QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId);
|
|
||||||
qCInfo(lcQpaScreen) << "Adding" << cocoaScreen
|
|
||||||
<< (isPrimary ? "as new primary screen" : "");
|
|
||||||
QWindowSystemInterface::handleScreenAdded(cocoaScreen, isPrimary);
|
|
||||||
}
|
|
||||||
|
|
||||||
QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId)
|
|
||||||
: QPlatformScreen(), m_displayId(displayId)
|
|
||||||
{
|
|
||||||
update(m_displayId);
|
|
||||||
m_cursor = new QCocoaCursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QCocoaScreen::cleanupScreens()
|
|
||||||
{
|
|
||||||
// Remove screens in reverse order to avoid crash in case of multiple screens
|
|
||||||
for (QScreen *screen : backwards(QGuiApplication::screens()))
|
|
||||||
static_cast<QCocoaScreen*>(screen->handle())->remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QCocoaScreen::remove()
|
|
||||||
{
|
|
||||||
// This may result in the application responding to QGuiApplication::screenRemoved
|
|
||||||
// by moving the window to another screen, either by setGeometry, or by setScreen.
|
|
||||||
// If the window isn't moved by the application, Qt will as a fallback move it to
|
|
||||||
// the primary screen via setScreen. Due to the way setScreen works, this won't
|
|
||||||
// actually recreate the window on the new screen, it will just assign the new
|
|
||||||
// QScreen to the window. The associated NSWindow will have an NSScreen determined
|
|
||||||
// by AppKit. AppKit will then move the window to another screen by changing the
|
|
||||||
// geometry, and we will get a callback in QCocoaWindow::windowDidMove and then
|
|
||||||
// QCocoaWindow::windowDidChangeScreen. At that point the window will appear to have
|
|
||||||
// already changed its screen, but that's only true if comparing the Qt screens,
|
|
||||||
// not when comparing the NSScreens.
|
|
||||||
qCInfo(lcQpaScreen) << "Removing " << this;
|
|
||||||
QWindowSystemInterface::handleScreenRemoved(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
QCocoaScreen::~QCocoaScreen()
|
|
||||||
{
|
|
||||||
Q_ASSERT_X(!screen(), "QCocoaScreen", "QScreen should be deleted first");
|
|
||||||
|
|
||||||
delete m_cursor;
|
|
||||||
|
|
||||||
CVDisplayLinkRelease(m_displayLink);
|
|
||||||
if (m_displayLinkSource)
|
|
||||||
dispatch_release(m_displayLinkSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString displayName(CGDirectDisplayID displayID)
|
|
||||||
{
|
|
||||||
QIOType<io_iterator_t> iterator;
|
|
||||||
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
|
|
||||||
IOServiceMatching("IODisplayConnect"), &iterator))
|
|
||||||
return QString();
|
|
||||||
|
|
||||||
QIOType<io_service_t> display;
|
|
||||||
while ((display = IOIteratorNext(iterator)) != 0)
|
|
||||||
{
|
|
||||||
NSDictionary *info = [(__bridge NSDictionary*)IODisplayCreateInfoDictionary(
|
|
||||||
display, kIODisplayOnlyPreferredName) autorelease];
|
|
||||||
|
|
||||||
if ([[info objectForKey:@kDisplayVendorID] longValue] != CGDisplayVendorNumber(displayID))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ([[info objectForKey:@kDisplayProductID] longValue] != CGDisplayModelNumber(displayID))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ([[info objectForKey:@kDisplaySerialNumber] longValue] != CGDisplaySerialNumber(displayID))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
NSDictionary *localizedNames = [info objectForKey:@kDisplayProductName];
|
|
||||||
if (![localizedNames count])
|
|
||||||
break; // Correct screen, but no name in dictionary
|
|
||||||
|
|
||||||
return QString::fromNSString([localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QCocoaScreen::update(CGDirectDisplayID displayId)
|
|
||||||
{
|
|
||||||
if (displayId != m_displayId) {
|
|
||||||
qCDebug(lcQpaScreen) << "Reconnecting" << this << "as display" << displayId;
|
|
||||||
m_displayId = displayId;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_ASSERT(isOnline());
|
|
||||||
|
|
||||||
const QRect previousGeometry = m_geometry;
|
|
||||||
const QRect previousAvailableGeometry = m_availableGeometry;
|
|
||||||
const QDpi previousLogicalDpi = m_logicalDpi;
|
|
||||||
const qreal previousRefreshRate = m_refreshRate;
|
|
||||||
|
|
||||||
// Some properties are only available via NSScreen
|
|
||||||
NSScreen *nsScreen = nativeScreen();
|
|
||||||
Q_ASSERT(nsScreen);
|
|
||||||
|
|
||||||
// The reference screen for the geometry is always the primary screen
|
|
||||||
QRectF primaryScreenGeometry = QRectF::fromCGRect(CGDisplayBounds(CGMainDisplayID()));
|
|
||||||
m_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect();
|
|
||||||
m_availableGeometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect();
|
|
||||||
|
|
||||||
m_devicePixelRatio = nsScreen.backingScaleFactor;
|
|
||||||
|
|
||||||
m_format = QImage::Format_RGB32;
|
|
||||||
m_depth = NSBitsPerPixelFromDepth(nsScreen.depth);
|
|
||||||
|
|
||||||
CGSize size = CGDisplayScreenSize(m_displayId);
|
|
||||||
m_physicalSize = QSizeF(size.width, size.height);
|
|
||||||
m_logicalDpi.first = 72;
|
|
||||||
m_logicalDpi.second = 72;
|
|
||||||
|
|
||||||
QCFType<CGDisplayModeRef> displayMode = CGDisplayCopyDisplayMode(m_displayId);
|
|
||||||
float refresh = CGDisplayModeGetRefreshRate(displayMode);
|
|
||||||
m_refreshRate = refresh > 0 ? refresh : 60.0;
|
|
||||||
|
|
||||||
m_name = displayName(m_displayId);
|
|
||||||
|
|
||||||
const bool didChangeGeometry = m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry;
|
|
||||||
|
|
||||||
if (didChangeGeometry)
|
|
||||||
QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry());
|
|
||||||
if (m_logicalDpi != previousLogicalDpi)
|
|
||||||
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second);
|
|
||||||
if (m_refreshRate != previousRefreshRate)
|
|
||||||
QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------- Display link -----------------------
|
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(lcQpaScreenUpdates, "qt.qpa.screen.updates", QtCriticalMsg);
|
|
||||||
|
|
||||||
void QCocoaScreen::requestUpdate()
|
|
||||||
{
|
|
||||||
Q_ASSERT(m_displayId);
|
|
||||||
|
|
||||||
if (!m_displayLink) {
|
|
||||||
CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink);
|
|
||||||
CVDisplayLinkSetOutputCallback(m_displayLink, [](CVDisplayLinkRef, const CVTimeStamp*,
|
|
||||||
const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void* displayLinkContext) -> int {
|
|
||||||
// FIXME: It would be nice if update requests would include timing info
|
|
||||||
static_cast<QCocoaScreen*>(displayLinkContext)->deliverUpdateRequests();
|
|
||||||
return kCVReturnSuccess;
|
|
||||||
}, this);
|
|
||||||
qCDebug(lcQpaScreenUpdates) << "Display link created for" << this;
|
|
||||||
|
|
||||||
// During live window resizing -[NSWindow _resizeWithEvent:] will spin a local event loop
|
|
||||||
// in event-tracking mode, dequeuing only the mouse drag events needed to update the window's
|
|
||||||
// frame. It will repeatedly spin this loop until no longer receiving any mouse drag events,
|
|
||||||
// and will then update the frame (effectively coalescing/compressing the events). Unfortunately
|
|
||||||
// the events are pulled out using -[NSApplication nextEventMatchingEventMask:untilDate:inMode:dequeue:]
|
|
||||||
// which internally uses CFRunLoopRunSpecific, so the event loop will also process GCD queues and other
|
|
||||||
// runloop sources that have been added to the tracking mode. This includes the GCD display-link
|
|
||||||
// source that we use to marshal the display-link callback over to the main thread. If the
|
|
||||||
// subsequent delivery of the update-request on the main thread stalls due to inefficient
|
|
||||||
// user code, the NSEventThread will have had time to deliver additional mouse drag events,
|
|
||||||
// and the logic in -[NSWindow _resizeWithEvent:] will keep on compressing events and never
|
|
||||||
// get to the point of actually updating the window frame, making it seem like the window
|
|
||||||
// is stuck in its original size. Only when the user stops moving their mouse, and the event
|
|
||||||
// queue is completely drained of drag events, will the window frame be updated.
|
|
||||||
|
|
||||||
// By keeping an event tap listening for drag events, registered as a version 1 runloop source,
|
|
||||||
// we prevent the GCD source from being prioritized, giving the resize logic enough time
|
|
||||||
// to finish coalescing the events. This is incidental, but conveniently gives us the behavior
|
|
||||||
// we are looking for, interleaving display-link updates and resize events.
|
|
||||||
static CFMachPortRef eventTap = []() {
|
|
||||||
CFMachPortRef eventTap = CGEventTapCreateForPid(getpid(), kCGTailAppendEventTap,
|
|
||||||
kCGEventTapOptionListenOnly, NSEventMaskLeftMouseDragged,
|
|
||||||
[](CGEventTapProxy, CGEventType type, CGEventRef event, void *) -> CGEventRef {
|
|
||||||
if (type == kCGEventTapDisabledByTimeout)
|
|
||||||
qCWarning(lcQpaScreenUpdates) << "Event tap disabled due to timeout!";
|
|
||||||
return event; // Listen only tap, so what we return doesn't really matter
|
|
||||||
}, nullptr);
|
|
||||||
CGEventTapEnable(eventTap, false); // Event taps are normally enabled when created
|
|
||||||
static CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
|
|
||||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
|
|
||||||
|
|
||||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
|
||||||
[center addObserverForName:NSWindowWillStartLiveResizeNotification object:nil queue:nil
|
|
||||||
usingBlock:^(NSNotification *notification) {
|
|
||||||
qCDebug(lcQpaScreenUpdates) << "Live resize of" << notification.object
|
|
||||||
<< "started. Enabling event tap";
|
|
||||||
CGEventTapEnable(eventTap, true);
|
|
||||||
}];
|
|
||||||
[center addObserverForName:NSWindowDidEndLiveResizeNotification object:nil queue:nil
|
|
||||||
usingBlock:^(NSNotification *notification) {
|
|
||||||
qCDebug(lcQpaScreenUpdates) << "Live resize of" << notification.object
|
|
||||||
<< "ended. Disabling event tap";
|
|
||||||
CGEventTapEnable(eventTap, false);
|
|
||||||
}];
|
|
||||||
return eventTap;
|
|
||||||
}();
|
|
||||||
Q_UNUSED(eventTap);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CVDisplayLinkIsRunning(m_displayLink)) {
|
|
||||||
qCDebug(lcQpaScreenUpdates) << "Starting display link for" << this;
|
|
||||||
CVDisplayLinkStart(m_displayLink);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to allow building up debug output in multiple steps
|
|
||||||
struct DeferredDebugHelper
|
|
||||||
{
|
|
||||||
DeferredDebugHelper(const QLoggingCategory &cat) {
|
|
||||||
if (cat.isDebugEnabled())
|
|
||||||
debug = new QDebug(QMessageLogger().debug(cat).nospace());
|
|
||||||
}
|
|
||||||
~DeferredDebugHelper() {
|
|
||||||
flushOutput();
|
|
||||||
}
|
|
||||||
void flushOutput() {
|
|
||||||
if (debug) {
|
|
||||||
delete debug;
|
|
||||||
debug = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QDebug *debug = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define qDeferredDebug(helper) if (Q_UNLIKELY(helper.debug)) *helper.debug
|
|
||||||
|
|
||||||
void QCocoaScreen::deliverUpdateRequests()
|
|
||||||
{
|
|
||||||
if (!isOnline())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QMacAutoReleasePool pool;
|
|
||||||
|
|
||||||
// The CVDisplayLink callback is a notification that it's a good time to produce a new frame.
|
|
||||||
// Since the callback is delivered on a separate thread we have to marshal it over to the
|
|
||||||
// main thread, as Qt requires update requests to be delivered there. This needs to happen
|
|
||||||
// asynchronously, as otherwise we may end up deadlocking if the main thread calls back
|
|
||||||
// into any of the CVDisplayLink APIs.
|
|
||||||
if (!NSThread.isMainThread) {
|
|
||||||
// We're explicitly not using the data of the GCD source to track the pending updates,
|
|
||||||
// as the data isn't reset to 0 until after the event handler, and also doesn't update
|
|
||||||
// during the event handler, both of which we need to track late frames.
|
|
||||||
const int pendingUpdates = ++m_pendingUpdates;
|
|
||||||
|
|
||||||
DeferredDebugHelper screenUpdates(lcQpaScreenUpdates());
|
|
||||||
qDeferredDebug(screenUpdates) << "display link callback for screen " << m_displayId;
|
|
||||||
|
|
||||||
if (const int framesAheadOfDelivery = pendingUpdates - 1) {
|
|
||||||
// If we have more than one update pending it means that a previous display link callback
|
|
||||||
// has not been fully processed on the main thread, either because GCD hasn't delivered
|
|
||||||
// it on the main thread yet, because the processing of the update request is taking
|
|
||||||
// too long, or because the update request was deferred due to window live resizing.
|
|
||||||
qDeferredDebug(screenUpdates) << ", " << framesAheadOfDelivery << " frame(s) ahead";
|
|
||||||
|
|
||||||
// We skip the frame completely if we're live-resizing, to not put any extra
|
|
||||||
// strain on the main thread runloop. Otherwise we assume we should push frames
|
|
||||||
// as fast as possible, and hopefully the callback will be delivered on the
|
|
||||||
// main thread just when the previous finished.
|
|
||||||
if (qt_apple_sharedApplication().keyWindow.inLiveResize) {
|
|
||||||
qDeferredDebug(screenUpdates) << "; waiting for main thread to catch up";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qDeferredDebug(screenUpdates) << "; signaling dispatch source";
|
|
||||||
|
|
||||||
if (!m_displayLinkSource) {
|
|
||||||
m_displayLinkSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
|
|
||||||
dispatch_source_set_event_handler(m_displayLinkSource, ^{
|
|
||||||
deliverUpdateRequests();
|
|
||||||
});
|
|
||||||
dispatch_resume(m_displayLinkSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch_source_merge_data(m_displayLinkSource, 1);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
DeferredDebugHelper screenUpdates(lcQpaScreenUpdates());
|
|
||||||
qDeferredDebug(screenUpdates) << "gcd event handler on main thread";
|
|
||||||
|
|
||||||
const int pendingUpdates = m_pendingUpdates;
|
|
||||||
if (pendingUpdates > 1)
|
|
||||||
qDeferredDebug(screenUpdates) << ", " << (pendingUpdates - 1) << " frame(s) behind display link";
|
|
||||||
|
|
||||||
screenUpdates.flushOutput();
|
|
||||||
|
|
||||||
bool pauseUpdates = true;
|
|
||||||
|
|
||||||
auto windows = QGuiApplication::allWindows();
|
|
||||||
for (int i = 0; i < windows.size(); ++i) {
|
|
||||||
QWindow *window = windows.at(i);
|
|
||||||
auto *platformWindow = static_cast<QCocoaWindow*>(window->handle());
|
|
||||||
if (!platformWindow)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!platformWindow->hasPendingUpdateRequest())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (window->screen() != screen())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Skip windows that are not doing update requests via display link
|
|
||||||
if (!platformWindow->updatesWithDisplayLink())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
platformWindow->deliverUpdateRequest();
|
|
||||||
|
|
||||||
// Another update request was triggered, keep the display link running
|
|
||||||
if (platformWindow->hasPendingUpdateRequest())
|
|
||||||
pauseUpdates = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pauseUpdates) {
|
|
||||||
// Pause the display link if there are no pending update requests
|
|
||||||
qCDebug(lcQpaScreenUpdates) << "Stopping display link for" << this;
|
|
||||||
CVDisplayLinkStop(m_displayLink);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const int missedUpdates = m_pendingUpdates.fetchAndStoreRelaxed(0) - pendingUpdates) {
|
|
||||||
qCWarning(lcQpaScreenUpdates) << "main thread missed" << missedUpdates
|
|
||||||
<< "update(s) from display link during update request delivery";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QCocoaScreen::isRunningDisplayLink() const
|
|
||||||
{
|
|
||||||
return m_displayLink && CVDisplayLinkIsRunning(m_displayLink);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------
|
|
||||||
|
|
||||||
QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingTypeHint() const
|
|
||||||
{
|
|
||||||
QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint();
|
|
||||||
if (type == QPlatformScreen::Subpixel_None) {
|
|
||||||
// Every OSX machine has RGB pixels unless a peculiar or rotated non-Apple screen is attached
|
|
||||||
type = QPlatformScreen::Subpixel_RGB;
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const
|
|
||||||
{
|
|
||||||
NSPoint screenPoint = mapToNative(point);
|
|
||||||
|
|
||||||
// Search (hit test) for the top-level window. [NSWidow windowNumberAtPoint:
|
|
||||||
// belowWindowWithWindowNumber] may return windows that are not interesting
|
|
||||||
// to Qt. The search iterates until a suitable window or no window is found.
|
|
||||||
NSInteger topWindowNumber = 0;
|
|
||||||
QWindow *window = nullptr;
|
|
||||||
do {
|
|
||||||
// Get the top-most window, below any previously rejected window.
|
|
||||||
topWindowNumber = [NSWindow windowNumberAtPoint:screenPoint
|
|
||||||
belowWindowWithWindowNumber:topWindowNumber];
|
|
||||||
|
|
||||||
// Continue the search if the window does not belong to this process.
|
|
||||||
NSWindow *nsWindow = [NSApp windowWithWindowNumber:topWindowNumber];
|
|
||||||
if (!nsWindow)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Continue the search if the window does not belong to Qt.
|
|
||||||
if (![nsWindow conformsToProtocol:@protocol(QNSWindowProtocol)])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
QCocoaWindow *cocoaWindow = qnsview_cast(nsWindow.contentView).platformWindow;
|
|
||||||
if (!cocoaWindow)
|
|
||||||
continue;
|
|
||||||
window = cocoaWindow->window();
|
|
||||||
|
|
||||||
// Continue the search if the window is not a top-level window.
|
|
||||||
if (!window->isTopLevel())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Stop searching. The current window is the correct window.
|
|
||||||
break;
|
|
||||||
} while (topWindowNumber > 0);
|
|
||||||
|
|
||||||
return window;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) const
|
|
||||||
{
|
|
||||||
// Determine the grab rect. FIXME: The rect should be bounded by the view's
|
|
||||||
// geometry, but note that for the pixeltool use case that window will be the
|
|
||||||
// desktop widget's view, which currently gets resized to fit one screen
|
|
||||||
// only, since its NSWindow has the NSWindowStyleMaskTitled flag set.
|
|
||||||
Q_UNUSED(view);
|
|
||||||
QRect grabRect = QRect(x, y, width, height);
|
|
||||||
qCDebug(lcQpaScreen) << "input grab rect" << grabRect;
|
|
||||||
|
|
||||||
// Find which displays to grab from, or all of them if the grab size is unspecified
|
|
||||||
const int maxDisplays = 128;
|
|
||||||
CGDirectDisplayID displays[maxDisplays];
|
|
||||||
CGDisplayCount displayCount;
|
|
||||||
CGRect cgRect = (width < 0 || height < 0) ? CGRectInfinite : grabRect.toCGRect();
|
|
||||||
const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
|
|
||||||
if (err || displayCount == 0)
|
|
||||||
return QPixmap();
|
|
||||||
|
|
||||||
// If the grab size is not specified, set it to be the bounding box of all screens,
|
|
||||||
if (width < 0 || height < 0) {
|
|
||||||
QRect windowRect;
|
|
||||||
for (uint i = 0; i < displayCount; ++i) {
|
|
||||||
QRect displayBounds = QRectF::fromCGRect(CGDisplayBounds(displays[i])).toRect();
|
|
||||||
windowRect = windowRect.united(displayBounds);
|
|
||||||
}
|
|
||||||
if (grabRect.width() < 0)
|
|
||||||
grabRect.setWidth(windowRect.width());
|
|
||||||
if (grabRect.height() < 0)
|
|
||||||
grabRect.setHeight(windowRect.height());
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(lcQpaScreen) << "final grab rect" << grabRect << "from" << displayCount << "displays";
|
|
||||||
|
|
||||||
// Grab images from each display
|
|
||||||
QVector<QImage> images;
|
|
||||||
QVector<QRect> destinations;
|
|
||||||
for (uint i = 0; i < displayCount; ++i) {
|
|
||||||
auto display = displays[i];
|
|
||||||
QRect displayBounds = QRectF::fromCGRect(CGDisplayBounds(display)).toRect();
|
|
||||||
QRect grabBounds = displayBounds.intersected(grabRect);
|
|
||||||
QRect displayLocalGrabBounds = QRect(QPoint(grabBounds.topLeft() - displayBounds.topLeft()), grabBounds.size());
|
|
||||||
QImage displayImage = qt_mac_toQImage(QCFType<CGImageRef>(CGDisplayCreateImageForRect(display, displayLocalGrabBounds.toCGRect())));
|
|
||||||
displayImage.setDevicePixelRatio(displayImage.size().width() / displayLocalGrabBounds.size().width());
|
|
||||||
images.append(displayImage);
|
|
||||||
QRect destBounds = QRect(QPoint(grabBounds.topLeft() - grabRect.topLeft()), grabBounds.size());
|
|
||||||
destinations.append(destBounds);
|
|
||||||
qCDebug(lcQpaScreen) << "grab display" << i << "global" << grabBounds << "local" << displayLocalGrabBounds
|
|
||||||
<< "grab image size" << displayImage.size() << "devicePixelRatio" << displayImage.devicePixelRatio();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the highest dpr, which becomes the dpr for the returned pixmap.
|
|
||||||
qreal dpr = 1.0;
|
|
||||||
for (uint i = 0; i < displayCount; ++i)
|
|
||||||
dpr = qMax(dpr, images.at(i).devicePixelRatio());
|
|
||||||
|
|
||||||
// Allocate target pixmap and draw each screen's content
|
|
||||||
qCDebug(lcQpaScreen) << "Create grap pixmap" << grabRect.size() << "at devicePixelRatio" << dpr;
|
|
||||||
QPixmap windowPixmap(grabRect.size() * dpr);
|
|
||||||
windowPixmap.setDevicePixelRatio(dpr);
|
|
||||||
windowPixmap.fill(Qt::transparent);
|
|
||||||
QPainter painter(&windowPixmap);
|
|
||||||
for (uint i = 0; i < displayCount; ++i)
|
|
||||||
painter.drawImage(destinations.at(i), images.at(i));
|
|
||||||
|
|
||||||
return windowPixmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QCocoaScreen::isOnline() const
|
|
||||||
{
|
|
||||||
// When a display is disconnected CGDisplayIsOnline and other CGDisplay
|
|
||||||
// functions that take a displayId will not return false, but will start
|
|
||||||
// returning -1 to signal that the displayId is invalid. Some functions
|
|
||||||
// will also assert or even crash in this case, so it's important that
|
|
||||||
// we double check if a display is online before calling other functions.
|
|
||||||
auto isOnline = CGDisplayIsOnline(m_displayId);
|
|
||||||
static const uint32_t kCGDisplayIsDisconnected = int32_t(-1);
|
|
||||||
return isOnline != kCGDisplayIsDisconnected && isOnline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Returns true if a screen is mirroring another screen
|
|
||||||
*/
|
|
||||||
bool QCocoaScreen::isMirroring() const
|
|
||||||
{
|
|
||||||
if (!isOnline())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return CGDisplayMirrorsDisplay(m_displayId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
The screen used as a reference for global window geometry
|
|
||||||
*/
|
|
||||||
QCocoaScreen *QCocoaScreen::primaryScreen()
|
|
||||||
{
|
|
||||||
// Note: The primary screen that Qt knows about may not match the current CGMainDisplayID()
|
|
||||||
// if macOS has not yet been able to inform us that the main display has changed, but we
|
|
||||||
// will update the primary screen accordingly once the reconfiguration callback comes in.
|
|
||||||
return static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<QPlatformScreen*> QCocoaScreen::virtualSiblings() const
|
|
||||||
{
|
|
||||||
QList<QPlatformScreen*> siblings;
|
|
||||||
|
|
||||||
// Screens on macOS are always part of the same virtual desktop
|
|
||||||
for (QScreen *screen : QGuiApplication::screens())
|
|
||||||
siblings << screen->handle();
|
|
||||||
|
|
||||||
return siblings;
|
|
||||||
}
|
|
||||||
|
|
||||||
QCocoaScreen *QCocoaScreen::get(NSScreen *nsScreen)
|
|
||||||
{
|
|
||||||
if (s_screenConfigurationBeforeUpdate) {
|
|
||||||
qCWarning(lcQpaScreen) << "Trying to resolve screen while waiting for screen reconfigure!";
|
|
||||||
if (!updateScreensIfNeeded())
|
|
||||||
qCWarning(lcQpaScreen) << "Failed to do last minute screen update. Expect crashes.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return get(nsScreen.qt_displayId);
|
|
||||||
}
|
|
||||||
|
|
||||||
QCocoaScreen *QCocoaScreen::get(CGDirectDisplayID displayId)
|
|
||||||
{
|
|
||||||
for (QScreen *screen : QGuiApplication::screens()) {
|
|
||||||
QCocoaScreen *cocoaScreen = static_cast<QCocoaScreen*>(screen->handle());
|
|
||||||
if (cocoaScreen->m_displayId == displayId)
|
|
||||||
return cocoaScreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
QCocoaScreen *QCocoaScreen::get(CFUUIDRef uuid)
|
|
||||||
{
|
|
||||||
for (QScreen *screen : QGuiApplication::screens()) {
|
|
||||||
auto *platformScreen = static_cast<QCocoaScreen*>(screen->handle());
|
|
||||||
if (!platformScreen->isOnline())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto displayId = platformScreen->displayId();
|
|
||||||
QCFType<CFUUIDRef> candidateUuid(CGDisplayCreateUUIDFromDisplayID(displayId));
|
|
||||||
Q_ASSERT(candidateUuid);
|
|
||||||
|
|
||||||
if (candidateUuid == uuid)
|
|
||||||
return platformScreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSScreen *QCocoaScreen::nativeScreen() const
|
|
||||||
{
|
|
||||||
if (!m_displayId)
|
|
||||||
return nil; // The display has been disconnected
|
|
||||||
|
|
||||||
for (NSScreen *screen in NSScreen.screens) {
|
|
||||||
if (screen.qt_displayId == m_displayId)
|
|
||||||
return screen;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
CGPoint QCocoaScreen::mapToNative(const QPointF &pos, QCocoaScreen *screen)
|
|
||||||
{
|
|
||||||
Q_ASSERT(screen);
|
|
||||||
return qt_mac_flip(pos, screen->geometry()).toCGPoint();
|
|
||||||
}
|
|
||||||
|
|
||||||
CGRect QCocoaScreen::mapToNative(const QRectF &rect, QCocoaScreen *screen)
|
|
||||||
{
|
|
||||||
Q_ASSERT(screen);
|
|
||||||
return qt_mac_flip(rect, screen->geometry()).toCGRect();
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF QCocoaScreen::mapFromNative(CGPoint pos, QCocoaScreen *screen)
|
|
||||||
{
|
|
||||||
Q_ASSERT(screen);
|
|
||||||
return qt_mac_flip(QPointF::fromCGPoint(pos), screen->geometry());
|
|
||||||
}
|
|
||||||
|
|
||||||
QRectF QCocoaScreen::mapFromNative(CGRect rect, QCocoaScreen *screen)
|
|
||||||
{
|
|
||||||
Q_ASSERT(screen);
|
|
||||||
return qt_mac_flip(QRectF::fromCGRect(rect), screen->geometry());
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG_STREAM
|
|
||||||
QDebug operator<<(QDebug debug, const QCocoaScreen *screen)
|
|
||||||
{
|
|
||||||
QDebugStateSaver saver(debug);
|
|
||||||
debug.nospace();
|
|
||||||
debug << "QCocoaScreen(" << (const void *)screen;
|
|
||||||
if (screen) {
|
|
||||||
debug << ", " << screen->name();
|
|
||||||
if (screen->isOnline()) {
|
|
||||||
if (CGDisplayIsAsleep(screen->displayId()))
|
|
||||||
debug << ", Sleeping";
|
|
||||||
if (auto mirroring = CGDisplayMirrorsDisplay(screen->displayId()))
|
|
||||||
debug << ", mirroring=" << mirroring;
|
|
||||||
} else {
|
|
||||||
debug << ", Offline";
|
|
||||||
}
|
|
||||||
debug << ", " << screen->geometry();
|
|
||||||
debug << ", dpr=" << screen->devicePixelRatio();
|
|
||||||
debug << ", displayId=" << screen->displayId();
|
|
||||||
|
|
||||||
if (auto nativeScreen = screen->nativeScreen())
|
|
||||||
debug << ", " << nativeScreen;
|
|
||||||
}
|
|
||||||
debug << ')';
|
|
||||||
return debug;
|
|
||||||
}
|
|
||||||
#endif // !QT_NO_DEBUG_STREAM
|
|
||||||
|
|
||||||
#include "qcocoascreen.moc"
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
@implementation NSScreen (QtExtras)
|
|
||||||
|
|
||||||
- (CGDirectDisplayID)qt_displayId
|
|
||||||
{
|
|
||||||
return [self.deviceDescription[@"NSScreenNumber"] unsignedIntValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -1,246 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2017 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 "qcocoasystemsettings.h"
|
|
||||||
|
|
||||||
#include "qcocoahelpers.h"
|
|
||||||
|
|
||||||
#include <QtCore/private/qcore_mac_p.h>
|
|
||||||
#include <QtGui/qfont.h>
|
|
||||||
#include <QtGui/private/qcoregraphics_p.h>
|
|
||||||
|
|
||||||
#if !QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
|
|
||||||
@interface NSColor (MojaveForwardDeclarations)
|
|
||||||
@property (class, strong, readonly) NSColor *selectedContentBackgroundColor NS_AVAILABLE_MAC(10_14);
|
|
||||||
@property (class, strong, readonly) NSColor *unemphasizedSelectedTextBackgroundColor NS_AVAILABLE_MAC(10_14);
|
|
||||||
@property (class, strong, readonly) NSColor *unemphasizedSelectedTextColor NS_AVAILABLE_MAC(10_14);
|
|
||||||
@property (class, strong, readonly) NSColor *unemphasizedSelectedContentBackgroundColor NS_AVAILABLE_MAC(10_14);
|
|
||||||
@property (class, strong, readonly) NSArray<NSColor *> *alternatingContentBackgroundColors NS_AVAILABLE_MAC(10_14);
|
|
||||||
// Missing from non-Mojave SDKs, even if introduced in 10.10
|
|
||||||
@property (class, strong, readonly) NSColor *linkColor NS_AVAILABLE_MAC(10_10);
|
|
||||||
@end
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
QPalette * qt_mac_createSystemPalette()
|
|
||||||
{
|
|
||||||
QColor qc;
|
|
||||||
|
|
||||||
// Standard palette initialization (copied from Qt 4 styles)
|
|
||||||
QBrush backgroundBrush = qt_mac_toQBrush([NSColor windowBackgroundColor]);
|
|
||||||
QColor background = backgroundBrush.color();
|
|
||||||
QColor light(background.lighter(110));
|
|
||||||
QColor dark(background.darker(160));
|
|
||||||
QColor mid(background.darker(140));
|
|
||||||
QPalette *palette = new QPalette(Qt::black, background, light, dark, mid, Qt::black, Qt::white);
|
|
||||||
|
|
||||||
palette->setBrush(QPalette::Window, backgroundBrush);
|
|
||||||
|
|
||||||
palette->setBrush(QPalette::Disabled, QPalette::WindowText, dark);
|
|
||||||
palette->setBrush(QPalette::Disabled, QPalette::Text, dark);
|
|
||||||
palette->setBrush(QPalette::Disabled, QPalette::ButtonText, dark);
|
|
||||||
palette->setBrush(QPalette::Disabled, QPalette::Base, backgroundBrush);
|
|
||||||
QBrush textBackgroundBrush = qt_mac_toQBrush([NSColor textBackgroundColor]);
|
|
||||||
palette->setBrush(QPalette::Active, QPalette::Base, textBackgroundBrush);
|
|
||||||
palette->setBrush(QPalette::Inactive, QPalette::Base, textBackgroundBrush);
|
|
||||||
palette->setColor(QPalette::Disabled, QPalette::Dark, QColor(191, 191, 191));
|
|
||||||
palette->setColor(QPalette::Active, QPalette::Dark, QColor(191, 191, 191));
|
|
||||||
palette->setColor(QPalette::Inactive, QPalette::Dark, QColor(191, 191, 191));
|
|
||||||
|
|
||||||
// System palette initialization:
|
|
||||||
QBrush br = qt_mac_toQBrush([NSColor selectedControlColor]);
|
|
||||||
palette->setBrush(QPalette::Active, QPalette::Highlight, br);
|
|
||||||
if (__builtin_available(macOS 10.14, *)) {
|
|
||||||
const auto inactiveHighlight = qt_mac_toQBrush([NSColor unemphasizedSelectedContentBackgroundColor]);
|
|
||||||
palette->setBrush(QPalette::Inactive, QPalette::Highlight, inactiveHighlight);
|
|
||||||
palette->setBrush(QPalette::Disabled, QPalette::Highlight, inactiveHighlight);
|
|
||||||
} else {
|
|
||||||
palette->setBrush(QPalette::Inactive, QPalette::Highlight, br);
|
|
||||||
palette->setBrush(QPalette::Disabled, QPalette::Highlight, br);
|
|
||||||
}
|
|
||||||
|
|
||||||
palette->setBrush(QPalette::Shadow, qt_mac_toQColor([NSColor shadowColor]));
|
|
||||||
|
|
||||||
qc = qt_mac_toQColor([NSColor controlTextColor]);
|
|
||||||
palette->setColor(QPalette::Active, QPalette::Text, qc);
|
|
||||||
palette->setColor(QPalette::Active, QPalette::WindowText, qc);
|
|
||||||
palette->setColor(QPalette::Active, QPalette::HighlightedText, qc);
|
|
||||||
palette->setColor(QPalette::Inactive, QPalette::Text, qc);
|
|
||||||
palette->setColor(QPalette::Inactive, QPalette::WindowText, qc);
|
|
||||||
palette->setColor(QPalette::Inactive, QPalette::HighlightedText, qc);
|
|
||||||
|
|
||||||
qc = qt_mac_toQColor([NSColor disabledControlTextColor]);
|
|
||||||
palette->setColor(QPalette::Disabled, QPalette::Text, qc);
|
|
||||||
palette->setColor(QPalette::Disabled, QPalette::WindowText, qc);
|
|
||||||
palette->setColor(QPalette::Disabled, QPalette::HighlightedText, qc);
|
|
||||||
|
|
||||||
palette->setBrush(QPalette::ToolTipBase, qt_mac_toQBrush([NSColor controlColor]));
|
|
||||||
|
|
||||||
// fix for https://bugreports.qt.io/browse/QTBUG-71740
|
|
||||||
palette->setColor(QPalette::Normal, QPalette::Link, qt_mac_toQColor([NSColor linkColor]));
|
|
||||||
|
|
||||||
return palette;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct QMacPaletteMap {
|
|
||||||
inline QMacPaletteMap(QPlatformTheme::Palette p, NSColor *a, NSColor *i) :
|
|
||||||
active(a), inactive(i), paletteRole(p) { }
|
|
||||||
|
|
||||||
NSColor *active;
|
|
||||||
NSColor *inactive;
|
|
||||||
QPlatformTheme::Palette paletteRole;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MAC_PALETTE_ENTRY(pal, active, inactive) \
|
|
||||||
QMacPaletteMap(pal, [NSColor active], [NSColor inactive])
|
|
||||||
static QMacPaletteMap mac_widget_colors[] = {
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::ToolButtonPalette, controlTextColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::ButtonPalette, controlTextColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::HeaderPalette, headerTextColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::ComboBoxPalette, controlTextColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::ItemViewPalette, textColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::MessageBoxLabelPalette, textColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::TabBarPalette, controlTextColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::LabelPalette, textColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::GroupBoxPalette, textColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::MenuPalette, controlTextColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::MenuBarPalette, controlTextColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::TextEditPalette, textColor, disabledControlTextColor),
|
|
||||||
MAC_PALETTE_ENTRY(QPlatformTheme::TextLineEditPalette, textColor, disabledControlTextColor)
|
|
||||||
};
|
|
||||||
#undef MAC_PALETTE_ENTRY
|
|
||||||
|
|
||||||
static const int mac_widget_colors_count = sizeof(mac_widget_colors) / sizeof(mac_widget_colors[0]);
|
|
||||||
|
|
||||||
QHash<QPlatformTheme::Palette, QPalette*> qt_mac_createRolePalettes()
|
|
||||||
{
|
|
||||||
QHash<QPlatformTheme::Palette, QPalette*> palettes;
|
|
||||||
QColor qc;
|
|
||||||
for (int i = 0; i < mac_widget_colors_count; i++) {
|
|
||||||
QPalette &pal = *qt_mac_createSystemPalette();
|
|
||||||
if (mac_widget_colors[i].active) {
|
|
||||||
qc = qt_mac_toQColor(mac_widget_colors[i].active);
|
|
||||||
pal.setColor(QPalette::Active, QPalette::Text, qc);
|
|
||||||
pal.setColor(QPalette::Inactive, QPalette::Text, qc);
|
|
||||||
pal.setColor(QPalette::Active, QPalette::WindowText, qc);
|
|
||||||
pal.setColor(QPalette::Inactive, QPalette::WindowText, qc);
|
|
||||||
pal.setColor(QPalette::Active, QPalette::HighlightedText, qc);
|
|
||||||
pal.setColor(QPalette::Inactive, QPalette::HighlightedText, qc);
|
|
||||||
pal.setColor(QPalette::Active, QPalette::ButtonText, qc);
|
|
||||||
pal.setColor(QPalette::Inactive, QPalette::ButtonText, qc);
|
|
||||||
qc = qt_mac_toQColor(mac_widget_colors[i].inactive);
|
|
||||||
pal.setColor(QPalette::Disabled, QPalette::Text, qc);
|
|
||||||
pal.setColor(QPalette::Disabled, QPalette::WindowText, qc);
|
|
||||||
pal.setColor(QPalette::Disabled, QPalette::HighlightedText, qc);
|
|
||||||
pal.setColor(QPalette::Disabled, QPalette::ButtonText, qc);
|
|
||||||
}
|
|
||||||
if (mac_widget_colors[i].paletteRole == QPlatformTheme::MenuPalette
|
|
||||||
|| mac_widget_colors[i].paletteRole == QPlatformTheme::MenuBarPalette) {
|
|
||||||
NSColor *selectedMenuItemColor = nil;
|
|
||||||
if (__builtin_available(macOS 10.14, *)) {
|
|
||||||
// Cheap approximation for NSVisualEffectView (see deprecation note for selectedMenuItemTextColor)
|
|
||||||
selectedMenuItemColor = [[NSColor selectedContentBackgroundColor] highlightWithLevel:0.4];
|
|
||||||
} else {
|
|
||||||
// selectedMenuItemColor would presumably be the correct color to use as the background
|
|
||||||
// for selected menu items. But that color is always blue, and doesn't follow the
|
|
||||||
// appearance color in system preferences. So we therefore deliberatly choose to use
|
|
||||||
// keyboardFocusIndicatorColor instead, which appears to have the same color value.
|
|
||||||
selectedMenuItemColor = [NSColor keyboardFocusIndicatorColor];
|
|
||||||
}
|
|
||||||
pal.setBrush(QPalette::Highlight, qt_mac_toQColor(selectedMenuItemColor));
|
|
||||||
qc = qt_mac_toQColor([NSColor labelColor]);
|
|
||||||
pal.setBrush(QPalette::ButtonText, qc);
|
|
||||||
pal.setBrush(QPalette::Text, qc);
|
|
||||||
qc = qt_mac_toQColor([NSColor selectedMenuItemTextColor]);
|
|
||||||
pal.setBrush(QPalette::HighlightedText, qc);
|
|
||||||
qc = qt_mac_toQColor([NSColor disabledControlTextColor]);
|
|
||||||
pal.setBrush(QPalette::Disabled, QPalette::Text, qc);
|
|
||||||
} else if ((mac_widget_colors[i].paletteRole == QPlatformTheme::ButtonPalette)
|
|
||||||
|| (mac_widget_colors[i].paletteRole == QPlatformTheme::HeaderPalette)
|
|
||||||
|| (mac_widget_colors[i].paletteRole == QPlatformTheme::TabBarPalette)) {
|
|
||||||
pal.setColor(QPalette::Disabled, QPalette::ButtonText,
|
|
||||||
pal.color(QPalette::Disabled, QPalette::Text));
|
|
||||||
pal.setColor(QPalette::Inactive, QPalette::ButtonText,
|
|
||||||
pal.color(QPalette::Inactive, QPalette::Text));
|
|
||||||
pal.setColor(QPalette::Active, QPalette::ButtonText,
|
|
||||||
pal.color(QPalette::Active, QPalette::Text));
|
|
||||||
} else if (mac_widget_colors[i].paletteRole == QPlatformTheme::ItemViewPalette) {
|
|
||||||
NSArray<NSColor *> *baseColors = nil;
|
|
||||||
NSColor *activeHighlightColor = nil;
|
|
||||||
if (__builtin_available(macOS 10.14, *)) {
|
|
||||||
baseColors = [NSColor alternatingContentBackgroundColors];
|
|
||||||
activeHighlightColor = [NSColor selectedContentBackgroundColor];
|
|
||||||
pal.setBrush(QPalette::Inactive, QPalette::HighlightedText,
|
|
||||||
qt_mac_toQBrush([NSColor unemphasizedSelectedTextColor]));
|
|
||||||
} else {
|
|
||||||
baseColors = [NSColor controlAlternatingRowBackgroundColors];
|
|
||||||
activeHighlightColor = [NSColor alternateSelectedControlColor];
|
|
||||||
pal.setBrush(QPalette::Inactive, QPalette::HighlightedText,
|
|
||||||
pal.brush(QPalette::Active, QPalette::Text));
|
|
||||||
}
|
|
||||||
pal.setBrush(QPalette::Base, qt_mac_toQBrush(baseColors[0]));
|
|
||||||
pal.setBrush(QPalette::AlternateBase, qt_mac_toQBrush(baseColors[1]));
|
|
||||||
pal.setBrush(QPalette::Active, QPalette::Highlight,
|
|
||||||
qt_mac_toQBrush(activeHighlightColor));
|
|
||||||
pal.setBrush(QPalette::Active, QPalette::HighlightedText,
|
|
||||||
qt_mac_toQBrush([NSColor alternateSelectedControlTextColor]));
|
|
||||||
pal.setBrush(QPalette::Inactive, QPalette::Text,
|
|
||||||
pal.brush(QPalette::Active, QPalette::Text));
|
|
||||||
} else if (mac_widget_colors[i].paletteRole == QPlatformTheme::TextEditPalette) {
|
|
||||||
pal.setBrush(QPalette::Active, QPalette::Base, qt_mac_toQColor([NSColor textBackgroundColor]));
|
|
||||||
pal.setBrush(QPalette::Inactive, QPalette::Text,
|
|
||||||
pal.brush(QPalette::Active, QPalette::Text));
|
|
||||||
pal.setBrush(QPalette::Inactive, QPalette::HighlightedText,
|
|
||||||
pal.brush(QPalette::Active, QPalette::Text));
|
|
||||||
} else if (mac_widget_colors[i].paletteRole == QPlatformTheme::TextLineEditPalette
|
|
||||||
|| mac_widget_colors[i].paletteRole == QPlatformTheme::ComboBoxPalette) {
|
|
||||||
pal.setBrush(QPalette::Active, QPalette::Base, qt_mac_toQColor([NSColor textBackgroundColor]));
|
|
||||||
pal.setBrush(QPalette::Disabled, QPalette::Base,
|
|
||||||
pal.brush(QPalette::Active, QPalette::Base));
|
|
||||||
} else if (mac_widget_colors[i].paletteRole == QPlatformTheme::LabelPalette) {
|
|
||||||
qc = qt_mac_toQColor([NSColor labelColor]);
|
|
||||||
pal.setBrush(QPalette::Inactive, QPalette::ToolTipText, qc);
|
|
||||||
}
|
|
||||||
palettes.insert(mac_widget_colors[i].paletteRole, &pal);
|
|
||||||
}
|
|
||||||
return palettes;
|
|
||||||
}
|
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
|
@ -1,234 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2018 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$
|
|
||||||
**
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
// This file is included from qnsview.mm, and only used to organize the code
|
|
||||||
|
|
||||||
@implementation QT_MANGLE_NAMESPACE(QNSView) (Drawing)
|
|
||||||
|
|
||||||
- (void)initDrawing
|
|
||||||
{
|
|
||||||
self.wantsLayer = [self layerExplicitlyRequested]
|
|
||||||
|| [self shouldUseMetalLayer]
|
|
||||||
|| [self layerEnabledByMacOS];
|
|
||||||
|
|
||||||
// Enable high-DPI OpenGL for retina displays. Enabling has the side
|
|
||||||
// effect that Cocoa will start calling glViewport(0, 0, width, height),
|
|
||||||
// overriding any glViewport calls in application code. This is usually not a
|
|
||||||
// problem, except if the application wants to have a "custom" viewport.
|
|
||||||
// (like the hellogl example)
|
|
||||||
if (m_platformWindow->window()->supportsOpenGL()) {
|
|
||||||
self.wantsBestResolutionOpenGLSurface = qt_mac_resolveOption(YES, m_platformWindow->window(),
|
|
||||||
"_q_mac_wantsBestResolutionOpenGLSurface", "QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
|
|
||||||
// See also QCocoaGLContext::makeCurrent for software renderer workarounds.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isOpaque
|
|
||||||
{
|
|
||||||
if (!m_platformWindow)
|
|
||||||
return true;
|
|
||||||
return m_platformWindow->isOpaque();
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isFlipped
|
|
||||||
{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
|
||||||
{
|
|
||||||
Q_UNUSED(dirtyRect);
|
|
||||||
|
|
||||||
if (!m_platformWindow)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QRegion exposedRegion;
|
|
||||||
const NSRect *dirtyRects;
|
|
||||||
NSInteger numDirtyRects;
|
|
||||||
[self getRectsBeingDrawn:&dirtyRects count:&numDirtyRects];
|
|
||||||
for (int i = 0; i < numDirtyRects; ++i)
|
|
||||||
exposedRegion += QRectF::fromCGRect(dirtyRects[i]).toRect();
|
|
||||||
|
|
||||||
qCDebug(lcQpaDrawing) << "[QNSView drawRect:]" << m_platformWindow->window() << exposedRegion;
|
|
||||||
m_platformWindow->handleExposeEvent(exposedRegion);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)layerEnabledByMacOS
|
|
||||||
{
|
|
||||||
// AppKit has its own logic for this, but if we rely on that, our layers are created
|
|
||||||
// by AppKit at a point where we've already set up other parts of the platform plugin
|
|
||||||
// based on the presence of layers or not. Once we've rewritten these parts to support
|
|
||||||
// dynamically picking up layer enablement we can let AppKit do its thing.
|
|
||||||
|
|
||||||
if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSBigSur)
|
|
||||||
return true; // Big Sur always enables layer-backing, regardless of SDK
|
|
||||||
|
|
||||||
if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave
|
|
||||||
&& QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave)
|
|
||||||
return true; // Mojave and Catalina enable layers based on the app's SDK
|
|
||||||
|
|
||||||
return false; // Prior versions needed explicitly enabled layer backing
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)layerExplicitlyRequested
|
|
||||||
{
|
|
||||||
static bool wantsLayer = [&]() {
|
|
||||||
int wantsLayer = qt_mac_resolveOption(-1, m_platformWindow->window(),
|
|
||||||
"_q_mac_wantsLayer", "QT_MAC_WANTS_LAYER");
|
|
||||||
|
|
||||||
if (wantsLayer != -1 && [self layerEnabledByMacOS]) {
|
|
||||||
qCWarning(lcQpaDrawing) << "Layer-backing can not be explicitly controlled on 10.14 when built against the 10.14 SDK";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return wantsLayer == 1;
|
|
||||||
}();
|
|
||||||
|
|
||||||
return wantsLayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)shouldUseMetalLayer
|
|
||||||
{
|
|
||||||
// MetalSurface needs a layer, and so does VulkanSurface (via MoltenVK)
|
|
||||||
QSurface::SurfaceType surfaceType = m_platformWindow->window()->surfaceType();
|
|
||||||
return surfaceType == QWindow::MetalSurface || surfaceType == QWindow::VulkanSurface;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CALayer *)makeBackingLayer
|
|
||||||
{
|
|
||||||
if ([self shouldUseMetalLayer]) {
|
|
||||||
// Check if Metal is supported. If it isn't then it's most likely
|
|
||||||
// too late at this point and the QWindow will be non-functional,
|
|
||||||
// but we can at least print a warning.
|
|
||||||
if (![MTLCreateSystemDefaultDevice() autorelease]) {
|
|
||||||
qWarning() << "QWindow initialization error: Metal is not supported";
|
|
||||||
return [super makeBackingLayer];
|
|
||||||
}
|
|
||||||
|
|
||||||
CAMetalLayer *layer = [CAMetalLayer layer];
|
|
||||||
|
|
||||||
// Set the contentsScale for the layer. This is normally done in
|
|
||||||
// viewDidChangeBackingProperties, however on startup that function
|
|
||||||
// is called before the layer is created here. The layer's drawableSize
|
|
||||||
// is updated from layoutSublayersOfLayer as usual.
|
|
||||||
layer.contentsScale = self.window.backingScaleFactor;
|
|
||||||
|
|
||||||
return layer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [super makeBackingLayer];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setLayer:(CALayer *)layer
|
|
||||||
{
|
|
||||||
qCDebug(lcQpaDrawing) << "Making" << self << "layer-backed with" << layer
|
|
||||||
<< "due to being" << ([self layerExplicitlyRequested] ? "explicitly requested"
|
|
||||||
: [self shouldUseMetalLayer] ? "needed by surface type" : "enabled by macOS");
|
|
||||||
[super setLayer:layer];
|
|
||||||
layer.delegate = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSViewLayerContentsRedrawPolicy)layerContentsRedrawPolicy
|
|
||||||
{
|
|
||||||
// We need to set this explicitly since the super implementation
|
|
||||||
// returns LayerContentsRedrawNever for custom layers like CAMetalLayer.
|
|
||||||
return NSViewLayerContentsRedrawDuringViewResize;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0 // Disabled until we enable lazy backingstore resizing
|
|
||||||
- (NSViewLayerContentsPlacement)layerContentsPlacement
|
|
||||||
{
|
|
||||||
// Always place the layer at top left without any automatic scaling,
|
|
||||||
// so that we can re-use larger layers when resizing a window down.
|
|
||||||
return NSViewLayerContentsPlacementTopLeft;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
- (void)updateMetalLayerDrawableSize:(CAMetalLayer *)layer
|
|
||||||
{
|
|
||||||
CGSize drawableSize = layer.bounds.size;
|
|
||||||
drawableSize.width *= layer.contentsScale;
|
|
||||||
drawableSize.height *= layer.contentsScale;
|
|
||||||
layer.drawableSize = drawableSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSublayersOfLayer:(CALayer *)layer
|
|
||||||
{
|
|
||||||
if ([layer isKindOfClass:CAMetalLayer.class])
|
|
||||||
[self updateMetalLayerDrawableSize:static_cast<CAMetalLayer* >(layer)];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)displayLayer:(CALayer *)layer
|
|
||||||
{
|
|
||||||
if (!NSThread.isMainThread) {
|
|
||||||
// Qt is calling AppKit APIs such as -[NSOpenGLContext setView:] on secondary threads,
|
|
||||||
// which we shouldn't do. This may result in AppKit (wrongly) triggering a display on
|
|
||||||
// the thread where we made the call, so block it here and defer to the main thread.
|
|
||||||
qCWarning(lcQpaDrawing) << "Display non non-main thread! Deferring to main thread";
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{ self.needsDisplay = YES; });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_ASSERT(layer == self.layer);
|
|
||||||
|
|
||||||
if (!m_platformWindow)
|
|
||||||
return;
|
|
||||||
|
|
||||||
qCDebug(lcQpaDrawing) << "[QNSView displayLayer]" << m_platformWindow->window();
|
|
||||||
|
|
||||||
// FIXME: Find out if there's a way to resolve the dirty rect like in drawRect:
|
|
||||||
m_platformWindow->handleExposeEvent(QRectF::fromCGRect(self.bounds).toRect());
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidChangeBackingProperties
|
|
||||||
{
|
|
||||||
CALayer *layer = self.layer;
|
|
||||||
if (!layer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
layer.contentsScale = self.window.backingScaleFactor;
|
|
||||||
|
|
||||||
// Metal layers must be manually updated on e.g. screen change
|
|
||||||
if ([layer isKindOfClass:CAMetalLayer.class]) {
|
|
||||||
[self updateMetalLayerDrawableSize:static_cast<CAMetalLayer* >(layer)];
|
|
||||||
[self setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user