diff options
author | Gregor Kleen <gkleen@yggdrasil.li> | 2025-08-29 23:06:55 +0200 |
---|---|---|
committer | Gregor Kleen <gkleen@yggdrasil.li> | 2025-08-29 23:06:55 +0200 |
commit | c3a8a171734bfeced58f4611365e85a6daed7db9 (patch) | |
tree | 235296a43af10ce96b5dd74e0523e59f4c1a8b12 | |
parent | 218ac55d86ee49d151c0ba2dfbca6da104c66703 (diff) | |
download | nixos-quickshell.tar nixos-quickshell.tar.gz nixos-quickshell.tar.bz2 nixos-quickshell.tar.xz nixos-quickshell.zip |
...quickshell
-rw-r--r-- | accounts/gkleen@sif/default.nix | 1 | ||||
-rw-r--r-- | accounts/gkleen@sif/shell/default.nix | 20 | ||||
-rw-r--r-- | accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt | 116 | ||||
-rw-r--r-- | accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp | 88 | ||||
-rw-r--r-- | accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp | 57 | ||||
-rw-r--r-- | accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h | 7 | ||||
-rw-r--r-- | accounts/gkleen@sif/shell/quickshell-plugins/default.nix | 20 | ||||
-rw-r--r-- | accounts/gkleen@sif/shell/quickshell/Bar.qml | 81 | ||||
-rw-r--r-- | accounts/gkleen@sif/shell/quickshell/shell.qml | 13 | ||||
-rw-r--r-- | home-modules/quickshell.nix | 68 |
10 files changed, 471 insertions, 0 deletions
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix index 64434bb8..d509eeef 100644 --- a/accounts/gkleen@sif/default.nix +++ b/accounts/gkleen@sif/default.nix | |||
@@ -71,6 +71,7 @@ in { | |||
71 | imports = [ | 71 | imports = [ |
72 | ./libvirt | 72 | ./libvirt |
73 | ./niri | 73 | ./niri |
74 | ./shell | ||
74 | ./synadm | 75 | ./synadm |
75 | flakeInputs.nix-index-database.homeModules.nix-index | 76 | flakeInputs.nix-index-database.homeModules.nix-index |
76 | flakeInputs.impermanence.nixosModules.home-manager.impermanence | 77 | flakeInputs.impermanence.nixosModules.home-manager.impermanence |
diff --git a/accounts/gkleen@sif/shell/default.nix b/accounts/gkleen@sif/shell/default.nix new file mode 100644 index 00000000..405ae4b6 --- /dev/null +++ b/accounts/gkleen@sif/shell/default.nix | |||
@@ -0,0 +1,20 @@ | |||
1 | { config, pkgs, lib, ... }: | ||
2 | |||
3 | { | ||
4 | config = { | ||
5 | programs.quickshell = { | ||
6 | enable = true; | ||
7 | config = { | ||
8 | src = ./quickshell; | ||
9 | replacements = { | ||
10 | coreutils = toString pkgs.coreutils; | ||
11 | }; | ||
12 | }; | ||
13 | }; | ||
14 | systemd.user.services.quickshell = { | ||
15 | Service = { | ||
16 | Environment = "QML_IMPORT_PATH=${pkgs.qt6Packages.callPackage ./quickshell-plugins {}}/${pkgs.qt6.qtbase.qtQmlPrefix}"; | ||
17 | }; | ||
18 | }; | ||
19 | }; | ||
20 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt b/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt new file mode 100644 index 00000000..aa363c4c --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt | |||
@@ -0,0 +1,116 @@ | |||
1 | set(INSTALL_QMLDIR "" CACHE STRING "QML install dir") | ||
2 | set(INSTALL_QML_PREFIX "" CACHE STRING "QML install prefix") | ||
3 | |||
4 | # There doesn't seem to be a standard cross-distro qml install path. | ||
5 | if ("${INSTALL_QMLDIR}" STREQUAL "" AND "${INSTALL_QML_PREFIX}" STREQUAL "") | ||
6 | message(WARNING "Neither INSTALL_QMLDIR nor INSTALL_QML_PREFIX is set. QML modules will not be installed.") | ||
7 | else() | ||
8 | if ("${INSTALL_QMLDIR}" STREQUAL "") | ||
9 | set(QML_FULL_INSTALLDIR "${CMAKE_INSTALL_PREFIX}/${INSTALL_QML_PREFIX}") | ||
10 | else() | ||
11 | set(QML_FULL_INSTALLDIR "${INSTALL_QMLDIR}") | ||
12 | endif() | ||
13 | |||
14 | message(STATUS "QML install dir: ${QML_FULL_INSTALLDIR}") | ||
15 | endif() | ||
16 | |||
17 | # Install a given target as a QML module. This is mostly pulled from ECM, as there does not seem | ||
18 | # to be an official way to do it. | ||
19 | # see https://github.com/KDE/extra-cmake-modules/blob/fe0f606bf7f222e36f7560fd7a2c33ef993e23bb/modules/ECMQmlModule6.cmake#L160 | ||
20 | function(install_qml_module arg_TARGET) | ||
21 | if (NOT DEFINED QML_FULL_INSTALLDIR) | ||
22 | return() | ||
23 | endif() | ||
24 | |||
25 | qt_query_qml_module(${arg_TARGET} | ||
26 | URI module_uri | ||
27 | VERSION module_version | ||
28 | PLUGIN_TARGET module_plugin_target | ||
29 | TARGET_PATH module_target_path | ||
30 | QMLDIR module_qmldir | ||
31 | TYPEINFO module_typeinfo | ||
32 | QML_FILES module_qml_files | ||
33 | RESOURCES module_resources | ||
34 | ) | ||
35 | |||
36 | set(module_dir "${QML_FULL_INSTALLDIR}/${module_target_path}") | ||
37 | |||
38 | if (NOT TARGET "${module_plugin_target}") | ||
39 | message(FATAL_ERROR "install_qml_modules called for a target without a plugin") | ||
40 | endif() | ||
41 | |||
42 | get_target_property(target_type "${arg_TARGET}" TYPE) | ||
43 | if (NOT "${target_type}" STREQUAL "STATIC_LIBRARY") | ||
44 | install( | ||
45 | TARGETS "${arg_TARGET}" | ||
46 | LIBRARY DESTINATION "${module_dir}" | ||
47 | RUNTIME DESTINATION "${module_dir}" | ||
48 | ) | ||
49 | |||
50 | install( | ||
51 | TARGETS "${module_plugin_target}" | ||
52 | LIBRARY DESTINATION "${module_dir}" | ||
53 | RUNTIME DESTINATION "${module_dir}" | ||
54 | ) | ||
55 | endif() | ||
56 | |||
57 | install(FILES "${module_qmldir}" DESTINATION "${module_dir}") | ||
58 | install(FILES "${module_typeinfo}" DESTINATION "${module_dir}") | ||
59 | |||
60 | # Install QML files | ||
61 | list(LENGTH module_qml_files num_files) | ||
62 | if (NOT "${module_qml_files}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0) | ||
63 | qt_query_qml_module(${arg_TARGET} QML_FILES_DEPLOY_PATHS qml_files_deploy_paths) | ||
64 | |||
65 | math(EXPR last_index "${num_files} - 1") | ||
66 | foreach(i RANGE 0 ${last_index}) | ||
67 | list(GET module_qml_files ${i} src_file) | ||
68 | list(GET qml_files_deploy_paths ${i} deploy_path) | ||
69 | get_filename_component(dst_name "${deploy_path}" NAME) | ||
70 | get_filename_component(dest_dir "${deploy_path}" DIRECTORY) | ||
71 | install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}") | ||
72 | endforeach() | ||
73 | endif() | ||
74 | |||
75 | # Install resources | ||
76 | list(LENGTH module_resources num_files) | ||
77 | if (NOT "${module_resources}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0) | ||
78 | qt_query_qml_module(${arg_TARGET} RESOURCES_DEPLOY_PATHS resources_deploy_paths) | ||
79 | |||
80 | math(EXPR last_index "${num_files} - 1") | ||
81 | foreach(i RANGE 0 ${last_index}) | ||
82 | list(GET module_resources ${i} src_file) | ||
83 | list(GET resources_deploy_paths ${i} deploy_path) | ||
84 | get_filename_component(dst_name "${deploy_path}" NAME) | ||
85 | get_filename_component(dest_dir "${deploy_path}" DIRECTORY) | ||
86 | install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}") | ||
87 | endforeach() | ||
88 | endif() | ||
89 | endfunction() | ||
90 | |||
91 | |||
92 | cmake_minimum_required(VERSION 3.20) | ||
93 | project(custom LANGUAGES CXX) | ||
94 | |||
95 | find_package(Qt6 REQUIRED COMPONENTS Core Qml) | ||
96 | |||
97 | qt_standard_project_setup(REQUIRES 6.6) | ||
98 | |||
99 | qt6_policy(SET QTP0001 NEW) | ||
100 | qt6_add_qml_module(customplugin | ||
101 | URI "Custom" | ||
102 | PLUGIN_TARGET customplugin | ||
103 | ) | ||
104 | |||
105 | target_sources(customplugin PRIVATE | ||
106 | Chrono.cpp Chrono.hpp | ||
107 | ) | ||
108 | |||
109 | target_compile_features(customplugin PUBLIC cxx_std_26) | ||
110 | |||
111 | target_link_libraries(customplugin PRIVATE | ||
112 | Qt6::Core | ||
113 | Qt6::Qml | ||
114 | ) | ||
115 | |||
116 | install_qml_module(customplugin) | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp new file mode 100644 index 00000000..929b7be6 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp | |||
@@ -0,0 +1,88 @@ | |||
1 | #include "Chrono.hpp" | ||
2 | |||
3 | #include <format> | ||
4 | #include <iostream> | ||
5 | #include <cmath> | ||
6 | |||
7 | Chrono::Chrono(QObject* parent): QObject(parent) { | ||
8 | QObject::connect(&this->timer, &QTimer::timeout, this, &Chrono::onTimeout); | ||
9 | this->update(); | ||
10 | } | ||
11 | |||
12 | bool Chrono::enabled() const { return this->mEnabled; } | ||
13 | |||
14 | void Chrono::setEnabled(bool enabled) { | ||
15 | if (enabled == this->mEnabled) return; | ||
16 | this->mEnabled = enabled; | ||
17 | emit this->enabledChanged(); | ||
18 | this->update(); | ||
19 | } | ||
20 | |||
21 | Chrono::Precision Chrono::precision() const { return this->mPrecision; } | ||
22 | |||
23 | void Chrono::setPrecision(Chrono::Precision precision) { | ||
24 | if (precision == this->mPrecision) return; | ||
25 | this->mPrecision = precision; | ||
26 | emit this->precisionChanged(); | ||
27 | this->update(); | ||
28 | } | ||
29 | |||
30 | void Chrono::onTimeout() { | ||
31 | this->setTime(this->targetTime); | ||
32 | this->schedule(this->targetTime); | ||
33 | } | ||
34 | |||
35 | void Chrono::update() { | ||
36 | if (this->mEnabled) { | ||
37 | this->setTime(std::chrono::time_point<std::chrono::system_clock>()); | ||
38 | this->schedule(std::chrono::time_point<std::chrono::system_clock>()); | ||
39 | } else { | ||
40 | this->timer.stop(); | ||
41 | } | ||
42 | } | ||
43 | |||
44 | void Chrono::setTime(const std::chrono::time_point<std::chrono::system_clock>& targetTime) { | ||
45 | auto currentTime = std::chrono::system_clock::now(); | ||
46 | auto offset = std::chrono::duration_cast<std::chrono::milliseconds>(targetTime - currentTime); | ||
47 | this->currentTime = abs(offset.count()) < 500 ? targetTime : currentTime; | ||
48 | |||
49 | switch (this->mPrecision) { | ||
50 | case Chrono::Hours: this->currentTime = std::chrono::time_point_cast<std::chrono::hours>(this->currentTime); | ||
51 | case Chrono::Minutes: this->currentTime = std::chrono::time_point_cast<std::chrono::minutes>(this->currentTime); | ||
52 | case Chrono::Seconds: this->currentTime = std::chrono::time_point_cast<std::chrono::seconds>(this->currentTime); | ||
53 | } | ||
54 | |||
55 | emit this->dateChanged(); | ||
56 | } | ||
57 | |||
58 | void Chrono::schedule(const std::chrono::time_point<std::chrono::system_clock>& targetTime) { | ||
59 | auto currentTime = std::chrono::system_clock::now(); | ||
60 | auto offset = std::chrono::duration_cast<std::chrono::milliseconds>(targetTime - currentTime); | ||
61 | auto nextTime = abs(offset.count()) < 500 ? targetTime : currentTime; | ||
62 | |||
63 | { | ||
64 | using namespace std::chrono_literals; | ||
65 | |||
66 | switch (this->mPrecision) { | ||
67 | case Chrono::Hours: nextTime = std::chrono::time_point_cast<std::chrono::hours>(nextTime) + 1h; | ||
68 | case Chrono::Minutes: nextTime = std::chrono::time_point_cast<std::chrono::minutes>(nextTime) + 1min; | ||
69 | case Chrono::Seconds: nextTime = std::chrono::time_point_cast<std::chrono::seconds>(nextTime) + 1s; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | this->targetTime = nextTime; | ||
74 | auto delay = std::chrono::duration_cast<std::chrono::milliseconds>(nextTime - currentTime); | ||
75 | this->timer.start(delay); | ||
76 | } | ||
77 | |||
78 | QString Chrono::format() const { return this->mFormat; } | ||
79 | void Chrono::setFormat(QString format) { | ||
80 | if (format == this->mFormat) return; | ||
81 | this->mFormat = format; | ||
82 | emit this->formatChanged(); | ||
83 | this->update(); | ||
84 | } | ||
85 | |||
86 | QString Chrono::date() const { | ||
87 | return QString::fromStdString(std::format(std::runtime_format(this->mFormat.toStdString()), std::chrono::zoned_time(std::chrono::current_zone(), std::chrono::time_point_cast<std::chrono::seconds>(this->currentTime)))); | ||
88 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp new file mode 100644 index 00000000..788fa88e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp | |||
@@ -0,0 +1,57 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <QObject> | ||
4 | #include <QTimer> | ||
5 | |||
6 | #include <qqmlintegration.h> | ||
7 | #include <chrono> | ||
8 | |||
9 | class Chrono : public QObject { | ||
10 | Q_OBJECT; | ||
11 | Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged); | ||
12 | Q_PROPERTY(Chrono::Precision precision READ precision WRITE setPrecision NOTIFY precisionChanged); | ||
13 | Q_PROPERTY(QString format READ format WRITE setFormat NOTIFY formatChanged); | ||
14 | Q_PROPERTY(QString date READ date NOTIFY dateChanged); | ||
15 | QML_ELEMENT; | ||
16 | |||
17 | public: | ||
18 | enum Precision : quint8 { | ||
19 | Hours = 1, | ||
20 | Minutes = 2, | ||
21 | Seconds = 3, | ||
22 | }; | ||
23 | Q_ENUM(Precision); | ||
24 | |||
25 | explicit Chrono(QObject* parent = nullptr); | ||
26 | |||
27 | bool enabled() const; | ||
28 | void setEnabled(bool enabled); | ||
29 | |||
30 | Chrono::Precision precision() const; | ||
31 | void setPrecision(Chrono::Precision precision); | ||
32 | |||
33 | QString format() const; | ||
34 | void setFormat (QString format); | ||
35 | |||
36 | QString date() const; | ||
37 | |||
38 | signals: | ||
39 | void enabledChanged(); | ||
40 | void precisionChanged(); | ||
41 | void formatChanged(); | ||
42 | void dateChanged(); | ||
43 | |||
44 | private slots: | ||
45 | void onTimeout(); | ||
46 | |||
47 | private: | ||
48 | bool mEnabled = true; | ||
49 | Chrono::Precision mPrecision = Chrono::Seconds; | ||
50 | QString mFormat = "{:%c}"; | ||
51 | QTimer timer; | ||
52 | std::chrono::time_point<std::chrono::system_clock> currentTime, targetTime; | ||
53 | |||
54 | void update(); | ||
55 | void setTime(const std::chrono::time_point<std::chrono::system_clock>& targetTime); | ||
56 | void schedule(const std::chrono::time_point<std::chrono::system_clock>& targetTime); | ||
57 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h b/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h new file mode 100644 index 00000000..e66ba9e3 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #include <QQmlEngineExtensionPlugin> | ||
2 | |||
3 | class CustomPlugin : public QQmlEngineExtensionPlugin | ||
4 | { | ||
5 | Q_OBJECT | ||
6 | Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid) | ||
7 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/default.nix b/accounts/gkleen@sif/shell/quickshell-plugins/default.nix new file mode 100644 index 00000000..fafea90e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/default.nix | |||
@@ -0,0 +1,20 @@ | |||
1 | { lib | ||
2 | , stdenv | ||
3 | , cmake | ||
4 | , qt6 | ||
5 | , fmt | ||
6 | }: | ||
7 | stdenv.mkDerivation rec { | ||
8 | name = "quickshell-custom"; | ||
9 | |||
10 | src = ./.; | ||
11 | nativeBuildInputs = [ cmake qt6.wrapQtAppsHook ]; | ||
12 | buildInputs = [ | ||
13 | qt6.qtbase | ||
14 | qt6.qtdeclarative | ||
15 | ]; | ||
16 | |||
17 | cmakeFlags = [ | ||
18 | (lib.cmakeFeature "INSTALL_QML_PREFIX" qt6.qtbase.qtQmlPrefix) | ||
19 | ]; | ||
20 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml new file mode 100644 index 00000000..b7235a61 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml | |||
@@ -0,0 +1,81 @@ | |||
1 | import Quickshell | ||
2 | import Quickshell.Io | ||
3 | import Custom as Custom | ||
4 | import QtQuick | ||
5 | |||
6 | |||
7 | PanelWindow { | ||
8 | property var modelData | ||
9 | |||
10 | anchors { | ||
11 | bottom: true | ||
12 | left: true | ||
13 | right: true | ||
14 | } | ||
15 | margins { | ||
16 | left: 26 + 8 | ||
17 | right: 26 + 8 | ||
18 | } | ||
19 | |||
20 | screen: modelData | ||
21 | implicitHeight: 21 | ||
22 | color: Qt.rgba(0, 0, 0, 0.66) | ||
23 | |||
24 | Row { | ||
25 | id: left | ||
26 | |||
27 | height: parent.height | ||
28 | anchors.left: parent.left | ||
29 | anchors.leftMargin: 8 | ||
30 | anchors.verticalCenter: parent.verticalCenter | ||
31 | spacing: 5 | ||
32 | |||
33 | Text { | ||
34 | color: "white" | ||
35 | anchors.verticalCenter: parent.verticalCenter | ||
36 | text: "left" | ||
37 | } | ||
38 | } | ||
39 | |||
40 | Row { | ||
41 | id: center | ||
42 | |||
43 | height: parent.height | ||
44 | anchors.centerIn: parent | ||
45 | spacing: 5 | ||
46 | |||
47 | Text { | ||
48 | color: "white" | ||
49 | anchors.verticalCenter: parent.verticalCenter | ||
50 | text: "center" | ||
51 | } | ||
52 | } | ||
53 | |||
54 | Row { | ||
55 | id: right | ||
56 | |||
57 | height: parent.height | ||
58 | anchors.right: parent.right | ||
59 | anchors.rightMargin: 8 | ||
60 | anchors.verticalCenter: parent.verticalCenter | ||
61 | spacing: 5 | ||
62 | |||
63 | Text { | ||
64 | id: clock | ||
65 | color: "white" | ||
66 | |||
67 | anchors.verticalCenter: parent.verticalCenter | ||
68 | |||
69 | Custom.Chrono { | ||
70 | id: chrono | ||
71 | format: "W{0:%V-%u} {0:%F} {0:%H:%M:%S%Ez}" | ||
72 | } | ||
73 | |||
74 | text: chrono.date | ||
75 | |||
76 | font.pointSize: 10 | ||
77 | font.family: "Fira Sans" | ||
78 | font.features: { "tnum": 1 } | ||
79 | } | ||
80 | } | ||
81 | } \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/shell.qml b/accounts/gkleen@sif/shell/quickshell/shell.qml new file mode 100644 index 00000000..35fe5344 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/shell.qml | |||
@@ -0,0 +1,13 @@ | |||
1 | import Quickshell | ||
2 | |||
3 | ShellRoot { | ||
4 | settings.watchFiles: false | ||
5 | |||
6 | Variants { | ||
7 | model: Quickshell.screens | ||
8 | |||
9 | delegate: Bar { | ||
10 | modelData: item | ||
11 | } | ||
12 | } | ||
13 | } | ||
diff --git a/home-modules/quickshell.nix b/home-modules/quickshell.nix new file mode 100644 index 00000000..dac7089f --- /dev/null +++ b/home-modules/quickshell.nix | |||
@@ -0,0 +1,68 @@ | |||
1 | { config, pkgs, lib, ... }: | ||
2 | |||
3 | let | ||
4 | cfg = config.programs.quickshell; | ||
5 | in { | ||
6 | disabledModules = ["programs/quickshell.nix"]; | ||
7 | |||
8 | options = { | ||
9 | programs.quickshell = { | ||
10 | enable = lib.mkEnableOption "quickshell"; | ||
11 | package = lib.mkPackageOption pkgs "quickshell" { nullable = true; }; | ||
12 | config.src = lib.mkOption { | ||
13 | type = lib.types.path; | ||
14 | }; | ||
15 | config.replacements = lib.mkOption { | ||
16 | type = lib.types.attrsOf lib.types.str; | ||
17 | default = {}; | ||
18 | }; | ||
19 | }; | ||
20 | }; | ||
21 | |||
22 | config = lib.mkIf cfg.enable { | ||
23 | home.packages = [ cfg.package ]; | ||
24 | |||
25 | xdg.configFile."quickshell".source = pkgs.stdenvNoCC.mkDerivation { | ||
26 | name = "quickshell"; | ||
27 | preferLocalBuild = true; | ||
28 | allowSubstitutes = false; | ||
29 | dontUnpack = true; | ||
30 | inherit (cfg.config) src; | ||
31 | buildPhase = '' | ||
32 | runHook preBuild | ||
33 | |||
34 | while IFS= read -r -d $'\0' file <&3; do | ||
35 | [[ -z $file ]] && continue | ||
36 | |||
37 | mkdir -p "$out"/"$(dirname "$file")" | ||
38 | substitute "$src"/"$file" "$out"/"$file" \ | ||
39 | ${lib.concatStringsSep " " ( | ||
40 | lib.concatLists (lib.mapAttrsToList (name: value: [ | ||
41 | "--replace-quiet" (lib.escapeShellArg "@${name}@") (lib.escapeShellArg value) | ||
42 | ]) cfg.config.replacements) | ||
43 | )} | ||
44 | done 3< <(find "$src" -type f -printf '%P\0') | ||
45 | |||
46 | runHook postBuild | ||
47 | ''; | ||
48 | }; | ||
49 | |||
50 | systemd.user.services.quickshell = { | ||
51 | Unit = { | ||
52 | Description = "quickshell"; | ||
53 | Documentation = "https://quickshell.org/docs/v${cfg.package.version}"; | ||
54 | PartOf = [ "graphical-session.target" ]; | ||
55 | After = [ "graphical-session-pre.target" ]; | ||
56 | }; | ||
57 | |||
58 | Service = { | ||
59 | ExecStart = lib.getExe cfg.package; | ||
60 | Restart = "always"; | ||
61 | }; | ||
62 | |||
63 | Install = { | ||
64 | WantedBy = [ "graphical-session.target" ]; | ||
65 | }; | ||
66 | }; | ||
67 | }; | ||
68 | } | ||