mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	qt: Migrate to Qt6. (#6418)
This commit is contained in:
		
							parent
							
								
									70335a7f4d
								
							
						
					
					
						commit
						2273df4d70
					
				
					 32 changed files with 299 additions and 464 deletions
				
			
		|  | @ -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} "</qresource></RCC>") | ||||
| 
 | ||||
|     # 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) | ||||
|  |  | |||
|  | @ -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(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,9 +3,8 @@ | |||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QCamera> | ||||
| #include <QCameraInfo> | ||||
| #include <QImageReader> | ||||
| #include <QMessageBox> | ||||
| #include <QMediaDevices> | ||||
| #include <QThread> | ||||
| #include "citra_qt/camera/qt_multimedia_camera.h" | ||||
| #include "citra_qt/main.h" | ||||
|  | @ -16,229 +15,85 @@ | |||
| 
 | ||||
| namespace Camera { | ||||
| 
 | ||||
| QList<QVideoFrame::PixelFormat> QtCameraSurface::supportedPixelFormats( | ||||
|     [[maybe_unused]] QAbstractVideoBuffer::HandleType handleType) const { | ||||
|     return QList<QVideoFrame::PixelFormat>() | ||||
|            << 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<QCamera::FrameRateRange, 13> 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<int>(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<CameraInterface> QtMultimediaCameraFactory::Create(const std::string& config, | ||||
|                                                                    const Service::CAM::Flip& flip) { | ||||
|     return std::make_unique<QtMultimediaCamera>(config, flip); | ||||
| } | ||||
| 
 | ||||
| std::array<std::shared_ptr<QtMultimediaCameraHandler>, 3> QtMultimediaCameraHandler::handlers; | ||||
| 
 | ||||
| std::array<bool, 3> QtMultimediaCameraHandler::status; | ||||
| 
 | ||||
| std::unordered_map<std::string, std::shared_ptr<QtMultimediaCameraHandler>> | ||||
|     QtMultimediaCameraHandler::loaded; | ||||
| 
 | ||||
| void QtMultimediaCameraHandler::Init() { | ||||
|     std::generate(std::begin(handlers), std::end(handlers), | ||||
|                   std::make_shared<QtMultimediaCameraHandler>); | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<QtMultimediaCameraHandler> QtMultimediaCameraHandler::GetHandler( | ||||
| std::shared_ptr<QtMultimediaCameraHandler> 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<QtMultimediaCameraHandler> 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<QtMultimediaCameraHandler>(camera_name); | ||||
|             handlers[camera_name] = handler; | ||||
|         } else { | ||||
|             LOG_INFO(Service_CAM, "Reusing existing handler for camera '{}'", camera_name); | ||||
|         } | ||||
|         return handler; | ||||
|     } else { | ||||
|         std::shared_ptr<QtMultimediaCameraHandler> handler; | ||||
|         QMetaObject::invokeMethod(this, "Create", Qt::BlockingQueuedConnection, | ||||
|                                   Q_RETURN_ARG(std::shared_ptr<QtMultimediaCameraHandler>, 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<Camera::QtMultimediaCameraHandler>& 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<QCameraInfo> cameras = QCameraInfo::availableCameras(); | ||||
|     for (const QCameraInfo& cameraInfo : cameras) { | ||||
|         if (cameraInfo.deviceName().toStdString() == camera_name) | ||||
|             camera = std::make_unique<QCamera>(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<QCamera>(*requested_camera); | ||||
|     } else { | ||||
|         camera = std::make_unique<QCamera>(); | ||||
|     } | ||||
|     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<QVideoSink>(); | ||||
|     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
 | ||||
|  |  | |||
|  | @ -4,99 +4,113 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
| #include <QAbstractVideoSurface> | ||||
| #include <QCamera> | ||||
| #include <QCameraViewfinderSettings> | ||||
| #include <QImage> | ||||
| #include <QMutex> | ||||
| #include <QMediaCaptureSession> | ||||
| #include <QVideoSink> | ||||
| #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<QVideoFrame::PixelFormat> 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<QCamera> camera; | ||||
|     std::unique_ptr<QVideoSink> 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<QtMultimediaCameraHandler> Create(const std::string& camera_name); | ||||
|     void PauseCameras(); | ||||
|     void ResumeCameras(); | ||||
| 
 | ||||
| private: | ||||
|     std::unordered_map<std::string, std::weak_ptr<QtMultimediaCameraHandler>> 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<QtMultimediaCameraHandler>& 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<QtMultimediaCameraHandler> handler; | ||||
| }; | ||||
| 
 | ||||
| /// This class is only an interface. It just calls QtMultimediaCameraHandlerFactory.
 | ||||
| class QtMultimediaCameraFactory final : public QtCameraFactory { | ||||
| public: | ||||
|     QtMultimediaCameraFactory( | ||||
|         const std::shared_ptr<QtMultimediaCameraHandlerFactory>& handler_factory) | ||||
|         : handler_factory(handler_factory) {} | ||||
| 
 | ||||
|     std::unique_ptr<CameraInterface> 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<QtMultimediaCameraHandler> GetHandler(const std::string& camera_name); | ||||
|     static void ReleaseHandler(const std::shared_ptr<QtMultimediaCameraHandler>& 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<QtMultimediaCamera>(handler_factory->Create(config), flip); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::unique_ptr<QCamera> camera; | ||||
|     QtCameraSurface camera_surface{}; | ||||
|     QCameraViewfinderSettings settings; | ||||
|     bool started = false; | ||||
|     bool paused = false; // was previously started but was paused, to be resumed
 | ||||
| 
 | ||||
|     static std::array<std::shared_ptr<QtMultimediaCameraHandler>, 3> handlers; | ||||
|     static std::array<bool, 3> status; | ||||
|     static std::unordered_map<std::string, std::shared_ptr<QtMultimediaCameraHandler>> loaded; | ||||
| 
 | ||||
|     friend class QtMultimediaCamera; // For access to camera_surface (and camera)
 | ||||
|     std::shared_ptr<QtMultimediaCameraHandlerFactory> handler_factory; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Camera
 | ||||
|  |  | |||
|  | @ -2,16 +2,16 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QCameraInfo> | ||||
| #include <QCameraDevice> | ||||
| #include <QDirIterator> | ||||
| #include <QFileDialog> | ||||
| #include <QImageReader> | ||||
| #include <QMediaDevices> | ||||
| #include <QMessageBox> | ||||
| #include <QWidget> | ||||
| #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<QCameraInfo> cameras = QCameraInfo::availableCameras(); | ||||
|     for (const QCameraInfo& cameraInfo : cameras) { | ||||
|         ui->system_camera->addItem(cameraInfo.deviceName()); | ||||
|     const QList<QCameraDevice> cameras = QMediaDevices::videoInputs(); | ||||
|     for (const QCameraDevice& camera : cameras) { | ||||
|         ui->system_camera->addItem(camera.description()); | ||||
|     } | ||||
|     UpdateCameraMode(); | ||||
|     SetConfiguration(); | ||||
|  |  | |||
|  | @ -579,7 +579,7 @@ void ConfigureInput::AutoMap() { | |||
| void ConfigureInput::HandleClick(QPushButton* button, | ||||
|                                  std::function<void(const Common::ParamPackage&)> 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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -324,22 +324,4 @@ | |||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>ConfigureMotionTouch</receiver> | ||||
|    <slot>ApplyConfiguration()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>220</x> | ||||
|      <y>380</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>220</x> | ||||
|      <y>200</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
|  |  | |||
|  | @ -283,7 +283,7 @@ void ConfigureSystem::SetConfiguration() { | |||
| 
 | ||||
|     ui->combo_init_clock->setCurrentIndex(static_cast<u8>(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<Settings::InitClock>(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; | ||||
|  |  | |||
|  | @ -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<QLabel*>(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<QMouseEvent*>(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(), | ||||
|  |  | |||
|  | @ -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()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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(); | ||||
| } | ||||
|  |  | |||
|  | @ -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(); | ||||
|     } | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -73,5 +73,3 @@ private: | |||
|     std::chrono::duration<double> rolling_average = {}; | ||||
|     bool eta_shown = false; | ||||
| }; | ||||
| 
 | ||||
| Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage); | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ | |||
| #include <clocale> | ||||
| #include <memory> | ||||
| #include <thread> | ||||
| #include <QDesktopWidget> | ||||
| #include <QFileDialog> | ||||
| #include <QFutureWatcher> | ||||
| #include <QLabel> | ||||
|  | @ -200,6 +199,11 @@ GMainWindow::GMainWindow() | |||
|     qRegisterMetaType<std::size_t>("std::size_t"); | ||||
|     qRegisterMetaType<Service::AM::InstallStatus>("Service::AM::InstallStatus"); | ||||
| 
 | ||||
|     // Register CameraFactory
 | ||||
|     qt_cameras = std::make_shared<Camera::QtMultimediaCameraHandlerFactory>(); | ||||
|     Camera::RegisterFactory("image", std::make_unique<Camera::StillImageCameraFactory>()); | ||||
|     Camera::RegisterFactory("qt", std::make_unique<Camera::QtMultimediaCameraFactory>(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<int>(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::StillImageCameraFactory>()); | ||||
|     Camera::RegisterFactory("qt", std::make_unique<Camera::QtMultimediaCameraFactory>()); | ||||
|     Camera::QtMultimediaCameraHandler::Init(); | ||||
| 
 | ||||
|     // Register frontend applets
 | ||||
|     Frontend::RegisterDefaultApplets(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<Camera::QtMultimediaCameraHandlerFactory> qt_cameras; | ||||
| 
 | ||||
| #ifdef __unix__ | ||||
|     QDBusObjectPath wake_lock{}; | ||||
| #endif | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| #include <QComboBox> | ||||
| #include <QFuture> | ||||
| #include <QIntValidator> | ||||
| #include <QRegExpValidator> | ||||
| #include <QRegularExpression> | ||||
| #include <QString> | ||||
| #include <QtConcurrent/QtConcurrentRun> | ||||
| #include "citra_qt/main.h" | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <QRegExp> | ||||
| #include <QRegularExpression> | ||||
| #include <QString> | ||||
| #include <QValidator> | ||||
| 
 | ||||
|  | @ -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; | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ | |||
| 
 | ||||
| #include <cstdlib> | ||||
| #include <QLineEdit> | ||||
| #include <QRegExpValidator> | ||||
| #include <QRegularExpression> | ||||
| #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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue