├── .clang-format ├── .github └── workflows │ └── nix.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── VERSION ├── flake.lock ├── flake.nix ├── nix ├── default.nix ├── overlays.nix └── shell.nix └── utils ├── dialog ├── CMakeLists.txt ├── Dialog.cpp ├── Dialog.hpp ├── main.cpp └── main.qml ├── donate-screen ├── CMakeLists.txt ├── DonateScreen.cpp ├── DonateScreen.hpp ├── assets │ └── heart.svg ├── main.cpp └── main.qml └── update-screen ├── CMakeLists.txt ├── Dialog.cpp ├── Dialog.hpp ├── UpdateScreen.cpp ├── UpdateScreen.hpp ├── assets └── heart.svg ├── dialogMain.qml ├── main.cpp └── main.qml /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | 5 | AccessModifierOffset: -2 6 | AlignAfterOpenBracket: Align 7 | AlignConsecutiveMacros: true 8 | AlignConsecutiveAssignments: true 9 | AlignEscapedNewlines: Right 10 | AlignOperands: false 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: true 16 | AllowShortCaseLabelsOnASingleLine: true 17 | AllowShortFunctionsOnASingleLine: Empty 18 | AllowShortIfStatementsOnASingleLine: Never 19 | AllowShortLambdasOnASingleLine: All 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: false 24 | AlwaysBreakTemplateDeclarations: Yes 25 | BreakBeforeBraces: Attach 26 | BreakBeforeTernaryOperators: false 27 | BreakConstructorInitializers: AfterColon 28 | ColumnLimit: 180 29 | CompactNamespaces: false 30 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 31 | ExperimentalAutoDetectBinPacking: false 32 | FixNamespaceComments: false 33 | IncludeBlocks: Preserve 34 | IndentCaseLabels: true 35 | IndentWidth: 4 36 | PointerAlignment: Left 37 | ReflowComments: false 38 | SortIncludes: false 39 | SortUsingDeclarations: false 40 | SpaceAfterCStyleCast: false 41 | SpaceAfterLogicalNot: false 42 | SpaceAfterTemplateKeyword: true 43 | SpaceBeforeCtorInitializerColon: true 44 | SpaceBeforeInheritanceColon: true 45 | SpaceBeforeParens: ControlStatements 46 | SpaceBeforeRangeBasedForLoopColon: true 47 | SpaceInEmptyParentheses: false 48 | SpacesBeforeTrailingComments: 1 49 | SpacesInAngles: false 50 | SpacesInCStyleCastParentheses: false 51 | SpacesInContainerLiterals: false 52 | SpacesInParentheses: false 53 | SpacesInSquareBrackets: false 54 | Standard: Auto 55 | TabWidth: 4 56 | UseTab: Never 57 | 58 | AllowShortEnumsOnASingleLine: false 59 | 60 | BraceWrapping: 61 | AfterEnum: false 62 | 63 | AlignConsecutiveDeclarations: AcrossEmptyLines 64 | 65 | NamespaceIndentation: All 66 | -------------------------------------------------------------------------------- /.github/workflows/nix.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request, workflow_dispatch] 4 | jobs: 5 | nix: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | 10 | - uses: DeterminateSystems/nix-installer-action@main 11 | 12 | # not needed (yet) 13 | # - uses: cachix/cachix-action@v12 14 | # with: 15 | # name: hyprland 16 | # authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 17 | 18 | - name: Build 19 | run: nix build --print-build-logs --keep-going 20 | 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | build/ 35 | .cache/ 36 | .vscode/ 37 | 38 | # nix 39 | result* 40 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | # Get version 4 | file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW) 5 | string(STRIP ${VER_RAW} VER) 6 | 7 | project(hyprland-qtutils VERSION ${VER} LANGUAGES CXX) 8 | 9 | set(CMAKE_CXX_STANDARD 23) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) 13 | find_package(PkgConfig REQUIRED) 14 | 15 | pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) 16 | 17 | qt_standard_project_setup(REQUIRES 6.5) 18 | 19 | add_subdirectory(utils/dialog) 20 | add_subdirectory(utils/update-screen) 21 | add_subdirectory(utils/donate-screen) 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2024, Hypr Development 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hyprland QT utils 2 | 3 | This repo houses some qt/qml utilities that might be used by various hypr* apps. 4 | 5 | ## Dependencies 6 | 7 | This depends on qt6 and qt6-qml, as well as hyprland-qt-support. 8 | 9 | ## Building 10 | 11 | You can build it with this command: 12 | ```sh 13 | cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build 14 | cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` 15 | ``` 16 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.4 -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "hyprland-qt-support": { 4 | "inputs": { 5 | "hyprlang": [ 6 | "hyprlang" 7 | ], 8 | "nixpkgs": [ 9 | "nixpkgs" 10 | ], 11 | "systems": [ 12 | "systems" 13 | ] 14 | }, 15 | "locked": { 16 | "lastModified": 1737634706, 17 | "narHash": "sha256-nGCibkfsXz7ARx5R+SnisRtMq21IQIhazp6viBU8I/A=", 18 | "owner": "hyprwm", 19 | "repo": "hyprland-qt-support", 20 | "rev": "8810df502cdee755993cb803eba7b23f189db795", 21 | "type": "github" 22 | }, 23 | "original": { 24 | "owner": "hyprwm", 25 | "repo": "hyprland-qt-support", 26 | "type": "github" 27 | } 28 | }, 29 | "hyprlang": { 30 | "inputs": { 31 | "hyprutils": "hyprutils", 32 | "nixpkgs": [ 33 | "nixpkgs" 34 | ], 35 | "systems": [ 36 | "systems" 37 | ] 38 | }, 39 | "locked": { 40 | "lastModified": 1737634606, 41 | "narHash": "sha256-W7W87Cv6wqZ9PHegI6rH1+ve3zJPiyevMFf0/HwdbCQ=", 42 | "owner": "hyprwm", 43 | "repo": "hyprlang", 44 | "rev": "f41271d35cc0f370d300413d756c2677f386af9d", 45 | "type": "github" 46 | }, 47 | "original": { 48 | "owner": "hyprwm", 49 | "repo": "hyprlang", 50 | "type": "github" 51 | } 52 | }, 53 | "hyprutils": { 54 | "inputs": { 55 | "nixpkgs": [ 56 | "hyprlang", 57 | "nixpkgs" 58 | ], 59 | "systems": [ 60 | "hyprlang", 61 | "systems" 62 | ] 63 | }, 64 | "locked": { 65 | "lastModified": 1737632363, 66 | "narHash": "sha256-X9I8POSlHxBVjD0fiX1O2j7U9Zi1+4rIkrsyHP0uHXY=", 67 | "owner": "hyprwm", 68 | "repo": "hyprutils", 69 | "rev": "006620eb29d54ea9086538891404c78563d1bae1", 70 | "type": "github" 71 | }, 72 | "original": { 73 | "owner": "hyprwm", 74 | "repo": "hyprutils", 75 | "type": "github" 76 | } 77 | }, 78 | "nixpkgs": { 79 | "locked": { 80 | "lastModified": 1737632463, 81 | "narHash": "sha256-38J9QfeGSej341ouwzqf77WIHAScihAKCt8PQJ+NH28=", 82 | "owner": "NixOS", 83 | "repo": "nixpkgs", 84 | "rev": "0aa475546ed21629c4f5bbf90e38c846a99ec9e9", 85 | "type": "github" 86 | }, 87 | "original": { 88 | "owner": "NixOS", 89 | "ref": "nixos-unstable", 90 | "repo": "nixpkgs", 91 | "type": "github" 92 | } 93 | }, 94 | "root": { 95 | "inputs": { 96 | "hyprland-qt-support": "hyprland-qt-support", 97 | "hyprlang": "hyprlang", 98 | "hyprutils": [ 99 | "hyprlang", 100 | "hyprutils" 101 | ], 102 | "nixpkgs": "nixpkgs", 103 | "systems": "systems" 104 | } 105 | }, 106 | "systems": { 107 | "locked": { 108 | "lastModified": 1689347949, 109 | "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", 110 | "owner": "nix-systems", 111 | "repo": "default-linux", 112 | "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", 113 | "type": "github" 114 | }, 115 | "original": { 116 | "owner": "nix-systems", 117 | "repo": "default-linux", 118 | "type": "github" 119 | } 120 | } 121 | }, 122 | "root": "root", 123 | "version": 7 124 | } 125 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Hyprland QT/qml utility apps"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 | systems.url = "github:nix-systems/default-linux"; 7 | 8 | hyprlang = { 9 | url = "github:hyprwm/hyprlang"; 10 | inputs.nixpkgs.follows = "nixpkgs"; 11 | inputs.systems.follows = "systems"; 12 | }; 13 | 14 | hyprutils.follows = "hyprlang/hyprutils"; 15 | 16 | hyprland-qt-support = { 17 | url = "github:hyprwm/hyprland-qt-support"; 18 | inputs.hyprlang.follows = "hyprlang"; 19 | inputs.nixpkgs.follows = "nixpkgs"; 20 | inputs.systems.follows = "systems"; 21 | }; 22 | }; 23 | 24 | outputs = { 25 | self, 26 | nixpkgs, 27 | systems, 28 | ... 29 | } @ inputs: let 30 | inherit (nixpkgs) lib; 31 | eachSystem = lib.genAttrs (import systems); 32 | pkgsFor = eachSystem ( 33 | system: 34 | import nixpkgs { 35 | localSystem = system; 36 | overlays = [self.overlays.default]; 37 | } 38 | ); 39 | in { 40 | overlays = import ./nix/overlays.nix {inherit inputs self lib;}; 41 | 42 | packages = eachSystem (system: { 43 | default = self.packages.${system}.hyprland-qtutils; 44 | inherit (pkgsFor.${system}) hyprland-qtutils; 45 | }); 46 | 47 | devShells = eachSystem (system: { 48 | default = import ./nix/shell.nix { 49 | pkgs = pkgsFor.${system}; 50 | inherit (pkgsFor.${system}) hyprland-qtutils; 51 | }; 52 | }); 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /nix/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | stdenv, 4 | cmake, 5 | pkg-config, 6 | hyprutils, 7 | hyprland-qt-support, 8 | pciutils, 9 | qt6, 10 | version ? "0", 11 | }: let 12 | inherit (lib.sources) cleanSource cleanSourceWith; 13 | inherit (lib.strings) hasSuffix makeBinPath; 14 | in 15 | stdenv.mkDerivation { 16 | pname = "hyprland-qtutils"; 17 | inherit version; 18 | 19 | src = cleanSourceWith { 20 | filter = name: _type: let 21 | baseName = baseNameOf (toString name); 22 | in 23 | ! (hasSuffix ".nix" baseName); 24 | src = cleanSource ../.; 25 | }; 26 | 27 | nativeBuildInputs = [ 28 | cmake 29 | pkg-config 30 | qt6.wrapQtAppsHook 31 | ]; 32 | 33 | buildInputs = [ 34 | hyprutils 35 | hyprland-qt-support 36 | qt6.qtbase 37 | qt6.qtsvg 38 | qt6.qtwayland 39 | ]; 40 | 41 | preFixup = '' 42 | qtWrapperArgs+=(--prefix PATH : "${makeBinPath [pciutils]}") 43 | ''; 44 | 45 | meta = { 46 | description = "Hyprland QT/qml utility apps"; 47 | homepage = "https://github.com/hyprwm/hyprland-qtlibs"; 48 | license = lib.licenses.bsd3; 49 | maintainers = [lib.maintainers.fufexan]; 50 | platforms = lib.platforms.linux; 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /nix/overlays.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs, 3 | self, 4 | lib, 5 | }: let 6 | mkDate = longDate: (lib.concatStringsSep "-" [ 7 | (builtins.substring 0 4 longDate) 8 | (builtins.substring 4 2 longDate) 9 | (builtins.substring 6 2 longDate) 10 | ]); 11 | date = mkDate (self.lastModifiedDate or "19700101"); 12 | version = lib.removeSuffix "\n" (builtins.readFile ../VERSION); 13 | in { 14 | default = self.overlays.hyprland-qtutils; 15 | 16 | hyprland-qtutils = lib.composeManyExtensions [ 17 | inputs.hyprutils.overlays.default 18 | inputs.hyprland-qt-support.overlays.default 19 | (final: prev: { 20 | hyprland-qtutils = final.callPackage ./. { 21 | stdenv = final.gcc14Stdenv; 22 | version = "${version}+date=${date}_${self.shortRev or "dirty"}"; 23 | }; 24 | }) 25 | ]; 26 | } 27 | -------------------------------------------------------------------------------- /nix/shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import {}, 3 | hyprland-qtutils ? pkgs.callPackage ./default.nix {}, 4 | ... 5 | }: pkgs.mkShell { 6 | inputsFrom = [ hyprland-qtutils ]; 7 | nativeBuildInputs = [ pkgs.clang-tools pkgs.pciutils ]; 8 | 9 | shellHook = let 10 | inherit (pkgs.lib.strings) concatMapStringsSep; 11 | qtLibPath = f: concatMapStringsSep ":" f (with pkgs.qt6; [ 12 | qtbase 13 | qtdeclarative 14 | qtwayland 15 | pkgs.hyprland-qt-support 16 | ]); 17 | in '' 18 | # Add Qt-related environment variables. 19 | export QT_PLUGIN_PATH=${qtLibPath (p: "${p}/lib/qt-6/plugins")} 20 | export QML2_IMPORT_PATH=${qtLibPath (p: "${p}/lib/qt-6/qml")} 21 | 22 | # Generate compile_commands.json 23 | CMAKE_EXPORT_COMPILE_COMMANDS=1 cmake -S . -B ./build 24 | ln -s build/compile_commands.json . 25 | ''; 26 | } 27 | -------------------------------------------------------------------------------- /utils/dialog/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project(hyprland-dialog VERSION ${VER} LANGUAGES CXX) 4 | 5 | set(CMAKE_CXX_STANDARD 23) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) 9 | find_package(PkgConfig REQUIRED) 10 | 11 | pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) 12 | 13 | qt_standard_project_setup(REQUIRES 6.5) 14 | 15 | qt_add_executable(hyprland-dialog 16 | main.cpp 17 | Dialog.cpp 18 | ) 19 | 20 | qt_add_qml_module(hyprland-dialog 21 | URI org.hyprland.dialog 22 | VERSION 1.0 23 | QML_FILES main.qml 24 | ) 25 | 26 | target_link_libraries(hyprland-dialog PRIVATE 27 | Qt6::Widgets Qt6::QuickControls2 Qt6::WaylandClientPrivate PkgConfig::hyprutils 28 | ) 29 | 30 | 31 | include(GNUInstallDirs) 32 | install(TARGETS hyprland-dialog 33 | BUNDLE DESTINATION . 34 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 35 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 36 | ) 37 | -------------------------------------------------------------------------------- /utils/dialog/Dialog.cpp: -------------------------------------------------------------------------------- 1 | #include "Dialog.hpp" 2 | #include 3 | #include 4 | using namespace Hyprutils::String; 5 | 6 | CDialog::CDialog(QObject* parent) : QObject(parent) { 7 | ; 8 | } 9 | 10 | void CDialog::onButtonPress(QString buttonName) { 11 | std::print("{}\n", trim(buttonName.toStdString())); 12 | exit(0); 13 | } 14 | -------------------------------------------------------------------------------- /utils/dialog/Dialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class CDialog : public QObject { 12 | Q_OBJECT; 13 | QML_NAMED_ELEMENT(Dialog); 14 | QML_SINGLETON; 15 | Q_PROPERTY(QString dialogTitle MEMBER title CONSTANT); 16 | Q_PROPERTY(QString dialogText MEMBER text CONSTANT); 17 | Q_PROPERTY(QVector dialogButtons MEMBER buttons CONSTANT); 18 | 19 | public: 20 | explicit CDialog(QObject* parent = nullptr); 21 | 22 | QString title, text; 23 | QVector buttons; 24 | 25 | Q_INVOKABLE void onButtonPress(QString buttonName = ""); 26 | }; 27 | -------------------------------------------------------------------------------- /utils/dialog/main.cpp: -------------------------------------------------------------------------------- 1 | #include "Dialog.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace Hyprutils::String; 11 | 12 | int main(int argc, char* argv[]) { 13 | // disable logs to not trash the stdout 14 | qputenv("QT_LOGGING_RULES", QByteArray("*.debug=false;qml=false")); 15 | 16 | QString appTitle; 17 | auto dialog = new CDialog(); 18 | 19 | for (int i = 1; i < argc; ++i) { 20 | std::string_view arg = argv[i]; 21 | 22 | if (arg == "--title") { 23 | if (i + 1 >= argc) { 24 | std::print(stderr, "--title requires a parameter\n"); 25 | return 1; 26 | } 27 | 28 | dialog->title = argv[i + 1]; 29 | 30 | i++; 31 | continue; 32 | } 33 | 34 | if (arg == "--apptitle") { 35 | if (i + 1 >= argc) { 36 | std::print(stderr, "--apptitle requires a parameter\n"); 37 | return 1; 38 | } 39 | 40 | appTitle = argv[i + 1]; 41 | 42 | i++; 43 | continue; 44 | } 45 | 46 | if (arg == "--text") { 47 | if (i + 1 >= argc) { 48 | std::print(stderr, "--text requires a parameter\n"); 49 | return 1; 50 | } 51 | 52 | dialog->text = argv[i + 1]; 53 | 54 | i++; 55 | continue; 56 | } 57 | 58 | if (arg == "--buttons") { 59 | if (i + 1 >= argc) { 60 | std::print(stderr, "--buttons requires a parameter\n"); 61 | return 1; 62 | } 63 | 64 | CVarList buttonz(argv[i + 1], 0, ';', true); 65 | 66 | for (auto& b : buttonz) { 67 | dialog->buttons.emplace_back(b.c_str()); 68 | } 69 | 70 | i++; 71 | continue; 72 | } 73 | 74 | std::print(stderr, "invalid arg {}\n", argv[i]); 75 | return 1; 76 | } 77 | 78 | QApplication app(argc, argv); 79 | app.setApplicationName(appTitle.isEmpty() ? dialog->title : appTitle); 80 | 81 | if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) 82 | QQuickStyle::setStyle("org.hyprland.style"); 83 | 84 | QQmlApplicationEngine engine; 85 | engine.rootContext()->setContextProperty("dialog", dialog); 86 | engine.load("qrc:/qt/qml/org/hyprland/dialog/main.qml"); 87 | 88 | return app.exec(); 89 | } 90 | -------------------------------------------------------------------------------- /utils/dialog/main.qml: -------------------------------------------------------------------------------- 1 | pragma ComponentBehavior: Bound 2 | 3 | import QtQuick 4 | import QtQuick.Controls 5 | import QtQuick.Layouts 6 | 7 | ApplicationWindow { 8 | id: window 9 | 10 | FontMetrics { id: fontMetrics } 11 | 12 | property var windowPadding: 10 13 | 14 | minimumWidth: Math.max(fontMetrics.height * 10, mainLayout.Layout.minimumWidth) + mainLayout.anchors.margins * 2 + windowPadding * 2 15 | minimumHeight: Math.max(fontMetrics.height * 10, mainLayout.Layout.minimumHeight) + mainLayout.anchors.margins * 2 + windowPadding * 2 16 | maximumWidth: minimumWidth 17 | maximumHeight: minimumHeight 18 | visible: true 19 | 20 | component Separator: Rectangle { 21 | color: Qt.darker(system.windowText, 1.5) 22 | } 23 | 24 | component VSeparator: Separator { 25 | implicitWidth: 1 26 | Layout.fillHeight: true 27 | Layout.topMargin: fontMetrics.height 28 | Layout.bottomMargin: fontMetrics.height 29 | } 30 | 31 | component HSeparator: Separator { 32 | implicitHeight: 1 33 | Layout.fillWidth: true 34 | Layout.leftMargin: fontMetrics.height * 8 35 | Layout.rightMargin: fontMetrics.height * 8 36 | } 37 | 38 | SystemPalette { 39 | id: system 40 | colorGroup: SystemPalette.Active 41 | } 42 | 43 | ColumnLayout { 44 | id: mainLayout 45 | spacing: fontMetrics.height 46 | 47 | anchors { 48 | fill: parent 49 | margins: 4 50 | } 51 | 52 | Text { 53 | font.pointSize: fontMetrics.height 54 | color: system.windowText 55 | text: dialog.dialogTitle 56 | Layout.alignment: Qt.AlignHCenter 57 | } 58 | 59 | HSeparator {} 60 | 61 | Text { 62 | color: system.windowText 63 | text: dialog.dialogText 64 | Layout.alignment: Qt.AlignHCenter 65 | } 66 | 67 | Rectangle { 68 | color: "transparent" 69 | Layout.minimumHeight: 10 70 | Layout.fillHeight: true 71 | } 72 | 73 | RowLayout { 74 | spacing: 6 75 | Layout.leftMargin: 20 76 | Layout.alignment: Qt.AlignRight 77 | 78 | Repeater { 79 | model: dialog.dialogButtons 80 | 81 | Button { 82 | required property int index; 83 | text: dialog.dialogButtons[index] 84 | onClicked: (e) => { 85 | dialog.onButtonPress(dialog.dialogButtons[index]) 86 | window.close() 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /utils/donate-screen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project(hyprland-donate-screen VERSION ${VER} LANGUAGES CXX) 4 | 5 | set(CMAKE_CXX_STANDARD 23) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) 9 | find_package(PkgConfig REQUIRED) 10 | 11 | pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) 12 | 13 | qt_standard_project_setup(REQUIRES 6.5) 14 | 15 | qt_add_executable(hyprland-donate-screen 16 | main.cpp 17 | DonateScreen.cpp 18 | ) 19 | 20 | qt_add_qml_module(hyprland-donate-screen 21 | URI org.hyprland.donate-screen 22 | VERSION 1.0 23 | QML_FILES main.qml 24 | ) 25 | 26 | qt_add_resources(hyprland-donate-screen "assets" 27 | PREFIX "/" 28 | FILES 29 | assets/heart.svg 30 | ) 31 | 32 | target_link_libraries(hyprland-donate-screen PRIVATE 33 | Qt6::Widgets Qt6::QuickControls2 Qt6::WaylandClientPrivate PkgConfig::hyprutils 34 | ) 35 | 36 | 37 | include(GNUInstallDirs) 38 | install(TARGETS hyprland-donate-screen 39 | BUNDLE DESTINATION . 40 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 41 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 42 | ) 43 | -------------------------------------------------------------------------------- /utils/donate-screen/DonateScreen.cpp: -------------------------------------------------------------------------------- 1 | #include "DonateScreen.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | using namespace Hyprutils::String; 9 | 10 | CDonateScreen::CDonateScreen(QObject* parent) : QObject(parent) { 11 | ; 12 | } 13 | 14 | void CDonateScreen::onButtonPress(QString buttonName) { 15 | if (buttonName == "quit") 16 | exit(0); 17 | if (buttonName == "donate") 18 | QDesktopServices::openUrl(QUrl("https://hyprland.org/support")); 19 | } 20 | -------------------------------------------------------------------------------- /utils/donate-screen/DonateScreen.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class CDonateScreen : public QObject { 12 | Q_OBJECT; 13 | QML_NAMED_ELEMENT(DonateScreen); 14 | QML_SINGLETON; 15 | 16 | public: 17 | explicit CDonateScreen(QObject* parent = nullptr); 18 | 19 | Q_INVOKABLE void onButtonPress(QString buttonName = ""); 20 | }; 21 | -------------------------------------------------------------------------------- /utils/donate-screen/assets/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /utils/donate-screen/main.cpp: -------------------------------------------------------------------------------- 1 | #include "DonateScreen.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace Hyprutils::String; 11 | 12 | int main(int argc, char* argv[]) { 13 | // disable logs to not trash the stdout 14 | qputenv("QT_LOGGING_RULES", QByteArray("*.debug=false;qml=false")); 15 | 16 | auto dialog = new CDonateScreen(); 17 | 18 | QApplication app(argc, argv); 19 | app.setApplicationName("Support Hyprland"); 20 | app.setApplicationDisplayName("Support Hyprland"); 21 | 22 | if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) 23 | QQuickStyle::setStyle("org.hyprland.style"); 24 | 25 | QQmlApplicationEngine engine; 26 | engine.rootContext()->setContextProperty("donateScreen", dialog); 27 | engine.load("qrc:/qt/qml/org/hyprland/donate-screen/main.qml"); 28 | 29 | return app.exec(); 30 | } 31 | -------------------------------------------------------------------------------- /utils/donate-screen/main.qml: -------------------------------------------------------------------------------- 1 | pragma ComponentBehavior: Bound 2 | 3 | import QtQuick 4 | import QtQuick.Controls 5 | import QtQuick.Layouts 6 | import './' 7 | 8 | ApplicationWindow { 9 | id: window 10 | 11 | FontMetrics { id: fontMetrics } 12 | 13 | property var windowPaddingH: 30 14 | property var windowPaddingV: 3 15 | 16 | minimumWidth: Math.max(fontMetrics.height * 9, mainLayout.Layout.minimumWidth) + mainLayout.anchors.margins * 2 + windowPaddingH * 2 17 | minimumHeight: Math.max(fontMetrics.height * 9, mainLayout.Layout.minimumHeight) + mainLayout.anchors.margins * 2 + windowPaddingV * 2 18 | maximumWidth: minimumWidth 19 | maximumHeight: minimumHeight 20 | visible: true 21 | 22 | component Separator: Rectangle { 23 | color: Qt.darker(system.windowText, 1.5) 24 | } 25 | 26 | component VSeparator: Separator { 27 | implicitWidth: 1 28 | Layout.fillHeight: true 29 | Layout.topMargin: fontMetrics.height 30 | Layout.bottomMargin: fontMetrics.height 31 | } 32 | 33 | component HSeparator: Separator { 34 | implicitHeight: 1 35 | Layout.fillWidth: true 36 | Layout.leftMargin: fontMetrics.height * 8 37 | Layout.rightMargin: fontMetrics.height * 8 38 | } 39 | 40 | SystemPalette { 41 | id: system 42 | colorGroup: SystemPalette.Active 43 | } 44 | 45 | ColumnLayout { 46 | id: mainLayout 47 | spacing: fontMetrics.height 48 | 49 | anchors { 50 | fill: parent 51 | margins: 4 52 | } 53 | 54 | Text { 55 | font.pointSize: fontMetrics.height 56 | color: system.windowText 57 | text: "Support Hyprland" 58 | Layout.alignment: Qt.AlignHCenter 59 | } 60 | 61 | HSeparator {} 62 | 63 | Text { 64 | color: system.windowText 65 | text: "Hyprland is maintained by volunteers, and led by one person in their free time.
Your support is valuable, and helps fund Hyprland's continued existence.

