diff --git a/.gitlab/ci/base_jobs.yml b/.gitlab/ci/base_jobs.yml index d97eab0f..b0963d36 100644 --- a/.gitlab/ci/base_jobs.yml +++ b/.gitlab/ci/base_jobs.yml @@ -35,7 +35,7 @@ - cd .. - git clone https://invent.kde.org/plasma/layer-shell-qt.git - cd layer-shell-qt - - cmake configure . -DCMAKE_PREFIX_PATH="./../../../aqt/6.6.0/gcc_64" + - cmake configure . -DCMAKE_PREFIX_PATH="./../../../aqt/6.6.1/gcc_64" - make - make install - cd .. diff --git a/.vscode/launch.json b/.vscode/launch.json index 0eaaad94..57905650 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -17,7 +17,7 @@ "environment": [ { "name": "Path", - "value": "${env:Path};${workspaceFolder}\\..\\aqt\\6.6.0\\msvc2019_64\\bin\\;${workspaceFolder}\\..\\aqt\\6.6.0\\msvc2019_64\\modules\\;${workspaceFolder}\\..\\aqt\\6.6.0\\msvc2019_64\\qml\\;${workspaceFolder}\\..\\vcpkg\\installed\\x64-windows\\debug\\bin;" + "value": "${env:Path};${workspaceFolder}\\..\\aqt\\6.6.1\\msvc2019_64\\bin\\;${workspaceFolder}\\..\\aqt\\6.6.1\\msvc2019_64\\modules\\;${workspaceFolder}\\..\\aqt\\6.6.1\\msvc2019_64\\qml\\;${workspaceFolder}\\..\\vcpkg\\installed\\x64-windows\\debug\\bin;" } ], "visualizerFile": "${workspaceFolder}/.vscode/qt.natvis.xml" diff --git a/CMake/CMakeVariables.h.in b/CMake/CMakeVariables.h.in index 662516a7..930271db 100644 --- a/CMake/CMakeVariables.h.in +++ b/CMake/CMakeVariables.h.in @@ -1,7 +1,10 @@ #pragma once +#define SCREENPLAY_VERSION "@SCREENPLAY_VERSION@" #define SCREENPLAY_SOURCE_DIR "@SCREENPLAY_SOURCE_DIR@" #define SCREENPLAY_GODOT_VERSION "@SCREENPLAY_GODOT_VERSION@" +#define SCREENPLAY_GODOT_VERSION_MAJOR @SCREENPLAY_GODOT_VERSION_MAJOR@ +#define SCREENPLAY_GODOT_VERSION_MINOR @SCREENPLAY_GODOT_VERSION_MINOR@ #define SCREENPLAY_GODOT_RELEASE_TYPE "@SCREENPLAY_GODOT_RELEASE_TYPE@" #define SCREENPLAY_BUILD_TYPE "@SCREENPLAY_BUILD_TYPE@" #define SCREENPLAY_GIT_BRANCH_NAME "@SCREENPLAY_GIT_BRANCH_NAME@" diff --git a/CMake/GenerateCMakeVariableHeader.cmake b/CMake/GenerateCMakeVariableHeader.cmake index 7325133a..1329b2c2 100644 --- a/CMake/GenerateCMakeVariableHeader.cmake +++ b/CMake/GenerateCMakeVariableHeader.cmake @@ -1,17 +1,24 @@ -# ! generate_cmake_variable_header : Generates a header CmakeVariables.h that contains defines for the variables specified in CmakeVariables.h.in! +# ! generate_cmake_variable_header : Generates a header CmakeVariables.h that contains defines for the variables specified in +# CmakeVariables.h.in! # # The generated CmakeVariables.h header can then be used to access e.g. the PROJECT_NAME define in C++ code. # -# Example -# generate_cmake_variable_header(${PROJECT_NAME}) +# Example generate_cmake_variable_header(${PROJECT_NAME}) # function(generate_cmake_variable_header TARGET) - # NOTE: Also add to CMakeVariables.h.in ! + # ⚠️ Also add to CMakeVariables.h.in ⚠️ + set(SCREENPLAY_SOURCE_DIR ${CMAKE_SOURCE_DIR}) + # Like v4.2-beta3 or v5.0.1-stable set(SCREENPLAY_GODOT_VERSION ${GODOT_VERSION}) + # Only single numbers + set(SCREENPLAY_GODOT_VERSION_MAJOR ${GODOT_VERSION_MAJOR}) + set(SCREENPLAY_GODOT_VERSION_MINOR ${GODOT_VERSION_MINOR}) + # stable, rc1 or beta5 set(SCREENPLAY_GODOT_RELEASE_TYPE ${GODOT_RELEASE_TYPE}) set(SCREENPLAY_BUILD_TYPE "${CMAKE_BUILD_TYPE}") set(SCREENPLAY_BUILD_DATE "${BUILD_DATE}") + set(SCREENPLAY_VERSION "${SCREENPLAY_VERSION}") set(SCREENPLAY_GIT_BRANCH_NAME "${GIT_BRANCH_NAME}") set(SCREENPLAY_GIT_COMMIT_HASH "${GIT_COMMIT_HASH}") @@ -19,7 +26,7 @@ function(generate_cmake_variable_header TARGET) if(${SCREENPLAY_DEPLOY}) set(SCREENPLAY_DEPLOY_VERSION 1) endif() - + set(SCREENPLAY_STEAM_VERSION 0) if(${SCREENPLAY_STEAM}) set(SCREENPLAY_STEAM_VERSION 1) @@ -27,7 +34,8 @@ function(generate_cmake_variable_header TARGET) # Specify the configuration file from which the header file will be generated configure_file(${CMAKE_SOURCE_DIR}/CMake/CMakeVariables.h.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/CMakeVariables.h @ONLY) - message(STATUS "GENERATE: ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/CMakeVariables.h and add ${TARGET} to ${CMAKE_CURRENT_BINARY_DIR}") + message( + STATUS "GENERATE: ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/CMakeVariables.h and add ${TARGET} to ${CMAKE_CURRENT_BINARY_DIR}") # Add the directory containing the generated header target_include_directories(${TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17a1347e..b0617451 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.22.0) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH}) include(GetProjectVersion) -get_project_version(PROJECT_VERSION) +get_project_version(SCREENPLAY_VERSION) # This must be set before project() if(APPLE) @@ -13,7 +13,7 @@ endif() project( ScreenPlay - VERSION ${PROJECT_VERSION} + VERSION ${SCREENPLAY_VERSION} DESCRIPTION "Modern, Cross Plattform, Live Wallpaper, Widgets and AppDrawer!" HOMEPAGE_URL "https://screen-play.app/" LANGUAGES CXX) @@ -43,9 +43,11 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0") # This subdirectoy is needed for OSX and Linux to fix linker errors because we would have ScreenPlayApp executable and folder for the qml # files in the same directory. set(SCREENPLAY_QML_MODULES_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/qml") + if(APPLE) set(SCREENPLAY_QML_MODULES_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ScreenPlay.app/Contents/MacOS/qml") endif() + # Adds the qml import path so QtCreator can find them list(APPEND QML_DIRS "${SCREENPLAY_QML_MODULES_PATH}") set(QML_IMPORT_PATH @@ -57,15 +59,26 @@ set(VCPKG_INSTALLED_PATH "${VCPKG_PATH}/installed/${VCPKG_ARCH}") set(VCPKG_BIN_PATH "${VCPKG_INSTALLED_PATH}/bin") # Godot Editor -set(GODOT_VERSION "v4.2") -set(GODOT_RELEASE_TYPE "beta4") +set(GODOT_VERSION_MAJOR "4") +set(GODOT_VERSION_MINOR "2") +set(GODOT_VERSION_PATCH "") +set(GODOT_RELEASE_TYPE "rc2") + +# Use an if statement to check if GODOT_VERSION_PATCH is empty or not +if(GODOT_VERSION_PATCH STREQUAL "") + # If patch version is empty, don't include it and the preceding dot + set(GODOT_VERSION "v${GODOT_VERSION_MAJOR}.${GODOT_VERSION_MINOR}-${GODOT_RELEASE_TYPE}") +else() + # If patch version is not empty, include it and the preceding dot + set(GODOT_VERSION "v${GODOT_VERSION_MAJOR}.${GODOT_VERSION_MINOR}.${GODOT_VERSION_PATCH}-${GODOT_RELEASE_TYPE}") +endif() if(WIN32) - set(GODOT_EDITOR_NAME "Godot_${GODOT_VERSION}-${GODOT_RELEASE_TYPE}_win64.exe") + set(GODOT_EDITOR_NAME "Godot_${GODOT_VERSION}_win64.exe") elseif(APPLE) set(GODOT_EDITOR_NAME "Godot.app") elseif(UNIX) - set(GODOT_EDITOR_NAME "Godot_${GODOT_VERSION}-${GODOT_RELEASE_TYPE}_linux.x86_64") + set(GODOT_EDITOR_NAME "Godot_${GODOT_VERSION}_linux.x86_64") else() message(FATAL_ERROR "Unsupported OS") endif() @@ -73,7 +86,7 @@ endif() option(SCREENPLAY_OSX_BUNDLE "Enable distribution macOS bundle" OFF) option(SCREENPLAY_STEAM "For FOSS distribution so we do not bundle proprietary code." ON) option(SCREENPLAY_DEPLOY "Marks this version as an official deploy version. This version uses different import paths and other settings." - OFF) + OFF) option(SCREENPLAY_TESTS "Enables UI tests." ON) option(SCREENPLAY_INSTALLER "Indicates whether an installer via the Qt Installer Framework is created." OFF) option(SCREENPLAY_GODOT "Compiles ScreenPlayGodotWallpaper." ON) @@ -166,7 +179,6 @@ if(${SCREENPLAY_INSTALLER}) endif() message(STATUS "[PROJECT] CMAKE_MODULE_PATH = ${CMAKE_MODULE_PATH}") -message(STATUS "[PROJECT] PROJECT_VERSION = ${PROJECT_VERSION}") message(STATUS "[PROJECT] CMAKE_VERSION = ${CMAKE_VERSION}") message(STATUS "[PROJECT] SCREENPLAY_QML_MODULES_PATH = ${SCREENPLAY_QML_MODULES_PATH}") message(STATUS "[PROJECT] CMAKE_TOOLCHAIN_FILE = ${CMAKE_TOOLCHAIN_FILE}") @@ -176,6 +188,7 @@ message(STATUS "[PROJECT] VCPKG_TARGET_TRIPLET = ${VCPKG_TARGET_TRIPLET} message(STATUS "[PROJECT] CMAKE_PREFIX_PATH = ${CMAKE_PREFIX_PATH}") message(STATUS "[PROJECT] GODOT_VERSION = ${GODOT_VERSION}") message(STATUS "[PROJECT] GODOT_EDITOR_NAME = ${GODOT_EDITOR_NAME}") +message(STATUS "[PROJECT] SCREENPLAY_VERSION = ${SCREENPLAY_VERSION}") message(STATUS "[OPTION] SCREENPLAY_DEPLOY = ${SCREENPLAY_DEPLOY}") message(STATUS "[OPTION] SCREENPLAY_INSTALLER = ${SCREENPLAY_INSTALLER}") message(STATUS "[OPTION] SCREENPLAY_STEAM = ${SCREENPLAY_STEAM}") diff --git a/CMakePresets.json b/CMakePresets.json index 492f4ec0..f75a1b54 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -19,7 +19,7 @@ }, "environment": { "qt_path": "${sourceDir}/../aqt", - "qt_version": "6.6.0" + "qt_version": "6.6.1" }, "toolset": { "value": "host=x64", @@ -38,46 +38,46 @@ } }, { - "name": "windows-debug-qt-6.6.0", + "name": "windows-debug-qt-6.6.1", "inherits": "default-windows", - "displayName": "MSVC SP Qt 6.6.0 Debug", - "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.0_MSVC_Debug", + "displayName": "MSVC SP Qt 6.6.1 Debug", + "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.1_MSVC_Debug", "environment": { - "qt_version": "6.6.0" + "qt_version": "6.6.1" }, "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, { - "name": "windows-relwithdebinfo-qt-6.6.0", + "name": "windows-relwithdebinfo-qt-6.6.1", "inherits": "default-windows", - "displayName": "MSVC SP Qt 6.6.0 RelWithDebInfo", - "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.0_MSVC_RelWithDebInfo", + "displayName": "MSVC SP Qt 6.6.1 RelWithDebInfo", + "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.1_MSVC_RelWithDebInfo", "environment": { - "qt_version": "6.6.0" + "qt_version": "6.6.1" }, "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } }, { - "name": "windows-release-qt-6.6.0", + "name": "windows-release-qt-6.6.1", "inherits": "default-windows", - "displayName": "MSVC SP Qt 6.6.0 Release", - "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.0_MSVC_Release", + "displayName": "MSVC SP Qt 6.6.1 Release", + "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.1_MSVC_Release", "environment": { - "qt_version": "6.6.0" + "qt_version": "6.6.1" }, "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } }, { - "name": "windows-release-qt-6.6.0-release-deploy", - "inherits": "windows-release-qt-6.6.0", - "displayName": "MSVC SP Qt 6.6.0 Release Steam Deploy", - "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.0_MSVC_Release_Steam_Deploy", + "name": "windows-release-qt-6.6.1-release-deploy", + "inherits": "windows-release-qt-6.6.1", + "displayName": "MSVC SP Qt 6.6.1 Release Steam Deploy", + "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.1_MSVC_Release_Steam_Deploy", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "SCREENPLAY_DEPLOY": "ON", @@ -89,7 +89,7 @@ "displayName": "ScreenPlay 64bit Debug Linux", "description": "Linux only!", "generator": "Ninja", - "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.0_GCC_Debug", + "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.1_GCC_Debug", "condition": { "type": "equals", "lhs": "${hostSystemName}", @@ -113,7 +113,7 @@ "name": "linux-relwithdebinfo", "displayName": "ScreenPlay 64bit RelWithDebInfo Linux", "inherits": "linux-generic-debug", - "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.0_GCC_RelWithDebInfo", + "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.1_GCC_RelWithDebInfo", "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } @@ -123,7 +123,7 @@ "displayName": "ScreenPlay 64bit Debug Linux using aqt", "description": "Linux only!", "generator": "Ninja", - "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.0_GCC_Debug", + "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.1_GCC_Debug", "condition": { "type": "equals", "lhs": "${hostSystemName}", @@ -140,7 +140,7 @@ "CMAKE_CXX_COMPILER": "g++", "CMAKE_C_COMPILER": "gcc", "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_PREFIX_PATH": "$env{qt_path}/6.6.0/gcc_64", + "CMAKE_PREFIX_PATH": "$env{qt_path}/6.6.1/gcc_64", "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../vcpkg/scripts/buildsystems/vcpkg.cmake", "VCPKG_TARGET_TRIPLET": "x64-linux" } @@ -149,7 +149,7 @@ "name": "linux-aqt-relwithdebinfo", "displayName": "ScreenPlay 64bit RelWithDebInfo Linux using aqt", "inherits": "linux-ubuntu-debug", - "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.0_GCC_RelWithDebInfo", + "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.1_GCC_RelWithDebInfo", "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } @@ -159,7 +159,7 @@ "displayName": "ScreenPlay 64bit Debug osx", "description": "Osx only!", "generator": "Ninja", - "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.0_Clang_Debug", + "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.1_Clang_Debug", "condition": { "type": "equals", "lhs": "${hostSystemName}", @@ -174,7 +174,7 @@ "CMAKE_CXX_COMPILER": "clang++", "CMAKE_C_COMPILER": "clang", "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_PREFIX_PATH": "$env{qt_path}/6.6.0/macos", + "CMAKE_PREFIX_PATH": "$env{qt_path}/6.6.1/macos", "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../vcpkg/scripts/buildsystems/vcpkg.cmake" } }, @@ -182,7 +182,7 @@ "name": "osx-relwithdebinfo", "displayName": "ScreenPlay 64bit RelWithDebInfo osx", "inherits": "osx-debug", - "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.0_Clang_RelWithDebInfo", + "binaryDir": "${sourceDir}/../build_ScreenPlay_Qt_6.6.1_Clang_RelWithDebInfo", "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } @@ -207,7 +207,7 @@ "name": "Test", "description": "", "displayName": "", - "configurePreset": "windows-release-qt-6.6.0" + "configurePreset": "windows-release-qt-6.6.1" } ] } \ No newline at end of file diff --git a/Content/wallpaper_godot_fjord/project.godot b/Content/wallpaper_godot_fjord/project.godot index cdad8da6..d60e8d8a 100644 --- a/Content/wallpaper_godot_fjord/project.godot +++ b/Content/wallpaper_godot_fjord/project.godot @@ -12,7 +12,7 @@ config_version=5 config/name="Fjord" run/main_scene="res://wallpaper.tscn" -config/features=PackedStringArray("4.1", "Mobile") +config/features=PackedStringArray("4.2", "Mobile") config/icon="res://icon.svg" [rendering] diff --git a/ScreenPlay/CMakeLists.txt b/ScreenPlay/CMakeLists.txt index 584b1c55..185b0c22 100644 --- a/ScreenPlay/CMakeLists.txt +++ b/ScreenPlay/CMakeLists.txt @@ -60,6 +60,7 @@ set(QML qml/Create/StartInfoLinkImage.qml qml/Create/Wizard.qml qml/Create/Wizards/GifWallpaper.qml + qml/Create/Wizards/GodotWallpaper.qml qml/Create/Wizards/HTMLWallpaper.qml qml/Create/Wizards/HTMLWidget.qml qml/Create/Wizards/Importh264/Importh264.qml @@ -76,8 +77,6 @@ set(QML qml/Create/Wizards/QMLWidget.qml qml/Create/Wizards/WebsiteWallpaper.qml qml/Create/Wizards/WizardPage.qml - qml/Create/WizardsFiles/QMLWallpaperMain.qml - qml/Create/WizardsFiles/QMLWidgetMain.qml qml/Installed/Installed.qml qml/Installed/InstalledNavigation.qml qml/Installed/InstalledWelcomeScreen.qml @@ -129,6 +128,7 @@ set(RESOURCES assets/icons/brand_reddit.svg assets/icons/brand_twitch.svg assets/icons/brand_twitter.svg + assets/icons/icon_cancel_presentation.svg assets/icons/exclamation-triangle-solid.svg assets/icons/font-awsome/close.svg assets/icons/font-awsome/frown-o.svg @@ -146,6 +146,7 @@ set(RESOURCES assets/icons/icon_document.svg assets/icons/icon_done.svg assets/icons/icon_download.svg + assets/icons/icon_edit.svg assets/icons/icon_emptyWidget.svg assets/icons/icon_folder_open.svg assets/icons/icon_forum.svg @@ -159,6 +160,7 @@ set(RESOURCES assets/icons/icon_movie.svg assets/icons/icon_new_releases.svg assets/icons/icon_open_in_new.svg + assets/icons/icon_open_in_new_black.svg assets/icons/icon_pause.svg assets/icons/icon_people.svg assets/icons/icon_play.svg @@ -175,7 +177,7 @@ set(RESOURCES assets/icons/icon_thumb_down.svg assets/icons/icon_thumb_up.svg assets/icons/icon_upload.svg - assets/icons/icon_video_settings_black_24dp.svg + assets/icons/icon_video_settings.svg assets/icons/icon_volume.svg assets/icons/icon_volume_mute.svg assets/icons/icon_volume_up.svg @@ -201,9 +203,6 @@ set(RESOURCES assets/licenses/Apache2.txt assets/licenses/OFL.txt assets/macos/app.screenplay.plist - assets/particle/backgroundGlow.png - assets/particle/dot.png - assets/shader/movingcolorramp.fsh assets/startinfo/blender.png assets/startinfo/flaticon.png assets/startinfo/forums.png @@ -232,7 +231,6 @@ set(RESOURCES assets/wizards/License_CC_Attribution-ShareAlike_4.0.txt assets/wizards/License_CC_Attribution_4.0.txt assets/wizards/License_GPL_3.0.txt - assets/wizards/QmlProject.qmlproject assets/WorkshopPreview.html legal/DataProtection.txt legal/gpl-3.0.txt @@ -242,6 +240,13 @@ set(RESOURCES profiles.json qml/Create/WizardsFiles/HTMLWallpaperMain.html qml/Create/WizardsFiles/HTMLWidgetMain.html + qml/Create/WizardsFiles/QmlProject.qmlproject + qml/Create/WizardsFiles/QMLWallpaperMain.qml + qml/Create/WizardsFiles/QMLWidgetMain.qml + qml/Create/WizardsFiles/Godot_v5/export_presets.cfg + qml/Create/WizardsFiles/Godot_v5/project.godot + qml/Create/WizardsFiles/Godot_v5/spinner.gd + qml/Create/WizardsFiles/Godot_v5/wallpaper.tscn qml/Create/WizardsFiles/QMLWallpaperMain.qml qml/Create/WizardsFiles/QMLWidgetMain.qml qtquickcontrols2.conf) diff --git a/ScreenPlay/assets/icons/icon_cancel_presentation.svg b/ScreenPlay/assets/icons/icon_cancel_presentation.svg new file mode 100644 index 00000000..c0da419c --- /dev/null +++ b/ScreenPlay/assets/icons/icon_cancel_presentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ScreenPlay/assets/icons/icon_edit.svg b/ScreenPlay/assets/icons/icon_edit.svg new file mode 100644 index 00000000..cb81b113 --- /dev/null +++ b/ScreenPlay/assets/icons/icon_edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ScreenPlay/assets/icons/icon_open_in_new_black.svg b/ScreenPlay/assets/icons/icon_open_in_new_black.svg new file mode 100644 index 00000000..42895ffd --- /dev/null +++ b/ScreenPlay/assets/icons/icon_open_in_new_black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ScreenPlay/assets/icons/icon_steam.svg b/ScreenPlay/assets/icons/icon_steam.svg index c7e45690..7a35f5b3 100644 --- a/ScreenPlay/assets/icons/icon_steam.svg +++ b/ScreenPlay/assets/icons/icon_steam.svg @@ -1 +1,5 @@ - \ No newline at end of file + + + + + diff --git a/ScreenPlay/assets/icons/icon_video_settings_black_24dp.svg b/ScreenPlay/assets/icons/icon_video_settings.svg similarity index 100% rename from ScreenPlay/assets/icons/icon_video_settings_black_24dp.svg rename to ScreenPlay/assets/icons/icon_video_settings.svg diff --git a/ScreenPlay/assets/particle/backgroundGlow.png b/ScreenPlay/assets/particle/backgroundGlow.png deleted file mode 100644 index ba18cc8d..00000000 Binary files a/ScreenPlay/assets/particle/backgroundGlow.png and /dev/null differ diff --git a/ScreenPlay/assets/particle/dot.png b/ScreenPlay/assets/particle/dot.png deleted file mode 100644 index 082a04d1..00000000 Binary files a/ScreenPlay/assets/particle/dot.png and /dev/null differ diff --git a/ScreenPlay/assets/shader/movingcolorramp.fsh b/ScreenPlay/assets/shader/movingcolorramp.fsh deleted file mode 100644 index efe1d207..00000000 --- a/ScreenPlay/assets/shader/movingcolorramp.fsh +++ /dev/null @@ -1,18 +0,0 @@ -uniform float time; -uniform float shaderOpacity; -uniform vec2 resolution; - -void main( void ) { - - - vec2 position = ( gl_FragCoord.xy / resolution.xy ); - - vec3 color1 = vec3(1.0, 0.6, 0.01); - vec3 color2 = vec3(0.97, 0.24, 0.24); - float mixValue = distance(position,vec2(0,1)); - - vec3 color = mix( color1, color2, mixValue); - - gl_FragColor = vec4(color,mixValue) * shaderOpacity; - -} diff --git a/ScreenPlay/assets/tests/video_import.mp4 b/ScreenPlay/assets/tests/video_import.mp4 deleted file mode 100644 index 6a2ac6d2..00000000 Binary files a/ScreenPlay/assets/tests/video_import.mp4 and /dev/null differ diff --git a/ScreenPlay/inc/public/ScreenPlay/create.h b/ScreenPlay/inc/public/ScreenPlay/create.h index ebadca23..511540be 100644 --- a/ScreenPlay/inc/public/ScreenPlay/create.h +++ b/ScreenPlay/inc/public/ScreenPlay/create.h @@ -32,6 +32,7 @@ namespace ScreenPlay { class Create : public QObject { Q_OBJECT QML_ELEMENT + QML_UNCREATABLE("") Q_PROPERTY(QString workingDir READ workingDir WRITE setWorkingDir NOTIFY workingDirChanged) Q_PROPERTY(float progress READ progress WRITE setProgress NOTIFY progressChanged) diff --git a/ScreenPlay/inc/public/ScreenPlay/globalvariables.h b/ScreenPlay/inc/public/ScreenPlay/globalvariables.h index 7b7a6a15..9d317aa2 100644 --- a/ScreenPlay/inc/public/ScreenPlay/globalvariables.h +++ b/ScreenPlay/inc/public/ScreenPlay/globalvariables.h @@ -16,6 +16,7 @@ namespace ScreenPlay { class GlobalVariables : public QObject { Q_OBJECT QML_ELEMENT + QML_UNCREATABLE("") Q_PROPERTY(QVersionNumber version READ version CONSTANT) Q_PROPERTY(QUrl localStoragePath READ localStoragePath WRITE setLocalStoragePath NOTIFY localStoragePathChanged FINAL) diff --git a/ScreenPlay/inc/public/ScreenPlay/installedlistmodel.h b/ScreenPlay/inc/public/ScreenPlay/installedlistmodel.h index 32bdd3dc..86b3f3ab 100644 --- a/ScreenPlay/inc/public/ScreenPlay/installedlistmodel.h +++ b/ScreenPlay/inc/public/ScreenPlay/installedlistmodel.h @@ -77,7 +77,7 @@ public slots: void append(const QString& projectJsonFilePath); void reset(); void init(); - void deinstallItemAt(const QString& absoluteStoragePath); + bool deinstallItemAt(const QString& absoluteStoragePath); void setCount(int count) { @@ -96,6 +96,7 @@ private: QFileSystemWatcher m_fileSystemWatcher; QVector m_screenPlayFiles; int m_count { 0 }; + QTimer m_reloadLimiter; std::atomic_bool m_isLoading { false }; const std::shared_ptr& m_globalVariables; diff --git a/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h b/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h index ed80ce6e..068c8d28 100644 --- a/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h +++ b/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h @@ -41,6 +41,7 @@ struct Monitor { class MonitorListModel : public QAbstractListModel { Q_OBJECT QML_ELEMENT + QML_UNCREATABLE("") public: explicit MonitorListModel(QObject* parent = nullptr); diff --git a/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h b/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h index 7c15c7de..8de67422 100644 --- a/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h +++ b/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h @@ -24,6 +24,7 @@ namespace ScreenPlay { class ScreenPlayManager : public QObject { Q_OBJECT QML_ELEMENT + QML_UNCREATABLE("") Q_PROPERTY(int activeWallpaperCounter READ activeWallpaperCounter WRITE setActiveWallpaperCounter NOTIFY activeWallpaperCounterChanged) Q_PROPERTY(int activeWidgetsCounter READ activeWidgetsCounter WRITE setActiveWidgetsCounter NOTIFY activeWidgetsCounterChanged) diff --git a/ScreenPlay/inc/public/ScreenPlay/screenplaywallpaper.h b/ScreenPlay/inc/public/ScreenPlay/screenplaywallpaper.h index 271b128b..81c7f38b 100644 --- a/ScreenPlay/inc/public/ScreenPlay/screenplaywallpaper.h +++ b/ScreenPlay/inc/public/ScreenPlay/screenplaywallpaper.h @@ -3,10 +3,13 @@ #pragma once #include +#include +#include #include #include #include - +#include +#include #include #include "ScreenPlay/globalvariables.h" @@ -19,27 +22,21 @@ namespace ScreenPlay { class ScreenPlayWallpaper : public QObject { Q_OBJECT QML_ELEMENT + QML_UNCREATABLE("") Q_PROPERTY(bool isConnected READ isConnected WRITE setIsConnected NOTIFY isConnectedChanged) - Q_PROPERTY(QVector screenNumber READ screenNumber WRITE setScreenNumber NOTIFY screenNumberChanged) - Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(float playbackRate READ playbackRate WRITE setPlaybackRate NOTIFY playbackRateChanged) Q_PROPERTY(bool isLooping READ isLooping WRITE setIsLooping NOTIFY isLoopingChanged) - Q_PROPERTY(QString file READ file WRITE setFile NOTIFY fileChanged) Q_PROPERTY(QString absolutePath READ absolutePath WRITE setAbsolutePath NOTIFY absolutePathChanged) Q_PROPERTY(QString previewImage READ previewImage WRITE setPreviewImage NOTIFY previewImageChanged) Q_PROPERTY(QString appID READ appID WRITE setAppID NOTIFY appIDChanged) - Q_PROPERTY(FillMode::FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) Q_PROPERTY(InstalledType::InstalledType type READ type WRITE setType NOTIFY typeChanged) public: - // Default constructor needed for qml engine - ScreenPlayWallpaper() { } - explicit ScreenPlayWallpaper( const QVector& screenNumber, const std::shared_ptr& globalVariables, @@ -210,7 +207,7 @@ public slots: } private: - bool exportGodotProject(const QString& absolutePath, int timeoutMilliseconds = 30000); + bool exportGodotProject(); private: const std::shared_ptr m_globalVariables; @@ -218,6 +215,7 @@ private: const std::shared_ptr m_settings; ProjectSettingsListModel m_projectSettingsListModel; + QJsonObject m_projectJson; QVector m_screenNumber; QProcess m_process; QString m_previewImage; diff --git a/ScreenPlay/inc/public/ScreenPlay/screenplaywidget.h b/ScreenPlay/inc/public/ScreenPlay/screenplaywidget.h index fa6e587f..9090902f 100644 --- a/ScreenPlay/inc/public/ScreenPlay/screenplaywidget.h +++ b/ScreenPlay/inc/public/ScreenPlay/screenplaywidget.h @@ -23,6 +23,7 @@ namespace ScreenPlay { class ScreenPlayWidget : public QObject { Q_OBJECT QML_ELEMENT + QML_UNCREATABLE("") Q_PROPERTY(QString absolutePath READ absolutePath WRITE setAbsolutePath NOTIFY absolutePathChanged) Q_PROPERTY(QString previewImage READ previewImage WRITE setPreviewImage NOTIFY previewImageChanged) diff --git a/ScreenPlay/inc/public/ScreenPlay/util.h b/ScreenPlay/inc/public/ScreenPlay/util.h index 2d2eaf59..2502d653 100644 --- a/ScreenPlay/inc/public/ScreenPlay/util.h +++ b/ScreenPlay/inc/public/ScreenPlay/util.h @@ -58,7 +58,8 @@ class Util : public QObject { Q_PROPERTY(QString debugMessages READ debugMessages NOTIFY debugMessagesChanged) public: - Util(); + Util( + const std::shared_ptr& globalVariables); ~Util(); QString debugMessages() const { return m_debugMessages; } @@ -82,6 +83,7 @@ public slots: void openFolderInExplorer(const QString& url) const; QString toLocal(const QString& url) const; bool exportProject(QString contentPath, QString exportFileName); + bool openGodotEditor(QString contentPath) const; bool importProject(QString archivePath, QString extractionPath); void requestAllLicenses(); void requestDataProtection(); @@ -124,6 +126,7 @@ private: QFuture m_requestAllLicensesFuture; std::unique_ptr m_compressor; std::unique_ptr m_extractor; + const std::shared_ptr& m_globalVariables; }; } diff --git a/ScreenPlay/inc/public/ScreenPlay/wizards.h b/ScreenPlay/inc/public/ScreenPlay/wizards.h index 10abc659..6a342689 100644 --- a/ScreenPlay/inc/public/ScreenPlay/wizards.h +++ b/ScreenPlay/inc/public/ScreenPlay/wizards.h @@ -2,6 +2,7 @@ #pragma once +#include #include #include #include @@ -34,9 +35,11 @@ namespace ScreenPlay { class Wizards : public QObject { Q_OBJECT QML_ELEMENT + QML_UNCREATABLE("CPP ONLY") public: - explicit Wizards(const std::shared_ptr& globalVariables, QObject* parent = nullptr); - Wizards() { } + explicit Wizards( + const std::shared_ptr& globalVariables, + QObject* parent = nullptr); enum class WizardResult { Ok, @@ -82,6 +85,14 @@ public slots: const QString& previewThumbnail, const QVector& tags); + void createGodotWallpaper( + const QString& title, + const QString& licenseName, + const QString& licenseFile, + const QString& createdBy, + const QString& previewThumbnail, + const QVector& tags); + void createGifWallpaper( const QString& title, const QString& licenseName, @@ -103,8 +114,16 @@ signals: private: const std::shared_ptr m_globalVariables; const std::optional createTemporaryFolder() const; + void createPreviewImage(const QString& name, const QString& targetPath); private: QFuture m_wizardFuture; + QVector m_gradientColors = { + QColor("#B71C1C"), + QColor("#1B5E20"), + QColor("#0D47A1"), + QColor("#FFD600"), + QColor("#4A148C") + }; }; } diff --git a/ScreenPlay/main.qml b/ScreenPlay/main.qml index 4ec06b04..06a58e21 100644 --- a/ScreenPlay/main.qml +++ b/ScreenPlay/main.qml @@ -48,6 +48,7 @@ ApplicationWindow { stackView.replace("qrc:/qml/ScreenPlayApp/qml/" + name + "/" + name + ".qml", { "modalSource": content }); + nav.setNavigation(name); sidebar.state = "inactive"; } @@ -56,7 +57,7 @@ ApplicationWindow { visible: false width: 1400 height: 810 - title: "ScreenPlay Alpha - V" + App.version() + title: "ScreenPlay Alpha - v" + App.version() minimumHeight: 450 minimumWidth: 1050 @@ -72,9 +73,10 @@ ApplicationWindow { // https://bugreports.qt.io/browse/QTBUG-86047 Material.accent: Material.color(Material.Orange) onVisibilityChanged: { - if (root.visibility === 2) - App.installedListModel.reset(); + if (root.visibility !== 2) + return; } + onClosing: close => { close.accepted = false; if (App.screenPlayManager.activeWallpaperCounter === 0 && App.screenPlayManager.activeWidgetsCounter === 0) { @@ -111,6 +113,7 @@ ApplicationWindow { App.showDockIcon(true); root.show(); } + App.installedListModel.reset(); } Item { diff --git a/ScreenPlay/qml/Create/CreateSidebar.qml b/ScreenPlay/qml/Create/CreateSidebar.qml index bf1ef42b..a46fe60b 100644 --- a/ScreenPlay/qml/Create/CreateSidebar.qml +++ b/ScreenPlay/qml/Create/CreateSidebar.qml @@ -42,9 +42,7 @@ Rectangle { function onWizardExited() { root.expanded = true; - stackView.clear(StackView.PushTransition); - stackView.push("qrc:/qml/ScreenPlayApp/qml/Create/StartInfo.qml"); - listView.currentIndex = 0; + App.util.setNavigation("Installed"); App.util.setNavigationActive(true); } @@ -87,6 +85,13 @@ Rectangle { objectName: "" } + ListElement { + headline: qsTr("Godot Wallpaper") + source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/GodotWallpaper.qml" + category: "Code Wallpaper" + objectName: "" + } + ListElement { headline: qsTr("QML Wallpaper") source: "qrc:/qml/ScreenPlayApp/qml/Create/Wizards/QMLWallpaper.qml" diff --git a/ScreenPlay/qml/Create/Wizards/GodotWallpaper.qml b/ScreenPlay/qml/Create/Wizards/GodotWallpaper.qml new file mode 100644 index 00000000..4f31d195 --- /dev/null +++ b/ScreenPlay/qml/Create/Wizards/GodotWallpaper.qml @@ -0,0 +1,101 @@ +import QtQuick +import Qt5Compat.GraphicalEffects +import QtQuick.Controls +import QtQuick.Controls.Material +import QtQuick.Layouts +import ScreenPlayApp +import ScreenPlay +import ScreenPlay.Create +import ScreenPlayUtil as Util + +WizardPage { + id: root + + sourceComponent: ColumnLayout { + id: rightWrapper + + function create() { + App.wizards.createGodotWallpaper(tfTitle.text, cbLicense.name, cbLicense.licenseFile, tfCreatedBy.text, previewSelector.imageSource, tagSelector.getTags()); + } + + spacing: 10 + + anchors { + top: parent.top + right: parent.right + left: parent.left + } + + Util.Headline { + text: qsTr("Create a Godot Wallpaper") + Layout.fillWidth: true + } + + Util.HeadlineSection { + text: qsTr("General") + } + + RowLayout { + spacing: 20 + + Util.TextField { + id: tfTitle + + Layout.fillWidth: true + placeholderText: qsTr("Wallpaper name") + required: true + onTextChanged: root.ready = text.length >= 1 + } + + Util.TextField { + id: tfCreatedBy + + Layout.fillWidth: true + placeholderText: qsTr("Created By") + } + } + + Util.TextField { + id: tfDescription + + Layout.fillWidth: true + placeholderText: qsTr("Description") + } + + Item { + height: 30 + } + + Util.HeadlineSection { + text: qsTr("License & Tags") + } + + RowLayout { + spacing: 20 + + Util.LicenseSelector { + id: cbLicense + } + + Util.TagSelector { + id: tagSelector + + Layout.fillWidth: true + } + } + + Item { + height: 30 + } + + Util.HeadlineSection { + text: qsTr("Preview Image") + } + + Util.ImageSelector { + id: previewSelector + + Layout.fillWidth: true + } + } +} diff --git a/ScreenPlay/qml/Create/Wizards/WebsiteWallpaper.qml b/ScreenPlay/qml/Create/Wizards/WebsiteWallpaper.qml index 605920aa..c906e5ce 100644 --- a/ScreenPlay/qml/Create/Wizards/WebsiteWallpaper.qml +++ b/ScreenPlay/qml/Create/Wizards/WebsiteWallpaper.qml @@ -10,16 +10,21 @@ import ScreenPlayUtil as Util WizardPage { id: root - + function isValidURL(string) { + var res = string.match(/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g); + return (res !== null); + } sourceComponent: ColumnLayout { - property bool ready: tfTitle.text.length >= 1 && tfUrl.text.length > 1 + id: layout + function validate() { + root.ready = tfTitle.text.length >= 1 && root.isValidURL(tfUrl.text); + } function create() { App.wizards.createWebsiteWallpaper(tfTitle.text, previewSelector.imageSource, tfUrl.text, tagSelector.getTags()); } spacing: 10 - onReadyChanged: root.ready = ready anchors { top: parent.top @@ -41,11 +46,10 @@ WizardPage { Util.TextField { id: tfTitle - Layout.fillWidth: true placeholderText: qsTr("Wallpaper name") required: true - onTextChanged: root.ready = text.length >= 1 + onTextChanged: layout.validate() } Util.TextField { @@ -65,10 +69,10 @@ WizardPage { Util.TextField { id: tfUrl - Layout.fillWidth: true required: true text: "https://" + onTextChanged: layout.validate() } Item { diff --git a/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/export_presets.cfg b/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/export_presets.cfg new file mode 100644 index 00000000..40ff0c71 --- /dev/null +++ b/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/export_presets.cfg @@ -0,0 +1,15 @@ +[preset.0] + +name="Windows Desktop" +platform="Windows Desktop" +runnable=true +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false diff --git a/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/project.godot b/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/project.godot new file mode 100644 index 00000000..ea0f2efb --- /dev/null +++ b/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/project.godot @@ -0,0 +1,15 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Test" +run/main_scene="res://wallpaper.tscn" +config/features=PackedStringArray("4.2", "Forward Plus") diff --git a/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/spinner.gd b/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/spinner.gd new file mode 100644 index 00000000..2e5cdb3b --- /dev/null +++ b/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/spinner.gd @@ -0,0 +1,6 @@ +extends CSGBox3D + +var rotation_speed = 3.0 + +func _process(delta): + rotate_y(rotation_speed * delta) diff --git a/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/wallpaper.tscn b/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/wallpaper.tscn new file mode 100644 index 00000000..714824ae --- /dev/null +++ b/ScreenPlay/qml/Create/WizardsFiles/Godot_v5/wallpaper.tscn @@ -0,0 +1,19 @@ +[gd_scene load_steps=2 format=3 uid="uid://d105uliklnkd5"] + +[ext_resource type="Script" path="res://spinner.gd" id="1_ggnsn"] + +[node name="Node3D" type="Node3D"] + +[node name="Camera3D" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 5) + +[node name="CSGBox3D" type="CSGBox3D" parent="."] +transform = Transform3D(0.707107, -0.5, -0.5, 0, 0.707107, -0.707107, 0.707107, 0.5, 0.5, 0, 0, 0) +script = ExtResource("1_ggnsn") + +[node name="OmniLight3D" type="OmniLight3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.41283, 1.52646) + +[node name="Label3D" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.874896, 0.37249) +text = "Godot Wallpaper" diff --git a/ScreenPlay/assets/wizards/QmlProject.qmlproject b/ScreenPlay/qml/Create/WizardsFiles/QmlProject.qmlproject similarity index 100% rename from ScreenPlay/assets/wizards/QmlProject.qmlproject rename to ScreenPlay/qml/Create/WizardsFiles/QmlProject.qmlproject diff --git a/ScreenPlay/qml/Installed/Installed.qml b/ScreenPlay/qml/Installed/Installed.qml index 5ca7e255..6f5528d1 100644 --- a/ScreenPlay/qml/Installed/Installed.qml +++ b/ScreenPlay/qml/Installed/Installed.qml @@ -41,7 +41,6 @@ Item { StackView.onActivated: { navWrapper.state = "in"; - App.installedListFilter.sortBySearchType(SearchType.All); checkIsContentInstalled(); } @@ -86,8 +85,6 @@ Item { property bool isScrolling: gridView.verticalVelocity !== 0 boundsBehavior: Flickable.DragOverBounds - maximumFlickVelocity: 3000 - flickDeceleration: 7500 anchors.fill: parent cellWidth: 340 cellHeight: 200 @@ -240,11 +237,19 @@ Item { contextMenu.publishedFileID = delegate.publishedFileID; contextMenu.absoluteStoragePath = delegate.absoluteStoragePath; contextMenu.fileName = delegate.customTitle; + contextMenu.type = delegate.type; + print(delegate.publishedFileID); + if (contextMenu.godotItem) + contextMenu.godotItem.destroy(); const pos = delegate.mapToItem(root, position.x, position.y); // Disable duplicate opening. The can happen if we // call popup when we are in the closing animtion. if (contextMenu.visible || contextMenu.opened) return; + if (delegate.type === InstalledType.GodotWallpaper) { + contextMenu.godotItem = editGodotWallpaperComp.createObject(); + contextMenu.insertItem(0, contextMenu.godotItem); + } contextMenu.popup(pos.x, pos.y); } } @@ -253,14 +258,30 @@ Item { snapMode: ScrollBar.SnapOnRelease } } + Component { + id: editGodotWallpaperComp + MenuItem { + text: qsTr("Edit Wallpaper") + objectName: "editWallpaper" + enabled: contextMenu.type === InstalledType.GodotWallpaper + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_edit.svg" + onClicked: { + App.util.openGodotEditor(contextMenu.absoluteStoragePath); + } + } + } Menu { id: contextMenu objectName: "installedItemContextMenu" - + // Must be var to support 64-bit size! property var publishedFileID: 0 + property var type: 0 property url absoluteStoragePath property string fileName + // We need to dynamically add this menu item + // if it is a Godot Wallpaper, see onOpenContextMenu + property var godotItem MenuItem { text: qsTr("Open containing folder") @@ -312,7 +333,9 @@ Item { anchors.centerIn: Overlay.overlay onAccepted: { root.sidebar.clear(); - App.installedListModel.deinstallItemAt(contextMenu.absoluteStoragePath); + if (!App.installedListModel.deinstallItemAt(contextMenu.absoluteStoragePath)) { + console.error("Unable to uninstall item", contextMenu.absoluteStoragePath); + } } } @@ -392,7 +415,8 @@ Item { importProjectErrorDialog.open(); return; } - var file = ""; // Convert url to string + var file = ""; + // Convert url to string file = "" + drop.urls[0]; if (!file.endsWith('.screenplay')) { importProjectErrorDialog.title = qsTr("File type not supported. We only support '.screenplay' files."); diff --git a/ScreenPlay/qml/Installed/ScreenPlayItem.qml b/ScreenPlay/qml/Installed/ScreenPlayItem.qml index e91f4167..79ef8247 100644 --- a/ScreenPlay/qml/Installed/ScreenPlayItem.qml +++ b/ScreenPlay/qml/Installed/ScreenPlayItem.qml @@ -14,6 +14,7 @@ Item { property string screenId property url absoluteStoragePath property int type: InstalledType.Unknown + // Must be var to make it work wit 64bit ints property var publishedFileID: 0 property int itemIndex property bool isScrolling: false diff --git a/ScreenPlay/qml/Navigation/Navigation.qml b/ScreenPlay/qml/Navigation/Navigation.qml index 16b5c639..7d80f5f1 100644 --- a/ScreenPlay/qml/Navigation/Navigation.qml +++ b/ScreenPlay/qml/Navigation/Navigation.qml @@ -13,7 +13,7 @@ Rectangle { id: root property string currentNavigationName: "Installed" - property var navArray: [navCreate, navWorkshop, navInstalled, navSettings, navCommunity] + property var navArray: [navCreate, navWorkshop, navInstalled, navCommunity, navSettings] property bool navActive: true property Item modalSource property int iconWidth: 16 @@ -28,13 +28,12 @@ Rectangle { else root.state = "disabled"; } - function setNavigation(name) { - var i = 0; - for (; i < navArray.length; i++) { - if (navArray[i].name === name) { + for (var i = 0; i < navArray.length; i++) { + if (navArray[i].objectName === name) { navArray[i].state = "active"; root.currentNavigationName = name; + tabBar.currentIndex = navArray[i].index; } else { navArray[i].state = "inactive"; } @@ -70,7 +69,7 @@ Rectangle { } TabBar { - id: row + id: tabBar height: 50 currentIndex: 2 @@ -84,55 +83,60 @@ Rectangle { CustomTabButton { id: navCreate + index: 0 icon.height: 22 icon.width: 22 text: qsTr("Create") + objectName: "Create" icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_plus.svg" onClicked: { root.onPageChanged("Create"); } - objectName: "createTab" } CustomTabButton { id: navWorkshop + index: 1 enabled: App.settings.steamVersion text: qsTr("Workshop") + objectName: "Workshop" icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_steam.svg" onClicked: { root.onPageChanged("Workshop"); } - objectName: "workshopTab" } CustomTabButton { id: navInstalled + index: 2 text: qsTr("Installed") + " " + App.installedListModel.count + objectName: "Installed" icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_installed.svg" onClicked: { root.onPageChanged("Installed"); } - objectName: "installedTab" } CustomTabButton { id: navCommunity + index: 3 text: qsTr("Community") + objectName: "Community" icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_community.svg" onClicked: { root.onPageChanged("Community"); } - objectName: "communityTab" } CustomTabButton { id: navSettings + index: 4 text: qsTr("Settings") + objectName: "Settings" icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_settings.svg" onClicked: { root.onPageChanged("Settings"); } - objectName: "settingsTab" } } @@ -142,6 +146,7 @@ Rectangle { font.pointSize: 12 height: parent.height width: implicitWidth + property int index: 0 background: Item { } font.capitalization: Font.MixedCase @@ -260,7 +265,7 @@ Rectangle { ToolButton { id: miConfig Layout.alignment: Qt.AlignVCenter - icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_video_settings_black_24dp.svg" + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_video_settings.svg" icon.width: root.iconWidth icon.height: root.iconHeight onClicked: App.util.setToggleWallpaperConfiguration() @@ -278,7 +283,7 @@ Rectangle { name: "disabled" PropertyChanges { - target: row + target: tabBar opacity: 0.3 } } @@ -289,7 +294,7 @@ Rectangle { to: "*" PropertyAnimation { - target: row + target: tabBar duration: 300 } } diff --git a/ScreenPlay/qml/Settings/SettingBool.qml b/ScreenPlay/qml/Settings/SettingBool.qml index d2915787..0f64af84 100644 --- a/ScreenPlay/qml/Settings/SettingBool.qml +++ b/ScreenPlay/qml/Settings/SettingBool.qml @@ -78,7 +78,7 @@ Item { id: radioButton checked: settingsBool.isChecked - onCheckedChanged: { + onClicked: { if (radioButton.checkState === Qt.Checked) checkboxChanged(true); else diff --git a/ScreenPlay/qml/Settings/Settings.qml b/ScreenPlay/qml/Settings/Settings.qml index b84dd0a8..67f48434 100644 --- a/ScreenPlay/qml/Settings/Settings.qml +++ b/ScreenPlay/qml/Settings/Settings.qml @@ -403,7 +403,7 @@ Item { Image { id: imgLogoHead - source: "https://assets.gitlab-static.net/uploads/-/system/user/avatar/64172/avatar.png" + source: "https://gitlab.com/uploads/-/system/user/avatar/64172/avatar.png" width: 120 height: 120 visible: false diff --git a/ScreenPlay/qml/TrayIcon.qml b/ScreenPlay/qml/TrayIcon.qml index 6e946522..5e509787 100644 --- a/ScreenPlay/qml/TrayIcon.qml +++ b/ScreenPlay/qml/TrayIcon.qml @@ -27,29 +27,99 @@ SystemTrayIcon { } } + function open() { + App.showDockIcon(true); + window.show(); + } + menu: Menu { MenuItem { + id: miOpenScreenPlay text: qsTr("Open ScreenPlay") + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_open_in_new_black.svg" onTriggered: { - App.showDockIcon(true); - window.show(); + root.open(); + } + } + MenuItem { + id: miChangeWallpaperSettings + text: qsTr("Change Wallpaper settings") + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_video_settings.svg" + onTriggered: { + root.open(); + App.util.setNavigation("Installed"); + App.util.setToggleWallpaperConfiguration(); + } + } + MenuItem { + separator: true + } + + MenuItem { + text: qsTr("Browse Workshop") + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_steam.svg" + onTriggered: { + root.open(); + App.util.setNavigation("Workshop"); } } + MenuItem { + id: miCreate + text: qsTr("Create new Wallpaper or Widgets") + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_plus.svg" + onTriggered: { + root.open(); + App.util.setNavigation("Create"); + } + } + MenuItem { + id: miSettings + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_settings.svg" + text: qsTr("Settings") + onTriggered: { + root.open(); + App.util.setNavigation("Settings"); + } + } + + MenuItem { + separator: true + } + MenuItem { + text: qsTr("Forums and Help") + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_supervisor_account.svg" + onTriggered: { + Qt.openUrlExternally("https://forum.screen-play.app/"); + } + } + MenuItem { + text: qsTr("Frequently Asked Questions (FAQ)") + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_help_center.svg" + onTriggered: { + Qt.openUrlExternally("https://kelteseth.gitlab.io/ScreenPlayDocs/Frequently%20Asked%20Questions/"); + } + } + MenuItem { + separator: true + } MenuItem { id: miMuteAll property bool isMuted: true text: qsTr("Mute all") + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_volume_mute.svg" onTriggered: { if (miMuteAll.isMuted) { isMuted = false; miMuteAll.text = qsTr("Mute all"); + miMuteAll.icon.source = "qrc:/qml/ScreenPlayApp/assets/icons/icon_volume_mute.svg" App.screenPlayManager.setAllWallpaperValue("muted", "true"); } else { isMuted = true; miMuteAll.text = qsTr("Unmute all"); + miMuteAll.icon.source = "qrc:/qml/ScreenPlayApp/assets/icons/icon_volume_up.svg" App.screenPlayManager.setAllWallpaperValue("muted", "false"); } } @@ -61,14 +131,17 @@ SystemTrayIcon { property bool isPlaying: false text: qsTr("Pause all") + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_pause.svg" onTriggered: { if (miStopAll.isPlaying) { isPlaying = false; miStopAll.text = qsTr("Pause all"); + miStopAll.icon.source = "qrc:/qml/ScreenPlayApp/assets/icons/icon_pause.svg" App.screenPlayManager.setAllWallpaperValue("isPlaying", "true"); } else { isPlaying = true; miStopAll.text = qsTr("Play all"); + miStopAll.icon.source = "qrc:/qml/ScreenPlayApp/assets/icons/icon_play.svg" App.screenPlayManager.setAllWallpaperValue("isPlaying", "false"); } } @@ -76,6 +149,7 @@ SystemTrayIcon { MenuItem { text: qsTr("Quit ScreenPlay") + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_cancel_presentation.svg" onTriggered: App.exit() } } diff --git a/ScreenPlay/src/app.cpp b/ScreenPlay/src/app.cpp index d8ba84f6..76836894 100644 --- a/ScreenPlay/src/app.cpp +++ b/ScreenPlay/src/app.cpp @@ -5,6 +5,7 @@ #include "ScreenPlayUtil/macutils.h" #endif +#include "ScreenPlay/CMakeVariables.h" #include "app.h" #include "steam/steam_qt_enums_generated.h" #include @@ -71,8 +72,22 @@ App::App() QGuiApplication::setOrganizationName("ScreenPlay"); QGuiApplication::setOrganizationDomain("screen-play.app"); QGuiApplication::setApplicationName("ScreenPlay"); - QGuiApplication::setApplicationVersion(QVersionNumber(0, 15, 0).toString()); + QGuiApplication::setApplicationVersion(QString(SCREENPLAY_VERSION)); QGuiApplication::setQuitOnLastWindowClosed(false); + // ScreenPlayManager first to check if another ScreenPlay Instace is running + m_screenPlayManager = std::make_unique(); + m_isAnotherScreenPlayInstanceRunning = m_screenPlayManager->isAnotherScreenPlayInstanceRunning(); +} + +/*! + \brief Used for initialization after the constructor. The sole purpose is to check if + another ScreenPlay instance is running and then quit early. This is also because we cannot + call QGuiApplication::quit(); in the SDKConnector before the app.exec(); ( the Qt main event + loop ) has started. +*/ +void App::init() +{ + qInfo() << "Init ScreenPlay"; QString fontsPath = QGuiApplication::instance()->applicationDirPath() + "/assets/fonts/"; #if defined(Q_OS_MACOS) @@ -121,26 +136,11 @@ App::App() "SearchType", "Error: only enums"); - // ScreenPlayManager first to check if another ScreenPlay Instace is running - m_screenPlayManager = std::make_unique(); - m_isAnotherScreenPlayInstanceRunning = m_screenPlayManager->isAnotherScreenPlayInstanceRunning(); -} - -/*! - \brief Used for initialization after the constructor. The sole purpose is to check if - another ScreenPlay instance is running and then quit early. This is also because we cannot - call QGuiApplication::quit(); in the SDKConnector before the app.exec(); ( the Qt main event - loop ) has started. -*/ -void App::init() -{ - using std::make_shared, std::make_unique; - // Util should be created as first so we redirect qDebugs etc. into the log - m_util = make_unique(); m_globalVariables = make_shared(); m_monitorListModel = make_shared(); + m_util = make_unique(m_globalVariables); m_profileListModel = make_shared(m_globalVariables); m_settings = make_shared(m_globalVariables); m_installedListModel = make_shared(m_globalVariables, m_settings); diff --git a/ScreenPlay/src/installedlistmodel.cpp b/ScreenPlay/src/installedlistmodel.cpp index faf4a2e9..ed555713 100644 --- a/ScreenPlay/src/installedlistmodel.cpp +++ b/ScreenPlay/src/installedlistmodel.cpp @@ -35,18 +35,23 @@ InstalledListModel::InstalledListModel( */ void InstalledListModel::init() { - if (!m_fileSystemWatcher.addPath(m_globalVariables->localStoragePath().toLocalFile())) { - qWarning() << "Could not setup file system watcher for changed files with path: " << m_globalVariables->localStoragePath().toLocalFile(); + QString projectsPath = m_globalVariables->localStoragePath().toLocalFile(); + QDirIterator projectFilesIter(projectsPath, { "*.qml", "*.html", "*.css", "*.js", "*.png", "project.json" }, QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories); + while (projectFilesIter.hasNext()) { + m_fileSystemWatcher.addPath(projectFilesIter.next()); } + m_reloadLimiter.setInterval(500); + m_reloadLimiter.setSingleShot(true); + QObject::connect(&m_reloadLimiter, &QTimer::timeout, this, [this]() { + reset(); + }); - auto reloadLambda = [this]() { - QTimer::singleShot(500, this, [this]() { - reset(); - }); + auto restartTimer = [this]() { + m_reloadLimiter.start(); }; - QObject::connect(&m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, reloadLambda); - QObject::connect(&m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, reloadLambda); + QObject::connect(&m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, restartTimer); + QObject::connect(&m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, restartTimer); } /*! @@ -54,28 +59,27 @@ void InstalledListModel::init() installed list. We wait for the qml engine to free all resources before we proceed. This like the preview.gif will be in use when clicking on an item */ -void InstalledListModel::deinstallItemAt(const QString& absoluteStoragePath) +bool InstalledListModel::deinstallItemAt(const QString& absoluteStoragePath) { - QTimer::singleShot(1000, this, [this, absoluteStoragePath]() { - int index = -1; - for (int i = 0; i < m_screenPlayFiles.size(); ++i) { - if (m_screenPlayFiles.at(i).projectJsonFilePath.absoluteFilePath() == absoluteStoragePath) { - index = i; - break; - } + const QString path = ScreenPlayUtil::toLocal(absoluteStoragePath); + int index = -1; + for (int i = 0; i < m_screenPlayFiles.size(); ++i) { + if (m_screenPlayFiles.at(i).projectJsonFilePath.path() == path) { + index = i; + break; } + } - if (index < 0 || index >= m_screenPlayFiles.count()) { - qWarning() << "Remove folder error, invalid index " << index; - return; - } + if (index < 0 || index >= m_screenPlayFiles.count()) { + qWarning() << "Remove folder error, invalid index " << index; + return false; + } - beginRemoveRows(QModelIndex(), index, index); - m_screenPlayFiles.removeAt(index); - endRemoveRows(); - - const QString path = ScreenPlayUtil::toLocal(absoluteStoragePath); + beginRemoveRows(QModelIndex(), index, index); + m_screenPlayFiles.removeAt(index); + endRemoveRows(); + QTimer::singleShot(1000, this, [this, path]() { QDir dir(path); bool success = true; if (!dir.exists()) { @@ -110,6 +114,7 @@ void InstalledListModel::deinstallItemAt(const QString& absoluteStoragePath) m_fileSystemWatcher.blockSignals(false); }); }); + return true; } /*! @@ -288,6 +293,10 @@ QVariantMap InstalledListModel::get(const QString& folderName) const */ void InstalledListModel::reset() { + if (m_isLoading) { + qInfo() << "loadInstalledContent is already running. Skip."; + return; + } beginResetModel(); m_screenPlayFiles.clear(); m_screenPlayFiles.squeeze(); diff --git a/ScreenPlay/src/monitorlistmodel.cpp b/ScreenPlay/src/monitorlistmodel.cpp index a5d2e954..904d57ce 100644 --- a/ScreenPlay/src/monitorlistmodel.cpp +++ b/ScreenPlay/src/monitorlistmodel.cpp @@ -114,16 +114,15 @@ void MonitorListModel::loadMonitors() #ifdef Q_OS_WIN QModelIndex index; - WinMonitorStats monitors; + auto monitors = WindowsIntegration().getAllMonitors(); // This offset lets us center the monitor selection view in the center int offsetX = 0; int offsetY = 0; - const int moinitorCount = monitors.iMonitors.size(); - for (int i = 0; i < moinitorCount; i++) { - const int x = monitors.rcMonitors[i].left; - const int y = monitors.rcMonitors[i].top; + for (auto& monitor : monitors) { + const int x = monitor.position.left; + const int y = monitor.position.top; if (x < 0) { offsetX += (x * -1); } @@ -132,11 +131,11 @@ void MonitorListModel::loadMonitors() } } - for (int i = 0; i < moinitorCount; i++) { - const int width = std::abs(monitors.rcMonitors[i].right - monitors.rcMonitors[i].left); - const int height = std::abs(monitors.rcMonitors[i].top - monitors.rcMonitors[i].bottom); - const int x = monitors.rcMonitors[i].left; - const int y = monitors.rcMonitors[i].top; + for (int i = 0; auto& monitor : monitors) { + const int width = std::abs(monitor.position.right - monitor.position.left); + const int height = std::abs(monitor.position.top - monitor.position.bottom); + const int x = monitor.position.left; + const int y = monitor.position.top; QRect geometry( x + offsetX, y + offsetY, @@ -145,6 +144,7 @@ void MonitorListModel::loadMonitors() beginInsertRows(index, m_monitorList.size(), m_monitorList.size()); m_monitorList.append(Monitor { i, geometry }); endInsertRows(); + i++; } #else QModelIndex index; diff --git a/ScreenPlay/src/profilelistmodel.cpp b/ScreenPlay/src/profilelistmodel.cpp index c92c6a1d..95252a25 100644 --- a/ScreenPlay/src/profilelistmodel.cpp +++ b/ScreenPlay/src/profilelistmodel.cpp @@ -22,7 +22,9 @@ namespace ScreenPlay { /*! Constructor */ -ProfileListModel::ProfileListModel(const std::shared_ptr& globalVariables, QObject* parent) +ProfileListModel::ProfileListModel( + const std::shared_ptr& globalVariables, + QObject* parent) : QAbstractListModel(parent) , m_globalVariables { globalVariables } { diff --git a/ScreenPlay/src/screenplaymanager.cpp b/ScreenPlay/src/screenplaymanager.cpp index ebc8dec2..7357bcb6 100644 --- a/ScreenPlay/src/screenplaymanager.cpp +++ b/ScreenPlay/src/screenplaymanager.cpp @@ -303,7 +303,7 @@ bool ScreenPlayManager::removeWallpaperAt(int index) */ bool ScreenPlayManager::requestProjectSettingsAtMonitorIndex(const int index) { - for (const std::shared_ptr& uPtrWallpaper : qAsConst(m_screenPlayWallpapers)) { + for (const std::shared_ptr& uPtrWallpaper : std::as_const(m_screenPlayWallpapers)) { if (uPtrWallpaper->screenNumber()[0] == index) { emit projectSettingsListModelResult( @@ -514,12 +514,12 @@ bool ScreenPlayManager::saveProfiles() m_saveLimiter.stop(); QJsonArray wallpaper {}; - for (const auto& activeWallpaper : qAsConst(m_screenPlayWallpapers)) { + for (const auto& activeWallpaper : std::as_const(m_screenPlayWallpapers)) { wallpaper.append(activeWallpaper->getActiveSettingsJson()); } QJsonArray widgets {}; - for (const auto& activeWidget : qAsConst(m_screenPlayWidgets)) { + for (const auto& activeWidget : std::as_const(m_screenPlayWidgets)) { widgets.append(activeWidget->getActiveSettingsJson()); } diff --git a/ScreenPlay/src/screenplaywallpaper.cpp b/ScreenPlay/src/screenplaywallpaper.cpp index f8c91aa1..6b90cad0 100644 --- a/ScreenPlay/src/screenplaywallpaper.cpp +++ b/ScreenPlay/src/screenplaywallpaper.cpp @@ -16,7 +16,8 @@ namespace ScreenPlay { /*! \brief Constructor for ScreenPlayWallpaper. */ -ScreenPlayWallpaper::ScreenPlayWallpaper(const QVector& screenNumber, +ScreenPlayWallpaper::ScreenPlayWallpaper( + const QVector& screenNumber, const std::shared_ptr& globalVariables, const QString& appID, const QString& absolutePath, @@ -42,7 +43,10 @@ ScreenPlayWallpaper::ScreenPlayWallpaper(const QVector& screenNumber, , m_playbackRate { playbackRate } , m_settings { settings } { - + std::optional projectOpt = ScreenPlayUtil::openJsonFileToObject(absolutePath + "/project.json"); + if (projectOpt.has_value()) { + m_projectJson = projectOpt.value(); + } QJsonObject projectSettingsListModelProperties; if (type == InstalledType::InstalledType::VideoWallpaper) { projectSettingsListModelProperties.insert("volume", m_volume); @@ -66,7 +70,7 @@ ScreenPlayWallpaper::ScreenPlayWallpaper(const QVector& screenNumber, QString tmpScreenNumber; if (m_screenNumber.length() > 1) { - for (const int number : qAsConst(m_screenNumber)) { + for (const int number : std::as_const(m_screenNumber)) { // IMPORTANT: NO TRAILING COMMA! if (number == m_screenNumber.back()) { tmpScreenNumber += QString::number(number); @@ -92,13 +96,22 @@ ScreenPlayWallpaper::ScreenPlayWallpaper(const QVector& screenNumber, if (m_type != InstalledType::InstalledType::GodotWallpaper) { m_appArgumentsList.append(" --disable-features=HardwareMediaKeyHandling"); } + if (m_type == InstalledType::InstalledType::GodotWallpaper) { + if (m_projectJson.contains("version")) { + const quint64 version = m_projectJson.value("version").toInt(); + const QString packageFileName = QString("project-v%1.zip").arg(version); + m_appArgumentsList.append(packageFileName); + } + } } bool ScreenPlayWallpaper::start() { if (m_type == InstalledType::InstalledType::GodotWallpaper) { - exportGodotProject(m_absolutePath); + if (!exportGodotProject()) + return false; } + m_process.setArguments(m_appArgumentsList); if (m_type == InstalledType::InstalledType::GodotWallpaper) { m_process.setProgram(m_globalVariables->godotWallpaperExecutablePath().toString()); @@ -126,7 +139,7 @@ bool ScreenPlayWallpaper::start() QJsonObject ScreenPlayWallpaper::getActiveSettingsJson() { QJsonArray screenNumber; - for (const int i : qAsConst(m_screenNumber)) { + for (const int i : std::as_const(m_screenNumber)) { screenNumber.append(i); } @@ -293,22 +306,40 @@ bool ScreenPlayWallpaper::replace( return success; } -bool ScreenPlayWallpaper::exportGodotProject(const QString& absolutePath, int timeoutMilliseconds) +/*! + \brief . +*/ +bool ScreenPlayWallpaper::exportGodotProject() { + if (!m_projectJson.contains("version")) + return false; + + const quint64 version = m_projectJson.value("version").toInt(); + const QString packageFileName = QString("project-v%1.zip").arg(version); + QFileInfo godotPackageFile(m_absolutePath + "/" + packageFileName); + // Skip reexport + if (godotPackageFile.exists()) + return true; + + qInfo() << "No suitable version found for Godot package" << packageFileName << " at" << godotPackageFile.absoluteFilePath() << " exporting a new pck as zip."; + // Prepare the Godot export command - const QList godotCmd = { "--export-pack", "--headless", "Windows Desktop", "project.zip" }; + const QList + godotCmd + = { "--export-pack", "--headless", "Windows Desktop", packageFileName }; // Create QProcess instance to run the command QProcess process; // Set the working directory to the given absolute path - process.setWorkingDirectory(absolutePath); + process.setWorkingDirectory(m_absolutePath); process.setProgram(m_globalVariables->godotEditorExecutablePath().toString()); // Start the Godot export process process.setArguments(godotCmd); process.start(); // Wait for the process to finish or timeout + const int timeoutMilliseconds = 30000; if (!process.waitForFinished(timeoutMilliseconds)) { qCritical() << "Godot export process timed out or failed to start."; return false; @@ -331,9 +362,9 @@ bool ScreenPlayWallpaper::exportGodotProject(const QString& absolutePath, int ti } // Check if the project.zip file was created - QString zipPath = QDir(absolutePath).filePath("project.zip"); + QString zipPath = QDir(m_absolutePath).filePath(packageFileName); if (!QFile::exists(zipPath)) { - qCritical() << "Expected export file (project.zip) was not created."; + qCritical() << "Expected export file (" << packageFileName << ") was not created."; return false; } @@ -342,7 +373,7 @@ bool ScreenPlayWallpaper::exportGodotProject(const QString& absolutePath, int ti // but for simplicity, we're just checking its size here) QFileInfo zipInfo(zipPath); if (zipInfo.size() <= 0) { - qCritical() << "The exported project.zip file seems to be invalid."; + qCritical() << "The exported " << packageFileName << " file seems to be invalid."; return false; } diff --git a/ScreenPlay/src/settings.cpp b/ScreenPlay/src/settings.cpp index 524573ca..2ecf72f6 100644 --- a/ScreenPlay/src/settings.cpp +++ b/ScreenPlay/src/settings.cpp @@ -161,7 +161,7 @@ void Settings::setupWidgetAndWindowPaths() m_globalVariables->setWidgetExecutablePath(QUrl(workingDir.path() + "/ScreenPlayWidget" + ScreenPlayUtil::executableBinEnding())); m_globalVariables->setWallpaperExecutablePath(QUrl(workingDir.path() + "/ScreenPlayWallpaper" + ScreenPlayUtil::executableBinEnding())); m_globalVariables->setGodotWallpaperExecutablePath(QUrl(workingDir.path() + "/ScreenPlayWallpaperGodot" + ScreenPlayUtil::executableBinEnding())); - const auto godotEditorName = "Godot_" + godotVersion + "-" + godotReleaseType + "_win64.exe"; + const auto godotEditorName = "Godot_" + godotVersion + "_win64.exe"; m_globalVariables->setGodotEditorExecutablePath(QUrl(workingDir.path() + "/" + godotEditorName)); } else if (osType == "macos") { // ScreenPlayTest is not bundled in an .app so the working directory diff --git a/ScreenPlay/src/util.cpp b/ScreenPlay/src/util.cpp index 669a91d6..7743c8c9 100644 --- a/ScreenPlay/src/util.cpp +++ b/ScreenPlay/src/util.cpp @@ -23,8 +23,10 @@ namespace ScreenPlay { /*! \brief Constructor. */ -Util::Util() +Util::Util( + const std::shared_ptr& globalVariables) : QObject(nullptr) + , m_globalVariables { globalVariables } { m_extractor = std::make_unique(); m_compressor = std::make_unique(); @@ -144,6 +146,15 @@ bool Util::exportProject(QString contentPath, QString exportFileName) return true; } +bool Util::openGodotEditor(QString contentPath) const +{ + const QList godotCmd = { "--editor", "--path", toLocal(contentPath) }; + QProcess process; + process.setProgram(m_globalVariables->godotEditorExecutablePath().toString()); + process.setArguments(godotCmd); + return process.startDetached(); +} + /*! \brief Imports a given project from a .screenplay zip file. The argument extractionPath must be copied otherwise it will get reset in qml before extracting. diff --git a/ScreenPlay/src/wizards.cpp b/ScreenPlay/src/wizards.cpp index 5fd438b1..d0b026cc 100644 --- a/ScreenPlay/src/wizards.cpp +++ b/ScreenPlay/src/wizards.cpp @@ -1,6 +1,11 @@ // SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only #include "ScreenPlay/wizards.h" +#include "ScreenPlay/CMakeVariables.h" #include "ScreenPlayUtil/util.h" +#include +#include +#include +#include namespace ScreenPlay { /*! @@ -67,15 +72,13 @@ void Wizards::createQMLWidget(const QString& title, } if (!previewThumbnail.isEmpty()) { - QUrl previewThumbnailUrl { previewThumbnail }; - QFileInfo previewImageFile(previewThumbnailUrl.toLocalFile()); - obj.insert("previewThumbnail", previewImageFile.fileName()); - obj.insert("preview", previewImageFile.fileName()); - if (!QFile::copy(previewThumbnailUrl.toLocalFile(), workingPath + "/" + previewImageFile.fileName())) { - qWarning() << "Could not copy" << previewThumbnailUrl.toLocalFile() << " to " << workingPath + "/" + previewImageFile.fileName(); - emit widgetCreationFinished(WizardResult::CopyError); + if (!Util::copyPreviewThumbnail(obj, previewThumbnail, workingPath)) { + emit widgetCreationFinished(WizardResult::CopyPreviewThumbnailError); return; } + } else { + obj.insert("preview", "preview.png"); + createPreviewImage(title, workingPath); } if (!Util::writeSettings(obj, workingPath + "/project.json")) { @@ -136,13 +139,13 @@ void Wizards::createHTMLWidget(const QString& title, QFileInfo previewImageFile(previewThumbnailUrl.toLocalFile()); if (!previewThumbnail.isEmpty()) { - obj.insert("previewThumbnail", previewImageFile.fileName()); - obj.insert("preview", previewImageFile.fileName()); - if (!QFile::copy(previewThumbnailUrl.toLocalFile(), workingPath + "/" + previewImageFile.fileName())) { - qWarning() << "Could not copy" << previewThumbnailUrl.toLocalFile() << " to " << workingPath + "/" + previewImageFile.fileName(); - emit widgetCreationFinished(WizardResult::CopyError); + if (!Util::copyPreviewThumbnail(obj, previewThumbnail, workingPath)) { + emit widgetCreationFinished(WizardResult::CopyPreviewThumbnailError); return; } + } else { + obj.insert("preview", "preview.png"); + createPreviewImage(title, workingPath); } if (!Util::writeSettings(obj, workingPath + "/project.json")) { @@ -201,15 +204,13 @@ void Wizards::createHTMLWallpaper( } if (!previewThumbnail.isEmpty()) { - QUrl previewThumbnailUrl { previewThumbnail }; - QFileInfo previewImageFile(previewThumbnailUrl.toLocalFile()); - obj.insert("previewThumbnail", previewImageFile.fileName()); - obj.insert("preview", previewImageFile.fileName()); - if (!QFile::copy(previewThumbnailUrl.toLocalFile(), workingPath + "/" + previewImageFile.fileName())) { - qWarning() << "Could not copy" << previewThumbnailUrl.toLocalFile() << " to " << workingPath + "/" + previewImageFile.fileName(); + if (!Util::copyPreviewThumbnail(obj, previewThumbnail, workingPath)) { emit widgetCreationFinished(WizardResult::CopyPreviewThumbnailError); return; } + } else { + obj.insert("preview", "preview.png"); + createPreviewImage(title, workingPath); } if (!Util::writeSettings(obj, workingPath + "/project.json")) { @@ -260,6 +261,9 @@ void Wizards::createQMLWallpaper( emit widgetCreationFinished(WizardResult::CopyPreviewThumbnailError); return; } + } else { + obj.insert("preview", "preview.png"); + createPreviewImage(title, workingPath); } if (!Util::writeFileFromQrc(":/qml/ScreenPlayApp/assets/wizards/" + licenseFile, workingPath + "/" + licenseFile)) { @@ -269,7 +273,7 @@ void Wizards::createQMLWallpaper( } const QString qmlproject = workingPath + "/" + title + ".qmlproject"; - if (!Util::writeFileFromQrc(":/qml/ScreenPlayApp/assets/wizards/" + QString("QmlProject.qmlproject"), qmlproject)) { + if (!Util::writeFileFromQrc(":/qml/ScreenPlayApp/qml/Create/WizardsFiles/" + QString("QmlProject.qmlproject"), qmlproject)) { qWarning() << "Could not write " << qmlproject; emit widgetCreationFinished(WizardResult::WriteLicenseFileError); return; @@ -289,6 +293,95 @@ void Wizards::createQMLWallpaper( }); } +/*! + \brief . +*/ +void Wizards::createGodotWallpaper( + const QString& title, + const QString& licenseName, + const QString& licenseFile, + const QString& createdBy, + const QString& previewThumbnail, + const QVector& tags) +{ + if (m_wizardFuture.isRunning()) { + qWarning() << "Another wizard is already running! Abort."; + return; + } + + m_wizardFuture = QtConcurrent::run([=]() { + std::optional folderName = createTemporaryFolder(); + + if (!folderName.has_value()) { + emit widgetCreationFinished(WizardResult::CreateProjectFolderError); + return; + } + + const QString workingPath = ScreenPlayUtil::toLocal(m_globalVariables->localStoragePath().toString() + "/" + folderName.value()); + + QJsonObject obj; + obj.insert("license", licenseName); + obj.insert("title", title); + obj.insert("createdBy", createdBy); + // Every version change will trigger an reexport + obj.insert("version", 1); + // Something like v4.2-beta3 or v5.0.1-stable + QString godotVersionMajor = QString::number(SCREENPLAY_GODOT_VERSION_MAJOR); + QString godotVersionMinor = QString::number(SCREENPLAY_GODOT_VERSION_MINOR); + obj.insert("godotVersionMajor", godotVersionMajor); + obj.insert("godotVersionMinor", godotVersionMinor); + obj.insert("tags", ScreenPlayUtil::fillArray(tags)); + obj.insert("type", QVariant::fromValue(InstalledType::InstalledType::GodotWallpaper).toString()); + obj.insert("file", "wallpaper.tscn"); + + if (!previewThumbnail.isEmpty()) { + if (!Util::copyPreviewThumbnail(obj, previewThumbnail, workingPath)) { + emit widgetCreationFinished(WizardResult::CopyPreviewThumbnailError); + return; + } + } else { + obj.insert("preview", "preview.png"); + createPreviewImage(title, workingPath); + } + + if (!Util::writeFileFromQrc(":/qml/ScreenPlayApp/assets/wizards/" + licenseFile, workingPath + "/" + licenseFile)) { + qWarning() << "Could not write " << licenseFile; + emit widgetCreationFinished(WizardResult::WriteLicenseFileError); + return; + } + + if (!Util::writeSettings(obj, workingPath + "/project.json")) { + emit widgetCreationFinished(WizardResult::WriteProjectFileError); + return; + } + + if (!Util::writeFileFromQrc(":/qml/ScreenPlayApp/qml/Create/WizardsFiles/Godot_v5/project.godot", workingPath + "/project.godot")) { + qWarning() << "Could not write project.godot"; + return; + } + + if (!Util::writeFileFromQrc(":/qml/ScreenPlayApp/qml/Create/WizardsFiles/Godot_v5/spinner.gd", workingPath + "/spinner.gd")) { + qWarning() << "Could not write spinner.gd"; + return; + } + + if (!Util::writeFileFromQrc(":/qml/ScreenPlayApp/qml/Create/WizardsFiles/Godot_v5/wallpaper.tscn", workingPath + "/wallpaper.tscn")) { + qWarning() << "Could not write wallpaper.tscn"; + return; + } + + // This presets file is needed for the export. Because we do only export + // package files, it does not matter that we hardcode "Windows Desktop" as + // export preset. + if (!Util::writeFileFromQrc(":/qml/ScreenPlayApp/qml/Create/WizardsFiles/Godot_v5/export_presets.cfg", workingPath + "/export_presets.cfg")) { + qWarning() << "Could not write export_presets.cfg"; + return; + } + + emit widgetCreationFinished(WizardResult::Ok, workingPath); + }); +} + void Wizards::createGifWallpaper( const QString& title, const QString& licenseName, @@ -378,6 +471,9 @@ void Wizards::createWebsiteWallpaper( emit widgetCreationFinished(WizardResult::CopyPreviewThumbnailError); return; } + } else { + obj.insert("preview", "preview.png"); + createPreviewImage(title, workingPath); } if (!Util::writeSettings(obj, workingPath + "/project.json")) { @@ -408,6 +504,48 @@ const std::optional Wizards::createTemporaryFolder() const return folderName; } + +/*! + \brief Creates a default preview.png. +*/ +void Wizards::createPreviewImage(const QString& name, const QString& targetPath) +{ + // Step 1: Create QImage and QPainter + QImage image(749, 442, QImage::Format_ARGB32); + QPainter painter(&image); + + // Step 2: Select Random Colors (example colors, replace with Material colors) + // These are just placeholder colors, replace with actual Material colors + int randomIndex1 = QRandomGenerator::global()->bounded(5); + int randomIndex2 = QRandomGenerator::global()->bounded(5); + + // Step 3: Create and Set Gradient + QLinearGradient gradient(QPointF(0, image.height()), QPointF(image.width(), 0)); + gradient.setColorAt(0, m_gradientColors[randomIndex1].darker()); // Dark color + gradient.setColorAt(1, m_gradientColors[randomIndex2].lighter()); // Bright color + + painter.fillRect(image.rect(), gradient); + + // Step 4: Draw Text + // Set the font size depending on the size of the image + painter.setPen(Qt::white); + int fontSize = qMin(image.width(), image.height()) / 10; // Adjust proportion as needed + QFont font = painter.font(); + font.setFamily("Noto Sans Light"); + font.setPointSize(fontSize); + painter.setFont(font); + + // Define a margin and adjust the rect accordingly + int margin = 30; + QRect rect(margin, margin, image.width() - 2 * margin, image.height() - 2 * margin); + + // Draw text within the adjusted rect + QTextOption option(Qt::AlignCenter); + painter.drawText(rect, name, option); + + // Step 5: Save Image + image.save(targetPath + "/preview.png"); +} } #include "moc_wizards.cpp" diff --git a/ScreenPlay/tests/tst_main.cpp b/ScreenPlay/tests/tst_main.cpp index 3963c817..4493986a 100644 --- a/ScreenPlay/tests/tst_main.cpp +++ b/ScreenPlay/tests/tst_main.cpp @@ -102,7 +102,7 @@ void ScreenPlayTest::import_convert_video() auto* createWallpaperInit = m_window->findChild("createWallpaperInit"); QVERIFY(createWallpaperInit); - const QString originalVideoPath = QString(SCREENPLAY_SOURCE_DIR) + "/ScreenPlay/assets/tests/video_import.mp4"; + const QString originalVideoPath = QString(SCREENPLAY_SOURCE_DIR) + "/Content/wallpaper_video_nebula_h264/mantissa.xyz_loop_072.mp4"; qInfo() << originalVideoPath; QVERIFY(QMetaObject::invokeMethod(createWallpaperInit, diff --git a/ScreenPlayUtil/CMakeLists.txt b/ScreenPlayUtil/CMakeLists.txt index 4c02679b..ba6c9cb4 100644 --- a/ScreenPlayUtil/CMakeLists.txt +++ b/ScreenPlayUtil/CMakeLists.txt @@ -107,3 +107,7 @@ if(WIN32) # Used for query windows monitor data target_link_libraries(${PROJECT_NAME} PUBLIC shcore.lib) endif() + +# ##### USE CMAKE VARIABLES IN CODE ##### +include(GenerateCMakeVariableHeader) +generate_cmake_variable_header(${PROJECT_NAME}) diff --git a/ScreenPlayUtil/src/logginghandler.cpp b/ScreenPlayUtil/src/logginghandler.cpp index 0b225b5a..5c61f061 100644 --- a/ScreenPlayUtil/src/logginghandler.cpp +++ b/ScreenPlayUtil/src/logginghandler.cpp @@ -8,6 +8,8 @@ #include #include +#include "ScreenPlayUtil/CMakeVariables.h" + #ifdef Q_OS_WINDOWS #ifndef NOMINMAX #define NOMINMAX @@ -29,11 +31,14 @@ LoggingHandler::LoggingHandler(const QString& logFileName) #ifdef Q_OS_WINDOWS // Enable UTF-8 support SetConsoleOutputCP(CP_UTF8); - - // QtCreator has issues with fmt prints - // https://bugreports.qt.io/browse/QTCREATORBUG-3994 - setbuf(stdout, NULL); - + // This is a QtCreator workaround and crashes std::fmt + // when running standalone. + if (!SCREENPLAY_DEPLOY_VERSION) { + // QtCreator has issues with fmt prints + // https://bugreports.qt.io/browse/QTCREATORBUG-3994 + setvbuf(stdout, NULL, _IONBF, 0); + qInfo() << "Setting setvbuf(stdout, NULL, _IONBF, 0); This unbuffered output causes crashes in release with threaded fmt!"; + } #endif qSetMessagePattern("[%{time dd.MM.yyyy h:mm:ss.zzz} %{if-debug}Debug%{endif}%{if-info}Info%{endif}%{if-warning}Warning%{endif}%{if-critical}Critical%{endif}%{if-fatal}Fatal%{endif}] %{file}:%{line} - %{message}"); @@ -227,7 +232,6 @@ void LoggingHandler::writeToConsole(QtMsgType type, const QMessageLogContext& co const auto filename = extractFileName(context); const auto function = extractFunction(context); const auto line = context.line; - fmt::print( "[{}] {} {}:{} - {}\n", fmt::styled(now.toStdString(), fmt::emphasis::bold), diff --git a/ScreenPlayUtil/src/projectfile.cpp b/ScreenPlayUtil/src/projectfile.cpp index b756d1cf..13fb6f68 100644 --- a/ScreenPlayUtil/src/projectfile.cpp +++ b/ScreenPlayUtil/src/projectfile.cpp @@ -36,22 +36,25 @@ bool ProjectFile::init() } type = typeParsed.value(); - // Required: - if (!obj.contains("file")) + // File is required. Website Wallpaper doe not have a file, but a url + if (!obj.contains("file") && type != ScreenPlay::InstalledType::InstalledType::WebsiteWallpaper) return false; - file = obj.value("file").toString(); - if (type == ScreenPlay::InstalledType::InstalledType::GodotWallpaper) { - QFileInfo fileInfo(folder.path() + "/wallpaper.tscn"); - if (!fileInfo.exists()) { - qCritical() << "Requested file:" << fileInfo.absoluteFilePath() << "does not exist!"; - return false; - } - } else { - QFileInfo fileInfo(folder.path() + "/" + file); - if (!fileInfo.exists()) { - qCritical() << "Requested file:" << fileInfo.absoluteFilePath() << "does not exist!"; - return false; + if (type != ScreenPlay::InstalledType::InstalledType::WebsiteWallpaper) { + file = obj.value("file").toString(); + + if (type == ScreenPlay::InstalledType::InstalledType::GodotWallpaper) { + QFileInfo fileInfo(folder.path() + "/wallpaper.tscn"); + if (!fileInfo.exists()) { + qCritical() << "Requested file:" << fileInfo.absoluteFilePath() << "does not exist!"; + return false; + } + } else { + QFileInfo fileInfo(folder.path() + "/" + file); + if (!fileInfo.exists()) { + qCritical() << "Requested file:" << fileInfo.absoluteFilePath() << "does not exist!"; + return false; + } } } @@ -105,13 +108,13 @@ bool ProjectFile::init() qWarning("Invalid videoCodec was specified inside the json object!"); } } else if (type == ScreenPlay::InstalledType::InstalledType::VideoWallpaper) { - qWarning("No videoCodec was specified inside the json object!"); + // qWarning("No videoCodec was specified inside the json object!"); if (file.endsWith(".mp4")) { videoCodec = ScreenPlay::VideoCodec::VideoCodec::H264; - qWarning("Eyeball to h264 because of .mp4"); + // qWarning("Eyeball to h264 because of .mp4"); } else if (file.endsWith(".webm")) { videoCodec = ScreenPlay::VideoCodec::VideoCodec::VP8; - qWarning("Eyeball to VP8 because of .webm"); + // qWarning("Eyeball to VP8 because of .webm"); } } diff --git a/ScreenPlayWallpaper/CMakeLists.txt b/ScreenPlayWallpaper/CMakeLists.txt index 63147167..3e7256ef 100644 --- a/ScreenPlayWallpaper/CMakeLists.txt +++ b/ScreenPlayWallpaper/CMakeLists.txt @@ -46,7 +46,7 @@ set(QML qml/Wallpaper.qml qml/WebsiteWallpaper.qml) -set(RESOURCES dot.png qtquickcontrols2.conf index.html) +set(RESOURCES qtquickcontrols2.conf index.html) set(LIB_SOURCES) set(LIB_HEADER) diff --git a/ScreenPlayWallpaper/Godot/GDExtention/CMakeLists.txt b/ScreenPlayWallpaper/Godot/GDExtention/CMakeLists.txt index 60ea81de..a201be38 100644 --- a/ScreenPlayWallpaper/Godot/GDExtention/CMakeLists.txt +++ b/ScreenPlayWallpaper/Godot/GDExtention/CMakeLists.txt @@ -32,8 +32,15 @@ project(ScreenPlayGodotWallpaper LANGUAGES CXX) # Create our library add_library(${PROJECT_NAME} SHARED) +# Runs after compilation Enable executing python scripts +find_package(Python COMPONENTS Interpreter) -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) +add_custom_command( + TARGET ${PROJECT_NAME} + POST_BUILD + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/Tools" + COMMAND "${Python_EXECUTABLE}" "build_godot.py" "--build_path" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" + COMMENT "Running post-build step") # LIB_ARCH is the architecture being built. It is set to the build system's architecture. For macOS, we build a universal library (both # arm64 and x86_64). diff --git a/ScreenPlayWallpaper/Godot/GDExtention/extern/godot-cpp b/ScreenPlayWallpaper/Godot/GDExtention/extern/godot-cpp index c1196a1a..32409472 160000 --- a/ScreenPlayWallpaper/Godot/GDExtention/extern/godot-cpp +++ b/ScreenPlayWallpaper/Godot/GDExtention/extern/godot-cpp @@ -1 +1 @@ -Subproject commit c1196a1ab0a1ca166d0e5e2f08f9fe4156118c5e +Subproject commit 32409472b785fd6f9062300664fa55b46bfd07e7 diff --git a/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.cpp b/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.cpp index 8d124242..7476a1d6 100644 --- a/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.cpp +++ b/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.cpp @@ -33,6 +33,9 @@ void ScreenPlayGodotWallpaper::_bind_methods() ClassDB::bind_method(godot::D_METHOD("get_activeScreensList"), &ScreenPlayGodotWallpaper::get_activeScreensList); ClassDB::bind_method(godot::D_METHOD("set_activeScreensList", "screens"), &ScreenPlayGodotWallpaper::set_activeScreensList); + ClassDB::bind_method(godot::D_METHOD("get_projectPackageFile"), &ScreenPlayGodotWallpaper::get_projectPackageFile); + ClassDB::bind_method(godot::D_METHOD("set_projectPackageFile", "projectPackageFile"), &ScreenPlayGodotWallpaper::set_projectPackageFile); + ClassDB::bind_method(godot::D_METHOD("get_projectPath"), &ScreenPlayGodotWallpaper::get_projectPath); ClassDB::bind_method(godot::D_METHOD("set_projectPath", "path"), &ScreenPlayGodotWallpaper::set_projectPath); @@ -54,6 +57,16 @@ void ScreenPlayGodotWallpaper::hideFromTaskbar(HWND hwnd) SetWindowLong(hwnd, GWL_EXSTYLE, lExStyle); } +godot::String ScreenPlayGodotWallpaper::get_projectPackageFile() const +{ + return m_projectPackageFile; +} + +void ScreenPlayGodotWallpaper::set_projectPackageFile(const godot::String& projectPackageFile) +{ + m_projectPackageFile = projectPackageFile; +} + bool ScreenPlayGodotWallpaper::configureWindowGeometry() { if (!m_windowsIntegration.searchWorkerWindowToParentTo()) { @@ -74,18 +87,26 @@ bool ScreenPlayGodotWallpaper::configureWindowGeometry() bool ScreenPlayGodotWallpaper::init(int activeScreen) { auto* displayServer = DisplayServer::get_singleton(); - { - int64_t handle_int = displayServer->window_get_native_handle(godot::DisplayServer::HandleType::WINDOW_HANDLE, activeScreen); - HWND hwnd = reinterpret_cast(static_cast(handle_int)); - m_windowsIntegration.setWindowHandle(hwnd); + + int64_t handle_int = displayServer->window_get_native_handle(godot::DisplayServer::HandleType::WINDOW_HANDLE); + HWND hwnd = reinterpret_cast(handle_int); + m_windowsIntegration.setWindowHandle(hwnd); + ShowWindow(m_windowsIntegration.windowHandle(), SW_HIDE); + if (!IsWindow(hwnd)) { + UtilityFunctions::print("ScreenPlayGodotWallpaper::init Could not get a valid window handle !", activeScreen, handle_int); + UtilityFunctions::print("init hwnd: ", (int64_t)hwnd, activeScreen, handle_int); + + std::vector monitors = m_windowsIntegration.getAllMonitors(); + for (const auto& monitor : monitors) { + UtilityFunctions::print(monitor.toString().c_str()); + } + return false; } - hideFromTaskbar(m_windowsIntegration.windowHandle()); if (!configureWindowGeometry()) { return false; } - ShowWindow(m_windowsIntegration.windowHandle(), SW_HIDE); - + hideFromTaskbar(m_windowsIntegration.windowHandle()); auto updateWindowSize = [&displayServer](const int width, const int height) { displayServer->window_set_size(godot::Vector2((real_t)width, (real_t)height)); }; @@ -101,38 +122,38 @@ bool ScreenPlayGodotWallpaper::init(int activeScreen) SetWindowText(m_windowsIntegration.windowHandle(), windowTitle.c_str()); ShowWindow(m_windowsIntegration.windowHandle(), SW_SHOW); - m_windowsIntegration.setupWindowMouseHook(); + // m_windowsIntegration.setupWindowMouseHook(); // Set up the mouse event handler - m_windowsIntegration.setMouseEventHandler([this](DWORD mouseButton, UINT type, POINT p) { - Ref mouse_event; - Ref motion_event; - switch (type) { - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - mouse_event.instantiate(); - mouse_event->set_position(Vector2(p.x, p.y)); - mouse_event->set_global_position(Vector2(p.x, p.y)); // Assuming global == local for this context - mouse_event->set_button_index( - type == WM_LBUTTONDOWN || type == WM_LBUTTONUP ? MOUSE_BUTTON_LEFT : MOUSE_BUTTON_RIGHT); - mouse_event->set_pressed(type == WM_LBUTTONDOWN || type == WM_RBUTTONDOWN); - break; - case WM_MOUSEMOVE: - motion_event.instantiate(); - motion_event->set_position(Vector2(p.x, p.y)); - motion_event->set_global_position(Vector2(p.x, p.y)); - break; - // Add more cases as needed - } + // m_windowsIntegration.setMouseEventHandler([this](DWORD mouseButton, UINT type, POINT p) { + // Ref mouse_event; + // Ref motion_event; + // switch (type) { + // case WM_LBUTTONDOWN: + // case WM_LBUTTONUP: + // case WM_RBUTTONDOWN: + // case WM_RBUTTONUP: + // mouse_event.instantiate(); + // mouse_event->set_position(Vector2(p.x, p.y)); + // mouse_event->set_global_position(Vector2(p.x, p.y)); // Assuming global == local for this context + // mouse_event->set_button_index( + // type == WM_LBUTTONDOWN || type == WM_LBUTTONUP ? MOUSE_BUTTON_LEFT : MOUSE_BUTTON_RIGHT); + // mouse_event->set_pressed(type == WM_LBUTTONDOWN || type == WM_RBUTTONDOWN); + // break; + // case WM_MOUSEMOVE: + // motion_event.instantiate(); + // motion_event->set_position(Vector2(p.x, p.y)); + // motion_event->set_global_position(Vector2(p.x, p.y)); + // break; + // // Add more cases as needed + // } - if (mouse_event.is_valid()) { - get_tree()->get_root()->get_viewport()->push_input(mouse_event); - } - if (motion_event.is_valid()) { - get_tree()->get_root()->get_viewport()->push_input(motion_event); - } - }); + // if (mouse_event.is_valid()) { + // get_tree()->get_root()->get_viewport()->push_input(mouse_event); + // } + // if (motion_event.is_valid()) { + // get_tree()->get_root()->get_viewport()->push_input(motion_event); + // } + // }); 2 return true; } @@ -222,18 +243,6 @@ void ScreenPlayGodotWallpaper::messageReceived(const std::string& key, const std std::cerr << "Out of range: " << oor.what() << std::endl; } } -void ScreenPlayGodotWallpaper::set_checkWallpaperVisible(bool visible) -{ - m_checkWallpaperVisible = visible; -} -bool ScreenPlayGodotWallpaper::get_screenPlayConnected() const -{ - return m_screenPlayConnected; -} -bool ScreenPlayGodotWallpaper::get_pipeConnected() const -{ - return m_pipeConnected; -} bool ScreenPlayGodotWallpaper::exit() { // Somehow this gets called at editor startup @@ -247,6 +256,18 @@ bool ScreenPlayGodotWallpaper::exit() ShowWindow(m_windowsIntegration.windowHandleWorker(), SW_HIDE); return true; } +void ScreenPlayGodotWallpaper::set_checkWallpaperVisible(bool visible) +{ + m_checkWallpaperVisible = visible; +} +bool ScreenPlayGodotWallpaper::get_screenPlayConnected() const +{ + return m_screenPlayConnected; +} +bool ScreenPlayGodotWallpaper::get_pipeConnected() const +{ + return m_pipeConnected; +} bool ScreenPlayGodotWallpaper::get_checkWallpaperVisible() const { return m_checkWallpaperVisible; diff --git a/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.h b/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.h index ea1d12ff..b8fcb18b 100644 --- a/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.h +++ b/ScreenPlayWallpaper/Godot/GDExtention/src/ScreenPlayGodotWallpaper.h @@ -57,6 +57,9 @@ public: bool send_ping(); bool exit(); + godot::String get_projectPackageFile() const; + void set_projectPackageFile(const godot::String& projectPackageFile); + protected: static void _bind_methods(); @@ -70,6 +73,7 @@ private: godot::String m_appID = ""; godot::String m_projectPath = ""; + godot::String m_projectPackageFile = ""; WindowsIntegration m_windowsIntegration; double m_timesinceLastRead = 0.0; bool m_pipeConnected = false; diff --git a/ScreenPlayWallpaper/Godot/ScreenPlayGodot/main.gd b/ScreenPlayWallpaper/Godot/ScreenPlayGodot/main.gd index 96b4569a..71d21a4c 100644 --- a/ScreenPlayWallpaper/Godot/ScreenPlayGodot/main.gd +++ b/ScreenPlayWallpaper/Godot/ScreenPlayGodot/main.gd @@ -44,7 +44,7 @@ func _ready(): get_tree().quit() return #screen_play_wallpaper.set_projectPath("C:\\Code\\cpp\\ScreenPlay\\ScreenPlay\\Content\\wallpaper_godot_fjord") - path = screen_play_wallpaper.get_projectPath() + "/project.zip" + path = screen_play_wallpaper.get_projectPath() + "/" + screen_play_wallpaper.get_projectPackageFile() else: get_tree().quit() return @@ -55,7 +55,7 @@ func _ready(): # yet setup via screenplay_manager.init() get_tree().quit() return - Engine.set_max_fps(24) + Engine.set_max_fps(144) var ok = screen_play_wallpaper.init(screen_play_wallpaper.get_activeScreensList()[0]) if not ok: @@ -99,7 +99,7 @@ func parse_args(): if args[0] == "res://main.tscn": offset = 1 - if args.size() < 7: # Adjust this number based on the expected number of arguments + if args.size() < 8: # Adjust this number based on the expected number of arguments print("Not enough arguments provided!") return false @@ -122,6 +122,7 @@ func parse_args(): var type = args[5] # This might need further parsing depending on its expected format screen_play_wallpaper.set_checkWallpaperVisible(args[6 + offset].to_lower() == "true") screen_play_wallpaper.set_activeScreensList(activeScreensList) + screen_play_wallpaper.set_projectPackageFile(args[7 + offset]) # Print or use the parsed values as needed print("Parsing done:", activeScreensList, screen_play_wallpaper.get_projectPath(), screen_play_wallpaper.get_appID(), screen_play_wallpaper.get_volume(), type, screen_play_wallpaper.get_checkWallpaperVisible()) diff --git a/ScreenPlayWallpaper/Godot/ScreenPlayGodot/project.godot b/ScreenPlayWallpaper/Godot/ScreenPlayGodot/project.godot index cb14a4c2..d48bcd61 100644 --- a/ScreenPlayWallpaper/Godot/ScreenPlayGodot/project.godot +++ b/ScreenPlayWallpaper/Godot/ScreenPlayGodot/project.godot @@ -23,14 +23,14 @@ config/icon="res://icon.svg" window/size/viewport_width=1 window/size/viewport_height=1 -window/size/mode=1 -window/size/initial_position_type=0 -window/size/initial_position=Vector2i(9999999, 9999999) +window/size/resizable=false window/size/borderless=true +window/size/transparent=true +window/energy_saving/keep_screen_on=false [editor] -run/main_run_args="\"0\" \"C:/Code/Cpp/ScreenPlay/ScreenPlay/Content/wallpaper_godot_fjord\" \"appID=test\" \"1\" \"Cover\" \"GodotWallpaper\" \"1\"" +run/main_run_args="\"1\" \"C:/Code/Cpp/ScreenPlay/ScreenPlay/Content/wallpaper_godot_fjord\" \"appID=test\" \"1\" \"Cover\" \"GodotWallpaper\" \"1\" \"project-v1.zip\"" [filesystem] diff --git a/ScreenPlayWallpaper/qml/Test.qml b/ScreenPlayWallpaper/qml/Test.qml index befb8cc3..299dc93e 100644 --- a/ScreenPlayWallpaper/qml/Test.qml +++ b/ScreenPlayWallpaper/qml/Test.qml @@ -104,14 +104,6 @@ Rectangle { } } - ImageParticle { - height: 16 - width: 16 - source: "dot.png" - system: particleSystem - opacity: root.imgOpacity - } - Text { id: txtMousePos @@ -216,20 +208,17 @@ Rectangle { spacing: 20 TextField { placeholderText: "Edit me" - } Button { text: "Exit" onClicked: { - Qt.callLater(function () { - Wallpaper.terminate(); - }); + Qt.callLater(function () { + Wallpaper.terminate(); + }); } } - } - MultimediaView { width: 1000 height: 400 diff --git a/ScreenPlayWallpaper/src/windowshook.cpp b/ScreenPlayWallpaper/src/windowshook.cpp deleted file mode 100644 index 032721f0..00000000 --- a/ScreenPlayWallpaper/src/windowshook.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "windowshook.h" - -BOOL CALLBACK WinMonitorStats::MonitorEnum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData) -{ - WinMonitorStats* pThis = reinterpret_cast(pData); - auto scaleFactor = DEVICE_SCALE_FACTOR::DEVICE_SCALE_FACTOR_INVALID; - GetScaleFactorForMonitor(hMon, &scaleFactor); - - UINT x = 0; - UINT y = 0; - GetDpiForMonitor(hMon, MONITOR_DPI_TYPE::MDT_RAW_DPI, &x, &y); - pThis->sizes.push_back({ x, y }); - pThis->scaleFactor.push_back(scaleFactor); - pThis->hMonitors.push_back(hMon); - pThis->hdcMonitors.push_back(hdc); - pThis->rcMonitors.push_back(*lprcMonitor); - pThis->iMonitors.push_back(static_cast(pThis->hdcMonitors.size())); - - // qInfo() << std::abs(lprcMonitor->right - lprcMonitor->left) << std::abs(lprcMonitor->top - lprcMonitor->bottom); - - return TRUE; -} -/*! - \brief Searches for the worker window for our window to parent to. -*/ -BOOL WINAPI SearchForWorkerWindow(HWND hwnd, LPARAM lparam) -{ - // 0xXXXXXXX "" WorkerW - // ... - // 0xXXXXXXX "" SHELLDLL_DefView - // 0xXXXXXXXX "FolderView" SysListView32 - // 0xXXXXXXXX "" WorkerW <---- We want this one - // 0xXXXXXXXX "Program Manager" Progman - if (FindWindowExW(hwnd, nullptr, L"SHELLDLL_DefView", nullptr)) - *reinterpret_cast(lparam) = FindWindowExW(nullptr, hwnd, L"WorkerW", nullptr); - return TRUE; -} - -bool WindowsHook::searchWorkerWindowToParentTo() -{ - - HWND progman_hwnd = FindWindowW(L"Progman", L"Program Manager"); - const DWORD WM_SPAWN_WORKER = 0x052C; - SendMessageTimeoutW(progman_hwnd, WM_SPAWN_WORKER, 0xD, 0x1, SMTO_NORMAL, - 10000, nullptr); - - return EnumWindows(SearchForWorkerWindow, reinterpret_cast(&windowHandleWorker)); -} - -/*! - \brief Returns scaling factor as reported by Windows. -*/ -float WindowsHook::getScaling(const int monitorIndex) const -{ - // Get all monitors - int monitorCount = GetSystemMetrics(SM_CMONITORS); - - if (monitorIndex < 0 || monitorIndex >= monitorCount) { - // Invalid monitor index - return 1.0f; - } - - DISPLAY_DEVICE displayDevice; - ZeroMemory(&displayDevice, sizeof(displayDevice)); - displayDevice.cb = sizeof(displayDevice); - - // Enumerate through monitors until we find the one we're looking for - for (int i = 0; EnumDisplayDevices(NULL, i, &displayDevice, 0); i++) { - if (i == monitorIndex) { - DEVMODE devMode; - ZeroMemory(&devMode, sizeof(devMode)); - devMode.dmSize = sizeof(devMode); - - // Get settings for selected monitor - if (!EnumDisplaySettings(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &devMode)) { - // Unable to get monitor settings - return 1.0f; - } - - // Get DPI for selected monitor - HMONITOR hMonitor = MonitorFromPoint({ devMode.dmPosition.x, devMode.dmPosition.y }, MONITOR_DEFAULTTONEAREST); - UINT dpiX = 0, dpiY = 0; - if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) { - return (float)dpiX / 96.0f; // Standard DPI is 96 - } - } - } - - // If we reach here, it means we couldn't find the monitor with the given index or couldn't get the DPI. - return 1.0f; -} - -/*! - \brief Returns true of at least one monitor has active scaling enabled. -*/ -bool WindowsHook::hasWindowScaling() const -{ - auto enumMonitorCallback = [](HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -> BOOL { - int scaling = GetDeviceCaps(hdcMonitor, LOGPIXELSX) / 96; - if (scaling != 1) { - *(bool*)dwData = true; - return false; // Stop enumeration - } - return true; // Continue enumeration - }; - - bool hasScaling = false; - EnumDisplayMonitors(NULL, NULL, enumMonitorCallback, (LPARAM)&hasScaling); - - return hasScaling; -} \ No newline at end of file diff --git a/ScreenPlayWallpaper/src/windowshook.h b/ScreenPlayWallpaper/src/windowshook.h deleted file mode 100644 index d291b36e..00000000 --- a/ScreenPlayWallpaper/src/windowshook.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include -#include -#include -#include -#include - -struct WinMonitorStats { - - WinMonitorStats() - { - EnumDisplayMonitors(NULL, NULL, MonitorEnum, (LPARAM)this); - } - - static BOOL CALLBACK MonitorEnum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData); - std::vector iMonitors; - std::vector hMonitors; - std::vector hdcMonitors; - std::vector rcMonitors; - std::vector scaleFactor; - std::vector> sizes; - int index = 0; -}; - -struct Point { - int x = 0; - int y = 0; -}; - -struct WindowsHook { - bool searchWorkerWindowToParentTo(); - float getScaling(const int monitorIndex) const; - bool hasWindowScaling() const; - HWND windowHandle {}; - HWND windowHandleWorker {}; - Point zeroPoint; -}; \ No newline at end of file diff --git a/ScreenPlayWallpaper/src/windowsintegration.cpp b/ScreenPlayWallpaper/src/windowsintegration.cpp index 5841bd6e..def9feef 100644 --- a/ScreenPlayWallpaper/src/windowsintegration.cpp +++ b/ScreenPlayWallpaper/src/windowsintegration.cpp @@ -68,7 +68,7 @@ BOOL SearchForWorkerWindow(HWND hwnd, LPARAM lparam) return TRUE; } -std::vector WindowsIntegration::GetAllMonitors() +std::vector WindowsIntegration::getAllMonitors() { std::vector monitors; @@ -150,7 +150,7 @@ WindowsIntegration::MonitorResult WindowsIntegration::setupWallpaperForOneScreen return { std::nullopt, MonitorResultStatus::WorkerWindowHandleInvalidError }; } - std::vector monitors = GetAllMonitors(); + std::vector monitors = getAllMonitors(); for (const auto& monitor : monitors) { monitor.print(); if (monitor.index != activeScreen) @@ -212,7 +212,7 @@ WindowsIntegration::MonitorResult WindowsIntegration::setupWallpaperForOneScreen */ WindowsIntegration::SpanResult WindowsIntegration::setupWallpaperForMultipleScreens(const std::vector& activeScreens) { - std::vector monitors = GetAllMonitors(); + std::vector monitors = getAllMonitors(); int leftmost = INT_MAX; int topmost = INT_MAX; @@ -272,7 +272,7 @@ WindowsIntegration::SpanResult WindowsIntegration::setupWallpaperForMultipleScre */ WindowsIntegration::SpanResult WindowsIntegration::setupWallpaperForAllScreens() { - std::vector monitors = GetAllMonitors(); + std::vector monitors = getAllMonitors(); int leftmost = INT_MAX; int topmost = INT_MAX; diff --git a/ScreenPlayWallpaper/src/windowsintegration.h b/ScreenPlayWallpaper/src/windowsintegration.h index b455150e..6643bd3d 100644 --- a/ScreenPlayWallpaper/src/windowsintegration.h +++ b/ScreenPlayWallpaper/src/windowsintegration.h @@ -7,6 +7,7 @@ // Do not change windows.h order ! #include +#include #include #include #include @@ -22,16 +23,21 @@ struct Monitor { RECT position; // Monitor's position and size SIZE size; // Monitor's width and height float scaleFactor; // Scale factor (DPI scaling as a factor, e.g., 1.5 for 150% scaling) - + std::string toString() const + { + return std::format( + "Monitor Info:\n" + "Monitor ID: {}\n" + "Index: {}\n" + "Position: ({}, {}, {}, {})\n" + "Size: ({}x{})\n" + "Scale Factor: {}\n", + (int64_t)monitorID, index, position.left, position.top, + position.right, position.bottom, size.cx, size.cy, scaleFactor); + } void print() const { - std::cout << "Monitor Info:" << std::endl; - std::cout << "Monitor ID: " << monitorID << std::endl; - std::cout << "Index: " << index << std::endl; - std::cout << "Position: (" << position.left << ", " << position.top << ", " - << position.right << ", " << position.bottom << ")" << std::endl; - std::cout << "Size: (" << size.cx << "x" << size.cy << ")" << std::endl; - std::cout << "Scale Factor: " << scaleFactor << std::endl; + std::cout << toString() << std::endl; } }; @@ -41,39 +47,6 @@ BOOL CALLBACK GetMonitorByHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcM BOOL CALLBACK FindTheDesiredWnd(HWND hWnd, LPARAM lParam); BOOL WINAPI SearchForWorkerWindow(HWND hwnd, LPARAM lparam); -struct WinMonitorStats { - - WinMonitorStats() - { - EnumDisplayMonitors(NULL, NULL, MonitorEnum, (LPARAM)this); - } - - static BOOL CALLBACK MonitorEnum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData) - { - WinMonitorStats* pThis = reinterpret_cast(pData); - auto scaleFactor = DEVICE_SCALE_FACTOR::DEVICE_SCALE_FACTOR_INVALID; - GetScaleFactorForMonitor(hMon, &scaleFactor); - - UINT x = 0; - UINT y = 0; - GetDpiForMonitor(hMon, MONITOR_DPI_TYPE::MDT_RAW_DPI, &x, &y); - pThis->sizes.push_back({ x, y }); - pThis->scaleFactor.push_back(scaleFactor); - pThis->hMonitors.push_back(hMon); - pThis->hdcMonitors.push_back(hdc); - pThis->rcMonitors.push_back(*lprcMonitor); - pThis->iMonitors.push_back(pThis->hdcMonitors.size()); - - return TRUE; - } - std::vector iMonitors; - std::vector hMonitors; - std::vector hdcMonitors; - std::vector rcMonitors; - std::vector scaleFactor; - std::vector> sizes; -}; - struct Point { int x = 0; int y = 0; @@ -112,7 +85,7 @@ public: bool searchWorkerWindowToParentTo(); float getScaling(const int monitorIndex) const; - std::vector GetAllMonitors(); + std::vector getAllMonitors(); int GetMonitorIndex(HMONITOR hMonitor); bool checkForFullScreenWindow(HWND windowHandle); WindowsIntegration::MonitorResult setupWallpaperForOneScreen(const int activeScreen, std::function updateWindowSize); diff --git a/ThirdParty/CMakeLists.txt b/ThirdParty/CMakeLists.txt index 2260531f..684f2937 100644 --- a/ThirdParty/CMakeLists.txt +++ b/ThirdParty/CMakeLists.txt @@ -11,7 +11,7 @@ FetchContent_Populate( FetchContent_Populate( qml-plausible GIT_REPOSITORY https://gitlab.com/kelteseth/qml-plausible.git - GIT_TAG 5069ba3bf25663ea06be8b94c398d6c61058d4d5 + GIT_TAG 322d8e17cab77b496f0d7fafb19f6dcda4193ed7 # Workaround because: 1. QtCreator cannot handle QML_ELEMENT stuff when it is in bin folder # https://bugreports.qt.io/browse/QTCREATORBUG-27083 SOURCE_DIR ${THIRD_PARTY_PATH}/qml-plausible) diff --git a/Tools/build.py b/Tools/build.py index e1eb815e..6d603e49 100755 --- a/Tools/build.py +++ b/Tools/build.py @@ -46,16 +46,18 @@ def execute( # temporary files in the build directory. clean_build_dir(build_config.build_folder) - # Build Godot Wallpaper - # Note: This must happen before building ScreenPlay! - if platform.system() == "Windows" and build_config.build_godot == "ON": - build_godot.build_godot(str(build_config.bin_dir), build_config.build_type) - # Runs cmake configure and cmake build step_time = time.time() build_result = build(build_config, build_result) build_duration = time.time() - step_time print(f"⏱️ build_duration: {build_duration}s") + + + # Build Godot Wallpaper + # Note: This must happen after building ScreenPlay! + if platform.system() == "Windows": + build_godot.build_godot(str(build_config.bin_dir), build_config.build_type) + # Copies all needed libraries and assets into the bin folder step_time = time.time() @@ -181,7 +183,6 @@ def build(build_config: BuildConfig, build_result: BuildResult) -> BuildResult: -DSCREENPLAY_STEAM={build_config.build_steam} \ -DSCREENPLAY_TESTS={build_config.build_tests} \ -DSCREENPLAY_DEPLOY={build_config.build_deploy} \ - -DSCREENPLAY_GODOT={build_config.build_godot} \ -DSCREENPLAY_INSTALLER={build_config.create_installer} \ -DSCREENPLAY_IFW_ROOT:STRING={build_config.ifw_root_path} \ -G "Ninja" \ @@ -287,14 +288,6 @@ def package(build_config: BuildConfig): print("Copy: ", file, build_config.bin_dir) shutil.copy2(file, build_config.bin_dir) - # Use Qt OpenSSLv3 - openSLL_path = Path( - f"{defines.QT_PATH}/Tools/OpenSSLv3/Win_x64/bin").resolve() - for file in openSLL_path.iterdir(): - if file.suffix == ".dll" and file.is_file(): - print("Copy: ", file, build_config.bin_dir) - shutil.copy2(file, build_config.bin_dir) - if not platform.system() == "Darwin": file_endings = [".ninja_deps", ".ninja", ".ninja_log", ".lib", ".a", ".exp", ".manifest", ".cmake", ".cbp", "CMakeCache.txt"] @@ -365,7 +358,6 @@ if __name__ == "__main__": help="Create a deploy version of ScreenPlay for sharing with the world. A not deploy version is for local development only!") parser.add_argument('--architecture', action="store", dest="build_architecture", default="", help="Sets the build architecture. Used to build x86 and ARM osx versions. Currently only works with x86_64 and arm64") - parser.add_argument('--build-godot', action="store", dest="build_godot", default="", help="Builds the Godot Wallpaper") args = parser.parse_args() qt_version = defines.QT_VERSION @@ -409,10 +401,6 @@ if __name__ == "__main__": if args.build_deploy: build_deploy = "ON" - build_godot = "OFF" - if args.build_godot: - build_godot = "ON" - create_installer = "OFF" if args.create_installer: create_installer = "ON" @@ -429,7 +417,6 @@ if __name__ == "__main__": build_config.build_steam = build_steam build_config.build_tests = build_tests build_config.build_deploy = build_deploy - build_config.build_godot = build_godot build_config.create_installer = create_installer build_config.build_type = build_type build_config.screenplay_version = screenplay_version diff --git a/Tools/build_and_publish_steam.py b/Tools/build_and_publish_steam.py index 807b3a8c..4f45725c 100644 --- a/Tools/build_and_publish_steam.py +++ b/Tools/build_and_publish_steam.py @@ -67,7 +67,6 @@ if __name__ == "__main__": build_config.build_tests = "OFF" build_config.build_deploy = "ON" build_config.create_installer = "OFF" - build_config.build_godot = "OFF" build_config.build_type = "release" build_config.screenplay_version = screenplay_version diff --git a/Tools/build_godot.py b/Tools/build_godot.py index 5340703e..e3e9eef0 100644 --- a/Tools/build_godot.py +++ b/Tools/build_godot.py @@ -12,7 +12,7 @@ import argparse def main(): # Parse build folder as arugment - parser = argparse.ArgumentParser(description='Build K3000Map to the bin build folder: D:/Backup/Code/Qt/build_ScreenPlay_Qt_6.6.0_MSVC_Debug/bin') + parser = argparse.ArgumentParser(description='Build K3000Map to the bin build folder: D:/Backup/Code/Qt/build_ScreenPlay_Qt_6.6.1_MSVC_Debug/bin') parser.add_argument('--build_path', dest="build_path", type=str, help='Build folder') parser.add_argument('--skip_if_exists', dest="skip_if_exists", default=False, action="store_true", help='Skips the build if the index.html file exists. This is used for faster CMake configure') @@ -20,7 +20,7 @@ def main(): if not args.build_path: print("ERROR: Please specify the build folder") - print("py build_godot.py --build_path D:/Backup/Code/Qt/build_ScreenPlay_Qt_6.6.0_MSVC_Debug/bin/") + print("py build_godot.py --build_path D:/Backup/Code/Qt/build_ScreenPlay_Qt_6.6.1_MSVC_Debug/bin/") exit() # if build path exists and contains a index.html file, skip the build diff --git a/Tools/defines.py b/Tools/defines.py index 362a0fe7..b7d84553 100644 --- a/Tools/defines.py +++ b/Tools/defines.py @@ -21,14 +21,15 @@ elif sys.platform == "linux": REPO_PATH = Path(__file__, "../../").resolve() THIRDPATH_PATH = Path(REPO_PATH, "ThirdParty").resolve() QT_PATH = path = Path(REPO_PATH, "../aqt").resolve() -QT_VERSION = "6.6.0" +QT_VERSION = "6.6.1" QT_BIN_PATH = QT_PATH.joinpath(f"{QT_VERSION}/{QT_PLATFORM}/bin") QT_TOOLS_PATH = QT_PATH.joinpath("Tools/") QT_IFW_VERSION = "4.6" -# 02.06.2023 https://gitlab.com/kelteseth/screenplay-vcpkg : -VCPKG_VERSION = "f06975f46d8c7a1dad916e1e997584f77ae0c34a" +# 25.11.2023 https://gitlab.com/kelteseth/screenplay-vcpkg : +VCPKG_VERSION = "cabba0d0379b78e34d0a0d37edb4c459c5a03337" VCPKG_BASE_PACKAGES = [ "curl", + "openssl", "cpp-httplib", "libarchive", "fmt", @@ -37,20 +38,25 @@ VCPKG_BASE_PACKAGES = [ PYTHON_EXECUTABLE = "python" if sys.platform == "win32" else "python3" FFMPEG_VERSION = "6.0" GODOT_VERSION = "4.2" -GODOT_RELEASE_TYPE = "beta5" -GODOT_DOWNLOAD_SERVER = "https://downloads.tuxfamily.org/godotengine" +GODOT_RELEASE_TYPE = "rc2" +GODOT_DOWNLOAD_SERVER = "https://github.com/godotengine/godot-builds/releases/download" if sys.platform == "win32": SCREENPLAYWALLPAPER_GODOT_EXECUTABLE = "ScreenPlayWallpaperGodot.exe" GODOT_EDITOR_EXECUTABLE = f"Godot_v{GODOT_VERSION}-{GODOT_RELEASE_TYPE}_win64.exe" + GODOT_EDITOR_DOWNLOAD_NAME = GODOT_EDITOR_EXECUTABLE + ".zip" GODOT_TEMPLATES_PATH = os.path.join(os.getenv( 'APPDATA'), f"Godot/templates/{GODOT_VERSION}.{GODOT_RELEASE_TYPE}") elif sys.platform == "darwin": SCREENPLAYWALLPAPER_GODOT_EXECUTABLE = "ScreenPlayWallpaperGodot.app" - GODOT_EDITOR_EXECUTABLE = f"Godot_v{GODOT_VERSION}-{GODOT_RELEASE_TYPE}_osx.universal" + # Godot_v4.2-beta6_macos.universal.zip + GODOT_EDITOR_EXECUTABLE = "Godot.app" + GODOT_EDITOR_DOWNLOAD_NAME = f"Godot_v{GODOT_VERSION}-{GODOT_RELEASE_TYPE}_macos.universal.zip" GODOT_TEMPLATES_PATH = "TODO" elif sys.platform == "linux": SCREENPLAYWALLPAPER_GODOT_EXECUTABLE = "ScreenPlayWallpaperGodot" - GODOT_EDITOR_EXECUTABLE = f"Godot_v{GODOT_VERSION}-{GODOT_RELEASE_TYPE}_x11.64" + # Godot_v4.2-beta6_linux.x86_64 + GODOT_EDITOR_EXECUTABLE = f"Godot_v{GODOT_VERSION}-{GODOT_RELEASE_TYPE}_linux.x86_64" + GODOT_EDITOR_DOWNLOAD_NAME = GODOT_EDITOR_EXECUTABLE + ".zip" # /home/eli/.local/share/godot/templates/ GODOT_TEMPLATES_PATH = os.path.join( Path.home(), f".local/share/godot/templates/{GODOT_VERSION}.{GODOT_RELEASE_TYPE}") diff --git a/Tools/macos_make_universal.py b/Tools/macos_make_universal.py new file mode 100644 index 00000000..b3817a4a --- /dev/null +++ b/Tools/macos_make_universal.py @@ -0,0 +1,95 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only +from distutils.dir_util import mkpath +import os +import subprocess +from pathlib import Path +import util +from util import run, run_and_capture_output +from sys import stdout + +stdout.reconfigure(encoding='utf-8') + + +def listfiles(path): + files = [] + ignored = ('.DS_Store',) # Ignored files + print(f"WALK: {path}") + + for dirName, _, fileList in os.walk(path): + for fname in fileList: + if fname in ignored: + continue # Skip ignored files + + file_path = os.path.join(dirName, fname) + if os.path.isfile(file_path) and (fname.endswith('.a')): + files.append(file_path) + if os.path.islink(file_path): + print(f"Warning: file {file_path} is a symlink!") + print("Symlink target: ", os.readlink(file_path)) + return files + +def is_mach_o_binary(file_path): + """ + Check if a file is a Mach-O binary using the file command. + """ + try: + result = subprocess.run(["file", file_path], capture_output=True, text=True) + return 'Mach-O' in result.stdout + except Exception as e: + print(f"Error checking file type of {file_path}: {e}") + return False + +# Merges x64 and arm64 vcpkg build into universal +def run_lipo(): + workspace_path = util.workspace_path() + vcpkg_installed_path = str(Path(workspace_path).joinpath("vcpkg/installed/").absolute()) + arm64_dir = f"{vcpkg_installed_path}/arm64-osx" + x64_dir = f"{vcpkg_installed_path}/x64-osx" + universal_dir = f"{vcpkg_installed_path}/64-osx-universal" + # Ensure the universal directory is created and empty + run(f"rm -rf {universal_dir}", workspace_path) + # Copy the x64 build as a base for the universal build + run(f"cp -a {arm64_dir} {universal_dir}", workspace_path) + + files = listfiles(str(universal_dir)) + + for file in files: + # Extract the relative path for file + rel_path = os.path.relpath(file, universal_dir) + + # Construct the corresponding arm64 and x64 file paths + arm64_file = os.path.join(arm64_dir, rel_path) + x64_file = os.path.join(x64_dir, rel_path) + universal_file = file # file is already in universal_dir + + #and is_mach_o_binary(arm64_file) + if os.path.exists(x64_file) : + run(f"lipo -create {arm64_file} {x64_file} -output {universal_file}") + print(f"Processing binary file: {universal_file}") + run(f"lipo -info {universal_file}") + else: + print(f"Skipping non-binary file: {universal_file}") + +def check_fat_binary(): + # Ensure the script starts from the correct directory + workspace_path = Path(util.workspace_path()) + + dir = 'vcpkg/installed/64-osx-universal' + universal_dir = str(workspace_path.joinpath(dir)) + print(f"check_fat_binary {universal_dir}") + files = listfiles(universal_dir) + + for file in files: + out = run_and_capture_output(f"lipo -info {file}") + if out.startswith('Non-fat'): + out = out.replace("\n","") + print(f"❌ {out}") + else: + print(f"✅ {file}") +def execute(): + run_lipo() + check_fat_binary() + +if __name__ == "__main__": + execute() diff --git a/Tools/setup.py b/Tools/setup.py index 89fdaa1d..4a16b8d4 100755 --- a/Tools/setup.py +++ b/Tools/setup.py @@ -7,6 +7,7 @@ import download_ffmpeg import defines import argparse import util +import macos_make_universal import datetime import setup_godot from sys import stdout @@ -54,17 +55,15 @@ def download(aqt_path: Path, qt_platform: Path): qt_packages = "qtwaylandcompositor " os = "linux" - # Windows: python -m aqt list-qt windows desktop --modules 6.6.0 win64_msvc2019_64 - # Linux: python3 -m aqt list-qt linux desktop --modules 6.6.0 gcc_64 qt_packages += "qt3d qtquick3d qtconnectivity qt5compat qtimageformats qtmultimedia qtshadertools qtwebchannel qtwebengine qtwebsockets qtwebview qtpositioning" + # Windows: python -m aqt list-qt windows desktop --modules 6.6.1 win64_msvc2019_64 + # Linux: python3 -m aqt list-qt linux desktop --modules 6.6.1 gcc_64 print(f"Downloading: {qt_packages} to {aqt_path}") execute(f"{defines.PYTHON_EXECUTABLE} -m aqt install-qt -O {aqt_path} {os} desktop {defines.QT_VERSION} {qt_platform} -m {qt_packages}") # Tools can only be installed one at the time: # see: python -m aqt list-tool windows desktop - tools = ["tools_ifw", "tools_qtcreator", "tools_ninja", "tools_cmake"] - if system() == "Windows": - tools += ["tools_opensslv3_x64"] + tools = ["tools_ifw"] for tool in tools: execute( f"{defines.PYTHON_EXECUTABLE} -m aqt install-tool -O {aqt_path} {os} desktop {tool}") @@ -107,9 +106,7 @@ def main(): parser = argparse.ArgumentParser( description='Build and Package ScreenPlay') parser.add_argument('--skip-aqt', action="store_true", dest="skip_aqt", - help="Downloads QtCreator and needed binaries \Windows: C:\aqt\nLinux & macOS:~/aqt/.") - parser.add_argument('--setup-godot', action="store_true", dest="setup_godot", - help="Downloads Godot Editor.") + help="Downloads QtCreator and needed binaries Windows: C:\\aqt\\nLinux & macOS:~/aqt/.") args = parser.parse_args() root_path = Path(util.cd_repo_root_path()) @@ -117,7 +114,7 @@ def main(): vcpkg_path = project_source_parent_path.joinpath("vcpkg").resolve() vcpkg_packages_list = defines.VCPKG_BASE_PACKAGES - if system() == "Windows" and args.setup_godot: + if system() != "Darwin": if not setup_godot.execute(): raise RuntimeError("Unable to download godot") @@ -140,7 +137,7 @@ def main(): platform_command.add("chmod +x bootstrap-vcpkg.sh", vcpkg_path) platform_command.add("./bootstrap-vcpkg.sh", vcpkg_path, False) platform_command.add("chmod +x vcpkg", vcpkg_path) - vcpkg_triplet = ["64-osx-universal"] + vcpkg_triplet = ["x64-osx","arm64-osx"] elif system() == "Linux": vcpkg_command = "./vcpkg" # vcpkg_packages_list.append("infoware[opengl]") @@ -153,7 +150,7 @@ def main(): raise NotImplementedError("Unknown system: {}".format(system())) print(f"Clone into {vcpkg_path}") - execute("git clone https://gitlab.com/kelteseth/screenplay-vcpkg vcpkg", + execute("git clone https://github.com/microsoft/vcpkg vcpkg", project_source_parent_path, True) execute("git fetch", vcpkg_path) execute(f"git checkout {defines.VCPKG_VERSION}", vcpkg_path) @@ -167,9 +164,15 @@ def main(): vcpkg_packages = " ".join(vcpkg_packages_list) execute( f"{vcpkg_command} install {vcpkg_packages} --triplet {triplet} --recurse", vcpkg_path, False) + + # Combine x64 and arm + if system() == "Darwin": + macos_make_universal.execute() if not args.skip_aqt: setup_qt() + + if __name__ == "__main__": main() diff --git a/Tools/setup_godot.py b/Tools/setup_godot.py index d8c39d7b..32de041b 100644 --- a/Tools/setup_godot.py +++ b/Tools/setup_godot.py @@ -8,19 +8,22 @@ from pathlib import Path import defines import util -def download_godot(version: str, exe_zip_filename: str, export_templates: str, download_destination_path: str) -> bool: - # https://downloads.tuxfamily.org/godotengine/4.2/beta4/Godot_v4.2-beta4_win64.exe.zip - # https://downloads.tuxfamily.org/godotengine/4.2/Godot_v4.2-beta4_win64.exe.zip - download_export_templates = f"{defines.GODOT_DOWNLOAD_SERVER}/{version}/{defines.GODOT_RELEASE_TYPE}/{export_templates}" +def download_godot(exe_zip_filename: str, export_templates: str, download_destination_path: str) -> bool: + # https://github.com/godotengine/godot-builds/releases/download/4.2-beta6/Godot_v4.2-beta6_win64.exe.zip + subfolder = f"{defines.GODOT_VERSION}-{defines.GODOT_RELEASE_TYPE}" + base_url = f"{defines.GODOT_DOWNLOAD_SERVER}/{subfolder}" + + download_export_templates = f"{base_url}/{export_templates}" exe_destination_filepath = os.path.join( download_destination_path, exe_zip_filename) export_templates_destination_path = os.path.join( download_destination_path, export_templates) - # Godot adds ".stable" to the folder names for full releases: "AppData/Roaming/Godot/templates/3.4.stable": - print(f"Downloading Godot from {defines.GODOT_DOWNLOAD_SERVER}/") - download_link = f"{defines.GODOT_DOWNLOAD_SERVER}/{version}/{defines.GODOT_RELEASE_TYPE}/{exe_zip_filename}" - util.download(download_link, exe_destination_filepath, False) + # Godot adds ".stable" to the folder names for full releases: "AppData/Roaming/Godot/templates/4.2.stable": + download_editor = f"{base_url}/{exe_zip_filename}" + print(f"⬇️ Downloading Godot editor {download_editor}") + util.download(download_editor, exe_destination_filepath, False) + print(f"⬇️ Downloading Godot export templates {download_export_templates}") util.download(download_export_templates, export_templates_destination_path, False) @@ -28,20 +31,20 @@ def download_godot(version: str, exe_zip_filename: str, export_templates: str, d def unzip_godot(exe_zip_filepath: str, export_templates_filepath: str, destination_path: str) -> bool: - print("Unzip Godot") + print("🪛 Unzip Godot") util.unzip(exe_zip_filepath, destination_path) # The export templates contain a templates subfolder in which the content is. This is bad because it clashes - # with the folder structure where the version comes after: AppData\Roaming\Godot\templates\3.3.4.stable - # Rename: AppData\Roaming\Godot\templates\templates - # to : AppData\Roaming\Godot\templates\3.4.stable + # with the folder structure where the version comes after: /home/eli/.local/share/godot/export_templates/ + # Rename: AppData\Roaming\Godot\export_templates\templates + # to : AppData\Roaming\Godot\export_templates\3.4.stable godot_templates_dir = "" if sys.platform == "win32": godot_templates_dir = os.path.join( - os.getenv('APPDATA'), "Godot/templates/") + os.getenv('APPDATA'), "Godot/export_templates") elif sys.platform == "linux": godot_templates_dir = os.path.join( - str(Path.home()), ".local/share/godot/templates/") + str(Path.home()), ".local/share/godot/export_templates") os.makedirs(godot_templates_dir, exist_ok=True) export_templates_destination_version = f"{godot_templates_dir}/{defines.GODOT_VERSION}.{defines.GODOT_RELEASE_TYPE}" @@ -54,14 +57,14 @@ def unzip_godot(exe_zip_filepath: str, export_templates_filepath: str, destinati os.rename(os.path.join(godot_templates_dir, "templates"), export_templates_destination_version) - print(f"Remove {exe_zip_filepath}") + print(f"🚮 Remove {exe_zip_filepath}") try: os.remove(exe_zip_filepath) except OSError as error: print(f"Error deleting file: {error}") return False - print(f"Remove {export_templates_filepath}") + print(f"🚮 Remove {export_templates_filepath}") try: os.remove(export_templates_filepath) except OSError as error: @@ -72,22 +75,22 @@ def unzip_godot(exe_zip_filepath: str, export_templates_filepath: str, destinati def setup_godot() -> bool: - print(f"Set up GODOT version {defines.GODOT_VERSION} {defines.GODOT_RELEASE_TYPE}") + print(f"🚀 Set up GODOT version {defines.GODOT_VERSION} {defines.GODOT_RELEASE_TYPE}") destination_path = os.path.join(defines.THIRDPATH_PATH, "Godot") export_templates = f"Godot_v{defines.GODOT_VERSION}-{defines.GODOT_RELEASE_TYPE}_export_templates.tpz" export_templates_filepath = os.path.join( destination_path, export_templates) - exe_zip_filename = defines.GODOT_EDITOR_EXECUTABLE + '.zip' - exe_zip_filepath = os.path.join(destination_path, exe_zip_filename) - download_godot(defines.GODOT_VERSION, exe_zip_filename, + exe_zip_filepath = os.path.join(destination_path, defines.GODOT_EDITOR_DOWNLOAD_NAME) + download_godot( defines.GODOT_EDITOR_DOWNLOAD_NAME, export_templates, destination_path) if not unzip_godot(exe_zip_filepath, export_templates_filepath, destination_path): return False - + # Linux needs to change file permission to be able to run godot if sys.platform == "linux": - execute(f"chmod +x {defines.GODOT_EDITOR_EXECUTABLE}", - destination_path, False) + command = f"chmod +x {defines.GODOT_EDITOR_EXECUTABLE}" + print(f"Make editor executable: {command} at {destination_path}") + util.run(command,destination_path) return True