diff --git a/.ci/linux-appimage/docker.sh b/.ci/linux-appimage/docker.sh index c3576cd332..b39522e9b3 100755 --- a/.ci/linux-appimage/docker.sh +++ b/.ci/linux-appimage/docker.sh @@ -17,12 +17,5 @@ rm -rf ./AppDir/usr/local #Circumvent missing LibFuse in Docker, by extracting the AppImage export APPIMAGE_EXTRACT_AND_RUN=1 -#Copy External Libraries -mkdir -p ./AppDir/usr/plugins/platformthemes -mkdir -p ./AppDir/usr/plugins/styles -cp /usr/lib/x86_64-linux-gnu/qt5/plugins/platformthemes/libqt5ct.so ./AppDir/usr/plugins/platformthemes -cp /usr/lib/x86_64-linux-gnu/qt5/plugins/platformthemes/libqgtk3.so ./AppDir/usr/plugins/platformthemes -cp /usr/lib/x86_64-linux-gnu/qt5/plugins/styles/libqt5ct-style.so ./AppDir/usr/plugins/styles - #Build AppImage -/linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage +QMAKE=/usr/lib/qt6/bin/qmake DEPLOY_PLATFORM_THEMES=1 /linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage diff --git a/.ci/linux-mingw/docker.sh b/.ci/linux-mingw/docker.sh index 95f0b27209..c26bfa30cc 100755 --- a/.ci/linux-mingw/docker.sh +++ b/.ci/linux-mingw/docker.sh @@ -17,15 +17,14 @@ echo 'Prepare binaries...' cd .. mkdir package -QT_PLATFORM_DLL_PATH='/usr/x86_64-w64-mingw32/lib/qt/plugins/platforms/' +QT_PLATFORM_DLL_PATH='/usr/x86_64-w64-mingw32/lib/qt6/plugins/platforms/' find build/ -name "citra*.exe" -exec cp {} 'package' \; # copy Qt plugins mkdir package/platforms cp "${QT_PLATFORM_DLL_PATH}/qwindows.dll" package/platforms/ -cp -rv "${QT_PLATFORM_DLL_PATH}/../mediaservice/" package/ +cp -rv "${QT_PLATFORM_DLL_PATH}/../multimedia/" package/ cp -rv "${QT_PLATFORM_DLL_PATH}/../imageformats/" package/ cp -rv "${QT_PLATFORM_DLL_PATH}/../styles/" package/ -rm -f package/mediaservice/*d.dll python3 .ci/linux-mingw/scan_dll.py package/*.exe package/imageformats/*.dll "package/" diff --git a/.ci/macos/deps.sh b/.ci/macos/deps.sh index 946043f5ca..ec9bb4311f 100755 --- a/.ci/macos/deps.sh +++ b/.ci/macos/deps.sh @@ -1,13 +1,3 @@ #!/bin/sh -ex brew install ccache ninja || true - -export QT_VER=5.15.8 - -mkdir tmp -cd tmp/ - -# install Qt -wget https://github.com/citra-emu/ext-macos-bin/raw/main/qt/qt-${QT_VER}.7z -7z x qt-${QT_VER}.7z -sudo cp -rv $(pwd)/qt-${QT_VER}/* /usr/local/ diff --git a/.ci/windows-msvc/build.sh b/.ci/windows-msvc/build.sh index fdd7574e36..68c2da01d2 100644 --- a/.ci/windows-msvc/build.sh +++ b/.ci/windows-msvc/build.sh @@ -6,7 +6,7 @@ cmake .. \ -G Ninja \ -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MSVCCache.cmake" \ -DCITRA_USE_CCACHE=ON \ - -DENABLE_QT_TRANSLATION=OFF \ + -DENABLE_QT_TRANSLATION=ON \ -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ -DUSE_DISCORD_PRESENCE=ON \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 60224f62cc..83b20193eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ option(USE_SYSTEM_SDL2 "Use the system SDL2 lib (instead of the bundled one)" OF # Set bundled qt as dependent options. option(ENABLE_QT "Enable the Qt frontend" ON) option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF) -CMAKE_DEPENDENT_OPTION(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF) +CMAKE_DEPENDENT_OPTION(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC OR APPLE" OFF) option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) if (MSVC) @@ -202,23 +202,23 @@ find_package(Threads REQUIRED) if (ENABLE_QT) if (CITRA_USE_BUNDLED_QT) - download_qt_external(5.15.2 QT_PREFIX) + download_qt_external(6.5.0 QT_PREFIX) list(APPEND CMAKE_PREFIX_PATH ${QT_PREFIX}) endif() - find_package(Qt5 REQUIRED COMPONENTS Widgets Multimedia Concurrent) + find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent) if (UNIX AND NOT APPLE) - find_package(Qt5 REQUIRED COMPONENTS DBus) + find_package(Qt6 REQUIRED COMPONENTS DBus) endif() if (ENABLE_QT_TRANSLATION) - find_package(Qt5 REQUIRED COMPONENTS LinguistTools) + find_package(Qt6 REQUIRED COMPONENTS LinguistTools) endif() if (NOT CITRA_USE_BUNDLED_QT) - # Make sure the Qt bin directory is in the prefix path for later consumers. - get_target_property(qmake_executable Qt5::qmake IMPORTED_LOCATION) + # Make sure the Qt bin directory is in the prefix path for later use, such as in post-build scripts. + get_target_property(qmake_executable Qt6::qmake IMPORTED_LOCATION) get_filename_component(qt_bin_dir "${qmake_executable}" DIRECTORY) list(APPEND CMAKE_PREFIX_PATH ${qt_bin_dir}) endif() @@ -282,8 +282,6 @@ if (APPLE) find_library(AVFOUNDATION_LIBRARY AVFoundation) set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${AVFOUNDATION_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY}) elseif (WIN32) - # WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista) - add_definitions(-D_WIN32_WINNT=0x0601 -DWINVER=0x0601) set(PLATFORM_LIBRARIES winmm ws2_32) if (MINGW) # PSAPI is the Process Status API diff --git a/CMakeModules/CopyCitraQt5Deps.cmake b/CMakeModules/CopyCitraQt5Deps.cmake deleted file mode 100644 index c2fdfb13c6..0000000000 --- a/CMakeModules/CopyCitraQt5Deps.cmake +++ /dev/null @@ -1,47 +0,0 @@ -function(copy_citra_Qt5_deps target_dir) - include(WindowsCopyFiles) - set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$/") - set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") - set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") - set(Qt5_MEDIASERVICE_DIR "${Qt5_DIR}/../../../plugins/mediaservice/") - set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/") - set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/") - set(PLATFORMS ${DLL_DEST}plugins/platforms/) - set(MEDIASERVICE ${DLL_DEST}plugins/mediaservice/) - set(STYLES ${DLL_DEST}plugins/styles/) - set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/) - windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} - icudt*.dll - icuin*.dll - icuuc*.dll - Qt5Core$<$:d>.* - Qt5Gui$<$:d>.* - Qt5Widgets$<$:d>.* - Qt5Concurrent$<$:d>.* - Qt5Multimedia$<$:d>.* - Qt5Network$<$:d>.* - ) - windows_copy_files(citra-qt ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$:d>.*) - windows_copy_files(citra-qt ${Qt5_MEDIASERVICE_DIR} ${MEDIASERVICE} - dsengine$<$:d>.* - wmfengine$<$:d>.* - ) - windows_copy_files(citra-qt ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$:d>.*) - windows_copy_files(${target_dir} ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} - qgif$<$:d>.dll - qicns$<$:d>.dll - qico$<$:d>.dll - qjpeg$<$:d>.dll - qsvg$<$:d>.dll - qtga$<$:d>.dll - qtiff$<$:d>.dll - qwbmp$<$:d>.dll - qwebp$<$:d>.dll - ) - - # Create an empty qt.conf file. Qt will detect that this file exists, and use the folder that its in as the root folder. - # This way it'll look for plugins in the root/plugins/ folder - add_custom_command(TARGET citra-qt POST_BUILD - COMMAND ${CMAKE_COMMAND} -E touch ${DLL_DEST}qt.conf - ) -endfunction(copy_citra_Qt5_deps) diff --git a/CMakeModules/CopyCitraQt6Deps.cmake b/CMakeModules/CopyCitraQt6Deps.cmake new file mode 100644 index 0000000000..4d5aa7d3f5 --- /dev/null +++ b/CMakeModules/CopyCitraQt6Deps.cmake @@ -0,0 +1,46 @@ +function(copy_citra_Qt6_deps target_dir) + include(WindowsCopyFiles) + set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$/") + set(Qt6_DLL_DIR "${Qt6_DIR}/../../../bin") + set(Qt6_PLATFORMS_DIR "${Qt6_DIR}/../../../plugins/platforms/") + set(Qt6_MULTIMEDIA_DIR "${Qt6_DIR}/../../../plugins/multimedia/") + set(Qt6_STYLES_DIR "${Qt6_DIR}/../../../plugins/styles/") + set(Qt6_IMAGEFORMATS_DIR "${Qt6_DIR}/../../../plugins/imageformats/") + set(PLATFORMS ${DLL_DEST}plugins/platforms/) + set(MULTIMEDIA ${DLL_DEST}plugins/multimedia/) + set(STYLES ${DLL_DEST}plugins/styles/) + set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/) + windows_copy_files(${target_dir} ${Qt6_DLL_DIR} ${DLL_DEST} + icudt*.dll + icuin*.dll + icuuc*.dll + Qt6Core$<$:d>.* + Qt6Gui$<$:d>.* + Qt6Widgets$<$:d>.* + Qt6Concurrent$<$:d>.* + Qt6Multimedia$<$:d>.* + Qt6Network$<$:d>.* + ) + windows_copy_files(citra-qt ${Qt6_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$:d>.*) + windows_copy_files(citra-qt ${Qt6_MULTIMEDIA_DIR} ${MULTIMEDIA} + windowsmediaplugin$<$:d>.* + ) + windows_copy_files(citra-qt ${Qt6_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$:d>.*) + windows_copy_files(${target_dir} ${Qt6_IMAGEFORMATS_DIR} ${IMAGEFORMATS} + qgif$<$:d>.dll + qicns$<$:d>.dll + qico$<$:d>.dll + qjpeg$<$:d>.dll + qsvg$<$:d>.dll + qtga$<$:d>.dll + qtiff$<$:d>.dll + qwbmp$<$:d>.dll + qwebp$<$:d>.dll + ) + + # Create an empty qt.conf file. Qt will detect that this file exists, and use the folder that its in as the root folder. + # This way it'll look for plugins in the root/plugins/ folder + add_custom_command(TARGET citra-qt POST_BUILD + COMMAND ${CMAKE_COMMAND} -E touch ${DLL_DEST}qt.conf + ) +endfunction(copy_citra_Qt6_deps) diff --git a/CMakeModules/DownloadExternals.cmake b/CMakeModules/DownloadExternals.cmake index bc6edc5816..bfdcf71aad 100644 --- a/CMakeModules/DownloadExternals.cmake +++ b/CMakeModules/DownloadExternals.cmake @@ -70,7 +70,7 @@ function(download_qt_external target prefix_var) set(install_args install-tool --outputdir ${base_path} ${host} desktop ${target}) else() set(prefix "${base_path}/${target}/${arch_path}") - set(install_args install-qt --outputdir ${base_path} ${host} desktop ${target} ${arch}) + set(install_args install-qt --outputdir ${base_path} ${host} desktop ${target} ${arch} -m qtmultimedia) endif() if (NOT EXISTS "${prefix}") diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 2bfc36c820..0678854887 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -209,7 +209,7 @@ if (ENABLE_QT_TRANSLATION) # Update source TS file if enabled if (GENERATE_QT_TRANSLATION) get_target_property(SRCS citra-qt SOURCES) - qt5_create_translation(QM_FILES ${SRCS} ${UIS} ${CITRA_QT_LANGUAGES}/en.ts) + qt6_create_translation(QM_FILES ${SRCS} ${UIS} ${CITRA_QT_LANGUAGES}/en.ts) add_custom_target(translation ALL DEPENDS ${CITRA_QT_LANGUAGES}/en.ts) endif() @@ -218,7 +218,7 @@ if (ENABLE_QT_TRANSLATION) list(REMOVE_ITEM LANGUAGES_TS ${CITRA_QT_LANGUAGES}/en.ts) # Compile TS files to QM files - qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) + qt6_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) # Build a QRC file from the QM file list set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) @@ -230,7 +230,7 @@ if (ENABLE_QT_TRANSLATION) file(APPEND ${LANGUAGES_QRC} "") # Add the QRC file to package in all QM files - qt5_add_resources(LANGUAGES ${LANGUAGES_QRC}) + qt6_add_resources(LANGUAGES ${LANGUAGES_QRC}) else() set(LANGUAGES) endif() @@ -257,7 +257,7 @@ if (APPLE) ) elseif(WIN32) # compile as a win32 gui application instead of a console application - target_link_libraries(citra-qt PRIVATE Qt5::WinMain) + target_link_libraries(citra-qt PRIVATE Qt6::EntryPointImplementation) if(MSVC) set_target_properties(citra-qt PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") elseif(MINGW) @@ -284,15 +284,15 @@ endif() create_target_directory_groups(citra-qt) target_link_libraries(citra-qt PRIVATE audio_core citra_common citra_core input_common network video_core) -target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Widgets Qt5::Multimedia Qt5::Concurrent) +target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt6::Widgets Qt6::Multimedia Qt6::Concurrent) target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) if (NOT WIN32) - target_include_directories(citra-qt PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) + target_include_directories(citra-qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) endif() if (UNIX AND NOT APPLE) - target_link_libraries(citra-qt PRIVATE Qt5::DBus) + target_link_libraries(citra-qt PRIVATE Qt6::DBus) endif() target_compile_definitions(citra-qt PRIVATE @@ -336,9 +336,9 @@ if(UNIX AND NOT APPLE) endif() if (MSVC) - include(CopyCitraQt5Deps) + include(CopyCitraQt6Deps) include(CopyCitraSDLDeps) - copy_citra_Qt5_deps(citra-qt) + copy_citra_Qt6_deps(citra-qt) copy_citra_SDL_deps(citra-qt) if (ENABLE_WEB_SERVICE AND OPENSSL_DLL_DIR) include(CopyCitraOpensslDeps) diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 1aa460eb25..4ae41f2ebb 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -533,7 +533,7 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { // TouchBegin always has exactly one touch point, so take the .first() - const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); + const auto [x, y] = ScaleTouch(event->points().first().position()); this->TouchPressed(x, y); } @@ -542,10 +542,10 @@ void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) { int active_points = 0; // average all active touch points - for (const auto& tp : event->touchPoints()) { + for (const auto& tp : event->points()) { if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) { active_points++; - pos += tp.pos(); + pos += tp.position(); } } diff --git a/src/citra_qt/camera/qt_multimedia_camera.cpp b/src/citra_qt/camera/qt_multimedia_camera.cpp index ec3a6f5a4c..972aa960fa 100644 --- a/src/citra_qt/camera/qt_multimedia_camera.cpp +++ b/src/citra_qt/camera/qt_multimedia_camera.cpp @@ -3,9 +3,8 @@ // Refer to the license.txt file included. #include -#include #include -#include +#include #include #include "citra_qt/camera/qt_multimedia_camera.h" #include "citra_qt/main.h" @@ -16,229 +15,85 @@ namespace Camera { -QList QtCameraSurface::supportedPixelFormats( - [[maybe_unused]] QAbstractVideoBuffer::HandleType handleType) const { - return QList() - << QVideoFrame::Format_RGB32 << QVideoFrame::Format_RGB24 - << QVideoFrame::Format_ARGB32_Premultiplied << QVideoFrame::Format_ARGB32 - << QVideoFrame::Format_RGB565 << QVideoFrame::Format_RGB555 - << QVideoFrame::Format_Jpeg - // the following formats are supported via Qt internal conversions - << QVideoFrame::Format_ARGB8565_Premultiplied << QVideoFrame::Format_BGRA32 - << QVideoFrame::Format_BGRA32_Premultiplied << QVideoFrame::Format_BGR32 - << QVideoFrame::Format_BGR24 << QVideoFrame::Format_BGR565 << QVideoFrame::Format_BGR555 - << QVideoFrame::Format_AYUV444 << QVideoFrame::Format_YUV444 - << QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12 << QVideoFrame::Format_UYVY - << QVideoFrame::Format_YUYV << QVideoFrame::Format_NV12 - << QVideoFrame::Format_NV21; // Supporting all the QImage convertible formats, ordered by - // QImage decoding performance -} - -bool QtCameraSurface::present(const QVideoFrame& frame) { - if (!frame.isValid()) { - return false; - } -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - QMutexLocker locker(&mutex); - // In Qt 5.15, the image is already flipped - current_frame = frame.image(); - locker.unlock(); -#else - QVideoFrame cloneFrame(frame); - cloneFrame.map(QAbstractVideoBuffer::ReadOnly); - const QImage image(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(), - QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat())); - QMutexLocker locker(&mutex); - current_frame = image.mirrored(true, true); - locker.unlock(); - cloneFrame.unmap(); -#endif - return true; -} - -QtMultimediaCamera::QtMultimediaCamera(const std::string& camera_name, - const Service::CAM::Flip& flip) - : QtCameraInterface(flip), handler(QtMultimediaCameraHandler::GetHandler(camera_name)) { - if (handler->thread() == QThread::currentThread()) { - handler->CreateCamera(camera_name); - } else { - QMetaObject::invokeMethod(handler.get(), "CreateCamera", Qt::BlockingQueuedConnection, - Q_ARG(const std::string&, camera_name)); - } -} - -QtMultimediaCamera::~QtMultimediaCamera() { - handler->StopCamera(); - QtMultimediaCameraHandler::ReleaseHandler(handler); -} - -void QtMultimediaCamera::StartCapture() { - if (handler->thread() == QThread::currentThread()) { - handler->StartCamera(); - } else { - QMetaObject::invokeMethod(handler.get(), "StartCamera", Qt::BlockingQueuedConnection); - } -} - -void QtMultimediaCamera::StopCapture() { - handler->StopCamera(); -} - -void QtMultimediaCamera::SetFrameRate(Service::CAM::FrameRate frame_rate) { - const std::array FrameRateList = { - /* Rate_15 */ QCamera::FrameRateRange(15, 15), - /* Rate_15_To_5 */ QCamera::FrameRateRange(5, 15), - /* Rate_15_To_2 */ QCamera::FrameRateRange(2, 15), - /* Rate_10 */ QCamera::FrameRateRange(10, 10), - /* Rate_8_5 */ QCamera::FrameRateRange(8.5, 8.5), - /* Rate_5 */ QCamera::FrameRateRange(5, 5), - /* Rate_20 */ QCamera::FrameRateRange(20, 20), - /* Rate_20_To_5 */ QCamera::FrameRateRange(5, 20), - /* Rate_30 */ QCamera::FrameRateRange(30, 30), - /* Rate_30_To_5 */ QCamera::FrameRateRange(5, 30), - /* Rate_15_To_10 */ QCamera::FrameRateRange(10, 15), - /* Rate_20_To_10 */ QCamera::FrameRateRange(10, 20), - /* Rate_30_To_10 */ QCamera::FrameRateRange(10, 30), - }; - - auto framerate = FrameRateList[static_cast(frame_rate)]; - - if (handler->camera->supportedViewfinderFrameRateRanges().contains(framerate)) { - handler->settings.setMinimumFrameRate(framerate.minimumFrameRate); - handler->settings.setMaximumFrameRate(framerate.maximumFrameRate); - } -} - -QImage QtMultimediaCamera::QtReceiveFrame() { - QMutexLocker locker(&handler->camera_surface.mutex); - return handler->camera_surface.current_frame; -} - -bool QtMultimediaCamera::IsPreviewAvailable() { - return handler->CameraAvailable(); -} - -std::unique_ptr QtMultimediaCameraFactory::Create(const std::string& config, - const Service::CAM::Flip& flip) { - return std::make_unique(config, flip); -} - -std::array, 3> QtMultimediaCameraHandler::handlers; - -std::array QtMultimediaCameraHandler::status; - -std::unordered_map> - QtMultimediaCameraHandler::loaded; - -void QtMultimediaCameraHandler::Init() { - std::generate(std::begin(handlers), std::end(handlers), - std::make_shared); -} - -std::shared_ptr QtMultimediaCameraHandler::GetHandler( +std::shared_ptr QtMultimediaCameraHandlerFactory::Create( const std::string& camera_name) { - if (loaded.count(camera_name)) { - return loaded.at(camera_name); - } - for (std::size_t i = 0; i < handlers.size(); i++) { - if (!status[i]) { - LOG_INFO(Service_CAM, "Successfully got handler {}", i); - status[i] = true; - loaded.emplace(camera_name, handlers[i]); - return handlers[i]; + if (thread() == QThread::currentThread()) { + std::shared_ptr handler; + if (!handlers.contains(camera_name) || !(handler = handlers[camera_name].lock())) { + LOG_INFO(Service_CAM, "Creating new handler for camera '{}'", camera_name); + handler = std::make_shared(camera_name); + handlers[camera_name] = handler; + } else { + LOG_INFO(Service_CAM, "Reusing existing handler for camera '{}'", camera_name); } + return handler; + } else { + std::shared_ptr handler; + QMetaObject::invokeMethod(this, "Create", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(std::shared_ptr, handler), + Q_ARG(std::string, camera_name)); + return handler; } - LOG_CRITICAL(Service_CAM, "All handlers taken up"); - return nullptr; } -void QtMultimediaCameraHandler::ReleaseHandler( - const std::shared_ptr& handler) { - for (std::size_t i = 0; i < handlers.size(); i++) { - if (handlers[i] == handler) { - LOG_INFO(Service_CAM, "Successfully released handler {}", i); - status[i] = false; - handlers[i]->started = false; - for (auto it = loaded.begin(); it != loaded.end(); it++) { - if (it->second == handlers[i]) { - loaded.erase(it); - break; - } - } - break; +void QtMultimediaCameraHandlerFactory::PauseCameras() { + LOG_INFO(Service_CAM, "Pausing all cameras"); + for (auto& handler_pair : handlers) { + auto handler = handler_pair.second.lock(); + if (handler && handler->IsActive()) { + handler->PauseCapture(); } } } -void QtMultimediaCameraHandler::CreateCamera(const std::string& camera_name) { - QList cameras = QCameraInfo::availableCameras(); - for (const QCameraInfo& cameraInfo : cameras) { - if (cameraInfo.deviceName().toStdString() == camera_name) - camera = std::make_unique(cameraInfo); +void QtMultimediaCameraHandlerFactory::ResumeCameras() { + LOG_INFO(Service_CAM, "Resuming all cameras"); + for (auto& handler_pair : handlers) { + auto handler = handler_pair.second.lock(); + if (handler && handler->IsPaused()) { + handler->StartCapture(); + } } - if (!camera) { // no cameras found, using default camera +} + +QtMultimediaCameraHandler::QtMultimediaCameraHandler(const std::string& camera_name) { + auto cameras = QMediaDevices::videoInputs(); + auto requested_camera = + std::find_if(cameras.begin(), cameras.end(), [camera_name](QCameraDevice& camera_info) { + return camera_info.description().toStdString() == camera_name; + }); + if (requested_camera != cameras.end()) { + camera = std::make_unique(*requested_camera); + } else { camera = std::make_unique(); } - settings.setMinimumFrameRate(30); - settings.setMaximumFrameRate(30); - camera->setViewfinder(&camera_surface); - camera->load(); - if (camera->supportedViewfinderPixelFormats().isEmpty()) { - // The gstreamer plugin (used on linux systems) returns an empty list on querying supported - // viewfinder pixel formats, and will not work without expliciting setting it to some value, - // so we are defaulting to RGB565 here which should be fairly widely supported. - settings.setPixelFormat(QVideoFrame::PixelFormat::Format_RGB565); - } + camera_surface = std::make_unique(); + capture_session.setVideoSink(camera_surface.get()); + capture_session.setCamera(camera.get()); } -void QtMultimediaCameraHandler::StopCamera() { - camera->stop(); - started = false; +QtMultimediaCameraHandler::~QtMultimediaCameraHandler() { + StopCapture(); } -void QtMultimediaCameraHandler::StartCamera() { +void QtMultimediaCameraHandler::StartCapture() { + if (!camera->isActive()) { #if defined(__APPLE__) - if (!AppleAuthorization::CheckAuthorizationForCamera()) { - LOG_ERROR(Service_CAM, "Unable to start camera due to lack of authorization"); - return; - } + if (!AppleAuthorization::CheckAuthorizationForCamera()) { + LOG_ERROR(Service_CAM, "Unable to start camera due to lack of authorization"); + return; + } #endif - camera->setViewfinderSettings(settings); - camera->start(); - started = true; + camera->start(); + } paused = false; } -bool QtMultimediaCameraHandler::CameraAvailable() const { - return camera && camera->isAvailable(); -} - -void QtMultimediaCameraHandler::StopCameras() { - LOG_INFO(Service_CAM, "Stopping all cameras"); - for (auto& handler : handlers) { - if (handler && handler->started) { - handler->StopCamera(); - handler->paused = true; - } - } -} - -void QtMultimediaCameraHandler::ResumeCameras() { - for (auto& handler : handlers) { - if (handler && handler->paused) { - handler->StartCamera(); - } - } -} - -void QtMultimediaCameraHandler::ReleaseHandlers() { - StopCameras(); - LOG_INFO(Service_CAM, "Releasing all handlers"); - for (std::size_t i = 0; i < handlers.size(); i++) { - status[i] = false; - handlers[i]->started = false; - handlers[i]->paused = false; +void QtMultimediaCameraHandler::StopCapture() { + if (camera->isActive()) { + camera->stop(); } + paused = false; } } // namespace Camera diff --git a/src/citra_qt/camera/qt_multimedia_camera.h b/src/citra_qt/camera/qt_multimedia_camera.h index 0990009273..44acb7bbd4 100644 --- a/src/citra_qt/camera/qt_multimedia_camera.h +++ b/src/citra_qt/camera/qt_multimedia_camera.h @@ -4,99 +4,113 @@ #pragma once -#include #include #include -#include -#include #include -#include #include -#include +#include +#include #include "citra_qt/camera/camera_util.h" #include "citra_qt/camera/qt_camera_base.h" #include "core/frontend/camera/interface.h" -class GMainWindow; - namespace Camera { -class QtCameraSurface final : public QAbstractVideoSurface { +// NOTE: Must be created on the Qt thread. QtMultimediaCameraHandlerFactory ensures this. +class QtMultimediaCameraHandler final : public QObject { + Q_OBJECT + public: - QList supportedPixelFormats( - QAbstractVideoBuffer::HandleType) const override; - bool present(const QVideoFrame&) override; + explicit QtMultimediaCameraHandler(const std::string& camera_name); + ~QtMultimediaCameraHandler(); + + void StartCapture(); + void StopCapture(); + + QImage QtReceiveFrame() { + return camera_surface->videoFrame().toImage(); + } + + bool IsPreviewAvailable() { + return camera->isAvailable(); + } + + bool IsActive() { + return camera->isActive(); + } + + [[nodiscard]] bool IsPaused() { + return paused; + } + + void PauseCapture() { + StopCapture(); + paused = true; + } private: - QMutex mutex; - QImage current_frame; - - friend class QtMultimediaCamera; // For access to current_frame + std::unique_ptr camera; + std::unique_ptr camera_surface; + QMediaCaptureSession capture_session{}; + bool paused = false; // was previously started but was paused, to be resumed }; -class QtMultimediaCameraHandler; +// NOTE: Must be created on the Qt thread. +class QtMultimediaCameraHandlerFactory final : public QObject { + Q_OBJECT + +public: + Q_INVOKABLE std::shared_ptr Create(const std::string& camera_name); + void PauseCameras(); + void ResumeCameras(); + +private: + std::unordered_map> handlers; +}; /// This class is only an interface. It just calls QtMultimediaCameraHandler. class QtMultimediaCamera final : public QtCameraInterface { public: - QtMultimediaCamera(const std::string& camera_name, const Service::CAM::Flip& flip); - ~QtMultimediaCamera(); - void StartCapture() override; - void StopCapture() override; - void SetFrameRate(Service::CAM::FrameRate frame_rate) override; - QImage QtReceiveFrame() override; - bool IsPreviewAvailable() override; + QtMultimediaCamera(const std::shared_ptr& handler, + const Service::CAM::Flip& flip) + : QtCameraInterface(flip), handler(handler) {} + + void StartCapture() override { + handler->StartCapture(); + } + + void StopCapture() override { + handler->StopCapture(); + } + + void SetFrameRate(Service::CAM::FrameRate frame_rate) override {} + + QImage QtReceiveFrame() override { + return handler->QtReceiveFrame(); + } + + bool IsPreviewAvailable() override { + return handler->IsPreviewAvailable(); + } private: std::shared_ptr handler; }; +/// This class is only an interface. It just calls QtMultimediaCameraHandlerFactory. class QtMultimediaCameraFactory final : public QtCameraFactory { public: + QtMultimediaCameraFactory( + const std::shared_ptr& handler_factory) + : handler_factory(handler_factory) {} + std::unique_ptr Create(const std::string& config, - const Service::CAM::Flip& flip) override; -}; - -class QtMultimediaCameraHandler final : public QObject { - Q_OBJECT - -public: - /// Creates the global handler. Must be called in UI thread. - static void Init(); - static std::shared_ptr GetHandler(const std::string& camera_name); - static void ReleaseHandler(const std::shared_ptr& handler); - - /** - * Creates the camera. - * Note: This function must be called via QMetaObject::invokeMethod in UI thread. - */ - Q_INVOKABLE void CreateCamera(const std::string& camera_name); - - /** - * Starts the camera. - * Note: This function must be called via QMetaObject::invokeMethod in UI thread when - * starting the camera for the first time. 'Resume' calls can be in other threads. - */ - Q_INVOKABLE void StartCamera(); - - void StopCamera(); - bool CameraAvailable() const; - static void StopCameras(); - static void ResumeCameras(); - static void ReleaseHandlers(); + const Service::CAM::Flip& flip) override { + return std::make_unique(handler_factory->Create(config), flip); + } private: - std::unique_ptr camera; - QtCameraSurface camera_surface{}; - QCameraViewfinderSettings settings; - bool started = false; - bool paused = false; // was previously started but was paused, to be resumed - - static std::array, 3> handlers; - static std::array status; - static std::unordered_map> loaded; - - friend class QtMultimediaCamera; // For access to camera_surface (and camera) + std::shared_ptr handler_factory; }; } // namespace Camera diff --git a/src/citra_qt/configuration/configure_camera.cpp b/src/citra_qt/configuration/configure_camera.cpp index 79f21bb74e..deb979d2ef 100644 --- a/src/citra_qt/configuration/configure_camera.cpp +++ b/src/citra_qt/configuration/configure_camera.cpp @@ -2,16 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include +#include #include #include #include +#include #include #include #include "citra_qt/configuration/configure_camera.h" #include "common/settings.h" #include "core/frontend/camera/factory.h" -#include "core/frontend/camera/interface.h" #include "core/hle/service/cam/cam.h" #include "ui_configure_camera.h" @@ -32,9 +32,9 @@ ConfigureCamera::ConfigureCamera(QWidget* parent) camera_name = Settings::values.camera_name; camera_config = Settings::values.camera_config; camera_flip = Settings::values.camera_flip; - QList cameras = QCameraInfo::availableCameras(); - for (const QCameraInfo& cameraInfo : cameras) { - ui->system_camera->addItem(cameraInfo.deviceName()); + const QList cameras = QMediaDevices::videoInputs(); + for (const QCameraDevice& camera : cameras) { + ui->system_camera->addItem(camera.description()); } UpdateCameraMode(); SetConfiguration(); diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp index b13013c1a1..77c7f601f4 100644 --- a/src/citra_qt/configuration/configure_input.cpp +++ b/src/citra_qt/configuration/configure_input.cpp @@ -579,7 +579,7 @@ void ConfigureInput::AutoMap() { void ConfigureInput::HandleClick(QPushButton* button, std::function new_input_setter, InputCommon::Polling::DeviceType type) { - previous_key_code = QKeySequence(button->text())[0]; + previous_key_code = QKeySequence(button->text())[0].toCombined(); button->setText(tr("[press key]")); button->setFocus(); diff --git a/src/citra_qt/configuration/configure_motion_touch.cpp b/src/citra_qt/configuration/configure_motion_touch.cpp index ed0c0b058a..3bfbba71d8 100644 --- a/src/citra_qt/configuration/configure_motion_touch.cpp +++ b/src/citra_qt/configuration/configure_motion_touch.cpp @@ -234,6 +234,8 @@ void ConfigureMotionTouch::ConnectEvents() { &ConfigureMotionTouch::OnConfigureTouchCalibration); connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this, &ConfigureMotionTouch::OnConfigureTouchFromButton); + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, + &ConfigureMotionTouch::ApplyConfiguration); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { if (CanCloseDialog()) { reject(); diff --git a/src/citra_qt/configuration/configure_motion_touch.ui b/src/citra_qt/configuration/configure_motion_touch.ui index 292b5c5bda..7f4a5c36ae 100644 --- a/src/citra_qt/configuration/configure_motion_touch.ui +++ b/src/citra_qt/configuration/configure_motion_touch.ui @@ -324,22 +324,4 @@ - - - buttonBox - accepted() - ConfigureMotionTouch - ApplyConfiguration() - - - 220 - 380 - - - 220 - 200 - - - - diff --git a/src/citra_qt/configuration/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp index 0b86906edc..8cfbefedf8 100644 --- a/src/citra_qt/configuration/configure_system.cpp +++ b/src/citra_qt/configuration/configure_system.cpp @@ -283,7 +283,7 @@ void ConfigureSystem::SetConfiguration() { ui->combo_init_clock->setCurrentIndex(static_cast(Settings::values.init_clock.GetValue())); QDateTime date_time; - date_time.setTime_t(Settings::values.init_time.GetValue()); + date_time.setSecsSinceEpoch(Settings::values.init_time.GetValue()); ui->edit_init_time->setDateTime(date_time); long long init_time_offset = Settings::values.init_time_offset.GetValue(); @@ -406,7 +406,7 @@ void ConfigureSystem::ApplyConfiguration() { Settings::values.init_clock = static_cast(ui->combo_init_clock->currentIndex()); - Settings::values.init_time = ui->edit_init_time->dateTime().toTime_t(); + Settings::values.init_time = ui->edit_init_time->dateTime().toSecsSinceEpoch(); s64 time_offset_time = ui->edit_init_time_offset_time->time().msecsSinceStartOfDay() / 1000; s64 time_offset_days = ui->edit_init_time_offset_days->value() * 86400; diff --git a/src/citra_qt/configuration/configure_touch_from_button.cpp b/src/citra_qt/configuration/configure_touch_from_button.cpp index 6dcd1cc4f6..76f2a77bc2 100644 --- a/src/citra_qt/configuration/configure_touch_from_button.cpp +++ b/src/citra_qt/configuration/configure_touch_from_button.cpp @@ -509,7 +509,8 @@ void TouchScreenPreview::mouseMoveEvent(QMouseEvent* event) { if (!coord_label) { return; } - const auto pos = MapToDeviceCoords(event->x(), event->y()); + const auto point = event->position().toPoint(); + const auto pos = MapToDeviceCoords(point.x(), point.y()); if (pos) { coord_label->setText(QStringLiteral("X: %1, Y: %2").arg(pos->x()).arg(pos->y())); } else { @@ -527,7 +528,8 @@ void TouchScreenPreview::mousePressEvent(QMouseEvent* event) { if (event->button() != Qt::MouseButton::LeftButton) { return; } - const auto pos = MapToDeviceCoords(event->x(), event->y()); + const auto point = event->position().toPoint(); + const auto pos = MapToDeviceCoords(point.x(), point.y()); if (pos) { emit DotAdded(*pos); } @@ -543,7 +545,7 @@ bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) { emit DotSelected(obj->property(PropId).toInt()); drag_state.dot = qobject_cast(obj); - drag_state.start_pos = mouse_event->globalPos(); + drag_state.start_pos = mouse_event->globalPosition().toPoint(); return true; } case QEvent::Type::MouseMove: { @@ -552,14 +554,13 @@ bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) { } const auto mouse_event = static_cast(event); if (!drag_state.active) { - drag_state.active = - (mouse_event->globalPos() - drag_state.start_pos).manhattanLength() >= - QApplication::startDragDistance(); + drag_state.active = (mouse_event->globalPosition().toPoint() - drag_state.start_pos) + .manhattanLength() >= QApplication::startDragDistance(); if (!drag_state.active) { break; } } - auto current_pos = mapFromGlobal(mouse_event->globalPos()); + auto current_pos = mapFromGlobal(mouse_event->globalPosition().toPoint()); current_pos.setX(std::clamp(current_pos.x(), contentsMargins().left(), contentsMargins().left() + contentsRect().width() - 1)); current_pos.setY(std::clamp(current_pos.y(), contentsMargins().top(), diff --git a/src/citra_qt/debugger/graphics/graphics_surface.cpp b/src/citra_qt/debugger/graphics/graphics_surface.cpp index b86d4e2d61..9089c5724d 100644 --- a/src/citra_qt/debugger/graphics/graphics_surface.cpp +++ b/src/citra_qt/debugger/graphics/graphics_surface.cpp @@ -40,8 +40,9 @@ void SurfacePicture::mousePressEvent(QMouseEvent* event) { } if (surface_widget) { - surface_widget->Pick(event->x() * pixmap.width() / width(), - event->y() * pixmap.height() / height()); + const auto pos = event->position().toPoint(); + surface_widget->Pick(pos.x() * pixmap.width() / width(), + pos.y() * pixmap.height() / height()); } } diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp index 06ae3bdaa2..dbeade3cd7 100644 --- a/src/citra_qt/debugger/profiler.cpp +++ b/src/citra_qt/debugger/profiler.cpp @@ -142,24 +142,28 @@ void MicroProfileWidget::hideEvent(QHideEvent* event) { } void MicroProfileWidget::mouseMoveEvent(QMouseEvent* event) { - MicroProfileMousePosition(event->x() / x_scale, event->y() / y_scale, 0); + const auto point = event->position().toPoint(); + MicroProfileMousePosition(point.x() / x_scale, point.y() / y_scale, 0); event->accept(); } void MicroProfileWidget::mousePressEvent(QMouseEvent* event) { - MicroProfileMousePosition(event->x() / x_scale, event->y() / y_scale, 0); + const auto point = event->position().toPoint(); + MicroProfileMousePosition(point.x() / x_scale, point.y() / y_scale, 0); MicroProfileMouseButton(event->buttons() & Qt::LeftButton, event->buttons() & Qt::RightButton); event->accept(); } void MicroProfileWidget::mouseReleaseEvent(QMouseEvent* event) { - MicroProfileMousePosition(event->x() / x_scale, event->y() / y_scale, 0); + const auto point = event->position().toPoint(); + MicroProfileMousePosition(point.x() / x_scale, point.y() / y_scale, 0); MicroProfileMouseButton(event->buttons() & Qt::LeftButton, event->buttons() & Qt::RightButton); event->accept(); } void MicroProfileWidget::wheelEvent(QWheelEvent* event) { - MicroProfileMousePosition(event->position().x() / x_scale, event->position().y() / y_scale, + const auto point = event->position().toPoint(); + MicroProfileMousePosition(point.x() / x_scale, point.y() / y_scale, event->angleDelta().y() / 120); event->accept(); } diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index 9fe4b4c0e6..19ad5f75fe 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -239,7 +239,8 @@ void GameList::OnTextChanged(const QString& new_text) { file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + file_title; if (ContainsAllWords(file_name, edit_filter_text) || - (file_program_id.count() == 16 && edit_filter_text.contains(file_program_id))) { + (file_program_id.length() == 16 && + edit_filter_text.contains(file_program_id))) { tree_view->setRowHidden(j, folder_index, false); ++result_count; } else { @@ -419,10 +420,10 @@ void GameList::DonePopulating(const QStringList& watch_list) { // Workaround: Add the watch paths in chunks to allow the gui to refresh // This prevents the UI from stalling when a large number of watch paths are added // Also artificially caps the watcher to a certain number of directories - constexpr int LIMIT_WATCH_DIRECTORIES = 5000; + constexpr qsizetype LIMIT_WATCH_DIRECTORIES = 5000; constexpr int SLICE_SIZE = 25; - int len = std::min(watch_list.length(), LIMIT_WATCH_DIRECTORIES); - for (int i = 0; i < len; i += SLICE_SIZE) { + const qsizetype len = std::min(watch_list.length(), LIMIT_WATCH_DIRECTORIES); + for (qsizetype i = 0; i < len; i += SLICE_SIZE) { watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE)); QCoreApplication::processEvents(); } diff --git a/src/citra_qt/hotkeys.cpp b/src/citra_qt/hotkeys.cpp index 89f5c809a2..bce0f64026 100644 --- a/src/citra_qt/hotkeys.cpp +++ b/src/citra_qt/hotkeys.cpp @@ -26,7 +26,7 @@ void HotkeyRegistry::SaveHotkeys() { void HotkeyRegistry::LoadHotkeys() { // Make sure NOT to use a reference here because it would become invalid once we call // beginGroup() - for (auto shortcut : UISettings::values.shortcuts) { + for (const auto shortcut : UISettings::values.shortcuts) { Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; if (!shortcut.shortcut.keyseq.isEmpty()) { hk.keyseq = @@ -40,7 +40,7 @@ void HotkeyRegistry::LoadHotkeys() { } } -QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { +QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QObject* widget) { Hotkey& hk = hotkey_groups[group][action]; if (!hk.shortcut) { diff --git a/src/citra_qt/hotkeys.h b/src/citra_qt/hotkeys.h index 85f4535293..1c10b04a7b 100644 --- a/src/citra_qt/hotkeys.h +++ b/src/citra_qt/hotkeys.h @@ -47,7 +47,7 @@ public: * will be the same. Thus, you shouldn't rely on the caller really being the * QShortcut's parent. */ - QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); + QShortcut* GetHotkey(const QString& group, const QString& action, QObject* widget); /** * Returns a QKeySequence object whose signal can be connected to QAction::setShortcut. diff --git a/src/citra_qt/loading_screen.cpp b/src/citra_qt/loading_screen.cpp index 7b9537ac95..56e4b4d64a 100644 --- a/src/citra_qt/loading_screen.cpp +++ b/src/citra_qt/loading_screen.cpp @@ -198,7 +198,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size void LoadingScreen::paintEvent(QPaintEvent* event) { QStyleOption opt; - opt.init(this); + opt.initFrom(this); QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); QWidget::paintEvent(event); diff --git a/src/citra_qt/loading_screen.h b/src/citra_qt/loading_screen.h index fb18f2be90..98d18e2433 100644 --- a/src/citra_qt/loading_screen.h +++ b/src/citra_qt/loading_screen.h @@ -73,5 +73,3 @@ private: std::chrono::duration rolling_average = {}; bool eta_shown = false; }; - -Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage); diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 5464689147..6a99129c60 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -200,6 +199,11 @@ GMainWindow::GMainWindow() qRegisterMetaType("std::size_t"); qRegisterMetaType("Service::AM::InstallStatus"); + // Register CameraFactory + qt_cameras = std::make_shared(); + Camera::RegisterFactory("image", std::make_unique()); + Camera::RegisterFactory("qt", std::make_unique(qt_cameras)); + LoadTranslation(); Pica::g_debug_context = Pica::DebugContext::Construct(); @@ -647,7 +651,7 @@ void GMainWindow::ShowUpdaterWidgets() { void GMainWindow::SetDefaultUIGeometry() { // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half - const QRect screenRect = QApplication::desktop()->screenGeometry(this); + const QRect screenRect = screen()->geometry(); const int w = screenRect.width() * 2 / 3; const int h = screenRect.height() / 2; @@ -1284,8 +1288,6 @@ void GMainWindow::ShutdownGame() { discord_rpc->Update(); - Camera::QtMultimediaCameraHandler::ReleaseHandlers(); - // The emulation is stopped, so closing the window or not does not matter anymore disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); disconnect(secondary_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); @@ -1344,7 +1346,7 @@ void GMainWindow::StoreRecentFile(const QString& filename) { void GMainWindow::UpdateRecentFiles() { const int num_recent_files = - std::min(UISettings::values.recent_files.size(), max_recent_files_item); + std::min(static_cast(UISettings::values.recent_files.size()), max_recent_files_item); for (int i = 0; i < num_recent_files; i++) { const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg( @@ -1648,7 +1650,7 @@ void GMainWindow::InstallCIA(QStringList filepaths) { progress_bar->show(); progress_bar->setMaximum(INT_MAX); - QtConcurrent::run([&, filepaths] { + (void)QtConcurrent::run([&, filepaths] { Service::AM::InstallStatus status; const auto cia_progress = [&](std::size_t written, std::size_t total) { emit UpdateProgress(written, total); @@ -1724,7 +1726,7 @@ void GMainWindow::OnMenuRecentFile() { } void GMainWindow::OnStartGame() { - Camera::QtMultimediaCameraHandler::ResumeCameras(); + qt_cameras->ResumeCameras(); PreventOSSleep(); @@ -1751,7 +1753,7 @@ void GMainWindow::OnRestartGame() { void GMainWindow::OnPauseGame() { emu_thread->SetRunning(false); - Camera::QtMultimediaCameraHandler::StopCameras(); + qt_cameras->PauseCameras(); UpdateMenuState(); AllowOSSleep(); @@ -2690,7 +2692,7 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { #undef main #endif -static void SetHighDPIAttributes() { +static Qt::HighDpiScaleFactorRoundingPolicy GetHighDpiRoundingPolicy() { #ifdef _WIN32 // For Windows, we want to avoid scaling artifacts on fractional scaling ratios. // This is done by setting the optimal scaling policy for the primary screen. @@ -2703,40 +2705,34 @@ static void SetHighDPIAttributes() { // Get the current screen geometry. const QScreen* primary_screen = QGuiApplication::primaryScreen(); if (primary_screen == nullptr) { - return; + return Qt::HighDpiScaleFactorRoundingPolicy::PassThrough; } const QRect screen_rect = primary_screen->geometry(); - const int real_width = screen_rect.width(); - const int real_height = screen_rect.height(); - const float real_ratio = primary_screen->logicalDotsPerInch() / 96.0f; + const qreal real_ratio = primary_screen->devicePixelRatio(); + const qreal real_width = std::trunc(screen_rect.width() * real_ratio); + const qreal real_height = std::trunc(screen_rect.height() * real_ratio); // Recommended minimum width and height for proper window fit. // Any screen with a lower resolution than this will still have a scale of 1. - constexpr float minimum_width = 1350.0f; - constexpr float minimum_height = 900.0f; + constexpr qreal minimum_width = 1350.0; + constexpr qreal minimum_height = 900.0; - const float width_ratio = std::max(1.0f, real_width / minimum_width); - const float height_ratio = std::max(1.0f, real_height / minimum_height); + const qreal width_ratio = std::max(1.0, real_width / minimum_width); + const qreal height_ratio = std::max(1.0, real_height / minimum_height); // Get the lower of the 2 ratios and truncate, this is the maximum integer scale. - const float max_ratio = std::trunc(std::min(width_ratio, height_ratio)); + const qreal max_ratio = std::trunc(std::min(width_ratio, height_ratio)); if (max_ratio > real_ratio) { - QApplication::setHighDpiScaleFactorRoundingPolicy( - Qt::HighDpiScaleFactorRoundingPolicy::Round); + return Qt::HighDpiScaleFactorRoundingPolicy::Round; } else { - QApplication::setHighDpiScaleFactorRoundingPolicy( - Qt::HighDpiScaleFactorRoundingPolicy::Floor); + return Qt::HighDpiScaleFactorRoundingPolicy::Floor; } #else // Other OSes should be better than Windows at fractional scaling. - QApplication::setHighDpiScaleFactorRoundingPolicy( - Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); + return Qt::HighDpiScaleFactorRoundingPolicy::PassThrough; #endif - - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); } int main(int argc, char* argv[]) { @@ -2748,16 +2744,14 @@ int main(int argc, char* argv[]) { QCoreApplication::setOrganizationName(QStringLiteral("Citra team")); QCoreApplication::setApplicationName(QStringLiteral("Citra")); - SetHighDPIAttributes(); + auto rounding_policy = GetHighDpiRoundingPolicy(); + QApplication::setHighDpiScaleFactorRoundingPolicy(rounding_policy); #ifdef __APPLE__ std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + ".."; chdir(bin_path.c_str()); #endif -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - // Disables the "?" button on all dialogs. Disabled by default on Qt6. - QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); -#endif + QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); QApplication app(argc, argv); @@ -2768,11 +2762,6 @@ int main(int argc, char* argv[]) { GMainWindow main_window; - // Register CameraFactory - Camera::RegisterFactory("image", std::make_unique()); - Camera::RegisterFactory("qt", std::make_unique()); - Camera::QtMultimediaCameraHandler::Init(); - // Register frontend applets Frontend::RegisterDefaultApplets(); diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index a36fd83c5f..a5ee269f63 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -49,6 +49,10 @@ class RegistersWidget; class Updater; class WaitTreeWidget; +namespace Camera { +class QtMultimediaCameraHandlerFactory; +} + namespace DiscordRPC { class DiscordInterface; } @@ -335,6 +339,8 @@ private: HotkeyRegistry hotkey_registry; + std::shared_ptr qt_cameras; + #ifdef __unix__ QDBusObjectPath wake_lock{}; #endif diff --git a/src/citra_qt/multiplayer/direct_connect.cpp b/src/citra_qt/multiplayer/direct_connect.cpp index 6cef558450..3b2b6549e4 100644 --- a/src/citra_qt/multiplayer/direct_connect.cpp +++ b/src/citra_qt/multiplayer/direct_connect.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include "citra_qt/main.h" diff --git a/src/citra_qt/multiplayer/validation.h b/src/citra_qt/multiplayer/validation.h index ecf3069d96..8763459edb 100644 --- a/src/citra_qt/multiplayer/validation.h +++ b/src/citra_qt/multiplayer/validation.h @@ -4,7 +4,7 @@ #pragma once -#include +#include #include #include @@ -30,15 +30,17 @@ public: private: /// room name can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20 - QRegExp room_name_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$")); - QRegExpValidator room_name; + QRegularExpression room_name_regex = + QRegularExpression(QStringLiteral("^[a-zA-Z0-9._\\- ]{4,20}$")); + QRegularExpressionValidator room_name; /// nickname can be alphanumeric and " " "_" "." and "-" and must have a size of 4-20 - QRegExp nickname_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$")); - QRegExpValidator nickname; + QRegularExpression nickname_regex = + QRegularExpression(QStringLiteral("^[a-zA-Z0-9._\\- ]{4,20}$")); + QRegularExpressionValidator nickname; /// ipv4 / ipv6 / hostnames - QRegExp ip_regex = QRegExp(QStringLiteral( + QRegularExpression ip_regex = QRegularExpression(QStringLiteral( // IPv4 regex "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|" // IPv6 regex @@ -59,7 +61,7 @@ private: "\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?$|" // Hostname regex "^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}$")); - QRegExpValidator ip; + QRegularExpressionValidator ip; /// port must be between 0 and 65535 QIntValidator port; diff --git a/src/citra_qt/util/spinbox.cpp b/src/citra_qt/util/spinbox.cpp index b635c3a15a..18af19695a 100644 --- a/src/citra_qt/util/spinbox.cpp +++ b/src/citra_qt/util/spinbox.cpp @@ -30,7 +30,7 @@ #include #include -#include +#include #include "citra_qt/util/spinbox.h" #include "common/assert.h" @@ -244,14 +244,15 @@ QValidator::State CSpinBox::validate(QString& input, int& pos) const { } // Match string - QRegExp num_regexp(regexp); + QRegularExpression num_regexp(QRegularExpression::anchoredPattern(regexp)); int num_pos = strpos; QString sub_input = input.mid(strpos, input.length() - strpos - suffix.length()); - if (!num_regexp.exactMatch(sub_input) && num_regexp.matchedLength() == 0) + auto match = num_regexp.match(sub_input); + if (!match.hasMatch()) return QValidator::Invalid; - sub_input = sub_input.left(num_regexp.matchedLength()); + sub_input = sub_input.left(match.capturedLength()); bool ok; qint64 val = sub_input.toLongLong(&ok, base); @@ -263,7 +264,7 @@ QValidator::State CSpinBox::validate(QString& input, int& pos) const { return QValidator::Invalid; // Make sure we are actually at the end of this string... - strpos += num_regexp.matchedLength(); + strpos += match.capturedLength(); if (!suffix.isEmpty() && input.mid(strpos) != suffix) { return QValidator::Invalid; diff --git a/src/core/loader/smdh.cpp b/src/core/loader/smdh.cpp index 4773d08e5f..4306a1f1bc 100644 --- a/src/core/loader/smdh.cpp +++ b/src/core/loader/smdh.cpp @@ -44,11 +44,11 @@ std::vector SMDH::GetIcon(bool large) const { return icon; } -std::array SMDH::GetShortTitle(Loader::SMDH::TitleLanguage language) const { +std::array SMDH::GetShortTitle(Loader::SMDH::TitleLanguage language) const { return titles[static_cast(language)].short_title; } -std::array SMDH::GetLongTitle(Loader::SMDH::TitleLanguage language) const { +std::array SMDH::GetLongTitle(Loader::SMDH::TitleLanguage language) const { return titles[static_cast(language)].long_title; } diff --git a/src/core/loader/smdh.h b/src/core/loader/smdh.h index 831266982b..ec37c4bba9 100644 --- a/src/core/loader/smdh.h +++ b/src/core/loader/smdh.h @@ -26,9 +26,9 @@ struct SMDH { INSERT_PADDING_BYTES(2); struct Title { - std::array short_title; - std::array long_title; - std::array publisher; + std::array short_title; + std::array long_title; + std::array publisher; }; std::array titles; @@ -88,14 +88,14 @@ struct SMDH { * @param language title language * @return UTF-16 array of the short title */ - std::array GetShortTitle(Loader::SMDH::TitleLanguage language) const; + std::array GetShortTitle(Loader::SMDH::TitleLanguage language) const; /** * Gets the long game title from SMDH * @param language title language * @return UTF-16 array of the long title */ - std::array GetLongTitle(Loader::SMDH::TitleLanguage language) const; + std::array GetLongTitle(Loader::SMDH::TitleLanguage language) const; std::vector GetRegions() const; };