summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif/shell
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif/shell')
-rw-r--r--accounts/gkleen@sif/shell/default.nix115
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt168
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp83
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp55
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp102
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp52
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp18
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp21
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp198
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp147
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h7
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/default.nix30
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml28
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml445
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml146
-rw-r--r--accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml817
-rw-r--r--accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml172
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Bar.qml117
-rw-r--r--accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml136
-rw-r--r--accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml117
-rw-r--r--accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml84
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Clock.qml295
-rw-r--r--accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml112
-rw-r--r--accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml47
-rw-r--r--accounts/gkleen@sif/shell/quickshell/LockSurface.qml227
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Lockscreen.qml132
-rw-r--r--accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml35
-rw-r--r--accounts/gkleen@sif/shell/quickshell/NiriIdle.qml30
-rw-r--r--accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml340
-rw-r--r--accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml266
-rw-r--r--accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml483
-rw-r--r--accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml49
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml75
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml18
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml22
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml8
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml194
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml162
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml63
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml8
-rw-r--r--accounts/gkleen@sif/shell/quickshell/SystemTray.qml201
-rw-r--r--accounts/gkleen@sif/shell/quickshell/UnixIPC.qml97
-rw-r--r--accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml163
-rw-r--r--accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml89
-rw-r--r--accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml56
-rw-r--r--accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml204
-rw-r--r--accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml120
-rw-r--r--accounts/gkleen@sif/shell/quickshell/displaymanager.qml115
-rw-r--r--accounts/gkleen@sif/shell/quickshell/shell.qml53
49 files changed, 6722 insertions, 0 deletions
diff --git a/accounts/gkleen@sif/shell/default.nix b/accounts/gkleen@sif/shell/default.nix
new file mode 100644
index 00000000..44462865
--- /dev/null
+++ b/accounts/gkleen@sif/shell/default.nix
@@ -0,0 +1,115 @@
1{ config, pkgs, lib, ... }:
2
3{
4 config = {
5 programs.quickshell = {
6 enable = true;
7 package = pkgs.symlinkJoin {
8 pname = pkgs.quickshell.pname + "-wrapped";
9 inherit (pkgs.quickshell) version meta;
10 paths = [ pkgs.quickshell ];
11 buildInputs = [ pkgs.makeWrapper ];
12 postBuild = ''
13 for binary in quickshell qs; do
14 wrapProgram $out/bin/$binary \
15 --prefix QML_IMPORT_PATH : ${pkgs.qt6Packages.callPackage ./quickshell-plugins {}}/${pkgs.qt6.qtbase.qtQmlPrefix}
16 done
17 '';
18 };
19 config = {
20 src = ./quickshell;
21 replacements = {
22 ignore_workspaces = builtins.toJSON (map ({ name, ... }: name) config.programs.niri.scratchspaces);
23 wallpapers = builtins.toJSON (pkgs.stdenvNoCC.mkDerivation {
24 name = "wallpapers";
25 srcs = [
26 (pkgs.fetchurl {
27 url = "https://esawebb.org/media/archives/images/publicationtiff10k/carinanebula3.tif";
28 hash = "sha256-YxZEweDKJfvfrdxb/QFmgJhcZDEJYxotoHrG+RRn1tw=";
29 })
30 (pkgs.fetchurl {
31 url = "https://esawebb.org/media/archives/images/original/pillarsofcreation_composite.tif";
32 hash = "sha256-qRiODxR0lZWdxgYXna0fNRFFDErpBJDwOJuQl6sNjRc=";
33 })
34 (pkgs.fetchurl {
35 url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2212a.tif";
36 hash = "sha256-l2fqE/z//C1a0xkvZwsnwPbTSb+WuA11h+SUl3E1dhw=";
37 })
38 (pkgs.fetchurl {
39 url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2415a.tif";
40 hash = "sha256-onBy7cPoUpDuzQStbY2E+qmlGgSLXPwFCLX53ukAb4c=";
41 })
42 (pkgs.fetchurl {
43 url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2330a.tif";
44 hash = "sha256-nn0ZtjZIrPcpj3YcLTsrL7XiXvyh3QYgCSmdDMD+3OM=";
45 })
46 (pkgs.fetchurl {
47 url = "https://esawebb.org/media/archives/images/original/weic2426a.tif";
48 hash = "sha256-EDnfPn3GE9jt6XPqiGInP7E2F3Az7d25NqATSWltDv0=";
49 })
50 (pkgs.fetchurl {
51 url = "https://esawebb.org/media/archives/images/original/weic2503a.tif";
52 hash = "sha256-3/RX6RQp8naELcgReHPd5/zhJkoCjnA10w5BEnNo+qI=";
53 })
54 (pkgs.fetchurl {
55 url = "https://esawebb.org/media/archives/images/original/weic2506a.tif";
56 hash = "sha256-aDld0aoY1owRxDVf7Jcyw71TH42M1foYotxn2thyFYw=";
57 })
58 (pkgs.fetchurl {
59 url = "https://esawebb.org/media/archives/images/original/weic2514a.tif";
60 hash = "sha256-jTi1G1Ofo5xsF6ggrbtYJHxqLaCQ7edM5B3uORiVQtg=";
61 })
62 (pkgs.fetchurl {
63 url = "https://esawebb.org/media/archives/images/original/weic2425c.tif";
64 hash = "sha256-oaEOexSJHEGj090dJF3ct5HAoR+Y5gRiPrUlxdvnTRo=";
65 })
66 ];
67
68 dontUnpack = true;
69
70 buildInputs = [ pkgs.imagemagick ];
71 buildPhase = ''
72 runHook preBuild
73
74 typeset sources=($srcs)
75
76 mkdir -p $out
77 magick ''${sources[0]} -crop 10000x5625+0+79 +repage -define jpeg:extent=10MB $out/carinanebula3.jpeg
78 magick ''${sources[1]} -crop 6716x3778+329+80 +repage -define jpeg:extent=10MB $out/pillarsofcreation_composite.jpeg
79 magick ''${sources[2]} -crop 10000x5625+0+79 +repage -define jpeg:extent=10MB $out/weic2212a.jpeg
80 magick ''${sources[3]} -crop 7650x4302+1166+389 +repage -define jpeg:extent=10MB $out/weic2415a.jpeg
81 magick ''${sources[4]} -crop 8732x4912+0+434 +repage -define jpeg:extent=10MB $out/weic2330a.jpeg
82 magick ''${sources[5]} -crop 5302x2982+636+0 +repage -define jpeg:extent=10MB $out/weic2426a.jpeg
83 magick ''${sources[6]} -crop 4328x2434+0+906 +repage -define jpeg:extent=10MB $out/weic2503a.jpeg
84 magick ''${sources[7]} -crop 4152x2335+0+666 +repage -define jpeg:extent=10MB $out/weic2506a.jpeg
85 magick ''${sources[8]} -crop 4320x2430+0+0 +repage -define jpeg:extent=10MB $out/weic2514a.jpeg
86 magick ''${sources[9]} -crop 5863x3298+0+477 +repage -define jpeg:extent=10MB $out/weic2425c.jpeg
87
88 runHook postBuild
89 '';
90 });
91 niri_session = builtins.toJSON [
92 (pkgs.writeShellScript "niri-session" ''
93 exec ${lib.getExe pkgs.dex} -w ${config.programs.niri.package}/share/wayland-sessions/niri.desktop &>/tmp/niri-session-$$.log
94 '')
95 # (lib.getExe pkgs.dex)
96 # "${config.programs.niri.package}/share/wayland-sessions/niri.desktop"
97 ];
98 username = builtins.toJSON config.home.username;
99 mdi = builtins.toJSON (pkgs.fetchFromGitHub {
100 owner = "Templarian";
101 repo = "MaterialDesign";
102 rev = "2424e748e0cc63ab7b9c095a099b9fe239b737c0";
103 hash = "sha256-QMGl7soAhErrrnY3aKOZpt49yebkSNzy10p/v5OaqQ0=";
104 });
105 worktime = builtins.toJSON (lib.getExe pkgs.worktime);
106 };
107 };
108 };
109 systemd.user.services.quickshell = {
110 Service = {
111 RuntimeDirectory = "quickshell";
112 };
113 };
114 };
115}
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..020c0515
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt
@@ -0,0 +1,168 @@
1set(INSTALL_QMLDIR "" CACHE STRING "QML install dir")
2set(INSTALL_QML_PREFIX "" CACHE STRING "QML install prefix")
3
4# There doesn't seem to be a standard cross-distro qml install path.
5if ("${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.")
7else()
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}")
15endif()
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
20function(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()
89endfunction()
90
91
92cmake_minimum_required(VERSION 3.20)
93project(custom LANGUAGES CXX)
94
95find_package(Qt6 REQUIRED COMPONENTS Core Qml DBus)
96
97qt_standard_project_setup(REQUIRES 6.6)
98
99qt6_policy(SET QTP0001 NEW)
100qt6_add_qml_module(customplugin
101 URI "Custom"
102 PLUGIN_TARGET customplugin
103)
104
105set_source_files_properties(org.keepassxc.KeePassXC.MainWindow.xml PROPERTIES
106 CLASSNAME DBusKeePassXC
107 NO_NAMESPACE TRUE
108)
109qt_add_dbus_interface(DBUS_INTERFACES
110 org.keepassxc.KeePassXC.MainWindow.xml
111 dbus_keepassxc
112)
113
114set_source_files_properties(org.freedesktop.systemd1.Manager.xml PROPERTIES
115 CLASSNAME DBusSystemdManager
116 NO_NAMESPACE TRUE
117)
118qt_add_dbus_interface(DBUS_INTERFACES
119 org.freedesktop.systemd1.Manager.xml
120 dbus_systemd_manager
121)
122
123set_source_files_properties(org.freedesktop.login1.Manager.xml PROPERTIES
124 CLASSNAME DBusLogindManager
125 NO_NAMESPACE TRUE
126)
127qt_add_dbus_interface(DBUS_INTERFACES
128 org.freedesktop.login1.Manager.xml
129 dbus_logind_manager
130)
131
132set_source_files_properties(org.freedesktop.login1.Session.xml PROPERTIES
133 CLASSNAME DBusLogindSession
134 NO_NAMESPACE TRUE
135)
136qt_add_dbus_interface(DBUS_INTERFACES
137 org.freedesktop.login1.Session.xml
138 dbus_logind_session
139)
140
141set_source_files_properties(org.freedesktop.DBus.Properties.xml PROPERTIES
142 CLASSNAME DBusProperties
143 NO_NAMESPACE TRUE
144)
145qt_add_dbus_interface(DBUS_INTERFACES
146 org.freedesktop.DBus.Properties.xml
147 dbus_properties
148)
149
150include_directories(${CMAKE_SOURCE_DIR}/build)
151
152target_compile_features(customplugin PUBLIC cxx_std_26)
153
154target_link_libraries(customplugin PRIVATE
155 Qt6::Core
156 Qt6::Qml
157 Qt6::DBus
158)
159
160target_sources(customplugin PRIVATE
161 Chrono.cpp Chrono.hpp
162 FileSelector.cpp FileSelector.hpp
163 KeePassXC.cpp KeePassXC.hpp
164 Systemd.cpp Systemd.hpp
165 ${DBUS_INTERFACES}
166)
167
168install_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..22b3469b
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp
@@ -0,0 +1,83 @@
1#include "Chrono.hpp"
2
3#include <format>
4#include <iostream>
5#include <cmath>
6
7Chrono::Chrono(QObject* parent): QObject(parent) {
8 QObject::connect(&this->timer, &QTimer::timeout, this, &Chrono::onTimeout);
9 this->update();
10}
11
12bool Chrono::enabled() const { return this->mEnabled; }
13
14void Chrono::setEnabled(bool enabled) {
15 if (enabled == this->mEnabled) return;
16 this->mEnabled = enabled;
17 emit this->enabledChanged();
18 this->update();
19}
20
21Chrono::Precision Chrono::precision() const { return this->mPrecision; }
22
23void Chrono::setPrecision(Chrono::Precision precision) {
24 if (precision == this->mPrecision) return;
25 this->mPrecision = precision;
26 emit this->precisionChanged();
27 this->update();
28}
29
30void Chrono::onTimeout() {
31 this->setTime(this->targetTime);
32 this->schedule(this->targetTime);
33}
34
35void 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
44void Chrono::setTime(const std::chrono::time_point<std::chrono::system_clock>& targetTime) {
45 using namespace std::chrono_literals;
46
47 auto currentTime = std::chrono::system_clock::now();
48 auto offset = std::chrono::duration_cast<std::chrono::milliseconds>(targetTime - currentTime);
49 this->currentTime = abs(offset) < 500ms ? targetTime : currentTime;
50
51 switch (this->mPrecision) {
52 case Chrono::Hours: this->currentTime = std::chrono::time_point_cast<std::chrono::hours>(this->currentTime);
53 case Chrono::Minutes: this->currentTime = std::chrono::time_point_cast<std::chrono::minutes>(this->currentTime);
54 case Chrono::Seconds: this->currentTime = std::chrono::time_point_cast<std::chrono::seconds>(this->currentTime);
55 }
56
57 emit this->dateChanged();
58}
59
60void Chrono::schedule(const std::chrono::time_point<std::chrono::system_clock>& targetTime) {
61 using namespace std::chrono_literals;
62
63 auto currentTime = std::chrono::system_clock::now();
64 auto offset = std::chrono::duration_cast<std::chrono::milliseconds>(targetTime - currentTime);
65 auto nextTime = abs(offset) < 500ms ? targetTime : currentTime;
66
67 switch (this->mPrecision) {
68 case Chrono::Hours: nextTime = std::chrono::time_point_cast<std::chrono::hours>(nextTime) + 1h;
69 case Chrono::Minutes: nextTime = std::chrono::time_point_cast<std::chrono::minutes>(nextTime) + 1min;
70 case Chrono::Seconds: nextTime = std::chrono::time_point_cast<std::chrono::seconds>(nextTime) + 1s;
71 }
72
73 this->targetTime = nextTime;
74 this->timer.start(std::chrono::duration_cast<std::chrono::milliseconds>(nextTime - currentTime));
75}
76
77QString Chrono::format(const QString& fmt) const {
78 return QString::fromStdString(std::format(std::runtime_format(fmt.toStdString()), std::chrono::zoned_time(std::chrono::current_zone(), std::chrono::time_point_cast<std::chrono::seconds>(this->currentTime))));
79}
80
81QDateTime Chrono::date() const {
82 return QDateTime::fromStdTimePoint(std::chrono::time_point_cast<std::chrono::milliseconds>(this->currentTime));
83}
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..04080187
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp
@@ -0,0 +1,55 @@
1#pragma once
2
3#include <chrono>
4
5#include <QDateTime>
6#include <QObject>
7#include <QTimer>
8
9#include <qqmlintegration.h>
10
11class Chrono : public QObject {
12 Q_OBJECT;
13 Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged);
14 Q_PROPERTY(Chrono::Precision precision READ precision WRITE setPrecision NOTIFY precisionChanged);
15 Q_PROPERTY(QDateTime date READ date NOTIFY dateChanged)
16 QML_ELEMENT;
17
18public:
19 enum Precision : quint8 {
20 Hours = 1,
21 Minutes = 2,
22 Seconds = 3,
23 };
24 Q_ENUM(Precision);
25
26 explicit Chrono(QObject* parent = nullptr);
27
28 bool enabled() const;
29 void setEnabled(bool enabled);
30
31 Chrono::Precision precision() const;
32 void setPrecision(Chrono::Precision precision);
33
34 Q_INVOKABLE QString format(const QString& fmt) const;
35
36 QDateTime date() const;
37
38signals:
39 void enabledChanged();
40 void precisionChanged();
41 void dateChanged();
42
43private slots:
44 void onTimeout();
45
46private:
47 bool mEnabled = true;
48 Chrono::Precision mPrecision = Chrono::Seconds;
49 QTimer timer;
50 std::chrono::time_point<std::chrono::system_clock> currentTime, targetTime;
51
52 void update();
53 void setTime(const std::chrono::time_point<std::chrono::system_clock>& targetTime);
54 void schedule(const std::chrono::time_point<std::chrono::system_clock>& targetTime);
55};
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp
new file mode 100644
index 00000000..d7051d2a
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp
@@ -0,0 +1,102 @@
1#include "FileSelector.hpp"
2
3#include <sstream>
4#include <vector>
5#include <random>
6#include <algorithm>
7
8#include <iostream>
9#include <format>
10
11namespace fs = std::filesystem;
12
13FileSelector::FileSelector(QObject* parent): QObject(parent) {
14 QObject::connect(&this->timer, &QTimer::timeout, this, &FileSelector::onTimeout);
15 this->timer.setTimerType(Qt::PreciseTimer);
16}
17
18QString FileSelector::directory() const {
19 return QString::fromStdString(this->mDirectory->string());
20}
21void FileSelector::setDirectory(QString directory) {
22 this->mDirectory = directory.toStdString();
23 if (this->mDirectory && this->mEpoch)
24 this->update();
25 emit this->directoryChanged();
26}
27
28unsigned int FileSelector::epoch() const {
29 return std::chrono::duration_cast<std::chrono::milliseconds>(*this->mEpoch).count();
30}
31void FileSelector::setEpoch(unsigned int epoch) {
32 this->mEpoch = std::chrono::milliseconds{epoch};
33 if (this->mDirectory && this->mEpoch)
34 this->update();
35 emit this->epochChanged();
36}
37
38QString FileSelector::seed() const {
39 return this->mSeed;
40}
41void FileSelector::setSeed(QString seed) {
42 this->mSeed = seed;
43 emit this->seedChanged();
44 if (this->mDirectory && this->mEpoch)
45 emit this->selectedChanged();
46}
47
48QString FileSelector::selected() const {
49 if (!this->mDirectory || !this->mEpoch)
50 return QString();
51
52 std::vector<fs::path> shuffled(this->mFiles.begin(), this->mFiles.end());
53 std::sort(shuffled.begin(), shuffled.end());
54
55 auto currentTime = std::chrono::system_clock::now();
56 uint64_t currentEpoch = currentTime.time_since_epoch() / *this->mEpoch;
57 std::chrono::milliseconds timeInEpoch = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime.time_since_epoch()) % *this->mEpoch;
58
59 std::ostringstream seed;
60 seed << this->mSeed.size() << ";" << this->mSeed.toStdString() << ";";
61 seed << *this->mEpoch << ";";
62 seed << currentEpoch << ";";
63 seed << this->mDirectory->string().size() << ";" << *this->mDirectory << ";";
64 seed << this->mFiles.size() << ";";
65 for (const fs::path& p: this->mFiles)
66 seed << p.string().size() << ";" << p << ";";
67
68 std::vector<std::seed_seq::result_type> v;
69 v.reserve(seed.str().size());
70 for (const char& c: seed.str())
71 v.push_back(c);
72
73 std::seed_seq engine_seed(v.begin(), v.end());
74 std::mt19937 g(engine_seed);
75 std::shuffle(shuffled.begin(), shuffled.end(), g);
76
77 std::vector<fs::path>::size_type ix = shuffled.size() * timeInEpoch / *this->mEpoch;
78 return QString::fromStdString((*this->mDirectory / shuffled[ix]).string());
79}
80
81void FileSelector::onTimeout() {
82 if (!this->mFiles.size())
83 return;
84
85 auto currentTime = std::chrono::system_clock::now();
86 uint64_t currentMinorEpoch = currentTime.time_since_epoch() / (*this->mEpoch / this->mFiles.size());
87 auto nextTime = std::chrono::time_point<std::chrono::system_clock>((currentMinorEpoch + 1) * (*this->mEpoch / this->mFiles.size()));
88 this->timer.start(std::chrono::duration_cast<std::chrono::milliseconds>(nextTime - currentTime));
89
90 emit this->selectedChanged();
91}
92
93void FileSelector::update() {
94 this->mFiles = std::set<fs::path>{};
95 for (const fs::directory_entry& entry:
96 fs::recursive_directory_iterator(*this->mDirectory, fs::directory_options::follow_directory_symlink))
97 {
98 this->mFiles.insert(fs::relative(entry, *this->mDirectory));
99 }
100
101 this->onTimeout();
102}
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp
new file mode 100644
index 00000000..72c4f2a7
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp
@@ -0,0 +1,52 @@
1#pragma once
2
3#include <filesystem>
4#include <chrono>
5#include <set>
6#include <optional>
7
8#include <QObject>
9#include <QTimer>
10
11#include <qqmlintegration.h>
12
13class FileSelector : public QObject {
14 Q_OBJECT;
15 Q_PROPERTY(QString directory READ directory WRITE setDirectory NOTIFY directoryChanged REQUIRED);
16 Q_PROPERTY(unsigned int epoch READ epoch WRITE setEpoch NOTIFY epochChanged REQUIRED);
17 Q_PROPERTY(QString seed READ seed WRITE setSeed NOTIFY seedChanged);
18 Q_PROPERTY(QString selected READ selected NOTIFY selectedChanged);
19 QML_ELEMENT;
20
21public:
22 explicit FileSelector(QObject* parent = nullptr);
23
24 QString directory() const;
25 void setDirectory(QString directory);
26
27 unsigned int epoch() const;
28 void setEpoch(unsigned int epoch);
29
30 QString seed() const;
31 void setSeed(QString seed);
32
33 QString selected() const;
34
35signals:
36 void directoryChanged();
37 void epochChanged();
38 void seedChanged();
39 void selectedChanged();
40
41private slots:
42 void onTimeout();
43
44private:
45 std::optional<std::filesystem::path> mDirectory;
46 std::optional<std::chrono::milliseconds> mEpoch;
47 std::set<std::filesystem::path> mFiles;
48 QString mSeed;
49 QTimer timer;
50
51 void update();
52};
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp
new file mode 100644
index 00000000..f6e4dd6e
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp
@@ -0,0 +1,18 @@
1#include "KeePassXC.hpp"
2
3#include <QDBusConnection>
4
5KeePassXC::KeePassXC() {
6 this->service = new DBusKeePassXC(DBusKeePassXC::staticInterfaceName(), "/keepassxc", QDBusConnection::sessionBus(), this);
7}
8KeePassXC::~KeePassXC() {
9 if (this->service)
10 delete this->service;
11}
12
13void KeePassXC::lockAllDatabases() {
14 if (!this->service)
15 return;
16
17 this->service->lockAllDatabases();
18}
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp
new file mode 100644
index 00000000..c4cd71e0
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp
@@ -0,0 +1,21 @@
1#pragma once
2
3#include "dbus_keepassxc.h"
4
5#include <QObject>
6#include <QtQmlIntegration/qqmlintegration.h>
7
8class KeePassXC : public QObject {
9 Q_OBJECT;
10 QML_SINGLETON;
11 QML_ELEMENT;
12
13public:
14 explicit KeePassXC();
15 ~KeePassXC();
16
17 Q_INVOKABLE void lockAllDatabases();
18
19private:
20 DBusKeePassXC* service = nullptr;
21};
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp
new file mode 100644
index 00000000..308659e9
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp
@@ -0,0 +1,198 @@
1#include "Systemd.hpp"
2
3#include <sstream>
4
5#include <QDBusConnection>
6#include <QDBusReply>
7#include <QDebug>
8#include <QDBusUnixFileDescriptor>
9#include <QDBusObjectPath>
10
11Systemd::Systemd(QObject* parent) : QObject(parent) {
12 this->systemdManager = new DBusSystemdManager(
13 "org.freedesktop.systemd1",
14 "/org/freedesktop/systemd1",
15 QDBusConnection::sessionBus(),
16 this
17 );
18 this->logindManager = new DBusLogindManager(
19 "org.freedesktop.login1",
20 "/org/freedesktop/login1",
21 QDBusConnection::systemBus(),
22 this
23 );
24
25 QDBusReply<QDBusObjectPath> sessionPath = this->logindManager->GetSession("auto");
26 qDebug() << sessionPath;
27 this->logindSession = new DBusLogindSession(
28 "org.freedesktop.login1",
29 sessionPath.value().path(),
30 QDBusConnection::systemBus(),
31 this
32 );
33 this->logindSessionProperties = new DBusProperties(
34 "org.freedesktop.login1",
35 sessionPath.value().path(),
36 QDBusConnection::systemBus(),
37 this
38 );
39
40 QObject::connect(this->logindManager, &DBusLogindManager::PrepareForShutdown, this, &Systemd::shutdown);
41 QObject::connect(this->logindManager, &DBusLogindManager::PrepareForSleep, this, &Systemd::sleep);
42 QObject::connect(this->logindSession, &DBusLogindSession::Lock, this, &Systemd::lock);
43 QObject::connect(this->logindSession, &DBusLogindSession::Unlock, this, &Systemd::unlock);
44 QObject::connect(this->logindSessionProperties, &DBusProperties::PropertiesChanged, this, &Systemd::onLogindSessionPropertiesChanged);
45}
46
47void Systemd::onLogindSessionPropertiesChanged(const QString& interface_name, const QVariantMap& changed_properties, const QStringList& invalidated_properties) {
48 if (changed_properties.contains("IdleHint") || invalidated_properties.contains("IdleHint"))
49 emit this->idleHintChanged();
50 if (changed_properties.contains("LockedHint") || invalidated_properties.contains("LockedHint"))
51 emit this->lockedHintChanged();
52}
53
54void Systemd::stopUserUnit(const QString& unit, const QString& mode) { this->systemdManager->StopUnit(unit, mode); }
55
56void Systemd::setBrightness(const QString& subsystem, const QString& name, quint32 brightness) { this->logindSession->SetBrightness(subsystem, name, brightness); }
57
58bool Systemd::idleHint() { return this->logindSession->idleHint(); }
59void Systemd::setIdleHint(bool idle) { this->logindSession->SetIdleHint(idle); }
60bool Systemd::lockedHint() { return this->logindSession->lockedHint(); }
61void Systemd::setLockedHint(bool locked) { this->logindSession->SetLockedHint(locked); }
62void Systemd::lockSession() { this->logindSession->call("Lock"); }
63void Systemd::suspend() { this->logindManager->Suspend(true); }
64void Systemd::hibernate() { this->logindManager->Hibernate(true); }
65
66std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::WhatItem what) {
67 if (what == SystemdInhibitorParams::Shutdown)
68 return "shutdown";
69 else if (what == SystemdInhibitorParams::Sleep)
70 return "sleep";
71 else if (what == SystemdInhibitorParams::Idle)
72 return "idle";
73 else if (what == SystemdInhibitorParams::HandlePowerKey)
74 return "handle-power-key";
75 else if (what == SystemdInhibitorParams::HandleSuspendKey)
76 return "handle-suspend-key";
77 else if (what == SystemdInhibitorParams::HandleHibernateKey)
78 return "handle-hibernate-key";
79 else if (what == SystemdInhibitorParams::HandleLidSwitch)
80 return "handle-lid-switch";
81 return "";
82}
83
84std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::What what) {
85 std::ostringstream res;
86 bool first = true;
87 for (const WhatItem& item: SystemdInhibitorParams::allWhatItems) {
88 if (!(what & item))
89 continue;
90
91 if (!first)
92 res << ":";
93 else
94 first = false;
95 res << SystemdInhibitorParams::toString(item);
96 }
97 return res.str();
98}
99
100std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::Mode mode) {
101 if (mode == SystemdInhibitorParams::Block)
102 return "block";
103 else if (mode == SystemdInhibitorParams::BlockWeak)
104 return "block-weak";
105 else if (mode == SystemdInhibitorParams::Delay)
106 return "delay";
107 return "";
108}
109
110bool SystemdInhibitor::enabled() const { return static_cast<bool>(this->activeInhibitor); }
111void SystemdInhibitor::setEnabled(bool enabled) {
112 this->mEnabled = enabled;
113
114 if (enabled)
115 this->update();
116 else
117 this->release();
118}
119
120SystemdInhibitorParams::What SystemdInhibitor::what() const { return this->mWhat; }
121void SystemdInhibitor::setWhat(SystemdInhibitorParams::What what) {
122 this->mWhat = what;
123 this->update();
124}
125
126QString SystemdInhibitor::who() const { return this->mWho; }
127void SystemdInhibitor::setWho(QString who) {
128 this->mWho = who;
129 this->update();
130}
131
132QString SystemdInhibitor::why() const { return this->mWhy; }
133void SystemdInhibitor::setWhy(QString why) {
134 this->mWhy = why;
135 this->update();
136}
137
138SystemdInhibitorParams::Mode SystemdInhibitor::mode() const { return this->mMode; }
139void SystemdInhibitor::setMode(SystemdInhibitorParams::Mode mode) {
140 this->mMode = mode;
141 this->update();
142}
143
144SystemdInhibitor::ActiveSystemdInhibitor::ActiveSystemdInhibitor(SystemdInhibitorParams::What what_, QString who_, QString why_, SystemdInhibitorParams::Mode mode_): what(what_), who(who_), why(why_), mode(mode_) {
145 DBusLogindManager logindManager(
146 "org.freedesktop.login1",
147 "/org/freedesktop/login1",
148 QDBusConnection::systemBus()
149 );
150 QDBusReply<QDBusUnixFileDescriptor> fd_ = logindManager.Inhibit(QString::fromStdString(SystemdInhibitorParams::toString(what)), who, why, QString::fromStdString(SystemdInhibitorParams::toString(mode)));
151 if (fd_.error().isValid())
152 throw fd_.error();
153 this->fd = ::dup(fd_.value().fileDescriptor());
154}
155SystemdInhibitor::ActiveSystemdInhibitor::~ActiveSystemdInhibitor() {
156 if (this->fd != -1)
157 ::close(this->fd);
158}
159
160void SystemdInhibitor::release() {
161 if (!this->activeInhibitor)
162 return;
163
164 this->activeInhibitor.reset();
165 emit this->enabledChanged();
166}
167
168void SystemdInhibitor::update() {
169 if (!this->mWhat || this->mWho.isEmpty() || this->mWhy.isEmpty() || !this->mMode || !this->mEnabled)
170 if (this->activeInhibitor)
171 this->release();
172 else
173 return;
174
175 if (this->activeInhibitor && this->mWhat == this->activeInhibitor->what && this->mWho == this->activeInhibitor->who && this->mWhy == this->activeInhibitor->why && this->mMode == this->activeInhibitor->mode)
176 return;
177
178 std::unique_ptr<ActiveSystemdInhibitor> otherInhibitor;
179 try {
180 otherInhibitor.reset(new SystemdInhibitor::ActiveSystemdInhibitor(this->mWhat, this->mWho, this->mWhy, this->mMode));
181 } catch (const QDBusError& err) {
182 qCritical().noquote()
183 << err.name().toStdString() << " " << err.message().toStdString();
184 return;
185 }
186 this->activeInhibitor.swap(otherInhibitor);
187
188 if (!otherInhibitor && this->activeInhibitor)
189 emit this->enabledChanged();
190 if (otherInhibitor && otherInhibitor->what != this->activeInhibitor->what)
191 emit this->whatChanged();
192 if (otherInhibitor && otherInhibitor->who != this->activeInhibitor->who)
193 emit this->whoChanged();
194 if (otherInhibitor && otherInhibitor->why != this->activeInhibitor->why)
195 emit this->whyChanged();
196 if (otherInhibitor && otherInhibitor->mode != this->activeInhibitor->mode)
197 emit this->modeChanged();
198}
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp
new file mode 100644
index 00000000..615024d2
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp
@@ -0,0 +1,147 @@
1#pragma once
2
3#include <cstdint>
4#include <memory>
5#include <string>
6
7#include <QObject>
8#include <QDBusInterface>
9#include <QtQmlIntegration/qqmlintegration.h>
10
11#include "dbus_systemd_manager.h"
12#include "dbus_logind_manager.h"
13#include "dbus_logind_session.h"
14#include "dbus_properties.h"
15
16class Systemd : public QObject {
17 Q_OBJECT;
18 QML_SINGLETON;
19 QML_ELEMENT;
20
21public:
22 explicit Systemd(QObject* parent = nullptr);
23
24 Q_PROPERTY(bool idleHint READ idleHint WRITE setIdleHint NOTIFY idleHintChanged)
25 Q_PROPERTY(bool lockedHint READ lockedHint WRITE setLockedHint NOTIFY lockedHintChanged)
26
27 Q_INVOKABLE void stopUserUnit(const QString& unit, const QString& mode);
28 Q_INVOKABLE void setBrightness(const QString& subsystem, const QString& name, quint32 brightness);
29 Q_INVOKABLE void setIdleHint(bool idle);
30 Q_INVOKABLE void setLockedHint(bool locked);
31 Q_INVOKABLE void lockSession();
32 Q_INVOKABLE void suspend();
33 Q_INVOKABLE void hibernate();
34
35 bool idleHint();
36 bool lockedHint();
37
38signals:
39 void shutdown(bool before);
40 void sleep(bool before);
41 void lock();
42 void unlock();
43 void idleHintChanged();
44 void lockedHintChanged();
45
46private slots:
47 void onLogindSessionPropertiesChanged(const QString& interface_name, const QVariantMap& changed_properties, const QStringList& invalidated_properties);
48
49private:
50 DBusSystemdManager* systemdManager;
51 DBusLogindManager* logindManager;
52 DBusLogindSession* logindSession;
53 DBusProperties* logindSessionProperties;
54};
55
56class SystemdInhibitorParams : public QObject {
57 Q_OBJECT;
58 QML_ELEMENT;
59 QML_SINGLETON;
60
61public:
62 enum WhatItem : uint8_t {
63 Shutdown = 0b1,
64 Sleep = 0b10,
65 Idle = 0b100,
66 HandlePowerKey = 0b1000,
67 HandleSuspendKey = 0b10000,
68 HandleHibernateKey = 0b100000,
69 HandleLidSwitch = 0b1000000,
70 };
71 Q_ENUM(WhatItem);
72 Q_DECLARE_FLAGS(What, WhatItem);
73
74 enum Mode : uint8_t {
75 Block = 1,
76 BlockWeak = 2,
77 Delay = 3,
78 };
79 Q_ENUM(Mode);
80
81 Q_INVOKABLE static std::string toString(WhatItem what);
82 Q_INVOKABLE static std::string toString(What what);
83 Q_INVOKABLE static std::string toString(Mode mode);
84
85 static constexpr WhatItem allWhatItems[] = { Shutdown, Sleep, Idle, HandlePowerKey, HandleSuspendKey, HandleHibernateKey, HandleLidSwitch };
86};
87Q_DECLARE_OPERATORS_FOR_FLAGS(SystemdInhibitorParams::What)
88
89class SystemdInhibitor : public QObject {
90 Q_OBJECT;
91 Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged);
92 Q_PROPERTY(SystemdInhibitorParams::What what READ what WRITE setWhat NOTIFY whatChanged);
93 Q_PROPERTY(QString who READ who WRITE setWho NOTIFY whoChanged);
94 Q_PROPERTY(QString why READ why WRITE setWhy NOTIFY whyChanged);
95 Q_PROPERTY(SystemdInhibitorParams::Mode mode READ mode WRITE setMode NOTIFY modeChanged);
96 QML_ELEMENT;
97
98public:
99 explicit SystemdInhibitor(QObject* parent = nullptr): QObject(parent) {}
100
101 bool enabled() const;
102 void setEnabled(bool enabled);
103
104 SystemdInhibitorParams::What what() const;
105 void setWhat(SystemdInhibitorParams::What what);
106
107 QString who() const;
108 void setWho(QString who);
109
110 QString why() const;
111 void setWhy(QString why);
112
113 SystemdInhibitorParams::Mode mode() const;
114 void setMode(SystemdInhibitorParams::Mode mode);
115
116 Q_INVOKABLE void release();
117
118signals:
119 void enabledChanged();
120 void whatChanged();
121 void whoChanged();
122 void whyChanged();
123 void modeChanged();
124
125private:
126 class ActiveSystemdInhibitor {
127 public:
128 uint32_t fd = -1;
129 SystemdInhibitorParams::What what;
130 QString who;
131 QString why;
132 SystemdInhibitorParams::Mode mode;
133
134 ActiveSystemdInhibitor(SystemdInhibitorParams::What what_, QString who_, QString why_, SystemdInhibitorParams::Mode mode_);
135 ~ActiveSystemdInhibitor();
136 };
137
138 void update();
139
140 bool mEnabled = true;
141 std::unique_ptr<ActiveSystemdInhibitor> activeInhibitor;
142 SystemdInhibitorParams::What mWhat = static_cast<SystemdInhibitorParams::What>(0);
143 QString mWho;
144 QString mWhy;
145 SystemdInhibitorParams::Mode mMode = static_cast<SystemdInhibitorParams::Mode>(0);
146};
147
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
3class 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..20a195eb
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/default.nix
@@ -0,0 +1,30 @@
1{ lib
2, stdenv
3, cmake
4, qt6
5, fmt
6, keepassxc
7, systemd
8}:
9
10stdenv.mkDerivation rec {
11 name = "quickshell-custom";
12
13 src = ./.;
14
15 prePatch = ''
16 cp ${keepassxc.src}/src/gui/org.keepassxc.KeePassXC.MainWindow.xml .
17 '';
18
19 nativeBuildInputs = [ cmake qt6.wrapQtAppsHook ];
20 buildInputs = [
21 qt6.qtbase
22 qt6.qtdeclarative
23 ];
24
25 cmakeFlags = [
26 (lib.cmakeFeature "INSTALL_QML_PREFIX" qt6.qtbase.qtQmlPrefix)
27 ];
28
29 LC_ALL = "C.UTF-8";
30}
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml
new file mode 100644
index 00000000..7588e7a5
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml
@@ -0,0 +1,28 @@
1<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
2"https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
3<node>
4 <interface name="org.freedesktop.DBus.Properties">
5 <method name="Get">
6 <arg name="interface_name" direction="in" type="s"/>
7 <arg name="property_name" direction="in" type="s"/>
8 <arg name="value" direction="out" type="v"/>
9 </method>
10 <method name="GetAll">
11 <arg name="interface_name" direction="in" type="s"/>
12 <arg name="props" direction="out" type="a{sv}"/>
13 <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
14 </method>
15 <method name="Set">
16 <arg name="interface_name" direction="in" type="s"/>
17 <arg name="property_name" direction="in" type="s"/>
18 <arg name="value" direction="in" type="v"/>
19 </method>
20 <signal name="PropertiesChanged">
21 <arg type="s" name="interface_name"/>
22 <arg type="a{sv}" name="changed_properties"/>
23 <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
24 <arg type="as" name="invalidated_properties"/>
25 </signal>
26 </interface>
27</node>
28
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml
new file mode 100644
index 00000000..120a06d9
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml
@@ -0,0 +1,445 @@
1<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
2"https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
3<node>
4 <interface name="org.freedesktop.login1.Manager">
5 <property name="EnableWallMessages" type="b" access="readwrite">
6 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
7 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
8 </property>
9 <property name="WallMessage" type="s" access="readwrite">
10 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
11 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
12 </property>
13 <property name="NAutoVTs" type="u" access="read">
14 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
15 </property>
16 <property name="KillOnlyUsers" type="as" access="read">
17 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
18 </property>
19 <property name="KillExcludeUsers" type="as" access="read">
20 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
21 </property>
22 <property name="KillUserProcesses" type="b" access="read">
23 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
24 </property>
25 <property name="RebootParameter" type="s" access="read">
26 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
27 </property>
28 <property name="RebootToFirmwareSetup" type="b" access="read">
29 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
30 </property>
31 <property name="RebootToBootLoaderMenu" type="t" access="read">
32 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
33 </property>
34 <property name="RebootToBootLoaderEntry" type="s" access="read">
35 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
36 </property>
37 <property name="BootLoaderEntries" type="as" access="read">
38 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
39 </property>
40 <property name="IdleHint" type="b" access="read">
41 </property>
42 <property name="IdleSinceHint" type="t" access="read">
43 </property>
44 <property name="IdleSinceHintMonotonic" type="t" access="read">
45 </property>
46 <property name="BlockInhibited" type="s" access="read">
47 </property>
48 <property name="BlockWeakInhibited" type="s" access="read">
49 </property>
50 <property name="DelayInhibited" type="s" access="read">
51 </property>
52 <property name="InhibitDelayMaxUSec" type="t" access="read">
53 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
54 </property>
55 <property name="UserStopDelayUSec" type="t" access="read">
56 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
57 </property>
58 <property name="SleepOperation" type="as" access="read">
59 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
60 </property>
61 <property name="HandlePowerKey" type="s" access="read">
62 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
63 </property>
64 <property name="HandlePowerKeyLongPress" type="s" access="read">
65 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
66 </property>
67 <property name="HandleRebootKey" type="s" access="read">
68 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
69 </property>
70 <property name="HandleRebootKeyLongPress" type="s" access="read">
71 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
72 </property>
73 <property name="HandleSuspendKey" type="s" access="read">
74 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
75 </property>
76 <property name="HandleSuspendKeyLongPress" type="s" access="read">
77 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
78 </property>
79 <property name="HandleHibernateKey" type="s" access="read">
80 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
81 </property>
82 <property name="HandleHibernateKeyLongPress" type="s" access="read">
83 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
84 </property>
85 <property name="HandleLidSwitch" type="s" access="read">
86 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
87 </property>
88 <property name="HandleLidSwitchExternalPower" type="s" access="read">
89 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
90 </property>
91 <property name="HandleLidSwitchDocked" type="s" access="read">
92 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
93 </property>
94 <property name="HandleSecureAttentionKey" type="s" access="read">
95 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
96 </property>
97 <property name="HoldoffTimeoutUSec" type="t" access="read">
98 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
99 </property>
100 <property name="IdleAction" type="s" access="read">
101 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
102 </property>
103 <property name="IdleActionUSec" type="t" access="read">
104 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
105 </property>
106 <property name="PreparingForShutdown" type="b" access="read">
107 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
108 </property>
109 <property name="PreparingForShutdownWithMetadata" type="a{sv}" access="read">
110 <annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantMap"/>
111 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
112 </property>
113 <property name="PreparingForSleep" type="b" access="read">
114 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
115 </property>
116 <!-- <property name="ScheduledShutdown" type="(st)" access="read"> -->
117 <!-- </property> -->
118 <property name="DesignatedMaintenanceTime" type="s" access="read">
119 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
120 </property>
121 <property name="Docked" type="b" access="read">
122 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
123 </property>
124 <property name="LidClosed" type="b" access="read">
125 </property>
126 <property name="OnExternalPower" type="b" access="read">
127 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
128 </property>
129 <property name="RemoveIPC" type="b" access="read">
130 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
131 </property>
132 <property name="RuntimeDirectorySize" type="t" access="read">
133 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
134 </property>
135 <property name="RuntimeDirectoryInodesMax" type="t" access="read">
136 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
137 </property>
138 <property name="InhibitorsMax" type="t" access="read">
139 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
140 </property>
141 <property name="NCurrentInhibitors" type="t" access="read">
142 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
143 </property>
144 <property name="SessionsMax" type="t" access="read">
145 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
146 </property>
147 <property name="NCurrentSessions" type="t" access="read">
148 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
149 </property>
150 <property name="StopIdleSessionUSec" type="t" access="read">
151 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
152 </property>
153 <method name="GetSession">
154 <arg type="s" name="session_id" direction="in"/>
155 <arg type="o" name="object_path" direction="out"/>
156 </method>
157 <method name="GetSessionByPID">
158 <arg type="u" name="pid" direction="in"/>
159 <arg type="o" name="object_path" direction="out"/>
160 </method>
161 <method name="GetUser">
162 <arg type="u" name="uid" direction="in"/>
163 <arg type="o" name="object_path" direction="out"/>
164 </method>
165 <method name="GetUserByPID">
166 <arg type="u" name="pid" direction="in"/>
167 <arg type="o" name="object_path" direction="out"/>
168 </method>
169 <method name="GetSeat">
170 <arg type="s" name="seat_id" direction="in"/>
171 <arg type="o" name="object_path" direction="out"/>
172 </method>
173 <!-- <method name="ListSessions"> -->
174 <!-- <arg type="a(susso)" name="sessions" direction="out"/> -->
175 <!-- </method> -->
176 <!-- <method name="ListSessionsEx"> -->
177 <!-- <arg type="a(sussussbto)" name="sessions" direction="out"/> -->
178 <!-- </method> -->
179 <!-- <method name="ListUsers"> -->
180 <!-- <arg type="a(uso)" name="users" direction="out"/> -->
181 <!-- </method> -->
182 <!-- <method name="ListSeats"> -->
183 <!-- <arg type="a(so)" name="seats" direction="out"/> -->
184 <!-- </method> -->
185 <!-- <method name="ListInhibitors"> -->
186 <!-- <arg type="a(ssssuu)" name="inhibitors" direction="out"/> -->
187 <!-- </method> -->
188 <!-- <method name="CreateSession"> -->
189 <!-- <arg type="u" name="uid" direction="in"/> -->
190 <!-- <arg type="u" name="pid" direction="in"/> -->
191 <!-- <arg type="s" name="service" direction="in"/> -->
192 <!-- <arg type="s" name="type" direction="in"/> -->
193 <!-- <arg type="s" name="class" direction="in"/> -->
194 <!-- <arg type="s" name="desktop" direction="in"/> -->
195 <!-- <arg type="s" name="seat_id" direction="in"/> -->
196 <!-- <arg type="u" name="vtnr" direction="in"/> -->
197 <!-- <arg type="s" name="tty" direction="in"/> -->
198 <!-- <arg type="s" name="display" direction="in"/> -->
199 <!-- <arg type="b" name="remote" direction="in"/> -->
200 <!-- <arg type="s" name="remote_user" direction="in"/> -->
201 <!-- <arg type="s" name="remote_host" direction="in"/> -->
202 <!-- <arg type="a(sv)" name="properties" direction="in"/> -->
203 <!-- <arg type="s" name="session_id" direction="out"/> -->
204 <!-- <arg type="o" name="object_path" direction="out"/> -->
205 <!-- <arg type="s" name="runtime_path" direction="out"/> -->
206 <!-- <arg type="h" name="fifo_fd" direction="out"/> -->
207 <!-- <arg type="u" name="uid" direction="out"/> -->
208 <!-- <arg type="s" name="seat_id" direction="out"/> -->
209 <!-- <arg type="u" name="vtnr" direction="out"/> -->
210 <!-- <arg type="b" name="existing" direction="out"/> -->
211 <!-- <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> -->
212 <!-- </method> -->
213 <!-- <method name="CreateSessionWithPIDFD"> -->
214 <!-- <arg type="u" name="uid" direction="in"/> -->
215 <!-- <arg type="h" name="pidfd" direction="in"/> -->
216 <!-- <arg type="s" name="service" direction="in"/> -->
217 <!-- <arg type="s" name="type" direction="in"/> -->
218 <!-- <arg type="s" name="class" direction="in"/> -->
219 <!-- <arg type="s" name="desktop" direction="in"/> -->
220 <!-- <arg type="s" name="seat_id" direction="in"/> -->
221 <!-- <arg type="u" name="vtnr" direction="in"/> -->
222 <!-- <arg type="s" name="tty" direction="in"/> -->
223 <!-- <arg type="s" name="display" direction="in"/> -->
224 <!-- <arg type="b" name="remote" direction="in"/> -->
225 <!-- <arg type="s" name="remote_user" direction="in"/> -->
226 <!-- <arg type="s" name="remote_host" direction="in"/> -->
227 <!-- <arg type="t" name="flags" direction="in"/> -->
228 <!-- <arg type="a(sv)" name="properties" direction="in"/> -->
229 <!-- <arg type="s" name="session_id" direction="out"/> -->
230 <!-- <arg type="o" name="object_path" direction="out"/> -->
231 <!-- <arg type="s" name="runtime_path" direction="out"/> -->
232 <!-- <arg type="h" name="fifo_fd" direction="out"/> -->
233 <!-- <arg type="u" name="uid" direction="out"/> -->
234 <!-- <arg type="s" name="seat_id" direction="out"/> -->
235 <!-- <arg type="u" name="vtnr" direction="out"/> -->
236 <!-- <arg type="b" name="existing" direction="out"/> -->
237 <!-- <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> -->
238 <!-- </method> -->
239 <method name="ReleaseSession">
240 <arg type="s" name="session_id" direction="in"/>
241 </method>
242 <method name="ActivateSession">
243 <arg type="s" name="session_id" direction="in"/>
244 </method>
245 <method name="ActivateSessionOnSeat">
246 <arg type="s" name="session_id" direction="in"/>
247 <arg type="s" name="seat_id" direction="in"/>
248 </method>
249 <method name="LockSession">
250 <arg type="s" name="session_id" direction="in"/>
251 </method>
252 <method name="UnlockSession">
253 <arg type="s" name="session_id" direction="in"/>
254 </method>
255 <method name="LockSessions">
256 </method>
257 <method name="UnlockSessions">
258 </method>
259 <method name="KillSession">
260 <arg type="s" name="session_id" direction="in"/>
261 <arg type="s" name="whom" direction="in"/>
262 <arg type="i" name="signal_number" direction="in"/>
263 </method>
264 <method name="KillUser">
265 <arg type="u" name="uid" direction="in"/>
266 <arg type="i" name="signal_number" direction="in"/>
267 </method>
268 <method name="TerminateSession">
269 <arg type="s" name="session_id" direction="in"/>
270 </method>
271 <method name="TerminateUser">
272 <arg type="u" name="uid" direction="in"/>
273 </method>
274 <method name="TerminateSeat">
275 <arg type="s" name="seat_id" direction="in"/>
276 </method>
277 <method name="SetUserLinger">
278 <arg type="u" name="uid" direction="in"/>
279 <arg type="b" name="enable" direction="in"/>
280 <arg type="b" name="interactive" direction="in"/>
281 </method>
282 <method name="AttachDevice">
283 <arg type="s" name="seat_id" direction="in"/>
284 <arg type="s" name="sysfs_path" direction="in"/>
285 <arg type="b" name="interactive" direction="in"/>
286 </method>
287 <method name="FlushDevices">
288 <arg type="b" name="interactive" direction="in"/>
289 </method>
290 <method name="PowerOff">
291 <arg type="b" name="interactive" direction="in"/>
292 </method>
293 <method name="PowerOffWithFlags">
294 <arg type="t" name="flags" direction="in"/>
295 </method>
296 <method name="Reboot">
297 <arg type="b" name="interactive" direction="in"/>
298 </method>
299 <method name="RebootWithFlags">
300 <arg type="t" name="flags" direction="in"/>
301 </method>
302 <method name="Halt">
303 <arg type="b" name="interactive" direction="in"/>
304 </method>
305 <method name="HaltWithFlags">
306 <arg type="t" name="flags" direction="in"/>
307 </method>
308 <method name="Suspend">
309 <arg type="b" name="interactive" direction="in"/>
310 </method>
311 <method name="SuspendWithFlags">
312 <arg type="t" name="flags" direction="in"/>
313 </method>
314 <method name="Hibernate">
315 <arg type="b" name="interactive" direction="in"/>
316 </method>
317 <method name="HibernateWithFlags">
318 <arg type="t" name="flags" direction="in"/>
319 </method>
320 <method name="HybridSleep">
321 <arg type="b" name="interactive" direction="in"/>
322 </method>
323 <method name="HybridSleepWithFlags">
324 <arg type="t" name="flags" direction="in"/>
325 </method>
326 <method name="SuspendThenHibernate">
327 <arg type="b" name="interactive" direction="in"/>
328 </method>
329 <method name="SuspendThenHibernateWithFlags">
330 <arg type="t" name="flags" direction="in"/>
331 </method>
332 <method name="Sleep">
333 <arg type="t" name="flags" direction="in"/>
334 </method>
335 <method name="CanPowerOff">
336 <arg type="s" name="result" direction="out"/>
337 </method>
338 <method name="CanReboot">
339 <arg type="s" name="result" direction="out"/>
340 </method>
341 <method name="CanHalt">
342 <arg type="s" name="result" direction="out"/>
343 </method>
344 <method name="CanSuspend">
345 <arg type="s" name="result" direction="out"/>
346 </method>
347 <method name="CanHibernate">
348 <arg type="s" name="result" direction="out"/>
349 </method>
350 <method name="CanHybridSleep">
351 <arg type="s" name="result" direction="out"/>
352 </method>
353 <method name="CanSuspendThenHibernate">
354 <arg type="s" name="result" direction="out"/>
355 </method>
356 <method name="CanSleep">
357 <arg type="s" name="result" direction="out"/>
358 </method>
359 <method name="ScheduleShutdown">
360 <arg type="s" name="type" direction="in"/>
361 <arg type="t" name="usec" direction="in"/>
362 </method>
363 <method name="CancelScheduledShutdown">
364 <arg type="b" name="cancelled" direction="out"/>
365 </method>
366 <method name="Inhibit">
367 <arg type="s" name="what" direction="in"/>
368 <arg type="s" name="who" direction="in"/>
369 <arg type="s" name="why" direction="in"/>
370 <arg type="s" name="mode" direction="in"/>
371 <arg type="h" name="pipe_fd" direction="out"/>
372 </method>
373 <method name="CanRebootParameter">
374 <arg type="s" name="result" direction="out"/>
375 </method>
376 <method name="SetRebootParameter">
377 <arg type="s" name="parameter" direction="in"/>
378 </method>
379 <method name="CanRebootToFirmwareSetup">
380 <arg type="s" name="result" direction="out"/>
381 </method>
382 <method name="SetRebootToFirmwareSetup">
383 <arg type="b" name="enable" direction="in"/>
384 </method>
385 <method name="CanRebootToBootLoaderMenu">
386 <arg type="s" name="result" direction="out"/>
387 </method>
388 <method name="SetRebootToBootLoaderMenu">
389 <arg type="t" name="timeout" direction="in"/>
390 </method>
391 <method name="CanRebootToBootLoaderEntry">
392 <arg type="s" name="result" direction="out"/>
393 </method>
394 <method name="SetRebootToBootLoaderEntry">
395 <arg type="s" name="boot_loader_entry" direction="in"/>
396 </method>
397 <method name="SetWallMessage">
398 <arg type="s" name="wall_message" direction="in"/>
399 <arg type="b" name="enable" direction="in"/>
400 </method>
401 <signal name="SecureAttentionKey">
402 <arg type="s" name="seat_id"/>
403 <arg type="o" name="object_path"/>
404 </signal>
405 <signal name="SessionNew">
406 <arg type="s" name="session_id"/>
407 <arg type="o" name="object_path"/>
408 </signal>
409 <signal name="SessionRemoved">
410 <arg type="s" name="session_id"/>
411 <arg type="o" name="object_path"/>
412 </signal>
413 <signal name="UserNew">
414 <arg type="u" name="uid"/>
415 <arg type="o" name="object_path"/>
416 </signal>
417 <signal name="UserRemoved">
418 <arg type="u" name="uid"/>
419 <arg type="o" name="object_path"/>
420 </signal>
421 <signal name="SeatNew">
422 <arg type="s" name="seat_id"/>
423 <arg type="o" name="object_path"/>
424 </signal>
425 <signal name="SeatRemoved">
426 <arg type="s" name="seat_id"/>
427 <arg type="o" name="object_path"/>
428 </signal>
429 <signal name="PrepareForShutdown">
430 <arg type="b" name="start"/>
431 </signal>
432 <signal name="PrepareForShutdownWithMetadata">
433 <arg type="b" name="start"/>
434 <arg type="a{sv}" name="metadata"/>
435 <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
436 </signal>
437 <signal name="PrepareForSleep">
438 <arg type="b" name="start"/>
439 </signal>
440 </interface>
441 <node name="user"/>
442 <node name="session"/>
443 <node name="seat"/>
444</node>
445
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml
new file mode 100644
index 00000000..7d6fc8ee
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml
@@ -0,0 +1,146 @@
1<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
2"https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
3<node>
4 <interface name="org.freedesktop.login1.Session">
5 <property name="Id" type="s" access="read">
6 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
7 </property>
8 <property name="User" type="o" access="read">
9 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
10 </property>
11 <property name="Name" type="s" access="read">
12 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
13 </property>
14 <property name="Timestamp" type="t" access="read">
15 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
16 </property>
17 <property name="TimestampMonotonic" type="t" access="read">
18 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
19 </property>
20 <property name="VTNr" type="u" access="read">
21 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
22 </property>
23 <property name="Seat" type="o" access="read">
24 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
25 </property>
26 <property name="TTY" type="s" access="read">
27 </property>
28 <property name="Display" type="s" access="read">
29 </property>
30 <property name="Remote" type="b" access="read">
31 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
32 </property>
33 <property name="RemoteHost" type="s" access="read">
34 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
35 </property>
36 <property name="RemoteUser" type="s" access="read">
37 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
38 </property>
39 <property name="Service" type="s" access="read">
40 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
41 </property>
42 <property name="Desktop" type="s" access="read">
43 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
44 </property>
45 <property name="Scope" type="s" access="read">
46 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
47 </property>
48 <property name="Leader" type="u" access="read">
49 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
50 </property>
51 <property name="Audit" type="u" access="read">
52 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
53 </property>
54 <property name="Type" type="s" access="read">
55 </property>
56 <!-- <property name="Class" type="s" access="read"> -->
57 <!-- </property> -->
58 <property name="Active" type="b" access="read">
59 </property>
60 <property name="State" type="s" access="read">
61 </property>
62 <property name="IdleHint" type="b" access="read">
63 </property>
64 <property name="IdleSinceHint" type="t" access="read">
65 </property>
66 <property name="IdleSinceHintMonotonic" type="t" access="read">
67 </property>
68 <property name="CanIdle" type="b" access="read">
69 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
70 </property>
71 <property name="CanLock" type="b" access="read">
72 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
73 </property>
74 <property name="LockedHint" type="b" access="read">
75 </property>
76 <method name="Terminate">
77 </method>
78 <method name="Activate">
79 </method>
80 <!-- <method name="Lock"> -->
81 <!-- </method> -->
82 <!-- <method name="Unlock"> -->
83 <!-- </method> -->
84 <method name="SetIdleHint">
85 <arg type="b" name="idle" direction="in"/>
86 </method>
87 <method name="SetLockedHint">
88 <arg type="b" name="locked" direction="in"/>
89 </method>
90 <method name="Kill">
91 <arg type="s" name="whom" direction="in"/>
92 <arg type="i" name="signal_number" direction="in"/>
93 </method>
94 <method name="TakeControl">
95 <arg type="b" name="force" direction="in"/>
96 </method>
97 <method name="ReleaseControl">
98 </method>
99 <method name="SetType">
100 <arg type="s" name="type" direction="in"/>
101 </method>
102 <!-- <method name="SetClass"> -->
103 <!-- <arg type="s" name="class" direction="in"/> -->
104 <!-- </method> -->
105 <method name="SetDisplay">
106 <arg type="s" name="display" direction="in"/>
107 </method>
108 <method name="SetTTY">
109 <arg type="h" name="tty_fd" direction="in"/>
110 </method>
111 <method name="TakeDevice">
112 <arg type="u" name="major" direction="in"/>
113 <arg type="u" name="minor" direction="in"/>
114 <arg type="h" name="fd" direction="out"/>
115 <arg type="b" name="inactive" direction="out"/>
116 </method>
117 <method name="ReleaseDevice">
118 <arg type="u" name="major" direction="in"/>
119 <arg type="u" name="minor" direction="in"/>
120 </method>
121 <method name="PauseDeviceComplete">
122 <arg type="u" name="major" direction="in"/>
123 <arg type="u" name="minor" direction="in"/>
124 </method>
125 <method name="SetBrightness">
126 <arg type="s" name="subsystem" direction="in"/>
127 <arg type="s" name="name" direction="in"/>
128 <arg type="u" name="brightness" direction="in"/>
129 </method>
130 <signal name="PauseDevice">
131 <arg type="u" name="major"/>
132 <arg type="u" name="minor"/>
133 <arg type="s" name="type"/>
134 </signal>
135 <signal name="ResumeDevice">
136 <arg type="u" name="major"/>
137 <arg type="u" name="minor"/>
138 <arg type="h" name="fd"/>
139 </signal>
140 <signal name="Lock">
141 </signal>
142 <signal name="Unlock">
143 </signal>
144 </interface>
145</node>
146
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml
new file mode 100644
index 00000000..b4f84a13
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml
@@ -0,0 +1,817 @@
1<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
2"https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
3<node>
4 <interface name="org.freedesktop.systemd1.Manager">
5 <property name="Version" type="s" access="read">
6 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
7 </property>
8 <property name="Features" type="s" access="read">
9 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
10 </property>
11 <property name="Virtualization" type="s" access="read">
12 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
13 </property>
14 <property name="ConfidentialVirtualization" type="s" access="read">
15 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
16 </property>
17 <property name="Architecture" type="s" access="read">
18 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
19 </property>
20 <property name="Tainted" type="s" access="read">
21 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
22 </property>
23 <property name="FirmwareTimestamp" type="t" access="read">
24 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
25 </property>
26 <property name="FirmwareTimestampMonotonic" type="t" access="read">
27 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
28 </property>
29 <property name="LoaderTimestamp" type="t" access="read">
30 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
31 </property>
32 <property name="LoaderTimestampMonotonic" type="t" access="read">
33 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
34 </property>
35 <property name="KernelTimestamp" type="t" access="read">
36 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
37 </property>
38 <property name="KernelTimestampMonotonic" type="t" access="read">
39 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
40 </property>
41 <property name="InitRDTimestamp" type="t" access="read">
42 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
43 </property>
44 <property name="InitRDTimestampMonotonic" type="t" access="read">
45 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
46 </property>
47 <property name="UserspaceTimestamp" type="t" access="read">
48 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
49 </property>
50 <property name="UserspaceTimestampMonotonic" type="t" access="read">
51 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
52 </property>
53 <property name="FinishTimestamp" type="t" access="read">
54 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
55 </property>
56 <property name="FinishTimestampMonotonic" type="t" access="read">
57 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
58 </property>
59 <property name="ShutdownStartTimestamp" type="t" access="read">
60 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
61 </property>
62 <property name="ShutdownStartTimestampMonotonic" type="t" access="read">
63 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
64 </property>
65 <property name="SecurityStartTimestamp" type="t" access="read">
66 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
67 </property>
68 <property name="SecurityStartTimestampMonotonic" type="t" access="read">
69 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
70 </property>
71 <property name="SecurityFinishTimestamp" type="t" access="read">
72 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
73 </property>
74 <property name="SecurityFinishTimestampMonotonic" type="t" access="read">
75 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
76 </property>
77 <property name="GeneratorsStartTimestamp" type="t" access="read">
78 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
79 </property>
80 <property name="GeneratorsStartTimestampMonotonic" type="t" access="read">
81 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
82 </property>
83 <property name="GeneratorsFinishTimestamp" type="t" access="read">
84 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
85 </property>
86 <property name="GeneratorsFinishTimestampMonotonic" type="t" access="read">
87 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
88 </property>
89 <property name="UnitsLoadStartTimestamp" type="t" access="read">
90 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
91 </property>
92 <property name="UnitsLoadStartTimestampMonotonic" type="t" access="read">
93 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
94 </property>
95 <property name="UnitsLoadFinishTimestamp" type="t" access="read">
96 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
97 </property>
98 <property name="UnitsLoadFinishTimestampMonotonic" type="t" access="read">
99 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
100 </property>
101 <property name="UnitsLoadTimestamp" type="t" access="read">
102 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
103 </property>
104 <property name="UnitsLoadTimestampMonotonic" type="t" access="read">
105 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
106 </property>
107 <property name="InitRDSecurityStartTimestamp" type="t" access="read">
108 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
109 </property>
110 <property name="InitRDSecurityStartTimestampMonotonic" type="t" access="read">
111 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
112 </property>
113 <property name="InitRDSecurityFinishTimestamp" type="t" access="read">
114 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
115 </property>
116 <property name="InitRDSecurityFinishTimestampMonotonic" type="t" access="read">
117 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
118 </property>
119 <property name="InitRDGeneratorsStartTimestamp" type="t" access="read">
120 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
121 </property>
122 <property name="InitRDGeneratorsStartTimestampMonotonic" type="t" access="read">
123 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
124 </property>
125 <property name="InitRDGeneratorsFinishTimestamp" type="t" access="read">
126 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
127 </property>
128 <property name="InitRDGeneratorsFinishTimestampMonotonic" type="t" access="read">
129 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
130 </property>
131 <property name="InitRDUnitsLoadStartTimestamp" type="t" access="read">
132 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
133 </property>
134 <property name="InitRDUnitsLoadStartTimestampMonotonic" type="t" access="read">
135 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
136 </property>
137 <property name="InitRDUnitsLoadFinishTimestamp" type="t" access="read">
138 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
139 </property>
140 <property name="InitRDUnitsLoadFinishTimestampMonotonic" type="t" access="read">
141 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
142 </property>
143 <property name="LogLevel" type="s" access="readwrite">
144 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
145 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
146 </property>
147 <property name="LogTarget" type="s" access="readwrite">
148 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
149 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
150 </property>
151 <property name="NNames" type="u" access="read">
152 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
153 </property>
154 <property name="NFailedUnits" type="u" access="read">
155 </property>
156 <property name="NJobs" type="u" access="read">
157 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
158 </property>
159 <property name="NInstalledJobs" type="u" access="read">
160 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
161 </property>
162 <property name="NFailedJobs" type="u" access="read">
163 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
164 </property>
165 <property name="Progress" type="d" access="read">
166 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
167 </property>
168 <property name="Environment" type="as" access="read">
169 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
170 </property>
171 <property name="ConfirmSpawn" type="b" access="read">
172 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
173 </property>
174 <property name="ShowStatus" type="b" access="read">
175 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
176 </property>
177 <property name="UnitPath" type="as" access="read">
178 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
179 </property>
180 <property name="DefaultStandardOutput" type="s" access="read">
181 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
182 </property>
183 <property name="DefaultStandardError" type="s" access="read">
184 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
185 </property>
186 <property name="WatchdogDevice" type="s" access="read">
187 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
188 </property>
189 <property name="WatchdogLastPingTimestamp" type="t" access="read">
190 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
191 </property>
192 <property name="WatchdogLastPingTimestampMonotonic" type="t" access="read">
193 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
194 </property>
195 <property name="RuntimeWatchdogUSec" type="t" access="readwrite">
196 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
197 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
198 </property>
199 <property name="RuntimeWatchdogPreUSec" type="t" access="readwrite">
200 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
201 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
202 </property>
203 <property name="RuntimeWatchdogPreGovernor" type="s" access="readwrite">
204 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
205 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
206 </property>
207 <property name="RebootWatchdogUSec" type="t" access="readwrite">
208 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
209 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
210 </property>
211 <property name="KExecWatchdogUSec" type="t" access="readwrite">
212 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
213 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
214 </property>
215 <property name="ServiceWatchdogs" type="b" access="readwrite">
216 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
217 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
218 </property>
219 <property name="ControlGroup" type="s" access="read">
220 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
221 </property>
222 <property name="SystemState" type="s" access="read">
223 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
224 </property>
225 <property name="ExitCode" type="y" access="read">
226 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
227 </property>
228 <property name="DefaultTimerAccuracyUSec" type="t" access="read">
229 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
230 </property>
231 <property name="DefaultTimeoutStartUSec" type="t" access="read">
232 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
233 </property>
234 <property name="DefaultTimeoutStopUSec" type="t" access="read">
235 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
236 </property>
237 <property name="DefaultTimeoutAbortUSec" type="t" access="read">
238 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
239 </property>
240 <property name="DefaultDeviceTimeoutUSec" type="t" access="read">
241 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
242 </property>
243 <property name="DefaultRestartUSec" type="t" access="read">
244 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
245 </property>
246 <property name="DefaultStartLimitIntervalUSec" type="t" access="read">
247 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
248 </property>
249 <property name="DefaultStartLimitBurst" type="u" access="read">
250 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
251 </property>
252 <property name="DefaultCPUAccounting" type="b" access="read">
253 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
254 </property>
255 <property name="DefaultBlockIOAccounting" type="b" access="read">
256 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
257 </property>
258 <property name="DefaultIOAccounting" type="b" access="read">
259 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
260 </property>
261 <property name="DefaultIPAccounting" type="b" access="read">
262 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
263 </property>
264 <property name="DefaultMemoryAccounting" type="b" access="read">
265 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
266 </property>
267 <property name="DefaultTasksAccounting" type="b" access="read">
268 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
269 </property>
270 <property name="DefaultLimitCPU" type="t" access="read">
271 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
272 </property>
273 <property name="DefaultLimitCPUSoft" type="t" access="read">
274 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
275 </property>
276 <property name="DefaultLimitFSIZE" type="t" access="read">
277 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
278 </property>
279 <property name="DefaultLimitFSIZESoft" type="t" access="read">
280 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
281 </property>
282 <property name="DefaultLimitDATA" type="t" access="read">
283 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
284 </property>
285 <property name="DefaultLimitDATASoft" type="t" access="read">
286 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
287 </property>
288 <property name="DefaultLimitSTACK" type="t" access="read">
289 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
290 </property>
291 <property name="DefaultLimitSTACKSoft" type="t" access="read">
292 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
293 </property>
294 <property name="DefaultLimitCORE" type="t" access="read">
295 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
296 </property>
297 <property name="DefaultLimitCORESoft" type="t" access="read">
298 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
299 </property>
300 <property name="DefaultLimitRSS" type="t" access="read">
301 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
302 </property>
303 <property name="DefaultLimitRSSSoft" type="t" access="read">
304 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
305 </property>
306 <property name="DefaultLimitNOFILE" type="t" access="read">
307 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
308 </property>
309 <property name="DefaultLimitNOFILESoft" type="t" access="read">
310 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
311 </property>
312 <property name="DefaultLimitAS" type="t" access="read">
313 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
314 </property>
315 <property name="DefaultLimitASSoft" type="t" access="read">
316 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
317 </property>
318 <property name="DefaultLimitNPROC" type="t" access="read">
319 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
320 </property>
321 <property name="DefaultLimitNPROCSoft" type="t" access="read">
322 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
323 </property>
324 <property name="DefaultLimitMEMLOCK" type="t" access="read">
325 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
326 </property>
327 <property name="DefaultLimitMEMLOCKSoft" type="t" access="read">
328 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
329 </property>
330 <property name="DefaultLimitLOCKS" type="t" access="read">
331 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
332 </property>
333 <property name="DefaultLimitLOCKSSoft" type="t" access="read">
334 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
335 </property>
336 <property name="DefaultLimitSIGPENDING" type="t" access="read">
337 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
338 </property>
339 <property name="DefaultLimitSIGPENDINGSoft" type="t" access="read">
340 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
341 </property>
342 <property name="DefaultLimitMSGQUEUE" type="t" access="read">
343 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
344 </property>
345 <property name="DefaultLimitMSGQUEUESoft" type="t" access="read">
346 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
347 </property>
348 <property name="DefaultLimitNICE" type="t" access="read">
349 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
350 </property>
351 <property name="DefaultLimitNICESoft" type="t" access="read">
352 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
353 </property>
354 <property name="DefaultLimitRTPRIO" type="t" access="read">
355 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
356 </property>
357 <property name="DefaultLimitRTPRIOSoft" type="t" access="read">
358 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
359 </property>
360 <property name="DefaultLimitRTTIME" type="t" access="read">
361 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
362 </property>
363 <property name="DefaultLimitRTTIMESoft" type="t" access="read">
364 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
365 </property>
366 <property name="DefaultTasksMax" type="t" access="read">
367 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
368 </property>
369 <property name="DefaultMemoryPressureThresholdUSec" type="t" access="read">
370 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
371 </property>
372 <property name="DefaultMemoryPressureWatch" type="s" access="read">
373 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
374 </property>
375 <property name="TimerSlackNSec" type="t" access="read">
376 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
377 </property>
378 <property name="DefaultOOMPolicy" type="s" access="read">
379 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
380 </property>
381 <property name="DefaultOOMScoreAdjust" type="i" access="read">
382 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
383 </property>
384 <property name="CtrlAltDelBurstAction" type="s" access="read">
385 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
386 </property>
387 <property name="SoftRebootsCount" type="u" access="read">
388 <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
389 </property>
390 <method name="GetUnit">
391 <arg type="s" name="name" direction="in"/>
392 <arg type="o" name="unit" direction="out"/>
393 </method>
394 <method name="GetUnitByPID">
395 <arg type="u" name="pid" direction="in"/>
396 <arg type="o" name="unit" direction="out"/>
397 </method>
398 <method name="GetUnitByInvocationID">
399 <arg type="ay" name="invocation_id" direction="in"/>
400 <arg type="o" name="unit" direction="out"/>
401 </method>
402 <method name="GetUnitByControlGroup">
403 <arg type="s" name="cgroup" direction="in"/>
404 <arg type="o" name="unit" direction="out"/>
405 </method>
406 <method name="GetUnitByPIDFD">
407 <arg type="h" name="pidfd" direction="in"/>
408 <arg type="o" name="unit" direction="out"/>
409 <arg type="s" name="unit_id" direction="out"/>
410 <arg type="ay" name="invocation_id" direction="out"/>
411 </method>
412 <method name="LoadUnit">
413 <arg type="s" name="name" direction="in"/>
414 <arg type="o" name="unit" direction="out"/>
415 </method>
416 <method name="StartUnit">
417 <arg type="s" name="name" direction="in"/>
418 <arg type="s" name="mode" direction="in"/>
419 <arg type="o" name="job" direction="out"/>
420 </method>
421 <method name="StartUnitWithFlags">
422 <arg type="s" name="name" direction="in"/>
423 <arg type="s" name="mode" direction="in"/>
424 <arg type="t" name="flags" direction="in"/>
425 <arg type="o" name="job" direction="out"/>
426 </method>
427 <method name="StartUnitReplace">
428 <arg type="s" name="old_unit" direction="in"/>
429 <arg type="s" name="new_unit" direction="in"/>
430 <arg type="s" name="mode" direction="in"/>
431 <arg type="o" name="job" direction="out"/>
432 </method>
433 <method name="StopUnit">
434 <arg type="s" name="name" direction="in"/>
435 <arg type="s" name="mode" direction="in"/>
436 <arg type="o" name="job" direction="out"/>
437 </method>
438 <method name="ReloadUnit">
439 <arg type="s" name="name" direction="in"/>
440 <arg type="s" name="mode" direction="in"/>
441 <arg type="o" name="job" direction="out"/>
442 </method>
443 <method name="RestartUnit">
444 <arg type="s" name="name" direction="in"/>
445 <arg type="s" name="mode" direction="in"/>
446 <arg type="o" name="job" direction="out"/>
447 </method>
448 <method name="TryRestartUnit">
449 <arg type="s" name="name" direction="in"/>
450 <arg type="s" name="mode" direction="in"/>
451 <arg type="o" name="job" direction="out"/>
452 </method>
453 <method name="ReloadOrRestartUnit">
454 <arg type="s" name="name" direction="in"/>
455 <arg type="s" name="mode" direction="in"/>
456 <arg type="o" name="job" direction="out"/>
457 </method>
458 <method name="ReloadOrTryRestartUnit">
459 <arg type="s" name="name" direction="in"/>
460 <arg type="s" name="mode" direction="in"/>
461 <arg type="o" name="job" direction="out"/>
462 </method>
463 <!-- <method name="EnqueueUnitJob"> -->
464 <!-- <arg type="s" name="name" direction="in"/> -->
465 <!-- <arg type="s" name="job_type" direction="in"/> -->
466 <!-- <arg type="s" name="job_mode" direction="in"/> -->
467 <!-- <arg type="u" name="job_id" direction="out"/> -->
468 <!-- <arg type="o" name="job_path" direction="out"/> -->
469 <!-- <arg type="s" name="unit_id" direction="out"/> -->
470 <!-- <arg type="o" name="unit_path" direction="out"/> -->
471 <!-- <arg type="s" name="job_type" direction="out"/> -->
472 <!-- <arg type="a(uosos)" name="affected_jobs" direction="out"/> -->
473 <!-- </method> -->
474 <method name="KillUnit">
475 <arg type="s" name="name" direction="in"/>
476 <arg type="s" name="whom" direction="in"/>
477 <arg type="i" name="signal" direction="in"/>
478 </method>
479 <method name="QueueSignalUnit">
480 <arg type="s" name="name" direction="in"/>
481 <arg type="s" name="whom" direction="in"/>
482 <arg type="i" name="signal" direction="in"/>
483 <arg type="i" name="value" direction="in"/>
484 </method>
485 <method name="CleanUnit">
486 <arg type="s" name="name" direction="in"/>
487 <arg type="as" name="mask" direction="in"/>
488 </method>
489 <method name="FreezeUnit">
490 <arg type="s" name="name" direction="in"/>
491 </method>
492 <method name="ThawUnit">
493 <arg type="s" name="name" direction="in"/>
494 </method>
495 <method name="ResetFailedUnit">
496 <arg type="s" name="name" direction="in"/>
497 </method>
498 <!-- <method name="SetUnitProperties"> -->
499 <!-- <arg type="s" name="name" direction="in"/> -->
500 <!-- <arg type="b" name="runtime" direction="in"/> -->
501 <!-- <arg type="a(sv)" name="properties" direction="in"/> -->
502 <!-- </method> -->
503 <method name="BindMountUnit">
504 <arg type="s" name="name" direction="in"/>
505 <arg type="s" name="source" direction="in"/>
506 <arg type="s" name="destination" direction="in"/>
507 <arg type="b" name="read_only" direction="in"/>
508 <arg type="b" name="mkdir" direction="in"/>
509 </method>
510 <!-- <method name="MountImageUnit"> -->
511 <!-- <arg type="s" name="name" direction="in"/> -->
512 <!-- <arg type="s" name="source" direction="in"/> -->
513 <!-- <arg type="s" name="destination" direction="in"/> -->
514 <!-- <arg type="b" name="read_only" direction="in"/> -->
515 <!-- <arg type="b" name="mkdir" direction="in"/> -->
516 <!-- <arg type="a(ss)" name="options" direction="in"/> -->
517 <!-- </method> -->
518 <method name="RefUnit">
519 <arg type="s" name="name" direction="in"/>
520 </method>
521 <method name="UnrefUnit">
522 <arg type="s" name="name" direction="in"/>
523 </method>
524 <!-- <method name="StartTransientUnit"> -->
525 <!-- <arg type="s" name="name" direction="in"/> -->
526 <!-- <arg type="s" name="mode" direction="in"/> -->
527 <!-- <arg type="a(sv)" name="properties" direction="in"/> -->
528 <!-- <arg type="a(sa(sv))" name="aux" direction="in"/> -->
529 <!-- <arg type="o" name="job" direction="out"/> -->
530 <!-- </method> -->
531 <!-- <method name="GetUnitProcesses"> -->
532 <!-- <arg type="s" name="name" direction="in"/> -->
533 <!-- <arg type="a(sus)" name="processes" direction="out"/> -->
534 <!-- </method> -->
535 <!-- <method name="AttachProcessesToUnit"> -->
536 <!-- <arg type="s" name="unit_name" direction="in"/> -->
537 <!-- <arg type="s" name="subcgroup" direction="in"/> -->
538 <!-- <arg type="au" name="pids" direction="in"/> -->
539 <!-- </method> -->
540 <method name="AbandonScope">
541 <arg type="s" name="name" direction="in"/>
542 </method>
543 <method name="GetJob">
544 <arg type="u" name="id" direction="in"/>
545 <arg type="o" name="job" direction="out"/>
546 </method>
547 <!-- <method name="GetJobAfter"> -->
548 <!-- <arg type="u" name="id" direction="in"/> -->
549 <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> -->
550 <!-- </method> -->
551 <!-- <method name="GetJobBefore"> -->
552 <!-- <arg type="u" name="id" direction="in"/> -->
553 <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> -->
554 <!-- </method> -->
555 <method name="CancelJob">
556 <arg type="u" name="id" direction="in"/>
557 </method>
558 <method name="ClearJobs">
559 </method>
560 <method name="ResetFailed">
561 </method>
562 <method name="SetShowStatus">
563 <arg type="s" name="mode" direction="in"/>
564 </method>
565 <!-- <method name="ListUnits"> -->
566 <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> -->
567 <!-- </method> -->
568 <!-- <method name="ListUnitsFiltered"> -->
569 <!-- <arg type="as" name="states" direction="in"/> -->
570 <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> -->
571 <!-- </method> -->
572 <!-- <method name="ListUnitsByPatterns"> -->
573 <!-- <arg type="as" name="states" direction="in"/> -->
574 <!-- <arg type="as" name="patterns" direction="in"/> -->
575 <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> -->
576 <!-- </method> -->
577 <!-- <method name="ListUnitsByNames"> -->
578 <!-- <arg type="as" name="names" direction="in"/> -->
579 <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> -->
580 <!-- </method> -->
581 <!-- <method name="ListJobs"> -->
582 <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> -->
583 <!-- </method> -->
584 <method name="Subscribe">
585 </method>
586 <method name="Unsubscribe">
587 </method>
588 <method name="Dump">
589 <arg type="s" name="output" direction="out"/>
590 </method>
591 <method name="DumpUnitsMatchingPatterns">
592 <arg type="as" name="patterns" direction="in"/>
593 <arg type="s" name="output" direction="out"/>
594 </method>
595 <method name="DumpByFileDescriptor">
596 <arg type="h" name="fd" direction="out"/>
597 </method>
598 <method name="DumpUnitsMatchingPatternsByFileDescriptor">
599 <arg type="as" name="patterns" direction="in"/>
600 <arg type="h" name="fd" direction="out"/>
601 </method>
602 <method name="Reload">
603 </method>
604 <method name="Reexecute">
605 <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
606 </method>
607 <method name="Exit">
608 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
609 </method>
610 <method name="Reboot">
611 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
612 </method>
613 <method name="SoftReboot">
614 <arg type="s" name="new_root" direction="in"/>
615 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
616 </method>
617 <method name="PowerOff">
618 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
619 </method>
620 <method name="Halt">
621 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
622 </method>
623 <method name="KExec">
624 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
625 </method>
626 <method name="SwitchRoot">
627 <arg type="s" name="new_root" direction="in"/>
628 <arg type="s" name="init" direction="in"/>
629 <annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
630 </method>
631 <method name="SetEnvironment">
632 <arg type="as" name="assignments" direction="in"/>
633 </method>
634 <method name="UnsetEnvironment">
635 <arg type="as" name="names" direction="in"/>
636 </method>
637 <method name="UnsetAndSetEnvironment">
638 <arg type="as" name="names" direction="in"/>
639 <arg type="as" name="assignments" direction="in"/>
640 </method>
641 <method name="EnqueueMarkedJobs">
642 <arg type="ao" name="jobs" direction="out"/>
643 </method>
644 <!-- <method name="ListUnitFiles"> -->
645 <!-- <arg type="a(ss)" name="unit_files" direction="out"/> -->
646 <!-- </method> -->
647 <!-- <method name="ListUnitFilesByPatterns"> -->
648 <!-- <arg type="as" name="states" direction="in"/> -->
649 <!-- <arg type="as" name="patterns" direction="in"/> -->
650 <!-- <arg type="a(ss)" name="unit_files" direction="out"/> -->
651 <!-- </method> -->
652 <method name="GetUnitFileState">
653 <arg type="s" name="file" direction="in"/>
654 <arg type="s" name="state" direction="out"/>
655 </method>
656 <!-- <method name="EnableUnitFiles"> -->
657 <!-- <arg type="as" name="files" direction="in"/> -->
658 <!-- <arg type="b" name="runtime" direction="in"/> -->
659 <!-- <arg type="b" name="force" direction="in"/> -->
660 <!-- <arg type="b" name="carries_install_info" direction="out"/> -->
661 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
662 <!-- </method> -->
663 <!-- <method name="DisableUnitFiles"> -->
664 <!-- <arg type="as" name="files" direction="in"/> -->
665 <!-- <arg type="b" name="runtime" direction="in"/> -->
666 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
667 <!-- </method> -->
668 <!-- <method name="EnableUnitFilesWithFlags"> -->
669 <!-- <arg type="as" name="files" direction="in"/> -->
670 <!-- <arg type="t" name="flags" direction="in"/> -->
671 <!-- <arg type="b" name="carries_install_info" direction="out"/> -->
672 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
673 <!-- </method> -->
674 <!-- <method name="DisableUnitFilesWithFlags"> -->
675 <!-- <arg type="as" name="files" direction="in"/> -->
676 <!-- <arg type="t" name="flags" direction="in"/> -->
677 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
678 <!-- </method> -->
679 <!-- <method name="DisableUnitFilesWithFlagsAndInstallInfo"> -->
680 <!-- <arg type="as" name="files" direction="in"/> -->
681 <!-- <arg type="t" name="flags" direction="in"/> -->
682 <!-- <arg type="b" name="carries_install_info" direction="out"/> -->
683 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
684 <!-- </method> -->
685 <!-- <method name="ReenableUnitFiles"> -->
686 <!-- <arg type="as" name="files" direction="in"/> -->
687 <!-- <arg type="b" name="runtime" direction="in"/> -->
688 <!-- <arg type="b" name="force" direction="in"/> -->
689 <!-- <arg type="b" name="carries_install_info" direction="out"/> -->
690 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
691 <!-- </method> -->
692 <!-- <method name="LinkUnitFiles"> -->
693 <!-- <arg type="as" name="files" direction="in"/> -->
694 <!-- <arg type="b" name="runtime" direction="in"/> -->
695 <!-- <arg type="b" name="force" direction="in"/> -->
696 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
697 <!-- </method> -->
698 <!-- <method name="PresetUnitFiles"> -->
699 <!-- <arg type="as" name="files" direction="in"/> -->
700 <!-- <arg type="b" name="runtime" direction="in"/> -->
701 <!-- <arg type="b" name="force" direction="in"/> -->
702 <!-- <arg type="b" name="carries_install_info" direction="out"/> -->
703 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
704 <!-- </method> -->
705 <!-- <method name="PresetUnitFilesWithMode"> -->
706 <!-- <arg type="as" name="files" direction="in"/> -->
707 <!-- <arg type="s" name="mode" direction="in"/> -->
708 <!-- <arg type="b" name="runtime" direction="in"/> -->
709 <!-- <arg type="b" name="force" direction="in"/> -->
710 <!-- <arg type="b" name="carries_install_info" direction="out"/> -->
711 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
712 <!-- </method> -->
713 <!-- <method name="MaskUnitFiles"> -->
714 <!-- <arg type="as" name="files" direction="in"/> -->
715 <!-- <arg type="b" name="runtime" direction="in"/> -->
716 <!-- <arg type="b" name="force" direction="in"/> -->
717 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
718 <!-- </method> -->
719 <!-- <method name="UnmaskUnitFiles"> -->
720 <!-- <arg type="as" name="files" direction="in"/> -->
721 <!-- <arg type="b" name="runtime" direction="in"/> -->
722 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
723 <!-- </method> -->
724 <!-- <method name="RevertUnitFiles"> -->
725 <!-- <arg type="as" name="files" direction="in"/> -->
726 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
727 <!-- </method> -->
728 <!-- <method name="SetDefaultTarget"> -->
729 <!-- <arg type="s" name="name" direction="in"/> -->
730 <!-- <arg type="b" name="force" direction="in"/> -->
731 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
732 <!-- </method> -->
733 <method name="GetDefaultTarget">
734 <arg type="s" name="name" direction="out"/>
735 </method>
736 <!-- <method name="PresetAllUnitFiles"> -->
737 <!-- <arg type="s" name="mode" direction="in"/> -->
738 <!-- <arg type="b" name="runtime" direction="in"/> -->
739 <!-- <arg type="b" name="force" direction="in"/> -->
740 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
741 <!-- </method> -->
742 <!-- <method name="AddDependencyUnitFiles"> -->
743 <!-- <arg type="as" name="files" direction="in"/> -->
744 <!-- <arg type="s" name="target" direction="in"/> -->
745 <!-- <arg type="s" name="type" direction="in"/> -->
746 <!-- <arg type="b" name="runtime" direction="in"/> -->
747 <!-- <arg type="b" name="force" direction="in"/> -->
748 <!-- <arg type="a(sss)" name="changes" direction="out"/> -->
749 <!-- </method> -->
750 <method name="GetUnitFileLinks">
751 <arg type="s" name="name" direction="in"/>
752 <arg type="b" name="runtime" direction="in"/>
753 <arg type="as" name="links" direction="out"/>
754 </method>
755 <method name="SetExitCode">
756 <arg type="y" name="number" direction="in"/>
757 </method>
758 <method name="LookupDynamicUserByName">
759 <arg type="s" name="name" direction="in"/>
760 <arg type="u" name="uid" direction="out"/>
761 </method>
762 <method name="LookupDynamicUserByUID">
763 <arg type="u" name="uid" direction="in"/>
764 <arg type="s" name="name" direction="out"/>
765 </method>
766 <!-- <method name="GetDynamicUsers"> -->
767 <!-- <arg type="a(us)" name="users" direction="out"/> -->
768 <!-- </method> -->
769 <!-- <method name="DumpUnitFileDescriptorStore"> -->
770 <!-- <arg type="s" name="name" direction="in"/> -->
771 <!-- <arg type="a(suuutuusu)" name="entries" direction="out"/> -->
772 <!-- </method> -->
773 <!-- <method name="StartAuxiliaryScope"> -->
774 <!-- <arg type="s" name="name" direction="in"/> -->
775 <!-- <arg type="ah" name="pidfds" direction="in"/> -->
776 <!-- <arg type="t" name="flags" direction="in"/> -->
777 <!-- <arg type="a(sv)" name="properties" direction="in"/> -->
778 <!-- <arg type="o" name="job" direction="out"/> -->
779 <!-- <annotation name="org.freedesktop.DBus.Deprecated" value="true"/> -->
780 <!-- </method> -->
781 <signal name="UnitNew">
782 <arg type="s" name="id"/>
783 <arg type="o" name="unit"/>
784 </signal>
785 <signal name="UnitRemoved">
786 <arg type="s" name="id"/>
787 <arg type="o" name="unit"/>
788 </signal>
789 <signal name="JobNew">
790 <arg type="u" name="id"/>
791 <arg type="o" name="job"/>
792 <arg type="s" name="unit"/>
793 </signal>
794 <signal name="JobRemoved">
795 <arg type="u" name="id"/>
796 <arg type="o" name="job"/>
797 <arg type="s" name="unit"/>
798 <arg type="s" name="result"/>
799 </signal>
800 <signal name="StartupFinished">
801 <arg type="t" name="firmware"/>
802 <arg type="t" name="loader"/>
803 <arg type="t" name="kernel"/>
804 <arg type="t" name="initrd"/>
805 <arg type="t" name="userspace"/>
806 <arg type="t" name="total"/>
807 </signal>
808 <signal name="UnitFilesChanged">
809 </signal>
810 <signal name="Reloading">
811 <arg type="b" name="active"/>
812 </signal>
813 </interface>
814 <node name="unit"/>
815 <node name="job"/>
816</node>
817
diff --git a/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml
new file mode 100644
index 00000000..dcc23279
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml
@@ -0,0 +1,172 @@
1import QtQuick
2import qs.Services
3import Quickshell
4import Quickshell.Widgets
5
6Item {
7 id: activeWindowDisplay
8
9 required property int maxWidth
10 required property var screen
11
12 property var activeWindow: {
13 let currWindowId = Array.from(NiriService.workspaces).find(ws => {
14 return ws.output === screen.name && ws.is_active;
15 })?.active_window_id;
16
17 return currWindowId ? Array.from(NiriService.windows).find(win => win.id == currWindowId) : null;
18 }
19 property var windowEntry: activeWindow ? DesktopEntries.heuristicLookup(activeWindow.app_id) : null
20
21 anchors.verticalCenter: parent.verticalCenter
22 width: activeWindowDisplayContent.width
23 height: parent.height
24
25 WrapperMouseArea {
26 id: widgetMouseArea
27
28 anchors.fill: parent
29
30 hoverEnabled: true
31
32 Item {
33 anchors.fill: parent
34
35 Row {
36 id: activeWindowDisplayContent
37
38 width: childrenRect.width
39 height: parent.height
40 anchors.verticalCenter: parent.verticalCenter
41 spacing: 8
42
43 IconImage {
44 id: activeWindowIcon
45
46 implicitSize: 14
47
48 anchors.verticalCenter: parent.verticalCenter
49
50 source: {
51 let icon = activeWindowDisplay.windowEntry?.icon
52 if (typeof icon === 'string' || icon instanceof String) {
53 if (icon.includes("?path=")) {
54 const split = icon.split("?path=")
55 if (split.length !== 2)
56 return icon
57 const name = split[0]
58 const path = split[1]
59 const fileName = name.substring(
60 name.lastIndexOf("/") + 1)
61 return `file://${path}/${fileName}`
62 } else
63 icon = Quickshell.iconPath(icon);
64 return icon
65 }
66 return ""
67 }
68 asynchronous: true
69 smooth: true
70 mipmap: true
71 }
72
73 Text {
74 id: windowTitle
75
76 width: Math.min(implicitWidth, activeWindowDisplay.maxWidth - activeWindowIcon.width - activeWindowDisplayContent.spacing)
77
78 property var appAliases: { "Firefox": "Mozilla Firefox", "mpv Media Player": "mpv", "Thunderbird": "Mozilla Thunderbird", "Thunderbird (LMU)": "Mozilla Thunderbird" }
79
80 elide: Text.ElideRight
81 maximumLineCount: 1
82 color: "white"
83 anchors.verticalCenter: parent.verticalCenter
84 text: {
85 if (!activeWindowDisplay.activeWindow)
86 return "";
87
88 var title = activeWindowDisplay.activeWindow.title;
89 var appName = activeWindowDisplay.windowEntry?.name;
90 if (appAliases[appName])
91 appName = appAliases[appName];
92 if (appName && title.endsWith(appName)) {
93 const oldTitle = title;
94 title = title.substring(0, title.length - appName.length);
95 title = title.replace(/\s*(—|-)\s*$/, "");
96 if (!title)
97 title = oldTitle;
98 }
99 return title;
100 }
101 }
102 }
103 }
104 }
105
106 Loader {
107 id: tooltipLoader
108
109 active: false
110
111 Connections {
112 target: widgetMouseArea
113 function onContainsMouseChanged() {
114 if (widgetMouseArea.containsMouse)
115 tooltipLoader.active = true;
116 }
117 }
118
119 PopupWindow {
120 id: tooltip
121
122 property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse
123
124 anchor {
125 item: widgetMouseArea
126 edges: Edges.Bottom | Edges.Left
127 }
128 visible: false
129
130 onNextVisibleChanged: hangTimer.restart()
131
132 Timer {
133 id: hangTimer
134 interval: tooltip.visible ? 100 : 500
135 onTriggered: {
136 tooltip.visible = tooltip.nextVisible;
137 if (!tooltip.visible)
138 tooltipLoader.active = false;
139 }
140 }
141
142 implicitWidth: widgetTooltipText.contentWidth + 16
143 implicitHeight: widgetTooltipText.contentHeight + 16
144 color: "black"
145
146 WrapperMouseArea {
147 id: tooltipMouseArea
148
149 hoverEnabled: true
150 enabled: true
151
152 anchors.fill: parent
153
154 Item {
155 anchors.fill: parent
156
157 Text {
158 id: widgetTooltipText
159
160 anchors.centerIn: parent
161
162 font.pointSize: 10
163 font.family: "Fira Mono"
164 color: "white"
165
166 text: JSON.stringify(Object.assign({}, activeWindowDisplay.activeWindow), null, 2)
167 }
168 }
169 }
170 }
171 }
172}
diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml
new file mode 100644
index 00000000..9210066c
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml
@@ -0,0 +1,117 @@
1import Quickshell
2import Quickshell.Wayland
3import QtQuick
4
5PanelWindow {
6 id: bar
7
8 required property var modelData
9 screen: modelData
10
11 WlrLayershell.namespace: "bar"
12
13 anchors {
14 top: true
15 left: true
16 right: true
17 }
18 margins {
19 left: 26 + 8
20 right: 26 + 8
21 }
22
23 implicitHeight: 21
24 color: "transparent"
25
26 Rectangle {
27 color: Qt.rgba(0, 0, 0, 0.75)
28 anchors.fill: parent
29 // bottomLeftRadius: 8
30 // bottomRightRadius: 8
31 }
32
33 Row {
34 id: left
35
36 height: parent.height
37 width: childrenRect.width
38 anchors.left: parent.left
39 anchors.leftMargin: 8
40 anchors.verticalCenter: parent.verticalCenter
41 spacing: 8
42
43 WorkspaceSwitcher {
44 screen: bar.screen
45 }
46 }
47
48 Row {
49 id: center
50
51 height: parent.height
52 width: childrenRect.width
53 anchors.centerIn: parent
54 spacing: 5
55
56 ActiveWindowDisplay {
57 screen: bar.screen
58 maxWidth: bar.width - 2*Math.max(left.width, right.width) - 2*8
59 }
60 }
61
62 Row {
63 id: right
64
65 height: parent.height
66 width: childrenRect.width
67 anchors.right: parent.right
68 anchors.rightMargin: 8
69 anchors.verticalCenter: parent.verticalCenter
70 spacing: 0
71
72 // WorktimeWidget { command: "time"; }
73
74 // WorktimeWidget { command: "today"; }
75
76 KeyboardLayout {}
77
78 Item {
79 visible: privacy.visible
80 height: parent.height
81 width: 8 - 4
82 }
83
84 PrivacyWidget {
85 id: privacy
86 }
87
88 Item {
89 visible: privacy.visible
90 height: parent.height
91 width: 8 - 4
92 }
93
94 SystemTray {}
95
96 PipewireWidget {}
97
98 BrightnessWidget {}
99
100 BatteryWidget {}
101
102 WaylandInhibitorWidget {
103 window: bar
104 }
105
106 NotificationInhibitorWidget {}
107
108 LidSwitchInhibitorWidget {}
109
110 Item {
111 height: parent.height
112 width: 8 - 4
113 }
114
115 Clock {}
116 }
117} \ No newline at end of file
diff --git a/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml
new file mode 100644
index 00000000..da17df2a
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml
@@ -0,0 +1,136 @@
1import QtQuick
2import Quickshell
3import Quickshell.Widgets
4import Quickshell.Services.UPower
5
6Item {
7 id: root
8
9 height: parent.height
10 width: batteryIcon.width + 8
11 anchors.verticalCenter: parent.verticalCenter
12
13 property var batteryDevice: Array.from(UPower.devices.values).find(dev => dev.isLaptopBattery)
14
15 WrapperMouseArea {
16 id: widgetMouseArea
17
18 anchors.fill: parent
19
20 hoverEnabled: true
21
22 Item {
23 anchors.fill: parent
24
25 MaterialDesignIcon {
26 id: batteryIcon
27
28 implicitSize: 14
29 anchors.centerIn: parent
30
31 icon: {
32 if (!root.batteryDevice?.ready)
33 return "battery-unknown";
34
35 if (root.batteryDevice.state == UPowerDeviceState.FullyCharged)
36 return "power-plug-battery";
37
38 const perdec = 10 * Math.max(1, Math.ceil(root.batteryDevice.percentage * 10));
39 if (root.batteryDevice.state == UPowerDeviceState.Charging)
40 return `battery-charging-${perdec}`;
41 if (perdec == 100)
42 return "battery";
43 return `battery-${perdec}`;
44 }
45 color: {
46 if (!root.batteryDevice?.ready)
47 return "#555";
48
49 if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 20 * 60)
50 return "#f2201f";
51 if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 40 * 60)
52 return "#f28a21";
53 if (root.batteryDevice.state != UPowerDeviceState.FullyCharged)
54 return "#fff";
55 return "#555";
56 }
57 }
58 }
59 }
60
61 PopupWindow {
62 id: tooltip
63
64 property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse
65
66 anchor {
67 item: widgetMouseArea
68 edges: Edges.Bottom | Edges.Left
69 }
70 visible: false
71
72 onNextVisibleChanged: hangTimer.restart()
73
74 Timer {
75 id: hangTimer
76 interval: 100
77 onTriggered: tooltip.visible = tooltip.nextVisible
78 }
79
80 implicitWidth: widgetTooltipText.contentWidth + 16
81 implicitHeight: widgetTooltipText.contentHeight + 16
82 color: "black"
83
84 WrapperMouseArea {
85 id: tooltipMouseArea
86
87 hoverEnabled: true
88 enabled: true
89
90 anchors.fill: parent
91
92 Item {
93 anchors.fill: parent
94
95 Text {
96 id: widgetTooltipText
97
98 anchors.centerIn: parent
99
100 font.pointSize: 10
101 font.family: "Fira Sans"
102 color: "white"
103
104 text: {
105 const stateStr = UPowerDeviceState.toString(root.batteryDevice.state);
106 var outStr = stateStr;
107 if (root.batteryDevice.state != UPowerDeviceState.FullyCharged)
108 outStr += ` ${Math.round(root.batteryDevice.percentage * 100)}%`;
109
110 function formatTime(t) {
111 var res = "";
112 for (const unit of [{ "s": "h", "v": 3600 }, { "s": "m", "v": 60 }, { "s": "s", "v": 1 }]) {
113 if (t < unit.v)
114 continue;
115 res += Math.floor(t / unit.v) + unit.s;
116 t %= unit.v;
117 }
118 return res;
119 }
120 if (root.batteryDevice.timeToEmpty != 0) {
121 const tStr = formatTime(Math.floor(root.batteryDevice.timeToEmpty / 60) * 60);
122 if (tStr)
123 outStr += " " + tStr;
124 } else if (root.batteryDevice.timeToFull != 0) {
125 const tStr = formatTime(Math.ceil(root.batteryDevice.timeToFull / 60) * 60);
126 if (tStr)
127 outStr += " " + tStr;
128 }
129
130 return outStr;
131 }
132 }
133 }
134 }
135 }
136}
diff --git a/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml b/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml
new file mode 100644
index 00000000..a432179e
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml
@@ -0,0 +1,117 @@
1import QtQuick
2import QtQuick.Layouts
3import Quickshell
4import Quickshell.Widgets
5import qs.Services
6
7Scope {
8 id: root
9
10 property bool show: false
11 property bool inhibited: true
12
13 Connections {
14 target: Brightness
15
16 function onCurrBrightnessChanged() {
17 root.show = true;
18 hideTimer.restart();
19 }
20 }
21
22 onShowChanged: {
23 if (show)
24 hideTimer.restart();
25 }
26
27 Timer {
28 id: hideTimer
29 interval: 1000
30 onTriggered: root.show = false
31 }
32
33 Timer {
34 id: startInhibit
35 interval: 100
36 running: true
37 onTriggered: {
38 root.show = false;
39 root.inhibited = false;
40 }
41 }
42
43 LazyLoader {
44 active: root.show && !root.inhibited
45
46 Variants {
47 model: Quickshell.screens
48
49 delegate: Scope {
50 id: screenScope
51
52 required property var modelData
53
54 PanelWindow {
55 id: window
56
57 screen: screenScope.modelData
58
59 anchors.top: true
60 margins.top: screen.height / 2 - 50 + 3.5
61 exclusiveZone: 0
62 exclusionMode: ExclusionMode.Ignore
63
64 implicitWidth: 400
65 implicitHeight: 50
66
67 mask: Region {}
68
69 color: "transparent"
70
71 Rectangle {
72 anchors.fill: parent
73 color: Qt.rgba(0, 0, 0, 0.75)
74 }
75
76 RowLayout {
77 id: layout
78
79 anchors.centerIn: parent
80
81 height: 50 - 8*2
82 width: 400 - 8*2
83
84 MaterialDesignIcon {
85 id: volumeIcon
86
87 implicitWidth: parent.height
88 implicitHeight: parent.height
89
90 icon: `brightness-${Math.min(7, Math.floor(Brightness.currBrightness * 7) + 1)}`
91 }
92
93 Rectangle {
94 Layout.fillWidth: true
95
96 implicitHeight: 10
97
98 color: "#50ffffff"
99
100 Rectangle {
101 anchors {
102 left: parent.left
103 top: parent.top
104 bottom: parent.bottom
105 }
106
107 color: "white"
108
109 implicitWidth: parent.width * Brightness.currBrightness
110 }
111 }
112 }
113 }
114 }
115 }
116 }
117}
diff --git a/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml
new file mode 100644
index 00000000..3bb5a80e
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml
@@ -0,0 +1,84 @@
1import QtQuick
2import Quickshell
3import Quickshell.Widgets
4import qs.Services
5
6Item {
7 height: parent.height
8 width: brightnessIcon.width + 8
9 anchors.verticalCenter: parent.verticalCenter
10
11 WrapperMouseArea {
12 id: widgetMouseArea
13
14 anchors.fill: parent
15
16 hoverEnabled: true
17
18 property real sensitivity: (1 / 50) / 120
19 onWheel: event => Brightness.currBrightness += event.angleDelta.y * sensitivity
20
21 Item {
22 anchors.fill: parent
23
24 MaterialDesignIcon {
25 id: brightnessIcon
26
27 implicitSize: 14
28 anchors.centerIn: parent
29
30 icon: `brightness-${Math.min(7, Math.floor(Brightness.currBrightness * 7) + 1)}`
31 color: "#555"
32 }
33 }
34 }
35
36 PopupWindow {
37 id: tooltip
38
39 property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse
40
41 anchor {
42 item: widgetMouseArea
43 edges: Edges.Bottom | Edges.Left
44 }
45 visible: false
46
47 onNextVisibleChanged: hangTimer.restart()
48
49 Timer {
50 id: hangTimer
51 interval: 100
52 onTriggered: tooltip.visible = tooltip.nextVisible
53 }
54
55 implicitWidth: widgetTooltipText.contentWidth + 16
56 implicitHeight: widgetTooltipText.contentHeight + 16
57 color: "black"
58
59 WrapperMouseArea {
60 id: tooltipMouseArea
61
62 hoverEnabled: true
63 enabled: true
64
65 anchors.fill: parent
66
67 Item {
68 anchors.fill: parent
69
70 Text {
71 id: widgetTooltipText
72
73 anchors.centerIn: parent
74
75 font.pointSize: 10
76 font.family: "Fira Sans"
77 color: "white"
78
79 text: `${Math.round(Brightness.currBrightness * 100)}%`
80 }
81 }
82 }
83 }
84}
diff --git a/accounts/gkleen@sif/shell/quickshell/Clock.qml b/accounts/gkleen@sif/shell/quickshell/Clock.qml
new file mode 100644
index 00000000..b7004528
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Clock.qml
@@ -0,0 +1,295 @@
1import QtQml
2import QtQuick
3import Quickshell
4import Custom as Custom
5import QtQuick.Controls
6import QtQuick.Layouts
7import Quickshell.Widgets
8
9Item {
10 id: clockItem
11
12 property bool calendarPopup: true
13
14 width: clock.contentWidth
15 height: parent.height
16 anchors.verticalCenter: parent.verticalCenter
17
18 WrapperMouseArea {
19 id: clockMouseArea
20
21 anchors.fill: parent
22 hoverEnabled: true
23 enabled: clockItem.calendarPopup
24
25 Item {
26 anchors.fill: parent
27
28 Text {
29 id: clock
30 color: "white"
31
32 anchors.verticalCenter: parent.verticalCenter
33
34 Custom.Chrono {
35 id: chrono
36
37 onDateChanged: clock.text = format("W{0:%V-%u} {0:%F} {0:%H:%M:%S%Ez}")
38 }
39
40 font.pointSize: 10
41 font.family: "Fira Sans"
42 font.features: { "tnum": 1 }
43 }
44 }
45 }
46
47 Loader {
48 id: tooltipLoader
49
50 active: false
51
52 Connections {
53 target: clockMouseArea
54 function onContainsMouseChanged() {
55 if (clockMouseArea.containsMouse)
56 tooltipLoader.active = true;
57 }
58 }
59
60 sourceComponent: PopupWindow {
61 id: tooltip
62
63 property bool nextVisible: clockMouseArea.containsMouse || tooltipMouseArea.containsMouse
64
65 anchor {
66 item: clockMouseArea
67 edges: Edges.Bottom | Edges.Left
68 }
69 visible: false
70
71 onNextVisibleChanged: hangTimer.restart()
72
73 Timer {
74 id: hangTimer
75 interval: 100
76 onTriggered: {
77 tooltip.visible = tooltip.nextVisible;
78 if (!tooltip.visible)
79 tooltipLoader.active = false;
80 }
81 }
82
83 implicitWidth: tooltipLayout.childrenRect.width + 16
84 implicitHeight: tooltipLayout.childrenRect.height + 16
85 color: "black"
86
87 onVisibleChanged: {
88 yearCalendar.year = chrono.date.getFullYear();
89 yearCalendar.angleRem = 0;
90 }
91
92 WrapperMouseArea {
93 id: tooltipMouseArea
94
95 hoverEnabled: true
96 enabled: true
97
98 onWheel: event => yearCalendar.scrollYear(event)
99
100 anchors.fill: parent
101
102 Item {
103 id: clockTooltipContent
104
105 anchors.fill: parent
106
107 ColumnLayout {
108 id: tooltipLayout
109
110 anchors {
111 left: parent.left
112 top: parent.top
113 leftMargin: 8
114 topMargin: 8
115 }
116
117 Text {
118 id: yearLabel
119
120 horizontalAlignment: Text.AlignHCenter
121
122 font.pointSize: 14
123 font.family: "Fira Sans"
124 font.features: { "tnum": 1 }
125 color: "white"
126
127 text: yearCalendar.year
128
129 Layout.fillWidth: true
130 Layout.bottomMargin: 8
131 }
132
133 GridLayout {
134 property int year: chrono.date.getFullYear()
135
136 id: yearCalendar
137
138 columns: 3
139 columnSpacing: 16
140 rowSpacing: 16
141
142 Layout.alignment: Qt.AlignHCenter
143 Layout.fillWidth: false
144
145 property real angleRem: 0
146 property real sensitivity: 1 / 120
147
148 function scrollYear(event) {
149 angleRem += event.angleDelta.y;
150 const d = Math.round(angleRem * sensitivity);
151 yearCalendar.year += d;
152 angleRem -= d / sensitivity;
153 }
154
155 Connections {
156 target: clockMouseArea
157 function onWheel(event) { yearCalendar.scrollYear(event); }
158 }
159
160 Repeater {
161 model: 12
162
163 GridLayout {
164 columns: 2
165
166 required property int index
167 property int month: index
168
169 id: monthCalendar
170
171 Layout.alignment: Qt.AlignTop | Qt.AlignRight
172 Layout.fillWidth: false
173
174 Text {
175 Layout.column: 1
176 Layout.fillWidth: true
177
178 horizontalAlignment: Text.AlignHCenter
179
180 font.pointSize: 10
181 font.family: "Fira Sans"
182
183 text: new Date(yearCalendar.year, monthCalendar.month, 1).toLocaleString(Qt.locale("en_DK"), "MMMM")
184
185 color: "#ffead3"
186 }
187
188 DayOfWeekRow {
189 locale: grid.locale
190
191 Layout.row: 1
192 Layout.column: 1
193 Layout.fillWidth: true
194
195 delegate: WrapperItem {
196 required property string shortName
197
198 width: dowLabel.contentWidth + 6
199
200 Text {
201 id: dowLabel
202
203 anchors.fill: parent
204
205 font.pointSize: 10
206 font.family: "Fira Sans"
207
208 text: parent.shortName
209 color: "#ffcc66"
210
211 horizontalAlignment: Text.AlignHCenter
212 verticalAlignment: Text.AlignVCenter
213 }
214 }
215 }
216
217 WeekNumberColumn {
218 month: grid.month
219 year: grid.year
220 locale: grid.locale
221
222 Layout.fillHeight: true
223
224 delegate: Text {
225 required property int weekNumber
226
227 opacity: {
228 const simple = new Date(weekNumber == 1 && monthCalendar.month == 12 ? yearCalendar.year + 1 : yearCalendar.year, 0, 1 + (weekNumber - 1) * 7);
229 const dayOfWeek = simple.getDay();
230 const isoWeekStart = simple;
231
232 isoWeekStart.setDate(simple.getDate() - dayOfWeek + 1);
233 if (dayOfWeek > 4) {
234 isoWeekStart.setDate(isoWeekStart.getDate() + 7);
235 }
236
237 for (let i = 0; i < 7; i++) {
238 const dayInWeek = new Date(isoWeekStart);
239 dayInWeek.setDate(dayInWeek.getDate() + i);
240 if (dayInWeek.getMonth() == monthCalendar.month)
241 return 1;
242 }
243
244 return 0;
245 }
246
247 font.pointSize: 10
248 font.family: "Fira Sans"
249 font.features: { "tnum": 1 }
250
251 text: weekNumber
252 color: "#99ffdd"
253
254 horizontalAlignment: Text.AlignRight
255 verticalAlignment: Text.AlignVCenter
256 }
257 }
258
259 MonthGrid {
260 id: grid
261
262 year: yearCalendar.year
263 month: monthCalendar.month
264 locale: Qt.locale("en_DK")
265
266 Layout.fillWidth: true
267 Layout.fillHeight: true
268
269 delegate: Text {
270 required property var model
271
272 opacity: model.month === monthCalendar.month ? 1 : 0
273
274 font.pointSize: 10
275 font.family: "Fira Sans"
276 font.features: { "tnum": 1 }
277
278 property bool today: chrono.date.getFullYear() == model.year && chrono.date.getMonth() == model.month && chrono.date.getDate() == model.day
279
280 text: model.day
281 color: today ? "#ff6699" : "white"
282
283 horizontalAlignment: Text.AlignRight
284 verticalAlignment: Text.AlignVCenter
285 }
286 }
287 }
288 }
289 }
290 }
291 }
292 }
293 }
294 }
295}
diff --git a/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml
new file mode 100644
index 00000000..46302e54
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml
@@ -0,0 +1,112 @@
1import Quickshell
2import QtQuick
3import qs.Services
4import Quickshell.Widgets
5
6Item {
7 width: kbdLabel.contentWidth + 8
8 height: parent.height
9 anchors.verticalCenter: parent.verticalCenter
10
11 WrapperMouseArea {
12 id: kbdMouseArea
13
14 anchors.fill: parent
15
16 hoverEnabled: true
17 cursorShape: Qt.PointingHandCursor
18 enabled: true
19 onClicked: {
20 NiriService.sendCommand({ "Action": { "SwitchLayout": { "layout": "Next" } } }, _ => {})
21 }
22 onWheel: event => {
23 NiriService.sendCommand({ "Action": { "SwitchLayout": { "layout": event.angleDelta > 0 ? "Next" : "Prev" } } }, _ => {})
24 }
25
26 Rectangle {
27 id: kbdWidget
28
29 property var keyboardAbbrev: { "English (programmer Dvorak)": "dvp", "English (US)": "us" }
30
31 anchors.fill: parent
32 color: {
33 if (kbdMouseArea.containsMouse) {
34 return "#33808080";
35 }
36 return "transparent";
37 }
38
39 Text {
40 id: kbdLabel
41
42 font.pointSize: 10
43 font.family: "Fira Sans"
44 color: {
45 if (NiriService.keyboardLayouts?.current_idx === 0)
46 return "#555";
47 return "white";
48 }
49 anchors.centerIn: parent
50
51 text: {
52 const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx];
53 if (!currentLayout)
54 return "";
55 return kbdWidget.keyboardAbbrev[currentLayout] ? kbdWidget.keyboardAbbrev[currentLayout] : currentLayout;
56 }
57 }
58 }
59 }
60
61 PopupWindow {
62 id: tooltip
63
64 property bool nextVisible: kbdMouseArea.containsMouse || tooltipMouseArea.containsMouse
65
66 anchor {
67 item: kbdMouseArea
68 edges: Edges.Bottom | Edges.Left
69 }
70 visible: false
71
72 onNextVisibleChanged: hangTimer.restart()
73
74 Timer {
75 id: hangTimer
76 interval: 100
77 onTriggered: tooltip.visible = tooltip.nextVisible
78 }
79
80 implicitWidth: kbdTooltipText.contentWidth + 16
81 implicitHeight: kbdTooltipText.contentHeight + 16
82 color: "black"
83
84 WrapperMouseArea {
85 id: tooltipMouseArea
86
87 hoverEnabled: true
88 enabled: true
89
90 anchors.fill: parent
91
92 Item {
93 anchors.fill: parent
94
95 Text {
96 id: kbdTooltipText
97
98 anchors.centerIn: parent
99
100 font.pointSize: 10
101 font.family: "Fira Sans"
102 color: "white"
103
104 text: {
105 const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx];
106 return currentLayout || "";
107 }
108 }
109 }
110 }
111 }
112}
diff --git a/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml
new file mode 100644
index 00000000..8410dcda
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml
@@ -0,0 +1,47 @@
1import Quickshell
2import QtQuick
3import Quickshell.Widgets
4import qs.Services
5
6Item {
7 id: root
8
9 width: icon.width + 8
10 height: parent.height
11 anchors.verticalCenter: parent.verticalCenter
12
13 WrapperMouseArea {
14 id: widgetMouseArea
15
16 anchors.fill: parent
17
18 hoverEnabled: true
19 cursorShape: Qt.PointingHandCursor
20
21 onClicked: InhibitorState.lidSwitchInhibited = !InhibitorState.lidSwitchInhibited
22
23 Rectangle {
24 anchors.fill: parent
25 color: {
26 if (widgetMouseArea.containsMouse) {
27 return "#33808080";
28 }
29 return "transparent";
30 }
31
32 Item {
33 anchors.fill: parent
34
35 MaterialDesignIcon {
36 id: icon
37
38 implicitSize: 14
39 anchors.centerIn: parent
40
41 icon: InhibitorState.lidSwitchInhibited ? "laptop-off" : "laptop"
42 color: InhibitorState.lidSwitchInhibited ? "#f28a21" : "#555"
43 }
44 }
45 }
46 }
47}
diff --git a/accounts/gkleen@sif/shell/quickshell/LockSurface.qml b/accounts/gkleen@sif/shell/quickshell/LockSurface.qml
new file mode 100644
index 00000000..f4f8f0cd
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/LockSurface.qml
@@ -0,0 +1,227 @@
1import Quickshell.Widgets
2import QtQuick.Effects
3import QtQuick.Layouts
4import QtQuick
5import QtQuick.Controls
6import QtQuick.Controls.Fusion
7import qs.Services
8import QtQml
9
10Item {
11 id: lockSurface
12
13 property var screen
14 property list<var> messages: []
15 property bool responseRequired: false
16 property bool responseVisible: false
17 property string currentText: ""
18 property bool authRunning: false
19
20 signal response(string responseText)
21
22 anchors.fill: parent
23
24 Item {
25 id: background
26
27 anchors.fill: parent
28
29 property Img current: one
30 property string source: selector.selected
31
32 WallpaperSelector {
33 id: selector
34 seed: lockSurface.screen?.name || ""
35 }
36
37 onSourceChanged: {
38 if (!source)
39 current = null;
40 else if (current === one)
41 two.update()
42 else
43 one.update()
44 }
45
46 Img { id: one }
47 Img { id: two }
48
49 component Img: Item {
50 id: img
51
52 property string source
53
54 function update() {
55 source = background.source || ""
56 }
57
58 anchors.fill: parent
59
60 Image {
61 id: imageSource
62
63 source: img.source
64 sourceSize: Qt.size(parent.width, parent.height)
65 fillMode: Image.PreserveAspectCrop
66 smooth: true
67 visible: false
68 asynchronous: true
69 cache: false
70
71 onStatusChanged: {
72 if (status === Image.Ready) {
73 background.current = img
74 }
75 }
76 }
77
78 MultiEffect {
79 id: imageEffect
80
81 source: imageSource
82 anchors.fill: parent
83 blurEnabled: true
84 blur: 1
85 blurMax: 64
86 blurMultiplier: 2
87
88 opacity: 0
89
90 states: State {
91 name: "visible"
92 when: background.current === img
93
94 PropertyChanges {
95 imageEffect.opacity: 1
96 }
97 StateChangeScript {
98 name: "unloadOther"
99 script: {
100 if (img === one)
101 two.source = ""
102 if (img === two)
103 one.source = ""
104 }
105 }
106 }
107
108 transitions: Transition {
109 SequentialAnimation {
110 NumberAnimation {
111 target: imageEffect
112 properties: "opacity"
113 duration: {
114 if (img === one && two.source == "" || img === two && one.source == "")
115 return 0;
116 return 5000;
117 }
118 easing.type: Easing.OutCubic
119 }
120 ScriptAction {
121 scriptName: "unloadOther"
122 }
123 }
124 }
125 }
126 }
127 }
128
129 Item {
130 anchors {
131 top: lockSurface.top
132 left: lockSurface.left
133 right: lockSurface.right
134 }
135
136 implicitWidth: lockSurface.width
137 implicitHeight: 21
138
139 Rectangle {
140 anchors.fill: parent
141 color: Qt.rgba(0, 0, 0, 0.75)
142 }
143
144 Clock {
145 anchors.centerIn: parent
146 calendarPopup: false
147 }
148 }
149
150 WrapperRectangle {
151 id: unlockUi
152
153 Keys.onPressed: event => {
154 if (!lockSurface.authRunning) {
155 event.accepted = true;
156 lockSurface.authRunning = true;
157 }
158 }
159 focus: !passwordBox.visible
160
161 visible: lockSurface.authRunning
162
163 color: Qt.rgba(0, 0, 0, 0.75)
164 margin: 8
165
166 anchors.centerIn: parent
167
168 ColumnLayout {
169 spacing: 4
170
171 BusyIndicator {
172 visible: running
173 running: !Array.from(lockSurface.messages).length && !lockSurface.responseRequired
174 }
175
176 Repeater {
177 model: lockSurface.messages
178
179 Text {
180 required property var modelData
181
182 font.pointSize: 10
183 font.family: "Fira Sans"
184 color: modelData.error ? "#f28a21" : "#ffffff"
185
186 text: String(modelData.text).trim()
187
188 Layout.fillWidth: true
189 horizontalAlignment: Text.AlignHCenter
190 }
191 }
192
193 TextField {
194 id: passwordBox
195
196 visible: lockSurface.responseRequired
197 echoMode: lockSurface.responseVisible ? TextInput.Normal : TextInput.Password
198 inputMethodHints: Qt.ImhSensitiveData
199
200 onTextChanged: lockSurface.currentText = passwordBox.text
201 onAccepted: {
202 passwordBox.readOnly = true;
203 lockSurface.response(lockSurface.currentText);
204 }
205
206 Connections {
207 target: lockSurface
208 function onCurrentTextChanged() {
209 passwordBox.text = lockSurface.currentText
210 }
211 }
212 Connections {
213 target: lockSurface
214 function onResponseRequiredChanged() {
215 if (lockSurface.responseRequired)
216 passwordBox.readOnly = false;
217 passwordBox.focus = true;
218 passwordBox.selectAll();
219 }
220 }
221
222 Layout.topMargin: 4
223 Layout.fillWidth: true
224 }
225 }
226 }
227}
diff --git a/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml
new file mode 100644
index 00000000..996fd41b
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml
@@ -0,0 +1,132 @@
1import Quickshell
2import Quickshell.Wayland
3import Quickshell.Io
4import Quickshell.Services.Pam
5import Quickshell.Services.Mpris
6import Custom as Custom
7import qs.Services
8import QtQml
9
10Scope {
11 id: lockscreen
12
13 property string currentText: ""
14
15 PamContext {
16 id: pam
17
18 property list<var> messages: []
19
20 config: "quickshell"
21 onCompleted: result => {
22 if (result === PamResult.Success) {
23 lock.locked = false;
24 }
25 }
26 onPamMessage: {
27 messages = Array.from(messages).concat([{ "text": pam.message, "error": pam.messageIsError }])
28 }
29 onActiveChanged: {
30 messages = [];
31 }
32 }
33
34 IpcHandler {
35 target: "Lockscreen"
36
37 function setLocked(locked: bool): void { lock.locked = locked; }
38 function getLocked(): bool { return lock.locked; }
39 }
40
41 Connections {
42 target: Custom.Systemd
43 function onSleep(before: bool) {
44 console.log(`received prepare for sleep ${before}`);
45 if (before)
46 lock.locked = true;
47 }
48 function onLock() { lock.locked = true; }
49 function onUnlock() { lock.locked = false; }
50 }
51
52 IdleMonitor {
53 id: idleMonitor
54 enabled: !lock.secure
55 timeout: 600
56 respectInhibitors: true
57
58 onIsIdleChanged: {
59 if (idleMonitor.isIdle)
60 lock.locked = true;
61 }
62 }
63
64 Custom.SystemdInhibitor {
65 enabled: !lock.secure
66
67 what: Custom.SystemdInhibitorParams.Sleep
68 who: "quickshell"
69 why: "Lock session"
70 mode: Custom.SystemdInhibitorParams.Delay
71 }
72
73 Binding {
74 target: NotificationManager
75 property: "lockscreenActive"
76 value: lock.locked
77 }
78
79 WlSessionLock {
80 id: lock
81
82 onLockStateChanged: {
83 if (!locked && pam.active)
84 pam.abort();
85
86 if (locked) {
87 NiriService.sendCommand({ "Action": { "PowerOffMonitors": {} } }, _ => {});
88 Custom.KeePassXC.lockAllDatabases();
89 Array.from(MprisProxy.players).forEach(player => {
90 if (player.canPause && player.isPlaying)
91 player.pause();
92 });
93 // Custom.Systemd.stopUserUnit("gpg-agent.service", "replace");
94 GpgAgent.reloadAgent();
95 }
96 }
97 Component.onCompleted: { (_ => {})(MprisProxy.players); }
98
99 onSecureStateChanged: Custom.Systemd.lockedHint = lock.secure
100
101 WlSessionLockSurface {
102 id: lockSurface
103
104 color: "black"
105
106 LockSurface {
107 id: surfaceContent
108
109 onResponse: responseText => pam.respond(responseText)
110 onAuthRunningChanged: {
111 if (authRunning)
112 pam.start();
113 }
114 Connections {
115 target: pam
116 function onMessagesChanged() { surfaceContent.messages = pam.messages; }
117 function onResponseRequiredChanged() { surfaceContent.responseRequired = pam.responseRequired; }
118 function onActiveChanged() { surfaceContent.authRunning = pam.active; }
119 }
120 onCurrentTextChanged: lockscreen.currentText = currentText
121 Connections {
122 target: lockscreen
123 function onCurrentTextChanged() { surfaceContent.currentText = lockscreen.currentText; }
124 }
125 Connections {
126 target: lockSurface
127 function onScreenChanged() { surfaceContent.screen = lockSurface.screen; }
128 }
129 }
130 }
131 }
132}
diff --git a/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml b/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml
new file mode 100644
index 00000000..155a009e
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml
@@ -0,0 +1,35 @@
1import QtQuick
2import QtQuick.Effects
3
4Item {
5 id: root
6
7 required property string icon
8 property color color: "white"
9
10 property real implicitSize: 0
11
12 readonly property real actualSize: Math.min(root.width, root.height)
13
14 implicitWidth: root.implicitSize
15 implicitHeight: root.implicitSize
16
17 Image {
18 id: sourceImage
19 source: "file://" + @mdi@ + "/svg/" + root.icon + ".svg"
20 anchors.fill: parent
21 fillMode: Image.PreserveAspectFit
22
23 sourceSize.width: root.actualSize
24 sourceSize.height: root.actualSize
25
26 layer.enabled: true
27 layer.effect: MultiEffect {
28 id: effect
29
30 brightness: 1
31 colorization: 1
32 colorizationColor: root.color
33 }
34 }
35}
diff --git a/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml b/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml
new file mode 100644
index 00000000..beff205c
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml
@@ -0,0 +1,30 @@
1import QtQml
2import Quickshell
3import Quickshell.Wayland
4import qs.Services
5import Custom as Custom
6
7Scope {
8 IdleMonitor {
9 id: idleMonitor30
10 timeout: 30
11
12 onIsIdleChanged: Custom.Systemd.setIdleHint(idleMonitor30.isIdle)
13 }
14 IdleMonitor {
15 id: idleMonitor540
16 timeout: 540
17
18 onIsIdleChanged: {
19 if (idleMonitor540.isIdle)
20 NiriService.sendCommand({ "Action": { "PowerOffMonitors": {} } }, _ => {});
21 }
22 }
23 Connections {
24 target: Custom.Systemd
25 function onSleep(before: bool) {
26 if (!before)
27 NiriService.sendCommand({ "Action": { "PowerOnMonitors": {} } }, _ => {});
28 }
29 }
30}
diff --git a/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml
new file mode 100644
index 00000000..cc0e49b1
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml
@@ -0,0 +1,340 @@
1import QtQml
2import QtQml.Models
3import QtQuick
4import Quickshell
5import Quickshell.Widgets
6import Quickshell.Wayland
7import qs.Services
8import QtQuick.Layouts
9import Quickshell.Services.Notifications
10
11Scope {
12 id: root
13
14 property var activeScreen: Array.from(Quickshell.screens).find(screen => screen.name === Array.from(NiriService.workspaces).find(ws => ws.is_focused)?.output) ?? null
15
16 Instantiator {
17 id: notifsRepeater
18
19 model: ScriptModel {
20 values: NotificationManager.groups
21 }
22
23 delegate: PanelWindow {
24 id: notifWindow
25
26 visible: NotificationManager.active
27
28 screen: root.activeScreen
29
30 WlrLayershell.namespace: "notifications"
31
32 required property var modelData
33 required property var index
34
35 property int activeIx: modelData.length - 1
36 onModelDataChanged: {
37 notifWindow.activeIx = modelData.length - 1;
38 }
39
40 property color textColor: {
41 if (notifWindow.modelData?.[notifWindow.activeIx]?.urgency == NotificationUrgency.Low)
42 return "#ff999999";
43 return "white";
44 }
45 property color backgroundColor: {
46 if (notifWindow.modelData?.[notifWindow.activeIx]?.urgency == NotificationUrgency.Critical)
47 return "#dd900000";
48 return "black";
49 }
50
51 anchors {
52 right: true
53 top: true
54 }
55
56 readonly property real spaceAbove: {
57 var res = 0;
58 for (let i = 0; i < notifWindow.index; i++) {
59 (_ => {})(notifsRepeater.objectAt(i).modelData);
60 res += notifsRepeater.objectAt(i).height + 8;
61 }
62 return res;
63 }
64
65 margins {
66 right: 26 + 8
67 top: 8 + spaceAbove
68 }
69
70 color: "transparent"
71
72 implicitHeight: Math.max(notifCount.visible ? notifCount.contentHeight : 0, notifSummary.contentHeight) + (notifBody.visible ? 8 + notifBody.contentHeight : 0) + (notifActions.visible ? 8 + notifActions.height : 0) + (notifTime.visible ? 8 + notifTime.contentHeight : 0) + 16
73 implicitWidth: 400
74
75 WrapperMouseArea {
76 enabled: true
77
78 anchors.fill: parent
79
80 cursorShape: Qt.PointingHandCursor
81
82 onClicked: {
83 for (const notif of notifWindow.modelData)
84 notif.dismiss();
85 }
86
87 property real angleRem: 0
88 property real sensitivity: 1 / 120
89 onWheel: event => {
90 angleRem += event.angleDelta.y;
91 const d = Math.round(angleRem * sensitivity);
92 angleRem -= d / sensitivity;
93 notifWindow.activeIx = ((notifWindow.modelData?.length ?? 1) + notifWindow.activeIx - d) % (notifWindow.modelData?.length ?? 1);
94 }
95
96 Rectangle {
97 color: notifWindow.backgroundColor
98 anchors.fill: parent
99 border {
100 color: Qt.hsla(195/360, 1, 0.45, 1)
101 width: 2
102 }
103
104 GridLayout {
105 id: notifLayout
106
107 width: 400 - 16
108 anchors.fill: parent
109 anchors.margins: 8
110 columnSpacing: 8
111 rowSpacing: 8
112
113 columns: notifImage.visible ? 3 : 2
114 rows: {
115 var res = 1;
116 if (notifBody.visible)
117 res += 1;
118 if (notifActions.visible)
119 res += 1;
120 if (notifTime.visible)
121 res += 1;
122 return res;
123 }
124
125 Text {
126 id: notifCount
127
128 visible: notifWindow.modelData?.length > 1 ?? false
129 text: `${notifWindow.activeIx + 1}/${notifWindow.modelData?.length ?? ""}`
130
131 font.pointSize: 10
132 font.family: "Fira Sans"
133 font.bold: true
134 font.features: { "tnum": 1 }
135 color: notifWindow.textColor
136 maximumLineCount: 1
137
138 Layout.fillWidth: false
139 Layout.row: 0
140 Layout.column: notifImage.visible ? 1 : 0
141 }
142
143 Text {
144 id: notifSummary
145
146 text: notifWindow.modelData?.[notifWindow.activeIx]?.summary ?? ""
147
148 font.pointSize: 10
149 font.family: "Fira Sans"
150 font.italic: true
151 color: notifWindow.textColor
152 maximumLineCount: 1
153 elide: Text.ElideRight
154
155 Layout.fillWidth: true
156 Layout.row: 0
157 Layout.column: (notifCount.visible ? 1 : 0) + (notifImage.visible ? 1 : 0)
158 Layout.columnSpan: notifCount.visible ? 1 : 2
159 }
160
161 Image {
162 id: notifImage
163
164 visible: (notifWindow.modelData?.[notifWindow.activeIx]?.image || notifWindow.modelData?.[notifWindow.activeIx]?.appIcon) ?? false
165
166 onStatusChanged: {
167 if (notifImage.status == Image.Error)
168 notifImage.visible = false;
169 }
170
171 source: (notifWindow.modelData?.[notifWindow.activeIx]?.image || notifWindow.modelData?.[notifWindow.activeIx]?.appIcon) ?? ""
172 fillMode: Image.PreserveAspectFit
173 asynchronous: true
174 smooth: true
175 mipmap: true
176
177 Layout.maximumWidth: 50
178 Layout.column: 0
179 Layout.row: 0
180 Layout.fillHeight: true
181 Layout.rowSpan: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0)
182 }
183
184 Text {
185 id: notifBody
186
187 visible: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? false
188 text: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? ""
189 textFormat: Text.RichText
190 wrapMode: Text.Wrap
191
192 font.pointSize: 10
193 font.family: "Fira Sans"
194 color: notifWindow.textColor
195
196 Layout.fillWidth: true
197 Layout.row: 1
198 Layout.column: notifImage.visible ? 1 : 0
199 Layout.columnSpan: notifCount.visible ? 2 : 1
200 }
201
202 Text {
203 id: notifTime
204
205 Connections {
206 target: NotificationManager.clock
207 function onDateChanged() {
208 notifTime.text = NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime);
209 }
210 }
211
212 visible: notifTime.text && notifTime.text !== "now"
213 text: NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime)
214
215 font.pointSize: 8
216 font.family: "Fira Sans"
217 font.italic: true
218 color: "#555"
219 maximumLineCount: 1
220 horizontalAlignment: Text.AlignRight
221
222 Layout.fillWidth: true
223 Layout.row: notifBody.visible ? 2 : 1
224 Layout.column: notifImage.visible ? 1 : 0
225 Layout.columnSpan: 2
226 }
227
228 RowLayout {
229 id: notifActions
230
231 visible: notifWindow.modelData?.[notifWindow.activeIx]?.actions.length > 0 ?? false
232
233 spacing: 8
234 uniformCellSizes: true
235
236 width: 400 - 16
237 Layout.row: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0)
238 Layout.column: 0
239 Layout.columnSpan: 2 + (notifImage.visible ? 1 : 0)
240
241 Repeater {
242 model: ScriptModel {
243 values: notifWindow.modelData?.[notifWindow.activeIx]?.actions
244 }
245
246 delegate: WrapperMouseArea {
247 id: actionMouseArea
248
249 required property var modelData
250
251 height: actionLabelWrapper.implicitHeight
252 Layout.fillWidth: true
253 Layout.horizontalStretchFactor: 1
254
255 hoverEnabled: true
256 cursorShape: Qt.PointingHandCursor
257
258 onClicked: actionMouseArea.modelData?.invoke()
259
260 Rectangle {
261 anchors.fill: parent
262
263 color: actionMouseArea.containsMouse ? "#20ffffff" : "transparent"
264
265 border {
266 width: 2
267 color: "#20ffffff"
268 }
269
270 WrapperItem {
271 id: actionLabelWrapper
272
273 margin: 8
274 anchors.centerIn: parent
275
276 RowLayout {
277 id: actionLabelLayout
278
279 spacing: 8
280
281 IconImage {
282 id: actionIcon
283
284 visible: notifWindow.modelData?.[notifWindow.activeIx]?.hasActionIcons
285
286 onStatusChanged: {
287 if (actionIcon.status == Image.Error)
288 actionIcon.visible = false;
289 }
290
291 implicitSize: 16
292 source: {
293 if (!actionIcon.visible)
294 return "";
295
296 let icon = actionMouseArea.modelData?.identifier ?? ""
297 if (icon.includes("?path=")) {
298 const split = icon.split("?path=")
299 if (split.length !== 2)
300 return icon
301 const name = split[0]
302 const path = split[1]
303 const fileName = name.substring(
304 name.lastIndexOf("/") + 1)
305 return `file://${path}/${fileName}`
306 }
307 return icon
308 }
309 asynchronous: true
310 smooth: true
311 mipmap: true
312
313 Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
314 }
315
316 Text {
317 id: actionLabel
318
319 visible: actionMouseArea.modelData?.text ?? false
320
321 text: actionMouseArea.modelData?.text ?? ""
322
323 font.pointSize: 10
324 font.family: "Fira Sans"
325 color: notifWindow.textColor
326 maximumLineCount: 1
327 elide: Text.ElideRight
328 }
329 }
330 }
331 }
332 }
333 }
334 }
335 }
336 }
337 }
338 }
339 }
340}
diff --git a/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml
new file mode 100644
index 00000000..b58467b3
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml
@@ -0,0 +1,266 @@
1import Quickshell
2import QtQuick
3import Quickshell.Widgets
4import qs.Services
5import QtQuick.Controls
6import QtQuick.Layouts
7import QtQuick.Shapes
8
9Item {
10 id: root
11
12 width: icon.width + 8
13 height: parent.height
14 anchors.verticalCenter: parent.verticalCenter
15
16 WrapperMouseArea {
17 id: widgetMouseArea
18
19 anchors.fill: parent
20
21 hoverEnabled: true
22 cursorShape: Qt.PointingHandCursor
23
24 onClicked: NotificationManager.displayInhibited = !NotificationManager.displayInhibited
25
26 Rectangle {
27 anchors.fill: parent
28 color: {
29 if (widgetMouseArea.containsMouse) {
30 return "#33808080";
31 }
32 return "transparent";
33 }
34
35 Item {
36 anchors.fill: parent
37
38 MaterialDesignIcon {
39 id: icon
40
41 implicitSize: 14
42 anchors.centerIn: parent
43
44 icon: NotificationManager.active ? "message" : "message-off"
45 color: {
46 if (!NotificationManager.active && !NotificationManager.displayInhibited)
47 return "#f28a21";
48 if (NotificationManager.displayInhibited)
49 return "white";
50 return "#555";
51 }
52 }
53 }
54 }
55 }
56
57 Loader {
58 id: tooltipLoader
59
60 active: false
61
62 Connections {
63 target: widgetMouseArea
64 function onContainsMouseChanged() {
65 if (widgetMouseArea.containsMouse)
66 tooltipLoader.active = true;
67 }
68 }
69
70 sourceComponent: PopupWindow {
71 id: tooltip
72
73 property bool nextVisible: NotificationManager.active && (widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse)
74
75 anchor {
76 item: widgetMouseArea
77 edges: Edges.Bottom | Edges.Left
78 }
79 visible: false
80
81 onNextVisibleChanged: hangTimer.restart()
82
83 Timer {
84 id: hangTimer
85 interval: tooltip.visible ? 100 : 500
86 onTriggered: {
87 tooltip.visible = tooltip.nextVisible;
88 if (!tooltip.visible)
89 tooltipLoader.active = false;
90 }
91 }
92
93 implicitWidth: 400
94 implicitHeight: Math.min(tooltip.screen.height * 0.66, Math.max(100, scroll.contentHeight + 16))
95 color: "black"
96
97 WrapperMouseArea {
98 id: tooltipMouseArea
99
100 hoverEnabled: true
101 enabled: true
102
103 anchors.fill: parent
104
105 WrapperItem {
106 margin: 8
107
108 ScrollView {
109 id: scroll
110
111 contentWidth: availableWidth
112 // ScrollBar.vertical.policy: ScrollBar.AlwaysOn
113
114 ColumnLayout {
115 id: historyLayout
116 anchors {
117 left: parent.left
118 right: parent.right
119 }
120
121 spacing: 8
122
123 Repeater {
124 model: ScriptModel {
125 values: [...NotificationManager.history].reverse().map(o => o.notification)
126 }
127
128 delegate: GridLayout {
129 id: notif
130
131 Layout.fillWidth: true
132 Layout.preferredHeight: notifSummary.contentHeight + (notifBody.visible ? notifBody.contentHeight + 8 : 0) + (notifSep.visible ? notifSep.height + 8 : 0) + notifTime.contentHeight + 8
133
134 required property var modelData
135 required property int index
136
137 columnSpacing: 8
138 rowSpacing: 8
139
140 columns: notifImage.visible ? 2 : 1
141 rows: {
142 var res = 2;
143 if (notifBody.visible)
144 res += 1;
145 if (notifSep.visible)
146 res += 1;
147 return res;
148 }
149
150 Shape {
151 id: notifSep
152
153 visible: notif.index != 0
154
155 height: 2
156 width: 400 - 32
157
158 ShapePath {
159 strokeWidth: 2
160 strokeColor: "#20ffffff"
161 startX: 0; startY: 0;
162 PathLine { x: 400 - 32; y: 0; }
163 }
164
165 Layout.row: 0
166 Layout.column: 0
167 Layout.columnSpan: notifImage.visible ? 2 : 1
168 Layout.alignment: Qt.AlignHCenter
169 }
170
171 Text {
172 id: notifSummary
173
174 text: notif.modelData?.summary ?? ""
175
176 font.pointSize: 10
177 font.family: "Fira Sans"
178 font.italic: true
179 color: "white"
180 maximumLineCount: 1
181 elide: Text.ElideRight
182
183 Layout.fillWidth: true
184 Layout.row: notifSep.visible ? 1 : 0
185 Layout.column: notifImage.visible ? 1 : 0
186 }
187
188 Image {
189 id: notifImage
190
191 visible: (notif.modelData?.image || notif.modelData?.appIcon) ?? false
192
193 onStatusChanged: {
194 if (notifImage.status == Image.Error)
195 notifImage.visible = false;
196 }
197
198 source: (notif.modelData?.image || notif.modelData?.appIcon) ?? ""
199 fillMode: Image.PreserveAspectFit
200 asynchronous: true
201 smooth: true
202 mipmap: true
203
204 Layout.maximumWidth: 50
205 Layout.column: 0
206 Layout.row: notifSep.visible ? 1 : 0
207 Layout.fillHeight: true
208 Layout.rowSpan: notifBody.visible ? 3 : 2
209 }
210
211 Text {
212 id: notifBody
213
214 visible: notif.modelData?.body ?? false
215 text: notif.modelData?.body ?? ""
216 textFormat: Text.RichText
217 wrapMode: Text.Wrap
218
219 font.pointSize: 10
220 font.family: "Fira Sans"
221 color: "white"
222
223 Layout.fillWidth: true
224 Layout.row: notifSep.visible ? 2 : 1
225 Layout.column: notifImage.visible ? 1 : 0
226 }
227
228 Text {
229 id: notifTime
230
231 Connections {
232 target: NotificationManager.clock
233 function onDateChanged() {
234 notifTime.text = NotificationManager.formatTime(notif.modelData?.receivedTime);
235 }
236 }
237
238 text: NotificationManager.formatTime(notif.modelData?.receivedTime)
239
240 font.pointSize: 8
241 font.family: "Fira Sans"
242 font.italic: true
243 color: "#555"
244 maximumLineCount: 1
245 horizontalAlignment: Text.AlignRight
246
247 Layout.fillWidth: true
248 Layout.row: {
249 var res = 1;
250 if (notifSep.visible)
251 res += 1;
252 if (notifBody.visible)
253 res += 1;
254 return res;
255 }
256 Layout.column: notifImage.visible ? 1 : 0
257 }
258 }
259 }
260 }
261 }
262 }
263 }
264 }
265 }
266}
diff --git a/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml
new file mode 100644
index 00000000..9c6b65a4
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml
@@ -0,0 +1,483 @@
1import QtQuick
2import QtQuick.Layouts
3import QtQuick.Controls.Fusion
4import Quickshell
5import Quickshell.Services.Pipewire
6import Quickshell.Widgets
7
8Item {
9 height: parent.height
10 width: volumeIcon.width + 8
11 anchors.verticalCenter: parent.verticalCenter
12
13 PwObjectTracker {
14 objects: [Pipewire.defaultAudioSink]
15 }
16
17 WrapperMouseArea {
18 id: widgetMouseArea
19
20 anchors.fill: parent
21 hoverEnabled: true
22 cursorShape: Qt.PointingHandCursor
23
24 onClicked: {
25 if (!Pipewire.defaultAudioSink)
26 return;
27 Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted;
28 }
29
30 property real sensitivity: (1 / 40) / 120
31 onWheel: event => {
32 if (!Pipewire.defaultAudioSink)
33 return;
34 Pipewire.defaultAudioSink.audio.volume += event.angleDelta.y * sensitivity;
35 }
36
37 Rectangle {
38 id: volumeWidget
39
40 anchors.fill: parent
41 color: {
42 if (widgetMouseArea.containsMouse)
43 return "#33808080";
44 return "transparent";
45 }
46
47 Item {
48 anchors.fill: parent
49
50 MaterialDesignIcon {
51 id: volumeIcon
52
53 implicitSize: 14
54 anchors.centerIn: parent
55
56 icon: {
57 if (!Pipewire.defaultAudioSink || Pipewire.defaultAudioSink.audio.muted)
58 return "volume-off";
59 if (Pipewire.defaultAudioSink.audio.volume <= 0.33)
60 return "volume-low";
61 if (Pipewire.defaultAudioSink.audio.volume <= 0.67)
62 return "volume-medium";
63 return "volume-high";
64 }
65 color: "#555"
66 }
67 }
68 }
69 }
70
71 Loader {
72 id: tooltipLoader
73
74 active: false
75
76 Connections {
77 target: widgetMouseArea
78 function onContainsMouseChanged() {
79 if (widgetMouseArea.containsMouse)
80 tooltipLoader.active = true;
81 }
82 }
83
84 PwObjectTracker {
85 objects: Pipewire.devices
86 }
87 PwObjectTracker {
88 objects: Pipewire.nodes
89 }
90
91 sourceComponent: PopupWindow {
92 id: tooltip
93
94 property bool openPopup: false
95 property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse || openPopup
96
97 anchor {
98 item: widgetMouseArea
99 edges: Edges.Bottom | Edges.Left
100 }
101 visible: false
102
103 onNextVisibleChanged: hangTimer.restart()
104
105 Timer {
106 id: hangTimer
107 interval: 100
108 onTriggered: {
109 tooltip.visible = tooltip.nextVisible;
110 if (!tooltip.visible)
111 tooltipLoader.active = false;
112 }
113 }
114
115 implicitWidth: tooltipContent.width
116 implicitHeight: tooltipContent.height
117 color: "transparent"
118
119 Rectangle {
120 width: tooltip.width
121 height: tooltipLayout.childrenRect.height + 16
122 color: "black"
123 }
124
125 WrapperItem {
126 id: tooltipContent
127
128 bottomMargin: Math.max(0, 200 - tooltipLayout.implicitHeight)
129
130 WrapperMouseArea {
131 id: tooltipMouseArea
132
133 hoverEnabled: true
134 enabled: true
135
136 WrapperItem {
137 margin: 8
138 bottomMargin: 8
139
140 GridLayout {
141 id: tooltipLayout
142
143 columns: 4
144
145 Repeater {
146 model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device")
147
148 Item {
149 id: descItem
150
151 required property var modelData
152 required property int index
153
154 Layout.column: 0
155 Layout.row: index
156
157 implicitWidth: descText.contentWidth
158 implicitHeight: descText.contentHeight
159
160 Text {
161 id: descText
162
163 color: "white"
164 font.pointSize: 10
165 font.family: "Fira Sans"
166
167 text: descItem.modelData.description
168 }
169 }
170 }
171
172 Repeater {
173 id: defaultSinkRepeater
174
175 model: {
176 Array.from(Pipewire.devices.values)
177 .filter(dev => dev.type == "Audio/Device")
178 .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSink && node.device?.id == device.id ));
179 }
180
181 Item {
182 id: defaultSinkItem
183
184 required property var modelData
185 required property int index
186
187 visible: Boolean(modelData)
188
189 PwObjectTracker {
190 objects: [defaultSinkItem.modelData]
191 }
192
193 Layout.column: 1
194 Layout.row: index
195
196 Layout.fillHeight: true
197
198 implicitWidth: 16 + 8
199
200 WrapperMouseArea {
201 id: defaultSinkMouseArea
202
203 anchors.fill: parent
204 hoverEnabled: true
205 cursorShape: Qt.PointingHandCursor
206
207 onClicked: {
208 Pipewire.preferredDefaultAudioSink = defaultSinkItem.modelData
209 }
210
211 onWheel: event => scrollVolume(event);
212 property real sensitivity: (1 / 40) / 120
213 function scrollVolume(event) {
214 defaultSinkItem.modelData.audio.volume += event.angleDelta.y * sensitivity;
215 }
216
217 Rectangle {
218 id: defaultSinkWidget
219
220 anchors.fill: parent
221 color: {
222 if (defaultSinkMouseArea.containsMouse)
223 return "#33808080";
224 return "transparent";
225 }
226
227 MaterialDesignIcon {
228 width: 16
229 height: 16
230 anchors.centerIn: parent
231
232 icon: {
233 if (defaultSinkItem.modelData?.id == Pipewire.defaultAudioSink?.id)
234 return "speaker";
235 return "speaker-off";
236 }
237 color: icon == "speaker" ? "white" : "#555"
238 }
239 }
240 }
241
242 PopupWindow {
243 id: volumeTooltip
244
245 property bool nextVisible: defaultSinkMouseArea.containsMouse || volumeTooltipMouseArea.containsMouse
246
247 anchor {
248 item: defaultSinkMouseArea
249 edges: Edges.Bottom | Edges.Left
250 }
251 visible: false
252
253 onNextVisibleChanged: volumeHangTimer.restart()
254
255 onVisibleChanged: tooltip.openPopup = volumeTooltip.visible
256
257 Timer {
258 id: volumeHangTimer
259 interval: 100
260 onTriggered: volumeTooltip.visible = volumeTooltip.nextVisible
261 }
262
263 implicitWidth: volumeTooltipText.contentWidth + 16
264 implicitHeight: volumeTooltipText.contentHeight + 16
265 color: "black"
266
267 WrapperMouseArea {
268 id: volumeTooltipMouseArea
269
270 hoverEnabled: true
271 enabled: true
272
273 onWheel: event => defaultSinkMouseArea.scrollVolume(event);
274
275 anchors.fill: parent
276
277 Item {
278 anchors.fill: parent
279
280 Text {
281 id: volumeTooltipText
282
283 anchors.centerIn: parent
284
285 font.pointSize: 10
286 font.family: "Fira Sans"
287 color: "white"
288
289 text: `${Math.round(defaultSinkItem.modelData?.audio?.volume * 100)}%`
290 }
291 }
292 }
293 }
294 }
295 }
296
297 Repeater {
298 id: defaultSourceRepeater
299
300 model: {
301 Array.from(Pipewire.devices.values)
302 .filter(dev => dev.type == "Audio/Device")
303 .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSource && node.device?.id == device.id ));
304 }
305
306 Item {
307 id: defaultSourceItem
308
309 required property var modelData
310 required property int index
311
312 visible: Boolean(modelData)
313
314 PwObjectTracker {
315 objects: [defaultSourceItem.modelData]
316 }
317
318 Layout.column: 2
319 Layout.row: index
320
321 Layout.fillHeight: true
322
323 implicitWidth: 16 + 8
324
325 WrapperMouseArea {
326 id: defaultSourceMouseArea
327
328 anchors.fill: parent
329 hoverEnabled: true
330 cursorShape: Qt.PointingHandCursor
331
332 onClicked: {
333 Pipewire.preferredDefaultAudioSource = defaultSourceItem.modelData
334 }
335
336 onWheel: event => scrollVolume(event);
337 property real sensitivity: (1 / 40) / 120
338 function scrollVolume(event) {
339 defaultSourceItem.modelData.audio.volume += event.angleDelta.y * sensitivity;
340 }
341
342 Rectangle {
343 id: defaultSourceWidget
344
345 anchors.fill: parent
346 color: {
347 if (defaultSourceMouseArea.containsMouse)
348 return "#33808080";
349 return "transparent";
350 }
351
352 MaterialDesignIcon {
353 width: 16
354 height: 16
355 anchors.centerIn: parent
356
357 icon: {
358 if (defaultSourceItem.modelData?.id == Pipewire.defaultAudioSource?.id)
359 return "microphone";
360 return "microphone-off";
361 }
362 color: icon == "microphone" ? "white" : "#555"
363 }
364 }
365 }
366
367 PopupWindow {
368 id: volumeTooltip
369
370 property bool nextVisible: defaultSourceMouseArea.containsMouse || volumeTooltipMouseArea.containsMouse
371
372 anchor {
373 item: defaultSourceMouseArea
374 edges: Edges.Bottom | Edges.Left
375 }
376 visible: false
377
378 onNextVisibleChanged: volumeHangTimer.restart()
379
380 onVisibleChanged: tooltip.openPopup = volumeTooltip.visible
381
382 Timer {
383 id: volumeHangTimer
384 interval: 100
385 onTriggered: volumeTooltip.visible = volumeTooltip.nextVisible
386 }
387
388 implicitWidth: volumeTooltipText.contentWidth + 16
389 implicitHeight: volumeTooltipText.contentHeight + 16
390 color: "black"
391
392 WrapperMouseArea {
393 id: volumeTooltipMouseArea
394
395 hoverEnabled: true
396 enabled: true
397
398 onWheel: event => defaultSourceMouseArea.scrollVolume(event);
399
400 anchors.fill: parent
401
402 Item {
403 anchors.fill: parent
404
405 Text {
406 id: volumeTooltipText
407
408 anchors.centerIn: parent
409
410 font.pointSize: 10
411 font.family: "Fira Sans"
412 color: "white"
413
414 text: `${Math.round(defaultSourceItem.modelData?.audio?.volume * 100)}%`
415 }
416 }
417 }
418 }
419 }
420 }
421
422 Repeater {
423 id: profileRepeater
424
425 model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device")
426
427 Item {
428 id: profileItem
429
430 required property var modelData
431 required property int index
432
433 PwObjectTracker {
434 objects: [profileItem.modelData]
435 }
436
437 Layout.column: 3
438 Layout.row: index
439
440 Layout.fillWidth: true
441
442 implicitWidth: Math.max(profileBox.implicitWidth, 300)
443 implicitHeight: profileBox.height
444
445 ComboBox {
446 id: profileBox
447
448 model: profileItem.modelData.profiles
449
450 textRole: "description"
451 valueRole: "index"
452 onActivated: profileItem.modelData.setProfile(currentValue)
453
454 anchors.fill: parent
455
456 implicitContentWidthPolicy: ComboBox.WidestText
457
458 Connections {
459 target: profileItem.modelData
460 function onCurrentProfileChanged() {
461 profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile);
462 }
463 }
464 Component.onCompleted: {
465 profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile);
466 }
467
468 Connections {
469 target: profileBox.popup
470 function onVisibleChanged() {
471 tooltip.openPopup = profileBox.popup.visible
472 }
473 }
474 }
475 }
476 }
477 }
478 }
479 }
480 }
481 }
482 }
483}
diff --git a/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml b/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml
new file mode 100644
index 00000000..d7ffadfe
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml
@@ -0,0 +1,49 @@
1import QtQuick
2import QtQuick.Layouts
3import Quickshell
4import Quickshell.Widgets
5import qs.Services
6
7Item {
8 height: parent.height
9 width: layout.childrenRect.width
10 anchors.verticalCenter: parent.verticalCenter
11
12 visible: Array.from(Privacy.activeItems).length > 0
13
14 RowLayout {
15 id: layout
16
17 anchors.fill: parent
18
19 spacing: 8
20
21 Repeater {
22 model: Privacy.activeItems
23
24 Item {
25 id: privacyItem
26
27 required property var modelData;
28
29 height: parent.height
30 width: icon.width
31
32 MaterialDesignIcon {
33 id: icon
34
35 implicitSize: 14
36 anchors.centerIn: parent
37
38 icon: {
39 if (privacyItem.modelData == Privacy.Item.Microphone)
40 return "microphone";
41 if (privacyItem.modelData == Privacy.Item.Screensharing)
42 return "monitor-share";
43 }
44 color: "#f2201f"
45 }
46 }
47 }
48 }
49}
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml b/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml
new file mode 100644
index 00000000..8318df50
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml
@@ -0,0 +1,75 @@
1pragma Singleton
2
3import QtQml
4import Quickshell
5import Quickshell.Io
6import Custom as Custom
7
8Singleton {
9 id: root
10
11 property string subsystem: "backlight"
12 property string device: "intel_backlight"
13
14 property real currBrightness
15 property real exponent: 4
16
17 function calcCurrBrightness() {
18 if (!currFile.loaded || !maxFile.loaded)
19 return undefined;
20 const curr = Number(currFile.text());
21 const max = Number(maxFile.text());
22 const val = Math.pow(curr / max, 1 / root.exponent);
23 return val;
24 }
25
26 Connections {
27 target: currFile
28 function onLoaded() {
29 const b = root.calcCurrBrightness();
30 if (typeof b !== 'undefined')
31 root.currBrightness = b;
32 }
33 }
34 Connections {
35 target: maxFile
36 function onLoaded() {
37 const b = root.calcCurrBrightness();
38 if (typeof b !== 'undefined')
39 root.currBrightness = b;
40 }
41 }
42
43 onCurrBrightnessChanged: {
44 root.currBrightness = Math.max(0, Math.min(1, root.currBrightness));
45
46 const prev = root.calcCurrBrightness();
47 if (typeof prev === 'undefined' || Math.abs(root.currBrightness - prev) < 0.01)
48 return;
49
50 const max = Number(maxFile.text());
51 const actual = Number(currFile.text());
52 let curr = Math.max(0, Math.min(max, Math.pow(root.currBrightness, root.exponent) * max));
53 if (Math.round(curr) == actual && curr < actual)
54 curr = Math.max(0, actual - 1);
55 else if (Math.round(curr) == actual && curr > actual)
56 curr = Math.min(max, actual + 1);
57 // root.currBrightness = Math.pow(curr / max, 1 / root.exponent);
58 Custom.Systemd.setBrightness(root.subsystem, root.device, Math.round(curr));
59 }
60
61 FileView {
62 id: currFile
63 path: `/sys/class/${root.subsystem}/${root.device}/brightness`
64 blockAllReads: true
65 watchChanges: true
66 onFileChanged: reload()
67 }
68 FileView {
69 id: maxFile
70 path: `/sys/class/${root.subsystem}/${root.device}/max_brightness`
71 blockAllReads: true
72 watchChanges: true
73 onFileChanged: reload()
74 }
75}
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml b/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml
new file mode 100644
index 00000000..3de69535
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml
@@ -0,0 +1,18 @@
1pragma Singleton
2
3import Quickshell
4import Quickshell.Io
5
6Singleton {
7 id: root
8
9 Socket {
10 id: agentSocket
11 connected: true
12 path: `${Quickshell.env("XDG_RUNTIME_DIR")}/gnupg/S.gpg-agent`
13 }
14
15 function reloadAgent() {
16 agentSocket.write("RELOADAGENT\n")
17 }
18}
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml b/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml
new file mode 100644
index 00000000..fe48fd7f
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml
@@ -0,0 +1,22 @@
1pragma Singleton
2
3import Quickshell
4import Custom as Custom
5
6Singleton {
7 id: inhibitorState
8
9 property bool waylandIdleInhibited: false
10 property alias lidSwitchInhibited: lidSwitchInhibitor.enabled
11
12 Custom.SystemdInhibitor {
13 id: lidSwitchInhibitor
14
15 enabled: false
16
17 what: Custom.SystemdInhibitorParams.HandleLidSwitch
18 who: "quickshell"
19 why: "User request"
20 mode: Custom.SystemdInhibitorParams.BlockWeak
21 }
22}
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml b/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml
new file mode 100644
index 00000000..e3ab9755
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml
@@ -0,0 +1,8 @@
1pragma Singleton
2
3import Quickshell
4import Quickshell.Services.Mpris
5
6Scope {
7 property list<var> players: Mpris.players.values
8}
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml b/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml
new file mode 100644
index 00000000..cce614eb
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml
@@ -0,0 +1,194 @@
1pragma Singleton
2
3import Quickshell
4import Quickshell.Io
5import QtQuick
6
7Singleton {
8 id: root
9
10 property var workspaces: []
11 property var outputs: {}
12 property var keyboardLayouts: {}
13 property var windows: []
14 readonly property string socketPath: Quickshell.env("NIRI_SOCKET")
15
16 function refreshOutputs() {
17 commandSocket.sendCommand("Outputs", data => {
18 outputs = data.Ok.Outputs;
19 });
20 }
21
22 function sendCommand(command, callback) {
23 commandSocket.sendCommand(command, callback);
24 }
25
26 Socket {
27 id: eventStreamSocket
28 path: root.socketPath
29 connected: true
30
31 property bool acked: false
32
33 onConnectionStateChanged: {
34 if (connected) {
35 acked = false;
36 write('"EventStream"\n');
37 }
38 }
39
40 parser: SplitParser {
41 onRead: line => {
42 try {
43 const event = JSON.parse(line)
44
45 // console.log(JSON.stringify(event))
46
47 if (event.WorkspacesChanged) {
48 root.workspaces = event.WorkspacesChanged.workspaces
49 root.refreshOutputs();
50 } else if (event.WorkspaceActivated)
51 eventWorkspaceActivated(event.WorkspaceActivated);
52 else if (event.WorkspaceUrgencyChanged)
53 eventWorkspaceUrgencyChanged(event.WorkspaceUrgencyChanged);
54 else if (event.WorkspaceActiveWindowChanged)
55 eventWorkspaceActiveWindowChanged(event.WorkspaceActiveWindowChanged);
56 else if (event.KeyboardLayoutsChanged)
57 root.keyboardLayouts = event.KeyboardLayoutsChanged.keyboard_layouts;
58 else if (event.KeyboardLayoutSwitched)
59 root.keyboardLayouts = Object.assign({}, root.keyboardLayouts, {"current_idx": event.KeyboardLayoutSwitched.idx });
60 else if (event.WindowsChanged)
61 root.windows = event.WindowsChanged.windows
62 else if (event.WindowOpenedOrChanged)
63 eventWindowOpenedOrChanged(event.WindowOpenedOrChanged);
64 else if (event.WindowClosed)
65 eventWindowClosed(event.WindowClosed);
66 else if (event.WindowFocusChanged)
67 eventWindowFocusChanged(event.WindowFocusChanged);
68 else if (event.WindowUrgencyChanged)
69 eventWindowUrgencyChanged(event.WindowUrgencyChanged);
70 else if (event.WindowLayoutsChanged)
71 eventWindowLayoutsChanged(event.WindowLayoutsChanged);
72 else if (event.Ok && !eventStreamSocket.acked) { eventStreamSocket.acked = true; }
73 else if (event.OverviewOpenedOrClosed) {}
74 else if (event.ConfigLoaded) {}
75 else
76 console.log(JSON.stringify(event));
77 } catch (e) {
78 console.warn("NiriService: Failed to parse event:", line, e)
79 }
80 }
81 }
82 }
83
84 Socket {
85 id: commandSocket
86 path: root.socketPath
87 connected: true
88
89 property var awaitingAnswer: null
90 property var cmdQueue: []
91
92 parser: SplitParser {
93 onRead: line => {
94 if (commandSocket.awaitingAnswer === null)
95 return;
96
97 try {
98 const response = JSON.parse(line);
99 commandSocket.awaitingAnswer.callback(response);
100 commandSocket.awaitingAnswer = null;
101 } catch (e) {
102 console.warn("NiriService: Failed to parse response:", line, e)
103 }
104 commandSocket._handleQueue();
105 }
106 }
107
108 onCmdQueueChanged: {
109 _handleQueue();
110 }
111 onAwaitingAnswerChanged: {
112 _handleQueue();
113 }
114
115 function _handleQueue() {
116 if (cmdQueue.length <= 0 || awaitingAnswer !== null)
117 return;
118
119 let localQueue = Array.from(cmdQueue);
120 awaitingAnswer = localQueue.shift();
121 cmdQueue = localQueue;
122 write(JSON.stringify(awaitingAnswer.command) + '\n');
123 }
124
125 function sendCommand(command, callback) {
126 cmdQueue = Array.from(cmdQueue).concat([{ "command": command, "callback": callback }])
127 }
128 }
129
130 function eventWorkspaceActivated(data) {
131 let relevant_output = null;
132 Array.from(root.workspaces).forEach(ws => {
133 if (data.id === ws.id)
134 relevant_output = ws.output;
135 });
136 root.workspaces = Array.from(root.workspaces).map(ws => {
137 if (data.focused)
138 ws.is_focused = false;
139 if (ws.output === relevant_output)
140 ws.is_active = false;
141 if (data.id === ws.id) {
142 ws.is_active = true;
143 ws.is_focused = data.focused;
144 }
145 return ws;
146 });
147 }
148 function eventWorkspaceUrgencyChanged(data) {
149 root.workspaces = Array.from(root.workspaces).map(ws => {
150 if (data.id == ws.id)
151 ws.is_urgent = data.urgent;
152 return ws;
153 });
154 }
155 function eventWorkspaceActiveWindowChanged(data) {
156 root.workspaces = Array.from(root.workspaces).map(ws => {
157 if (data.workspace_id === ws.id)
158 ws.active_window_id = data.active_window_id;
159 return ws;
160 });
161 }
162 function eventWindowOpenedOrChanged(data) {
163 root.windows = Array.from(root.windows).map(win => {
164 if (data.window.is_focused)
165 win.is_focused = false;
166 return win;
167 }).filter(win => win.id !== data.window.id).concat([data.window]);
168 }
169 function eventWindowClosed(data) {
170 root.windows = Array.from(root.windows).filter(win => win.id !== data.id);
171 }
172 function eventWindowFocusChanged(data) {
173 root.windows = Array.from(root.windows).map(win => {
174 win.is_focused = win.id === data.id;
175 return win;
176 });
177 }
178 function eventWindowUrgencyChanged(data) {
179 root.windows = Array.from(root.windows).map(win => {
180 if (win.id === data.id)
181 win.is_urgent = data.urgent;
182 return win;
183 });
184 }
185 function eventWindowLayoutsChanged(data) {
186 root.windows = Array.from(root.windows).map(win => {
187 Array.from(data.changes).forEach(change => {
188 if (win.id === change[0])
189 win.layout = change[1];
190 });
191 return win;
192 });
193 }
194}
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml
new file mode 100644
index 00000000..f02d1695
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml
@@ -0,0 +1,162 @@
1pragma Singleton
2
3import QtQml
4import Quickshell
5import Quickshell.Services.Notifications
6
7Singleton {
8 id: root
9
10 readonly property bool active: !root.lockscreenActive && !root.displayInhibited
11 property bool lockscreenActive: false
12 property bool displayInhibited: false
13 property alias trackedNotifications: server.trackedNotifications
14 readonly property var groups: {
15 function matchesGroupKey(notif, groupKey) {
16 var matches = true;
17 for (const prop in groupKey.test) {
18 if (notif[prop] !== groupKey.test[prop]) {
19 matches = false;
20 break;
21 }
22 }
23 return matches;
24 }
25
26 var groups = new Map();
27 var notifs = new Array();
28 for (const [ix, notif] of server.trackedNotifications.values.entries()) {
29 var didGroup = false;
30 for (const groupKey of root.groupKeys) {
31 if (!matchesGroupKey(notif, groupKey))
32 continue;
33
34 const key = JSON.stringify({
35 "key": groupKey,
36 "values": Object.assign({}, ...(Array.from(groupKey["group-by"]).map(prop => {
37 var res = {};
38 res[prop] = notif[prop];
39 return res;
40 })))
41 });
42 if (!groups.has(key))
43 groups.set(key, new Array());
44 groups.get(key).push({ "ix": ix, "notif": notif });
45 didGroup = true;
46 break;
47 }
48
49 if (!didGroup)
50 notifs.push([{ "ix": ix, "notif": notif }]);
51 }
52 notifs.push(...groups.values());
53 notifs.sort((as, bs) => Math.min(...(as.map(o => o.ix))) - Math.min(...(bs.map(o => o.ix))));
54 return notifs.map(ns => ns.map(n => n.notif));
55 }
56
57 property var groupKeys: [
58 { "test": { "appName": "Element" }, "group-by": [ "summary" ] }
59 ];
60
61 property int historyLimit: 100
62 property var history: []
63
64 Component {
65 id: expirationTimer
66
67 QtObject {
68 id: timer
69
70 required property QtObject parent
71 required property int expirationTime
72
73 property list<QtObject> data: [
74 Timer {
75 running: root.active && !timer.expired
76 interval: timer.expirationTime
77 onTriggered: {
78 timer.parent.expirationTimer.destroy();
79 timer.parent.expirationTimer = null;
80 timer.parent.expire();
81 }
82 }
83 ]
84 }
85 }
86
87 Component {
88 id: notificationLock
89
90 RetainableLock {}
91 }
92
93 readonly property SystemClock clock: SystemClock {
94 precision: SystemClock.Minutes
95 }
96
97 function formatTime(time) {
98 const now = root.clock.date;
99 const diff = now - time;
100 const minutes = Math.ceil(diff / 60000);
101 const hours = Math.floor(minutes / 60);
102
103 if (hours < 1) {
104 if (minutes < 1)
105 return "now";
106 if (minutes == 1)
107 return "1 minute";
108 return `${minutes} minutes`;
109 }
110
111 const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate())
112 const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate())
113 const days = Math.floor((nowDate - timeDate) / (1000 * 86400))
114
115 const timeStr = time.toLocaleTimeString(Qt.locale(), "HH:mm");
116
117 if (days === 0)
118 return timeStr;
119 if (days === 1)
120 return `yesterday ${timeStr}`;
121
122 const dateStr = time.toLocaleTimeString(Qt.locale(), "YYYY-MM-DD");
123 return `${dateStr} ${timeStr}`;
124 }
125
126 NotificationServer {
127 id: server
128
129 bodySupported: true
130 actionsSupported: true
131 actionIconsSupported: true
132 imageSupported: true
133 bodyMarkupSupported: true
134 bodyImagesSupported: true
135
136 onNotification: notification => {
137 var timeout = notification.expireTimeout * 1000;
138 if (notification.appName == "poweralertd")
139 timeout = 2000;
140 if (timeout > 0) {
141 Object.defineProperty(notification, "expirationTimer", { configurable: true, enumerable: true, writable: true });
142 notification.expirationTimer = expirationTimer.createObject(notification, { parent: notification, expirationTime: timeout });
143 }
144 Object.defineProperty(notification, "receivedTime", { configurable: true, enumerable: true, writable: true });
145 notification.receivedTime = root.clock.date;
146 notification.closed.connect((reason) => server.onNotificationClosed(notification, reason));
147 notification.tracked = true;
148 }
149
150 function onNotificationClosed(notification, reason) {
151 while (root.history.length >= root.historyLimit) {
152 root.history[0].lock.locked = false;
153 root.history.shift();
154 }
155
156 root.history.push({
157 lock: notificationLock.createObject(root, { locked: true, object: notification }),
158 notification: notification
159 });
160 }
161 }
162}
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml b/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml
new file mode 100644
index 00000000..9c813e49
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml
@@ -0,0 +1,63 @@
1pragma Singleton
2
3import QtQml
4import Quickshell
5import Quickshell.Services.Pipewire
6
7Singleton {
8 id: root
9
10 PwObjectTracker {
11 objects: Pipewire.nodes.values
12 }
13
14 enum Item {
15 Microphone,
16 Screensharing
17 }
18
19 readonly property list<var> activeItems: {
20 var items = [];
21 if (microphoneActive)
22 items.push(Privacy.Item.Microphone);
23 if (screensharingActive)
24 items.push(Privacy.Item.Screensharing);
25 return items;
26 }
27
28 readonly property bool microphoneActive: {
29 if (!Pipewire.ready || !Pipewire.nodes?.values) {
30 return false
31 }
32
33 for (const node of Pipewire.nodes.values) {
34 if (!node || (node.type & PwNodeType.AudioInStream) != PwNodeType.AudioInStream)
35 continue;
36
37 if (node.properties?.["stream.monitor"] === "true")
38 continue;
39
40 if (node.audio?.muted)
41 continue;
42
43 return true;
44 }
45
46 return false;
47 }
48
49 readonly property bool screensharingActive: {
50 if (!Pipewire.ready || !Pipewire.nodes?.values) {
51 return false
52 }
53
54 for (const node of Pipewire.nodes.values) {
55 if (!node || (node.type & PwNodeType.VideoInStream) != PwNodeType.VideoInStream)
56 continue;
57
58 return true;
59 }
60
61 return false;
62 }
63}
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml b/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml
new file mode 100644
index 00000000..3c524955
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml
@@ -0,0 +1,8 @@
1import Custom as Custom
2
3Custom.FileSelector {
4 id: root
5
6 directory: @wallpapers@
7 epoch: 72000000
8}
diff --git a/accounts/gkleen@sif/shell/quickshell/SystemTray.qml b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml
new file mode 100644
index 00000000..f7b4ed96
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml
@@ -0,0 +1,201 @@
1import QtQuick
2import QtQuick.Effects
3import Quickshell
4import Quickshell.Widgets
5import Quickshell.Services.SystemTray
6
7Item {
8 anchors.verticalCenter: parent.verticalCenter
9 width: systemTrayRow.childrenRect.width
10 height: parent.height
11 clip: true
12
13 Row {
14 id: systemTrayRow
15 anchors.centerIn: parent
16 width: childrenRect.width
17 height: parent.height
18 spacing: 0
19
20 Repeater {
21 model: ScriptModel {
22 values: {
23 var trayItems = Array.from(SystemTray.items.values).filter(item => item.status !== Status.Passive);
24 trayItems.sort((a, b) => a.category !== b.category ? b.category - a.category : a.id.localeCompare(b.id))
25 return trayItems;
26 }
27 }
28
29 delegate: Item {
30 id: trayItemWrapper
31
32 required property var modelData
33 required property int index
34
35 property var trayItem: modelData
36 property string iconSource: {
37 let icon = trayItem && trayItem.icon
38 if (typeof icon === 'string' || icon instanceof String) {
39 if (icon.includes("?path=")) {
40 const split = icon.split("?path=")
41 if (split.length !== 2)
42 return icon
43 const name = split[0]
44 const path = split[1]
45 const fileName = name.substring(
46 name.lastIndexOf("/") + 1)
47 return `file://${path}/${fileName}`
48 }
49 return icon
50 }
51 return ""
52 }
53
54 width: icon.width + 6
55 height: parent.height
56 anchors.verticalCenter: parent.verticalCenter
57
58 WrapperMouseArea {
59 id: trayItemArea
60
61 anchors.fill: parent
62 acceptedButtons: Qt.LeftButton | Qt.RightButton
63 hoverEnabled: true
64 cursorShape: trayItem.onlyMenu ? Qt.ArrowCursor : Qt.PointingHandCursor
65 onClicked: mouse => {
66 if (!trayItem)
67 return
68
69 if (mouse.button === Qt.LeftButton
70 && !trayItem.onlyMenu) {
71 trayItem.activate()
72 return
73 }
74
75 if (trayItem.hasMenu) {
76 var globalPos = mapToGlobal(0, 0)
77 var currentScreen = screen || Screen
78 var screenX = currentScreen.x || 0
79 var relativeX = globalPos.x - screenX
80 menuAnchor.menu = trayItem.menu
81 menuAnchor.anchor.window = bar
82 menuAnchor.anchor.rect = Qt.rect(
83 relativeX,
84 21,
85 parent.width, 1)
86 menuAnchor.open()
87 }
88 }
89
90 Rectangle {
91 anchors.fill: parent
92 color: {
93 if (trayItemArea.containsMouse)
94 return "#33808080";
95 return "transparent";
96 }
97
98 Item {
99 anchors.fill: parent
100
101 layer.enabled: true
102 layer.effect: MultiEffect {
103 colorization: 1
104 colorizationColor: "#555"
105 }
106
107 IconImage {
108 id: icon
109
110 anchors.centerIn: parent
111 implicitSize: 16
112 source: trayItemWrapper.iconSource
113 asynchronous: true
114 smooth: true
115 mipmap: true
116
117 layer.enabled: true
118 layer.effect: MultiEffect {
119 id: effect
120
121 brightness: 1
122 }
123 }
124 }
125 }
126 }
127
128 PopupWindow {
129 id: tooltip
130
131 property bool nextVisible: (trayItem.tooltipTitle || trayItem.tooltipDescription) && (trayItemArea.containsMouse || tooltipMouseArea.containsMouse) && !menuAnchor.visible
132
133 anchor {
134 item: trayItemArea
135 edges: Edges.Bottom
136 }
137
138 visible: false
139 onNextVisibleChanged: hangTimer.restart()
140
141 Timer {
142 id: hangTimer
143 interval: 100
144 onTriggered: tooltip.visible = tooltip.nextVisible
145 }
146
147 color: "black"
148
149 implicitWidth: Math.max(tooltipTitle.contentWidth, tooltipDescription.contentWidth) + 16
150 implicitHeight: (trayItem.tooltipTitle ? tooltipTitle.contentHeight : 0) + (trayItem.tooltipDescription ? tooltipDescription.contentHeight : 0) + 16
151
152 WrapperMouseArea {
153 id: tooltipMouseArea
154
155 hoverEnabled: true
156 enabled: true
157
158 margin: 4
159
160 anchors.fill: parent
161
162 Item {
163 anchors.fill: parent
164
165 Column {
166 anchors.centerIn: parent
167 Text {
168 id: tooltipTitle
169
170 enabled: trayItem.tooltipTitle
171
172 font.pointSize: 10
173 font.family: "Fira Sans"
174 font.bold: true
175 color: "white"
176
177 text: trayItem.tooltipTitle
178 }
179
180 Text {
181 id: tooltipDescription
182
183 enabled: trayItem.tooltipDescription
184
185 font.pointSize: 10
186 font.family: "Fira Sans"
187 color: "white"
188
189 text: trayItem.tooltipDescription
190 }
191 }
192 }
193 }
194 }
195 }
196 }
197 }
198 QsMenuAnchor {
199 id: menuAnchor
200 }
201}
diff --git a/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml
new file mode 100644
index 00000000..05a40dbc
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml
@@ -0,0 +1,97 @@
1import Quickshell
2import Quickshell.Io
3import Quickshell.Services.Pipewire
4import Quickshell.Services.Mpris
5import qs.Services
6import Custom as Custom
7import QtQml
8
9Scope {
10 id: root
11
12 SocketServer {
13 active: true
14 path: `${Quickshell.env("XDG_RUNTIME_DIR")}/shell.sock`
15 handler: Socket {
16 parser: SplitParser {
17 onRead: line => {
18 const command = (() => {
19 try {
20 return JSON.parse(line);
21 } catch (e) {
22 console.warn("UnixIPC: Failed to parse command:", line, e);
23 }
24 })();
25 if (!command)
26 return;
27
28 if (command.Volume)
29 root.onCommandVolume(command.Volume);
30 else if (command.Brightness)
31 root.onCommandBrightness(command.Brightness);
32 else if (command.LockSession)
33 Custom.Systemd.lockSession();
34 else if (command.Suspend)
35 Custom.Systemd.suspend();
36 else if (command.Hibernate)
37 Custom.Systemd.hibernate();
38 else if (command.Mpris)
39 root.onCommandMpris(command.Mpris);
40 else if (command.Notifications)
41 root.onCommandNotifications(command.Notifications);
42 else
43 console.warn("UnixIPC: Command not handled:", JSON.stringify(command));
44 }
45 }
46
47 onError: e => {
48 if (e == 1)
49 return;
50 console.warn("QLocalSocket::LocalSocketError", e);
51 }
52 }
53 }
54
55 PwObjectTracker {
56 objects: [ Pipewire.defaultAudioSink, Pipewire.defaultAudioSource ]
57 }
58 function onCommandVolume(command) {
59 if (command.muted === "toggle")
60 Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted;
61 if (command.volume === "up")
62 Pipewire.defaultAudioSink.audio.volume += 0.02;
63 if (command.volume === "down")
64 Pipewire.defaultAudioSink.audio.volume -= 0.02;
65
66 if (command["mic-muted"] === "toggle")
67 Pipewire.defaultAudioSource.audio.muted = !Pipewire.defaultAudioSource.audio.muted;
68 }
69
70 function onCommandBrightness(command) {
71 if (command === "up")
72 Brightness.currBrightness += 0.02
73 if (command === "down")
74 Brightness.currBrightness -= 0.02
75 }
76
77 function onCommandMpris(command) {
78 if (command.PauseAll)
79 Array.from(MprisProxy.players).forEach(player => {
80 if (player.canPause && player.isPlaying)
81 player.pause();
82 });
83 }
84 Component.onCompleted: { (_ => {})(MprisProxy.players); }
85
86 function onCommandNotifications(command) {
87 if (command.DismissGroup && NotificationManager.active) {
88 if (NotificationManager.groups.length > 0)
89 for (const notif of [...NotificationManager.groups[0]])
90 notif.dismiss();
91 }
92 if (command.DismissAll && NotificationManager.active) {
93 for (const notif of [...NotificationManager.trackedNotifications.values])
94 notif.dismiss();
95 }
96 }
97}
diff --git a/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml b/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml
new file mode 100644
index 00000000..653f4763
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml
@@ -0,0 +1,163 @@
1import QtQuick
2import QtQuick.Layouts
3import Quickshell
4import Quickshell.Services.Pipewire
5import Quickshell.Widgets
6
7Scope {
8 id: root
9
10 property string show: ""
11 property bool inhibited: true
12
13 PwObjectTracker {
14 objects: [ Pipewire.defaultAudioSink, Pipewire.defaultAudioSource ]
15 }
16
17 Connections {
18 enabled: Pipewire.defaultAudioSink
19 target: Pipewire.defaultAudioSink?.audio
20
21 function onVolumeChanged() {
22 root.show = "sink";
23 hideTimer.restart();
24 }
25 function onMutedChanged() {
26 root.show = "sink";
27 hideTimer.restart();
28 }
29 }
30
31 Connections {
32 enabled: Pipewire.defaultAudioSource
33 target: Pipewire.defaultAudioSource?.audio
34
35 function onVolumeChanged() {
36 root.show = "source";
37 hideTimer.restart();
38 }
39 function onMutedChanged() {
40 root.show = "source";
41 hideTimer.restart();
42 }
43 }
44
45 onShowChanged: {
46 if (show)
47 hideTimer.restart();
48 }
49
50 Timer {
51 id: hideTimer
52 interval: 1000
53 onTriggered: root.show = ""
54 }
55
56 Timer {
57 id: startInhibit
58 interval: 100
59 running: true
60 onTriggered: {
61 root.show = "";
62 root.inhibited = false;
63 }
64 }
65
66 LazyLoader {
67 active: root.show && !root.inhibited
68
69 Variants {
70 model: Quickshell.screens
71
72 delegate: Scope {
73 id: screenScope
74
75 required property var modelData
76
77 PanelWindow {
78 id: window
79
80 screen: screenScope.modelData
81
82 anchors.top: true
83 margins.top: screen.height / 2 - 50 + 3.5
84 exclusiveZone: 0
85 exclusionMode: ExclusionMode.Ignore
86
87 implicitWidth: 400
88 implicitHeight: 50
89
90 mask: Region {}
91
92 color: "transparent"
93
94 Rectangle {
95 anchors.fill: parent
96 color: Qt.rgba(0, 0, 0, 0.75)
97 }
98
99 RowLayout {
100 id: layout
101
102 anchors.centerIn: parent
103
104 height: 50 - 8*2
105 width: 400 - 8*2
106
107 MaterialDesignIcon {
108 id: volumeIcon
109
110 implicitWidth: parent.height
111 implicitHeight: parent.height
112
113 icon: {
114 if (root.show == "sink") {
115 if (!Pipewire.defaultAudioSink || Pipewire.defaultAudioSink.audio.muted)
116 return "volume-off";
117 if (Pipewire.defaultAudioSink.audio.volume <= 0.33)
118 return "volume-low";
119 if (Pipewire.defaultAudioSink.audio.volume <= 0.67)
120 return "volume-medium";
121 return "volume-high";
122 } else if (root.show == "source") {
123 if (!Pipewire.defaultAudioSource || Pipewire.defaultAudioSource.audio.muted)
124 return "microphone-off";
125 if (Pipewire.defaultAudioSource.audio.volume > 1)
126 return "microphone-plus";
127 return "microphone";
128 }
129 return "volume-high";
130 }
131 }
132
133 Rectangle {
134 Layout.fillWidth: true
135
136 implicitHeight: 10
137
138 color: "#50ffffff"
139
140 Rectangle {
141 anchors {
142 left: parent.left
143 top: parent.top
144 bottom: parent.bottom
145 }
146
147 color: Pipewire.defaultAudioSink?.audio.muted ? "#70ffffff" : "white"
148
149 implicitWidth: {
150 if (root.show == "sink")
151 return parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0);
152 else if (root.show == "source")
153 return parent.width * Math.min(1, (Pipewire.defaultAudioSource?.audio.volume ?? 0));
154 return 0;
155 }
156 }
157 }
158 }
159 }
160 }
161 }
162 }
163}
diff --git a/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml b/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml
new file mode 100644
index 00000000..4f85a900
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml
@@ -0,0 +1,89 @@
1import QtQuick
2import Quickshell
3import qs.Services
4
5Item {
6 id: root
7
8 anchors.fill: parent
9
10 required property string screen
11
12 property Img current: one
13 property string source: selector.selected
14
15 WallpaperSelector {
16 id: selector
17 seed: screen
18 }
19
20 onSourceChanged: {
21 if (!source)
22 current = null;
23 else if (current === one)
24 two.update()
25 else
26 one.update()
27 }
28
29 Img { id: one }
30 Img { id: two }
31
32 component Img: Image {
33 id: img
34
35 function update() {
36 source = root.source || ""
37 }
38
39 anchors.fill: parent
40 fillMode: Image.PreserveAspectCrop
41 smooth: true
42 asynchronous: true
43 cache: false
44
45 opacity: 0
46
47 onStatusChanged: {
48 if (status === Image.Ready) {
49 root.current = this
50 }
51 }
52
53 states: State {
54 name: "visible"
55 when: root.current === img
56
57 PropertyChanges {
58 img.opacity: 1
59 }
60 StateChangeScript {
61 name: "unloadOther"
62 script: {
63 if (img === one)
64 two.source = ""
65 if (img === two)
66 one.source = ""
67 }
68 }
69 }
70
71 transitions: Transition {
72 SequentialAnimation {
73 NumberAnimation {
74 target: img
75 properties: "opacity"
76 duration: {
77 if (img === one && two.source == "" || img === two && one.source == "")
78 return 0;
79 return 5000;
80 }
81 easing.type: Easing.OutCubic
82 }
83 ScriptAction {
84 scriptName: "unloadOther"
85 }
86 }
87 }
88 }
89}
diff --git a/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml
new file mode 100644
index 00000000..0512ff51
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml
@@ -0,0 +1,56 @@
1import Quickshell
2import QtQuick
3import Quickshell.Widgets
4import Quickshell.Wayland
5import qs.Services
6
7Item {
8 id: root
9
10 required property var window
11
12 width: icon.width + 8
13 height: parent.height
14 anchors.verticalCenter: parent.verticalCenter
15
16 IdleInhibitor {
17 id: inhibitor
18 enabled: InhibitorState.waylandIdleInhibited
19 window: root.window
20 }
21
22 WrapperMouseArea {
23 id: widgetMouseArea
24
25 anchors.fill: parent
26
27 hoverEnabled: true
28 cursorShape: Qt.PointingHandCursor
29
30 onClicked: InhibitorState.waylandIdleInhibited = !InhibitorState.waylandIdleInhibited
31
32 Rectangle {
33 anchors.fill: parent
34 color: {
35 if (widgetMouseArea.containsMouse) {
36 return "#33808080";
37 }
38 return "transparent";
39 }
40
41 Item {
42 anchors.fill: parent
43
44 MaterialDesignIcon {
45 id: icon
46
47 implicitSize: 14
48 anchors.centerIn: parent
49
50 icon: inhibitor.enabled ? "eye" : "eye-off"
51 color: inhibitor.enabled ? "white" : "#555"
52 }
53 }
54 }
55 }
56}
diff --git a/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml
new file mode 100644
index 00000000..3ae94346
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml
@@ -0,0 +1,204 @@
1import Quickshell
2import QtQuick
3import qs.Services
4import Quickshell.Widgets
5import QtQuick.Layouts
6
7Row {
8 id: workspaces
9
10 required property var screen
11
12 property var ignoreWorkspaces: @ignore_workspaces@
13
14 height: parent.height
15 anchors.verticalCenter: parent.verticalCenter
16 spacing: 0
17
18 Repeater {
19 model: ScriptModel {
20 values: {
21 let currWorkspaces = NiriService.workspaces;
22 const ignoreWorkspaces = Array.from(workspaces.ignoreWorkspaces);
23 currWorkspaces = currWorkspaces.filter(ws => ws.output == workspaces.screen.name).filter(ws => ws.is_active || ignoreWorkspaces.every(iws => iws !== ws.name));
24 currWorkspaces.sort((a, b) => {
25 if (NiriService.outputs?.[a.output]?.logical?.x !== NiriService.outputs?.[b.output]?.logical?.x)
26 return NiriService.outputs?.[a.output]?.logical?.x - NiriService.outputs?.[b.output]?.logical?.x
27 if (NiriService.outputs?.[a.output]?.logical?.y !== NiriService.outputs?.[b.output]?.logical?.y)
28 return NiriService.outputs?.[a.output]?.logical?.y - NiriService.outputs?.[b.output]?.logical?.y
29 return a.idx - b.idx;
30 });
31 return currWorkspaces;
32 }
33 }
34
35 Item {
36 id: wsItem
37
38 property var workspaceData: modelData
39
40 width: wsLabel.contentWidth + 8
41 height: parent.height
42 anchors.verticalCenter: parent.verticalCenter
43
44 WrapperMouseArea {
45 id: mouseArea
46
47 anchors.fill: parent
48
49 hoverEnabled: true
50 cursorShape: Qt.PointingHandCursor
51 enabled: true
52 onClicked: {
53 NiriService.sendCommand({ "Action": { "FocusWorkspace": { "reference": { "Id": workspaceData.id } } } }, _ => {});
54 }
55
56 Rectangle {
57 anchors.fill: parent
58
59 color: {
60 if (mouseArea.containsMouse) {
61 return "#33808080";
62 }
63 return "transparent";
64 }
65
66 Text {
67 id: wsLabel
68
69 anchors.centerIn: parent
70
71 font.pointSize: 10
72 font.family: "Fira Sans"
73 color: {
74 if (workspaceData.is_active)
75 return "#23fd00";
76 if (workspaceData.active_window_id === null)
77 return "#555";
78 return "white";
79 }
80
81 text: workspaceData.name ? workspaceData.name : workspaceData.idx
82 }
83 }
84 }
85
86 PopupWindow {
87 id: tooltip
88
89 property bool nextVisible: (mouseArea.containsMouse || tooltipMouseArea.containsMouse) && [...windowsModel.values].length > 0
90
91 anchor {
92 item: mouseArea
93 edges: Edges.Bottom | Edges.Left
94 }
95 visible: false
96
97 onNextVisibleChanged: hangTimer.restart()
98
99 Timer {
100 id: hangTimer
101 interval: 100
102 onTriggered: tooltip.visible = tooltip.nextVisible
103 }
104
105 implicitWidth: tooltipContent.implicitWidth
106 implicitHeight: tooltipContent.implicitHeight
107 color: "black"
108
109 WrapperMouseArea {
110 id: tooltipMouseArea
111
112 hoverEnabled: true
113 enabled: true
114
115 anchors.fill: parent
116
117 WrapperItem {
118 id: tooltipContent
119
120 margin: 0
121
122 ColumnLayout {
123 spacing: 0
124
125 Repeater {
126 model: ScriptModel {
127 id: windowsModel
128
129 values: {
130 let currWindows = Array.from(NiriService.windows).filter(win => win.workspace_id == wsItem.workspaceData.id);
131 currWindows.sort((a, b) => {
132 if (a.is_floating !== b.is_floating)
133 return b.is_floating - a.is_floating;
134 if (a.layout.tile_pos_in_workspace_view?.[0] !== b.layout.tile_pos_in_workspace_view?.[0])
135 return a.layout.tile_pos_in_workspace_view?.[0] - b.layout.tile_pos_in_workspace_view?.[0]
136 if (a.layout.tile_pos_in_workspace_view?.[1] !== b.layout.tile_pos_in_workspace_view?.[1])
137 return a.layout.tile_pos_in_workspace_view?.[1] - b.layout.tile_pos_in_workspace_view?.[1]
138 if (a.layout.pos_in_scrolling_layout?.[0] !== b.layout.pos_in_scrolling_layout?.[0])
139 return a.layout.pos_in_scrolling_layout?.[0] - b.layout.pos_in_scrolling_layout?.[0]
140 if (a.layout.pos_in_scrolling_layout?.[1] !== b.layout.pos_in_scrolling_layout?.[1])
141 return a.layout.pos_in_scrolling_layout?.[1] - b.layout.pos_in_scrolling_layout?.[1]
142 if (a.app_id !== b.app_id)
143 return a.app_id.localeCompare(b.app_id);
144
145 return a.title.localeCompare(b.title);
146 });
147 return currWindows;
148 }
149 }
150
151 WrapperMouseArea {
152 id: windowMouseArea
153
154 required property int index
155 required property var modelData
156 property var windowData: modelData
157
158 hoverEnabled: true
159 cursorShape: Qt.PointingHandCursor
160 enabled: true
161
162 Layout.fillWidth: true
163
164 onClicked: {
165 NiriService.sendCommand({ "Action": { "FocusWindow": { "id": windowData.id } } }, _ => {})
166 }
167
168 WrapperRectangle {
169 color: windowMouseArea.containsMouse ? "#33808080" : "transparent";
170
171 WrapperItem {
172 rightMargin: 8
173 leftMargin: 8
174 topMargin: windowMouseArea.index == 0 ? 8 : 4
175 bottomMargin: windowMouseArea.index == windowsModel.values.length - 1 ? 8 : 4
176
177 Text {
178 id: windowLabel
179
180 font.pointSize: 10
181 font.family: "Fira Sans"
182 color: {
183 if (windowData.is_focused)
184 return "#23fd00";
185 if (NiriService.workspaces.find(ws => ws.id == windowData.workspace_id)?.active_window_id == windowData.id)
186 return "white";
187 return "#555";
188 }
189
190 text: windowData.title
191
192 horizontalAlignment: Text.AlignLeft
193 }
194 }
195 }
196 }
197 }
198 }
199 }
200 }
201 }
202 }
203 }
204}
diff --git a/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml
new file mode 100644
index 00000000..04bcc581
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml
@@ -0,0 +1,120 @@
1import QtQml
2import Quickshell
3import Quickshell.Io
4import QtQuick
5import Quickshell.Widgets
6
7Item {
8 id: root
9
10 required property string command
11 property var state: null
12
13 height: parent.height
14 width: label.contentWidth + 8
15 anchors.verticalCenter: parent.verticalCenter
16
17 Process {
18 id: process
19 running: true
20 command: [ @worktime@, root.command, "--waybar" ]
21 stdout: StdioCollector {
22 id: processCollector
23 onStreamFinished: {
24 try {
25 root.state = JSON.parse(processCollector.text);
26 } catch (e) {
27 console.warn("Worktime: Failed to parse output:", processCollector.text, e);
28 }
29 }
30 }
31 }
32
33 Timer {
34 running: true
35 interval: 60
36 repeat: true
37 onTriggered: process.running = true
38 }
39
40 WrapperMouseArea {
41 id: mouseArea
42
43 anchors.fill: parent
44
45 enabled: true
46 hoverEnabled: true
47
48 Item {
49 anchors.fill: parent
50
51 Text {
52 id: label
53
54 anchors.centerIn: parent
55
56 visible: root.state?.text ?? false
57 text: root.state?.text ?? ""
58
59 font.pointSize: 10
60 font.family: "Fira Sans"
61 color: {
62 if (root.state?.class == "running")
63 return "white";
64 if (root.state?.class == "over")
65 return "#f28a21";
66 return "#555";
67 }
68 }
69 }
70 }
71
72 PopupWindow {
73 id: tooltip
74
75 property bool nextVisible: Boolean(root.state?.tooltip ?? false) && (mouseArea.containsMouse || tooltipMouseArea.containsMouse)
76
77 anchor {
78 item: mouseArea
79 edges: Edges.Bottom | Edges.Left
80 }
81 visible: false
82
83 onNextVisibleChanged: hangTimer.restart()
84
85 Timer {
86 id: hangTimer
87 interval: 100
88 onTriggered: tooltip.visible = tooltip.nextVisible
89 }
90
91 implicitWidth: tooltipText.contentWidth + 16
92 implicitHeight: tooltipText.contentHeight + 16
93 color: "black"
94
95 WrapperMouseArea {
96 id: tooltipMouseArea
97
98 enabled: true
99 hoverEnabled: true
100
101 anchors.fill: parent
102
103 Item {
104 anchors.fill: parent
105
106 Text {
107 id: tooltipText
108
109 anchors.centerIn: parent
110
111 font.pointSize: 10
112 font.family: "Fira Sans"
113 color: "white"
114
115 text: root.state?.tooltip ?? ""
116 }
117 }
118 }
119 }
120}
diff --git a/accounts/gkleen@sif/shell/quickshell/displaymanager.qml b/accounts/gkleen@sif/shell/quickshell/displaymanager.qml
new file mode 100644
index 00000000..b452c03d
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/displaymanager.qml
@@ -0,0 +1,115 @@
1//@ pragma UseQApplication
2
3import Quickshell
4import Quickshell.Wayland
5import Quickshell.Io
6import Quickshell.Services.Greetd
7import QtQml
8
9
10ShellRoot {
11 id: displaymanager
12
13 settings.watchFiles: false
14
15 property string currentText: ""
16 property string username: @username@
17 property list<string> command: @niri_session@
18 property list<var> messages: []
19 property bool responseRequired: false
20 property bool responseVisible: false
21
22 signal startAuth()
23
24 onStartAuth: {
25 if (Greetd.state !== GreetdState.Inactive)
26 Greetd.cancelSession();
27 displaymanager.messages = [];
28 Greetd.createSession(displaymanager.username);
29 }
30
31 Connections {
32 target: Greetd
33 function onStateChanged() {
34 console.log("greetd state: ", GreetdState.toString(Greetd.state));
35 if (Greetd.state === GreetdState.ReadyToLaunch)
36 Greetd.launch(displaymanager.command);
37 }
38 function onAuthMessage(message: string, error: bool, responseRequired: bool, echoResponse: bool) {
39 displaymanager.responseVisible = echoResponse;
40 displaymanager.responseRequired = responseRequired;
41 displaymanager.messages = Array.from(displaymanager.messages).concat([{ "text": message, "error": error }]);
42 }
43 function onAuthFailure(message: string) {
44 displaymanager.responseRequired = false;
45 displaymanager.messages = Array.from(displaymanager.messages).concat([{ "text": message, "error": true }]);
46 }
47 }
48
49 Component.onCompleted: {
50 if (Greetd.state !== GreetdState.Inactive)
51 Greetd.cancelSession();
52 }
53
54 Variants {
55 model: Quickshell.screens
56
57 delegate: Scope {
58 id: screenScope
59
60 required property var modelData
61
62 PanelWindow {
63 color: "black"
64
65 screen: screenScope.modelData
66
67 WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
68
69 anchors.top: true
70 anchors.bottom: true
71 anchors.left: true
72 anchors.right: true
73
74 LockSurface {
75 id: surfaceContent
76
77 screen: screenScope.modelData
78
79 onCurrentTextChanged: displaymanager.currentText = currentText
80 Connections {
81 target: displaymanager
82 function onCurrentTextChanged() { surfaceContent.currentText = displaymanager.currentText; }
83 function onMessagesChanged() { surfaceContent.messages = Array.from(displaymanager.messages); }
84 function onResponseRequiredChanged() { surfaceContent.responseRequired = displaymanager.responseRequired; }
85 function onResponseVisibleChanged() { surfaceContent.responseVisible = displaymanager.responseVisible; }
86 }
87
88 onResponse: responseText => Greetd.respond(responseText);
89 Connections {
90 target: Greetd
91 function onStateChanged() {
92 if (Greetd.state === GreetdState.Authenticating) {
93 surfaceContent.authRunning = true;
94 } else {
95 surfaceContent.authRunning = false;
96 }
97 }
98 }
99
100 onAuthRunningChanged: {
101 if (surfaceContent.authRunning && Greetd.state !== GreetdState.Authenticating)
102 displaymanager.startAuth();
103 }
104 Component.onCompleted: {
105 surfaceContent.authRunning = Greetd.state === GreetdState.Authenticating
106 surfaceContent.messages = Array.from(displaymanager.messages);
107 surfaceContent.responseVisible = displaymanager.responseVisible;
108 surfaceContent.responseRequired = displaymanager.responseRequired;
109 surfaceContent.currentText = displaymanager.currentText;
110 }
111 }
112 }
113 }
114 }
115}
diff --git a/accounts/gkleen@sif/shell/quickshell/shell.qml b/accounts/gkleen@sif/shell/quickshell/shell.qml
new file mode 100644
index 00000000..fb8b16dc
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/shell.qml
@@ -0,0 +1,53 @@
1//@ pragma UseQApplication
2
3import Quickshell
4import Quickshell.Wayland
5
6ShellRoot {
7 settings.watchFiles: false
8
9 Variants {
10 model: Quickshell.screens
11
12 delegate: Scope {
13 id: screenScope
14
15 required property var modelData
16
17 PanelWindow {
18 id: bgWindow
19
20 screen: screenScope.modelData
21
22 WlrLayershell.layer: WlrLayer.Background
23 WlrLayershell.namespace: "background"
24 exclusionMode: ExclusionMode.Ignore
25
26 anchors.top: true
27 anchors.bottom: true
28 anchors.left: true
29 anchors.right: true
30
31 color: "black"
32
33 WallpaperBackground {
34 screen: bgWindow.screen.name
35 }
36 }
37
38 Bar {
39 modelData: screenScope.modelData
40 }
41 }
42 }
43
44 Lockscreen {}
45 NiriIdle {}
46
47 VolumeOSD {}
48 BrightnessOSD {}
49
50 NotificationDisplay {}
51
52 UnixIPC {}
53}