You can donate once, or monthly, and it takes less than 5 minutes." 66 | Layout.alignment: Qt.AlignHCenter 67 | horizontalAlignment: Text.AlignHCenter 68 | textFormat: TextEdit.RichText 69 | onLinkActivated: Qt.openUrlExternally(link) 70 | } 71 | 72 | Rectangle { 73 | color: "transparent" 74 | Layout.minimumHeight: 4 75 | Layout.fillHeight: true 76 | } 77 | 78 | RowLayout { 79 | spacing: 6 80 | Layout.leftMargin: 20 81 | Layout.alignment: Qt.AlignRight 82 | 83 | Button { 84 | icon.color: "transparent" 85 | icon.source: "qrc:/assets/heart.svg" 86 | icon.height: 18 87 | icon.width: 18 88 | text: "Donate" 89 | onClicked: (e) => { 90 | donateScreen.onButtonPress("donate"); 91 | } 92 | } 93 | 94 | Button { 95 | text: "No thanks" 96 | onClicked: (e) => { 97 | donateScreen.onButtonPress("quit"); 98 | } 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /utils/update-screen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project(hyprland-update-screen VERSION ${VER} LANGUAGES CXX) 4 | 5 | set(CMAKE_CXX_STANDARD 23) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) 9 | find_package(PkgConfig REQUIRED) 10 | 11 | pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) 12 | 13 | qt_standard_project_setup(REQUIRES 6.5) 14 | 15 | qt_add_executable(hyprland-update-screen 16 | main.cpp 17 | Dialog.cpp 18 | Dialog.hpp 19 | UpdateScreen.cpp 20 | ) 21 | 22 | qt_add_qml_module(hyprland-update-screen 23 | URI org.hyprland.update-screen 24 | VERSION 1.0 25 | QML_FILES main.qml dialogMain.qml 26 | ) 27 | 28 | qt_add_resources(hyprland-update-screen "assets" 29 | PREFIX "/" 30 | FILES 31 | assets/heart.svg 32 | ) 33 | 34 | target_link_libraries(hyprland-update-screen PRIVATE 35 | Qt6::Widgets Qt6::QuickControls2 Qt6::WaylandClientPrivate PkgConfig::hyprutils 36 | ) 37 | 38 | 39 | include(GNUInstallDirs) 40 | install(TARGETS hyprland-update-screen 41 | BUNDLE DESTINATION . 42 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 43 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 44 | ) 45 | -------------------------------------------------------------------------------- /utils/update-screen/Dialog.cpp: -------------------------------------------------------------------------------- 1 | #include "Dialog.hpp" 2 | 3 | CDialog::CDialog(QObject* parent) : QObject(parent) { 4 | ; 5 | } 6 | 7 | void CDialog::onButtonPress(QString buttonName) { 8 | ; 9 | } 10 | -------------------------------------------------------------------------------- /utils/update-screen/Dialog.hpp: -------------------------------------------------------------------------------- 1 | ../dialog/Dialog.hpp -------------------------------------------------------------------------------- /utils/update-screen/UpdateScreen.cpp: -------------------------------------------------------------------------------- 1 | #include "UpdateScreen.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | using namespace Hyprutils::String; 9 | 10 | CUpdateScreen::CUpdateScreen(QObject* parent) : QObject(parent) { 11 | ; 12 | } 13 | 14 | void CUpdateScreen::onButtonPress(QString buttonName) { 15 | if (buttonName == "quit") 16 | exit(0); 17 | if (buttonName == "donate") 18 | QDesktopServices::openUrl(QUrl("https://hyprland.org/support")); 19 | } 20 | -------------------------------------------------------------------------------- /utils/update-screen/UpdateScreen.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class CUpdateScreen : public QObject { 12 | Q_OBJECT; 13 | QML_NAMED_ELEMENT(UpdateScreen); 14 | QML_SINGLETON; 15 | Q_PROPERTY(QString newVersion MEMBER newVersion CONSTANT); 16 | 17 | public: 18 | explicit CUpdateScreen(QObject* parent = nullptr); 19 | 20 | QString newVersion; 21 | 22 | Q_INVOKABLE void onButtonPress(QString buttonName = ""); 23 | }; 24 | -------------------------------------------------------------------------------- /utils/update-screen/assets/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /utils/update-screen/dialogMain.qml: -------------------------------------------------------------------------------- 1 | ../dialog/main.qml -------------------------------------------------------------------------------- /utils/update-screen/main.cpp: -------------------------------------------------------------------------------- 1 | #include "UpdateScreen.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Dialog.hpp" 10 | 11 | using namespace Hyprutils::String; 12 | 13 | int main(int argc, char* argv[]) { 14 | // disable logs to not trash the stdout 15 | qputenv("QT_LOGGING_RULES", QByteArray("*.debug=false;qml=false")); 16 | 17 | auto dialog = new CUpdateScreen(); 18 | 19 | for (int i = 1; i < argc; ++i) { 20 | std::string_view arg = argv[i]; 21 | 22 | if (arg == "--new-version") { 23 | if (i + 1 >= argc) { 24 | std::println(stderr, "--new-version requires a parameter"); 25 | return 1; 26 | } 27 | 28 | dialog->newVersion = argv[i + 1]; 29 | 30 | i++; 31 | continue; 32 | } 33 | 34 | std::println(stderr, "invalid arg {}", argv[i]); 35 | return 1; 36 | } 37 | 38 | if (dialog->newVersion.isEmpty()) { 39 | std::println(stderr, "missing --new-version"); 40 | return 1; 41 | } 42 | 43 | QApplication app(argc, argv); 44 | app.setApplicationName("Hyprland Updated!"); 45 | app.setApplicationDisplayName("Hyprland Updated"); 46 | 47 | if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) 48 | QQuickStyle::setStyle("org.hyprland.style"); 49 | 50 | // This entire mechanism fucking sucks, 51 | // but I also suck at qml and I want to avoid spawning a new process as it takes a while. 52 | auto popup = new CDialog(); 53 | popup->title = "Information"; 54 | popup->text = "If you wish to disable this dialog, set ecosystem:no_update_news to true in your Hyprland config."; 55 | popup->buttons = {"ok"}; 56 | 57 | QQmlApplicationEngine engine; 58 | engine.rootContext()->setContextProperty("updateScreen", dialog); 59 | engine.rootContext()->setContextProperty("dialog", popup); 60 | engine.load("qrc:/qt/qml/org/hyprland/update-screen/main.qml"); 61 | 62 | return app.exec(); 63 | } 64 | -------------------------------------------------------------------------------- /utils/update-screen/main.qml: -------------------------------------------------------------------------------- 1 | pragma ComponentBehavior: Bound 2 | 3 | import QtQuick 4 | import QtQuick.Controls 5 | import QtQuick.Layouts 6 | import './' 7 | 8 | ApplicationWindow { 9 | id: window 10 | 11 | FontMetrics { id: fontMetrics } 12 | 13 | property var windowPadding: 10 14 | 15 | minimumWidth: Math.max(fontMetrics.height * 10, mainLayout.Layout.minimumWidth) + mainLayout.anchors.margins * 2 + windowPadding * 2 16 | minimumHeight: Math.max(fontMetrics.height * 10, mainLayout.Layout.minimumHeight) + mainLayout.anchors.margins * 2 + windowPadding * 2 17 | maximumWidth: minimumWidth 18 | maximumHeight: minimumHeight 19 | visible: true 20 | 21 | component Separator: Rectangle { 22 | color: Qt.darker(system.windowText, 1.5) 23 | } 24 | 25 | component VSeparator: Separator { 26 | implicitWidth: 1 27 | Layout.fillHeight: true 28 | Layout.topMargin: fontMetrics.height 29 | Layout.bottomMargin: fontMetrics.height 30 | } 31 | 32 | component HSeparator: Separator { 33 | implicitHeight: 1 34 | Layout.fillWidth: true 35 | Layout.leftMargin: fontMetrics.height * 8 36 | Layout.rightMargin: fontMetrics.height * 8 37 | } 38 | 39 | SystemPalette { 40 | id: system 41 | colorGroup: SystemPalette.Active 42 | } 43 | 44 | ColumnLayout { 45 | id: mainLayout 46 | spacing: fontMetrics.height 47 | 48 | anchors { 49 | fill: parent 50 | margins: 4 51 | } 52 | 53 | Text { 54 | font.pointSize: fontMetrics.height 55 | color: system.windowText 56 | text: "Hyprland updated to " + updateScreen.newVersion + "!" 57 | Layout.alignment: Qt.AlignHCenter 58 | } 59 | 60 | HSeparator {} 61 | 62 | Text { 63 | color: system.windowText 64 | text: "Hyprland has been updated! 😄

Please check the release notes on GitHub: https://github.com/hyprwm/Hyprland/releases

Every release may come with breaking changes, so if you get any config errors, try checking the latest release notes.

If you are using plugins, you will need to rebuild them." 65 | Layout.alignment: Qt.AlignHCenter 66 | horizontalAlignment: Text.AlignHCenter 67 | textFormat: TextEdit.RichText 68 | onLinkActivated: Qt.openUrlExternally(link) 69 | } 70 | 71 | Rectangle { 72 | color: "transparent" 73 | Layout.minimumHeight: 10 74 | Layout.fillHeight: true 75 | } 76 | 77 | RowLayout { 78 | spacing: 6 79 | Layout.leftMargin: 20 80 | Layout.alignment: Qt.AlignRight 81 | 82 | Button { 83 | icon.color: "transparent" 84 | icon.source: "qrc:/assets/heart.svg" 85 | icon.height: 18 86 | icon.width: 18 87 | text: "Support the project" 88 | onClicked: (e) => { 89 | updateScreen.onButtonPress("donate"); 90 | } 91 | } 92 | 93 | Button { 94 | text: "Don't show this when I update" 95 | onClicked: (e) => { 96 | var component = Qt.createComponent("dialogMain.qml") 97 | var newWindow = component.createObject(window) 98 | newWindow.show() 99 | } 100 | } 101 | 102 | Button { 103 | text: "Thanks!" 104 | onClicked: (e) => { 105 | updateScreen.onButtonPress("quit"); 106 | } 107 | } 108 | } 109 | } 110 | } 111 | --------------------------------------------------------------------------------