├── src ├── io │ ├── plugin.cpp │ ├── module.md │ ├── test │ │ ├── process.hpp │ │ ├── datastream.hpp │ │ ├── CMakeLists.txt │ │ └── process.cpp │ ├── CMakeLists.txt │ ├── processcore.cpp │ └── ipccomm.hpp ├── core │ ├── test │ │ ├── popupwindow.hpp │ │ ├── transformwatcher.hpp │ │ ├── stacklist.hpp │ │ ├── CMakeLists.txt │ │ ├── ringbuf.hpp │ │ └── scriptmodel.hpp │ ├── shell.cpp │ ├── common.cpp │ ├── iconprovider.hpp │ ├── common.hpp │ ├── model.cpp │ ├── enginecontext.hpp │ ├── platformmenu_p.hpp │ ├── incubator.cpp │ ├── iconimageprovider.hpp │ ├── shell.hpp │ ├── toolsupport.hpp │ ├── doc.hpp │ ├── persistentprops.cpp │ ├── elapsedtimer.cpp │ ├── singleton.hpp │ ├── incubator.hpp │ ├── module.md │ ├── rootwrapper.hpp │ ├── scan.hpp │ ├── desktopentrymonitor.hpp │ ├── types.cpp │ ├── plugin.cpp │ ├── instanceinfo.hpp │ ├── easingcurve.cpp │ ├── instanceinfo.cpp │ ├── logging_qtprivate.hpp │ ├── CMakeLists.txt │ ├── plugin.hpp │ ├── elapsedtimer.hpp │ ├── easingcurve.hpp │ ├── imageprovider.hpp │ ├── platformmenu.hpp │ ├── singleton.cpp │ ├── logcat.hpp │ ├── persistentprops.hpp │ └── desktopentrymonitor.cpp ├── widgets │ ├── cliprect.cpp │ ├── ClippingWrapperRectangleInternal.qml │ ├── module.md │ ├── cliprect.hpp │ ├── CMakeLists.txt │ └── shaders │ │ └── cliprect.frag ├── window │ ├── panelinterface.cpp │ ├── test │ │ ├── windowattached.hpp │ │ ├── CMakeLists.txt │ │ └── popupwindow.hpp │ ├── init.cpp │ └── CMakeLists.txt ├── crash │ ├── main.hpp │ ├── interface.hpp │ ├── handler.hpp │ └── CMakeLists.txt ├── wayland │ ├── platformmenu.hpp │ ├── util.hpp │ ├── screencopy │ │ ├── build.hpp.in │ │ ├── wlr_screencopy │ │ │ ├── CMakeLists.txt │ │ │ └── wlr_screencopy.hpp │ │ ├── hyprland_screencopy │ │ │ ├── CMakeLists.txt │ │ │ ├── hyprland_screencopy.hpp │ │ │ └── hyprland_screencopy_p.hpp │ │ ├── manager.hpp │ │ ├── image_copy_capture │ │ │ ├── CMakeLists.txt │ │ │ ├── image_copy_capture.hpp │ │ │ └── image_copy_capture_p.hpp │ │ ├── CMakeLists.txt │ │ └── manager.cpp │ ├── util.cpp │ ├── hyprland │ │ ├── module.md │ │ ├── focus_grab │ │ │ ├── manager.hpp │ │ │ ├── manager.cpp │ │ │ ├── CMakeLists.txt │ │ │ └── grab.hpp │ │ ├── surface │ │ │ ├── manager.hpp │ │ │ ├── manager.cpp │ │ │ ├── CMakeLists.txt │ │ │ ├── surface.hpp │ │ │ └── surface.cpp │ │ ├── global_shortcuts │ │ │ ├── CMakeLists.txt │ │ │ ├── shortcut.cpp │ │ │ ├── shortcut.hpp │ │ │ ├── manager.hpp │ │ │ └── manager.cpp │ │ ├── test │ │ │ └── manual │ │ │ │ ├── workspaces.qml │ │ │ │ ├── toplevel-association.qml │ │ │ │ └── toplevels.qml │ │ ├── ipc │ │ │ └── CMakeLists.txt │ │ └── CMakeLists.txt │ ├── module.md │ ├── xdgshell.cpp │ ├── idle_inhibit │ │ ├── test │ │ │ └── manual │ │ │ │ └── idle_inhibit.qml │ │ ├── CMakeLists.txt │ │ ├── proto.hpp │ │ └── proto.cpp │ ├── buffer │ │ ├── manager_p.hpp │ │ ├── CMakeLists.txt │ │ ├── qsg.hpp │ │ └── shm.hpp │ ├── popupanchor.hpp │ ├── xdgshell.hpp │ ├── session_lock │ │ ├── shell_integration.hpp │ │ ├── CMakeLists.txt │ │ ├── shell_integration.cpp │ │ ├── manager.cpp │ │ ├── manager.hpp │ │ ├── lock.hpp │ │ ├── lock.cpp │ │ └── surface.hpp │ ├── wlr_layershell │ │ ├── shell_integration.hpp │ │ ├── shell_integration.cpp │ │ └── CMakeLists.txt │ ├── output_tracking.hpp │ ├── idle_notify │ │ ├── CMakeLists.txt │ │ ├── test │ │ │ └── manual │ │ │ │ └── idle_notify.qml │ │ ├── proto.hpp │ │ └── monitor.cpp │ ├── shortcuts_inhibit │ │ ├── CMakeLists.txt │ │ └── test │ │ │ └── manual │ │ │ └── test.qml │ └── toplevel_management │ │ ├── CMakeLists.txt │ │ └── manager.hpp ├── launch │ ├── main.hpp │ └── CMakeLists.txt ├── main.cpp ├── dbus │ ├── dbusmenu │ │ ├── module.md │ │ ├── CMakeLists.txt │ │ └── dbus_menu_types.hpp │ ├── dbus_objectmanager_types.hpp │ ├── bus.hpp │ ├── org.freedesktop.DBus.ObjectManager.xml │ ├── objectmanager.hpp │ ├── org.freedesktop.DBus.Properties.xml │ ├── CMakeLists.txt │ └── bus.cpp ├── services │ ├── mpris │ │ ├── module.md │ │ ├── org.mpris.MediaPlayer2.xml │ │ ├── org.mpris.MediaPlayer2.Player.xml │ │ ├── CMakeLists.txt │ │ └── watcher.hpp │ ├── greetd │ │ ├── module.md │ │ ├── CMakeLists.txt │ │ └── qml.cpp │ ├── pipewire │ │ ├── module.md │ │ ├── connection.cpp │ │ ├── connection.hpp │ │ ├── CMakeLists.txt │ │ ├── metadata.hpp │ │ ├── core.hpp │ │ └── device.hpp │ ├── status_notifier │ │ ├── module.md │ │ ├── org.kde.StatusNotifierWatcher.xml │ │ ├── qml.hpp │ │ ├── qml.cpp │ │ ├── dbus_item_types.hpp │ │ ├── host.hpp │ │ ├── CMakeLists.txt │ │ └── watcher.hpp │ ├── upower │ │ ├── module.md │ │ ├── org.freedesktop.UPower.xml │ │ ├── org.freedesktop.UPower.Device.xml │ │ └── CMakeLists.txt │ ├── notifications │ │ ├── module.md │ │ ├── CMakeLists.txt │ │ ├── dbusimage.hpp │ │ └── org.freedesktop.Notifications.xml │ ├── pam │ │ ├── CMakeLists.txt │ │ ├── subprocess.hpp │ │ ├── ipc.hpp │ │ └── ipc.cpp │ ├── CMakeLists.txt │ └── polkit │ │ ├── CMakeLists.txt │ │ ├── qml.cpp │ │ ├── gobjectref.hpp │ │ └── session.hpp ├── bluetooth │ ├── org.bluez.Device.xml │ ├── org.bluez.Adapter.xml │ ├── module.md │ └── CMakeLists.txt ├── debug │ ├── CMakeLists.txt │ └── lint.hpp ├── ipc │ ├── CMakeLists.txt │ └── ipccommand.hpp ├── x11 │ ├── i3 │ │ ├── module.md │ │ ├── CMakeLists.txt │ │ └── ipc │ │ │ ├── CMakeLists.txt │ │ │ ├── listener.cpp │ │ │ ├── workspace.cpp │ │ │ └── listener.hpp │ ├── util.hpp │ ├── CMakeLists.txt │ ├── init.cpp │ └── util.cpp ├── ui │ ├── CMakeLists.txt │ ├── reload_popup.hpp │ └── reload_popup.cpp ├── build │ ├── build.hpp.in │ └── CMakeLists.txt └── CMakeLists.txt ├── changelog ├── v0.1.0.md ├── v0.2.1.md └── next.md ├── .github ├── ISSUE_TEMPLATE │ └── config.yml └── workflows │ └── lint.yml ├── overlay.nix ├── ci ├── variations.nix └── matrix.nix ├── assets ├── org.quickshell.desktop └── quickshell.svg ├── .gitignore ├── .editorconfig ├── README.md ├── flake.lock ├── flake.nix ├── cmake ├── util.cmake └── pch.cmake ├── shell.nix └── Justfile /src/io/plugin.cpp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/core/test/popupwindow.hpp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /changelog/v0.1.0.md: -------------------------------------------------------------------------------- 1 | Initial release 2 | -------------------------------------------------------------------------------- /src/widgets/cliprect.cpp: -------------------------------------------------------------------------------- 1 | #include "cliprect.hpp" // NOLINT 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /src/window/panelinterface.cpp: -------------------------------------------------------------------------------- 1 | #include "panelinterface.hpp" // NOLINT 2 | -------------------------------------------------------------------------------- /src/crash/main.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void qsCheckCrash(int argc, char** argv); 4 | -------------------------------------------------------------------------------- /src/wayland/platformmenu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void installPlatformMenuHook(); 4 | -------------------------------------------------------------------------------- /src/launch/main.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace qs::launch { 4 | 5 | int main(int argc, char** argv); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "launch/main.hpp" 2 | 3 | int main(int argc, char** argv) { return qs::launch::main(argc, argv); } 4 | -------------------------------------------------------------------------------- /overlay.nix: -------------------------------------------------------------------------------- 1 | { rev ? null }: (final: prev: { 2 | quickshell = final.callPackage ./default.nix { 3 | gitRev = rev; 4 | }; 5 | }) 6 | -------------------------------------------------------------------------------- /ci/variations.nix: -------------------------------------------------------------------------------- 1 | { 2 | clangStdenv, 3 | gccStdenv, 4 | }: { 5 | clang = { stdenv = clangStdenv; }; 6 | gcc = { stdenv = gccStdenv; }; 7 | } 8 | -------------------------------------------------------------------------------- /assets/org.quickshell.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.5 3 | Type=Application 4 | NoDisplay=true 5 | 6 | Name=Quickshell 7 | Icon=org.quickshell 8 | -------------------------------------------------------------------------------- /src/dbus/dbusmenu/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.DBusMenu" 2 | description = "Types related to DBusMenu (used in system tray)" 3 | headers = [ "dbusmenu.hpp" ] 4 | ----- 5 | -------------------------------------------------------------------------------- /src/services/mpris/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.Services.Mpris" 2 | description = "Mpris Service" 3 | headers = [ 4 | "player.hpp", 5 | "watcher.hpp", 6 | ] 7 | ----- 8 | -------------------------------------------------------------------------------- /src/services/greetd/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.Services.Greetd" 2 | description = "Greetd integration" 3 | headers = [ 4 | "qml.hpp", 5 | "connection.hpp", 6 | ] 7 | ----- 8 | -------------------------------------------------------------------------------- /src/services/pipewire/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.Services.Pipewire" 2 | description = "Pipewire API" 3 | headers = [ 4 | "qml.hpp", 5 | "link.hpp", 6 | "node.hpp", 7 | ] 8 | ----- 9 | -------------------------------------------------------------------------------- /src/services/mpris/org.mpris.MediaPlayer2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/services/status_notifier/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.Services.SystemTray" 2 | description = "Types for implementing a system tray" 3 | headers = [ "qml.hpp", "item.hpp" ] 4 | ----- 5 | -------------------------------------------------------------------------------- /src/core/shell.cpp: -------------------------------------------------------------------------------- 1 | #include "shell.hpp" 2 | 3 | #include "qmlglobal.hpp" 4 | 5 | QuickshellSettings* ShellRoot::settings() const { // NOLINT 6 | return QuickshellSettings::instance(); 7 | } 8 | -------------------------------------------------------------------------------- /src/services/upower/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.Services.UPower" 2 | description = "UPower Service" 3 | headers = [ 4 | "core.hpp", 5 | "device.hpp", 6 | "powerprofiles.hpp", 7 | ] 8 | ----- 9 | -------------------------------------------------------------------------------- /src/wayland/util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../window/proxywindow.hpp" 4 | 5 | namespace qs::wayland::util { 6 | 7 | void scheduleCommit(ProxyWindowBase* window); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/services/notifications/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.Services.Notifications" 2 | description = "Types for implementing a notification daemon" 3 | headers = [ "qml.hpp", "notification.hpp" ] 4 | ----- 5 | -------------------------------------------------------------------------------- /src/wayland/screencopy/build.hpp.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // NOLINTBEGIN 3 | #cmakedefine01 SCREENCOPY_ICC 4 | #cmakedefine01 SCREENCOPY_WLR 5 | #cmakedefine01 SCREENCOPY_HYPRLAND_TOPLEVEL 6 | // NOLINTEND 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # related repos 2 | /docs 3 | /examples 4 | 5 | # build files 6 | /result 7 | /build/ 8 | /compile_commands.json 9 | 10 | # clangd 11 | /.cache 12 | 13 | # direnv 14 | /.envrc 15 | /.direnv/ 16 | -------------------------------------------------------------------------------- /src/core/common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | 3 | #include 4 | 5 | namespace qs { 6 | 7 | const QDateTime Common::LAUNCH_TIME = QDateTime::currentDateTime(); 8 | 9 | } // namespace qs 10 | -------------------------------------------------------------------------------- /src/bluetooth/org.bluez.Device.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/debug/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-debug STATIC 2 | lint.cpp 3 | ) 4 | 5 | qs_pch(quickshell-debug) 6 | target_link_libraries(quickshell-debug PRIVATE Qt::Quick) 7 | target_link_libraries(quickshell PRIVATE quickshell-debug) 8 | -------------------------------------------------------------------------------- /src/io/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.Io" 2 | description = "Io types" 3 | headers = [ 4 | "datastream.hpp", 5 | "socket.hpp", 6 | "process.hpp", 7 | "fileview.hpp", 8 | "jsonadapter.hpp", 9 | "ipchandler.hpp", 10 | ] 11 | ----- 12 | -------------------------------------------------------------------------------- /src/widgets/ClippingWrapperRectangleInternal.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | 3 | ClippingRectangle { 4 | id: root 5 | property alias __implicitWidthInternal: root.implicitWidth 6 | property alias __implicitHeightInternal: root.implicitHeight 7 | } 8 | -------------------------------------------------------------------------------- /src/core/iconprovider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | QIcon getEngineImageAsIcon(QQmlEngine* engine, const QUrl& url); 8 | QIcon getCurrentEngineImageAsIcon(const QUrl& url); 9 | -------------------------------------------------------------------------------- /src/debug/lint.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace qs::debug { 7 | 8 | void lintObjectTree(QObject* object); 9 | void lintItemTree(QQuickItem* item); 10 | 11 | } // namespace qs::debug 12 | -------------------------------------------------------------------------------- /src/ipc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-ipc STATIC 2 | ipc.cpp 3 | ) 4 | 5 | qs_pch(quickshell-ipc) 6 | 7 | target_link_libraries(quickshell-ipc PRIVATE Qt::Quick Qt::Network) 8 | 9 | target_link_libraries(quickshell PRIVATE quickshell-ipc) 10 | -------------------------------------------------------------------------------- /src/wayland/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.hpp" 2 | 3 | #include "../window/proxywindow.hpp" 4 | 5 | namespace qs::wayland::util { 6 | 7 | void scheduleCommit(ProxyWindowBase* window) { window->schedulePolish(); } 8 | 9 | } // namespace qs::wayland::util 10 | -------------------------------------------------------------------------------- /src/io/test/process.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class TestProcess: public QObject { 7 | Q_OBJECT; 8 | 9 | private slots: 10 | static void startAfterReload(); 11 | static void testExec(); 12 | }; 13 | -------------------------------------------------------------------------------- /src/bluetooth/org.bluez.Adapter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/io/test/datastream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class TestSplitParser: public QObject { 7 | Q_OBJECT; 8 | 9 | private slots: 10 | void splits_data(); // NOLINT 11 | void splits(); 12 | void initBuffer(); 13 | }; 14 | -------------------------------------------------------------------------------- /src/x11/i3/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.I3" 2 | description = "I3 specific Quickshell types" 3 | headers = [ 4 | "ipc/connection.hpp", 5 | "ipc/controller.hpp", 6 | "ipc/qml.hpp", 7 | "ipc/workspace.hpp", 8 | "ipc/monitor.hpp", 9 | "ipc/listener.hpp", 10 | ] 11 | ----- 12 | -------------------------------------------------------------------------------- /src/core/common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace qs { 7 | 8 | struct Common { 9 | static const QDateTime LAUNCH_TIME; 10 | static inline QProcessEnvironment INITIAL_ENVIRONMENT = {}; // NOLINT 11 | }; 12 | 13 | } // namespace qs 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = tab 8 | 9 | [*.nix] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [*.{yml,yaml}] 14 | indent_style = space 15 | indent_size = 2 16 | 17 | [*.scm] 18 | indent_style = space -------------------------------------------------------------------------------- /src/core/test/transformwatcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class TestTransformWatcher: public QObject { 7 | Q_OBJECT; 8 | 9 | private slots: 10 | void aParentOfB(); 11 | void bParentOfA(); 12 | void aParentChainB(); 13 | void multiWindow(); 14 | }; 15 | -------------------------------------------------------------------------------- /src/core/test/stacklist.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class TestStackList: public QObject { 7 | Q_OBJECT; 8 | 9 | private slots: 10 | static void push(); 11 | static void pushAndGrow(); 12 | static void copy(); 13 | static void viewVla(); 14 | static void viewVlaGrown(); 15 | }; 16 | -------------------------------------------------------------------------------- /src/dbus/dbus_objectmanager_types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using DBusObjectManagerInterfaces = QHash; 10 | using DBusObjectManagerObjects = QHash; 11 | -------------------------------------------------------------------------------- /src/crash/interface.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class CrashReporterGui: public QWidget { 6 | public: 7 | CrashReporterGui(QString reportFolder, int pid); 8 | 9 | private slots: 10 | void openFolder(); 11 | 12 | static void openReportUrl(); 13 | static void cancel(); 14 | 15 | private: 16 | QString reportFolder; 17 | }; 18 | -------------------------------------------------------------------------------- /src/wayland/hyprland/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.Hyprland" 2 | description = "Hyprland specific Quickshell types" 3 | headers = [ 4 | "ipc/connection.hpp", 5 | "ipc/monitor.hpp", 6 | "ipc/workspace.hpp", 7 | "ipc/hyprland_toplevel.hpp", 8 | "ipc/qml.hpp", 9 | "focus_grab/qml.hpp", 10 | "global_shortcuts/qml.hpp", 11 | "surface/qml.hpp", 12 | ] 13 | ----- 14 | -------------------------------------------------------------------------------- /src/bluetooth/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.Bluetooth" 2 | description = "Bluetooth API" 3 | headers = [ 4 | "bluez.hpp", 5 | "adapter.hpp", 6 | "device.hpp", 7 | ] 8 | ----- 9 | This module exposes Bluetooth management APIs provided by the BlueZ DBus interface. 10 | Both DBus and BlueZ must be running to use it. 11 | 12 | See the @@Quickshell.Bluetooth.Bluetooth singleton. 13 | -------------------------------------------------------------------------------- /src/wayland/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.Wayland" 2 | description = "Wayland specific Quickshell types" 3 | headers = [ 4 | "wlr_layershell/wlr_layershell.hpp", 5 | "session_lock.hpp", 6 | "toplevel_management/qml.hpp", 7 | "screencopy/view.hpp", 8 | "idle_inhibit/inhibitor.hpp", 9 | "idle_notify/monitor.hpp", 10 | "shortcuts_inhibit/inhibitor.hpp", 11 | ] 12 | ----- 13 | -------------------------------------------------------------------------------- /src/widgets/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell.Widgets" 2 | description = "Bundled widgets" 3 | 4 | headers = [ 5 | "wrapper.hpp", 6 | "marginwrapper.hpp", 7 | ] 8 | 9 | qml_files = [ 10 | "IconImage.qml", 11 | "ClippingRectangle.qml", 12 | "WrapperItem.qml", 13 | "WrapperMouseArea.qml", 14 | "WrapperRectangle.qml", 15 | "ClippingWrapperRectangle.qml", 16 | ] 17 | ----- 18 | -------------------------------------------------------------------------------- /src/dbus/bus.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace qs::dbus { 10 | 11 | void tryLaunchService( 12 | QObject* parent, 13 | QDBusConnection& connection, 14 | const QString& serviceName, 15 | const std::function& callback 16 | ); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/core/model.cpp: -------------------------------------------------------------------------------- 1 | #include "model.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | QHash UntypedObjectModel::roleNames() const { 8 | return {{Qt::UserRole, "modelData"}}; 9 | } 10 | 11 | UntypedObjectModel* UntypedObjectModel::emptyInstance() { 12 | static auto* instance = new ObjectModel(nullptr); 13 | return instance; 14 | } 15 | -------------------------------------------------------------------------------- /src/window/test/windowattached.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class TestWindowAttachment: public QObject { 7 | Q_OBJECT; 8 | 9 | private slots: 10 | static void attachedAfterReload(); 11 | static void attachedBeforeReload(); 12 | static void earlyAttachReloaded(); 13 | static void owningWindowChanged(); 14 | static void nonItemParents(); 15 | }; 16 | -------------------------------------------------------------------------------- /src/wayland/xdgshell.cpp: -------------------------------------------------------------------------------- 1 | #include "xdgshell.hpp" 2 | 3 | #include 4 | 5 | namespace qs::wayland::xdg_shell { 6 | 7 | XdgWmBase::XdgWmBase(): QWaylandClientExtensionTemplate(6) { this->initialize(); } 8 | 9 | XdgWmBase* XdgWmBase::instance() { 10 | static auto* instance = new XdgWmBase(); // NOLINT 11 | return instance; 12 | } 13 | 14 | } // namespace qs::wayland::xdg_shell 15 | -------------------------------------------------------------------------------- /src/core/enginecontext.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "qsintercept.hpp" 4 | #include "scan.hpp" 5 | #include "singleton.hpp" 6 | 7 | class EngineContext { 8 | public: 9 | explicit EngineContext(const QmlScanner& scanner); 10 | 11 | private: 12 | const QmlScanner& scanner; 13 | QQmlEngine engine; 14 | QsInterceptNetworkAccessManagerFactory interceptFactory; 15 | SingletonRegistry singletonRegistry; 16 | }; 17 | -------------------------------------------------------------------------------- /ci/matrix.nix: -------------------------------------------------------------------------------- 1 | { 2 | qtver, 3 | compiler, 4 | }: let 5 | checkouts = import ./nix-checkouts.nix; 6 | nixpkgs = checkouts.${builtins.replaceStrings ["."] ["_"] qtver}; 7 | compilerOverride = (nixpkgs.callPackage ./variations.nix {}).${compiler}; 8 | pkg = (nixpkgs.callPackage ../default.nix {}).override (compilerOverride // { 9 | wayland-protocols = checkouts.latest.wayland-protocols; 10 | }); 11 | in pkg 12 | -------------------------------------------------------------------------------- /src/window/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | function (qs_test name) 2 | add_executable(${name} ${ARGN}) 3 | target_link_libraries(${name} PRIVATE Qt::Quick Qt::Test quickshell-window quickshell-core quickshell-ui quickshell-io) 4 | add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $) 5 | endfunction() 6 | 7 | qs_test(popupwindow popupwindow.cpp) 8 | qs_test(windowattached windowattached.cpp) 9 | -------------------------------------------------------------------------------- /src/wayland/idle_inhibit/test/manual/idle_inhibit.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls 3 | import Quickshell 4 | import Quickshell.Wayland 5 | 6 | FloatingWindow { 7 | id: root 8 | color: contentItem.palette.window 9 | 10 | CheckBox { 11 | id: enableCb 12 | anchors.centerIn: parent 13 | text: "Enable Inhibitor" 14 | } 15 | 16 | IdleInhibitor { 17 | window: root 18 | enabled: enableCb.checked 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/window/test/popupwindow.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class TestPopupWindow: public QObject { 7 | Q_OBJECT; 8 | 9 | private slots: 10 | void initiallyVisible(); 11 | void reloadReparent(); 12 | void reloadUnparent(); 13 | void invisibleWithoutParent(); 14 | void moveWithParent(); 15 | void attachParentLate(); 16 | void reparentLate(); 17 | void xMigrationFix(); 18 | }; 19 | -------------------------------------------------------------------------------- /src/wayland/buffer/manager_p.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "dmabuf.hpp" 4 | #include "manager.hpp" 5 | 6 | namespace qs::wayland::buffer { 7 | 8 | class WlBufferManagerPrivate { 9 | public: 10 | explicit WlBufferManagerPrivate(WlBufferManager* manager); 11 | 12 | void dmabufReady(); 13 | 14 | WlBufferManager* manager; 15 | dmabuf::LinuxDmabufManager dmabuf; 16 | 17 | bool mReady = false; 18 | }; 19 | 20 | } // namespace qs::wayland::buffer 21 | -------------------------------------------------------------------------------- /src/ui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-ui STATIC 2 | reload_popup.cpp 3 | ) 4 | 5 | # do not install this module 6 | qt_add_qml_module(quickshell-ui 7 | URI Quickshell._InternalUi 8 | VERSION 0.1 9 | DEPENDENCIES QtQuick 10 | QML_FILES 11 | Tooltip.qml 12 | ReloadPopup.qml 13 | ) 14 | 15 | qs_module_pch(quickshell-ui SET large) 16 | 17 | target_link_libraries(quickshell-ui PRIVATE Qt::Quick) 18 | target_link_libraries(quickshell PRIVATE quickshell-uiplugin) 19 | -------------------------------------------------------------------------------- /src/io/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | function (qs_test name) 2 | add_executable(${name} ${ARGN}) 3 | target_link_libraries(${name} PRIVATE Qt::Quick Qt::Network Qt::Test quickshell-io quickshell-core quickshell-window quickshell-ui) 4 | add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $) 5 | endfunction() 6 | 7 | qs_test(datastream datastream.cpp ../datastream.cpp) 8 | qs_test(process process.cpp ../process.cpp ../datastream.cpp ../processcore.cpp) 9 | -------------------------------------------------------------------------------- /src/wayland/popupanchor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../core/popupanchor.hpp" 6 | 7 | class WaylandPopupPositioner: public PopupPositioner { 8 | public: 9 | void reposition(PopupAnchor* anchor, QWindow* window, bool onlyIfDirty = true) override; 10 | [[nodiscard]] bool shouldRepositionOnMove() const override; 11 | 12 | private: 13 | static void setFlags(PopupAnchor* anchor, QWindow* window); 14 | }; 15 | 16 | void installPopupPositioner(); 17 | -------------------------------------------------------------------------------- /src/build/build.hpp.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // NOLINTBEGIN 4 | #define GIT_REVISION "@GIT_REVISION@" 5 | #define DISTRIBUTOR "@DISTRIBUTOR@" 6 | #define DISTRIBUTOR_DEBUGINFO_AVAILABLE @DEBUGINFO_AVAILABLE@ 7 | #define CRASH_REPORTER @CRASH_REPORTER_DEF@ 8 | #define BUILD_TYPE "@CMAKE_BUILD_TYPE@" 9 | #define COMPILER "@CMAKE_CXX_COMPILER_ID@ (@CMAKE_CXX_COMPILER_VERSION@)" 10 | #define COMPILE_FLAGS "@CMAKE_CXX_FLAGS@" 11 | #define BUILD_CONFIGURATION "@QS_BUILD_OPTIONS@" 12 | // NOLINTEND 13 | -------------------------------------------------------------------------------- /src/crash/handler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../core/instanceinfo.hpp" 6 | namespace qs::crash { 7 | 8 | struct CrashHandlerPrivate; 9 | 10 | class CrashHandler { 11 | public: 12 | explicit CrashHandler(); 13 | ~CrashHandler(); 14 | Q_DISABLE_COPY_MOVE(CrashHandler); 15 | 16 | void init(); 17 | void setRelaunchInfo(const RelaunchInfo& info); 18 | 19 | private: 20 | CrashHandlerPrivate* d; 21 | }; 22 | 23 | } // namespace qs::crash 24 | -------------------------------------------------------------------------------- /src/core/platformmenu_p.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace qs::menu::platform { 6 | 7 | class PlatformMenuQMenu: public QMenu { 8 | public: 9 | explicit PlatformMenuQMenu() = default; 10 | ~PlatformMenuQMenu() override; 11 | Q_DISABLE_COPY_MOVE(PlatformMenuQMenu); 12 | 13 | void setVisible(bool visible) override; 14 | 15 | PlatformMenuQMenu* containingMenu = nullptr; 16 | QPoint targetPosition; 17 | }; 18 | 19 | } // namespace qs::menu::platform 20 | -------------------------------------------------------------------------------- /src/core/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | function (qs_test name) 2 | add_executable(${name} ${ARGN}) 3 | target_link_libraries(${name} PRIVATE Qt::Quick Qt::Test quickshell-core quickshell-window quickshell-ui quickshell-io) 4 | add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $) 5 | endfunction() 6 | 7 | qs_test(transformwatcher transformwatcher.cpp) 8 | qs_test(ringbuffer ringbuf.cpp) 9 | qs_test(scriptmodel scriptmodel.cpp) 10 | qs_test(stacklist stacklist.cpp) 11 | -------------------------------------------------------------------------------- /src/core/incubator.cpp: -------------------------------------------------------------------------------- 1 | #include "incubator.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "logcat.hpp" 8 | 9 | QS_LOGGING_CATEGORY(logIncubator, "quickshell.incubator", QtWarningMsg); 10 | 11 | void QsQmlIncubator::statusChanged(QQmlIncubator::Status status) { 12 | switch (status) { 13 | case QQmlIncubator::Ready: emit this->completed(); break; 14 | case QQmlIncubator::Error: emit this->failed(); break; 15 | default: break; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/ipc/ipccommand.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../io/ipccomm.hpp" 6 | #include "ipc.hpp" 7 | 8 | namespace qs::ipc { 9 | 10 | struct IpcKillCommand: std::monostate { 11 | static void exec(IpcServerConnection* /*unused*/); 12 | }; 13 | 14 | using IpcCommand = std::variant< 15 | std::monostate, 16 | IpcKillCommand, 17 | qs::io::ipc::comm::QueryMetadataCommand, 18 | qs::io::ipc::comm::StringCallCommand, 19 | qs::io::ipc::comm::StringPropReadCommand>; 20 | 21 | } // namespace qs::ipc 22 | -------------------------------------------------------------------------------- /src/widgets/cliprect.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class ClippingRectangleBorder { 10 | Q_GADGET; 11 | Q_PROPERTY(QColor color MEMBER color); 12 | Q_PROPERTY(bool pixelAligned MEMBER pixelAligned); 13 | Q_PROPERTY(int width MEMBER width); 14 | QML_VALUE_TYPE(clippingRectangleBorder); 15 | 16 | public: 17 | QColor color = Qt::black; 18 | bool pixelAligned = true; 19 | int width = 0; 20 | }; 21 | -------------------------------------------------------------------------------- /src/wayland/screencopy/wlr_screencopy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-wayland-screencopy-wlr STATIC 2 | wlr_screencopy.cpp 3 | ) 4 | 5 | wl_proto(wlp-wlr-screencopy wlr-screencopy-unstable-v1 "${CMAKE_CURRENT_SOURCE_DIR}") 6 | 7 | target_link_libraries(quickshell-wayland-screencopy-wlr PRIVATE 8 | Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 9 | Qt::Quick # for pch 10 | ) 11 | 12 | target_link_libraries(quickshell-wayland-screencopy-wlr PUBLIC wlp-wlr-screencopy) 13 | 14 | qs_pch(quickshell-wayland-screencopy-wlr SET large) 15 | -------------------------------------------------------------------------------- /src/wayland/xdgshell.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace qs::wayland::xdg_shell { 7 | 8 | // Hack that binds xdg_wm_base twice as QtWaylandXdgShell headers are not exported anywhere. 9 | 10 | class XdgWmBase 11 | : public QWaylandClientExtensionTemplate 12 | , public QtWayland::xdg_wm_base { 13 | public: 14 | static XdgWmBase* instance(); 15 | 16 | private: 17 | explicit XdgWmBase(); 18 | }; 19 | 20 | } // namespace qs::wayland::xdg_shell 21 | -------------------------------------------------------------------------------- /src/x11/i3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-i3 STATIC) 2 | 3 | target_link_libraries(quickshell-i3 PRIVATE ${QT_DEPS}) 4 | 5 | set(I3_MODULES) 6 | 7 | if (I3_IPC) 8 | add_subdirectory(ipc) 9 | list(APPEND I3_MODULES Quickshell.I3._Ipc) 10 | endif() 11 | 12 | qt_add_qml_module(quickshell-i3 13 | URI Quickshell.I3 14 | VERSION 0.1 15 | IMPORTS ${I3_MODULES} 16 | ) 17 | 18 | install_qml_module(quickshell-i3) 19 | 20 | qs_pch(quickshell-i3) 21 | qs_pch(quickshell-i3plugin) 22 | 23 | target_link_libraries(quickshell PRIVATE quickshell-i3plugin) 24 | -------------------------------------------------------------------------------- /src/launch/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(CLI11 CONFIG REQUIRED) 2 | 3 | qt_add_library(quickshell-launch STATIC 4 | parsecommand.cpp 5 | command.cpp 6 | launch.cpp 7 | main.cpp 8 | ) 9 | 10 | target_link_libraries(quickshell-launch PRIVATE 11 | Qt::Quick Qt::Widgets CLI11::CLI11 quickshell-build 12 | ) 13 | 14 | qs_add_pchset(launch 15 | DEPENDENCIES Qt::Core CLI11::CLI11 16 | HEADERS 17 | 18 | 19 | ) 20 | 21 | qs_pch(quickshell-launch SET launch) 22 | 23 | target_link_libraries(quickshell PRIVATE quickshell-launch) 24 | -------------------------------------------------------------------------------- /src/services/pipewire/connection.cpp: -------------------------------------------------------------------------------- 1 | #include "connection.hpp" 2 | 3 | #include 4 | 5 | namespace qs::service::pipewire { 6 | 7 | PwConnection::PwConnection(QObject* parent): QObject(parent) { 8 | if (this->core.isValid()) { 9 | this->registry.init(this->core); 10 | } 11 | } 12 | 13 | PwConnection* PwConnection::instance() { 14 | static PwConnection* instance = nullptr; // NOLINT 15 | 16 | if (instance == nullptr) { 17 | instance = new PwConnection(); 18 | } 19 | 20 | return instance; 21 | } 22 | 23 | } // namespace qs::service::pipewire 24 | -------------------------------------------------------------------------------- /src/services/upower/org.freedesktop.UPower.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/core/test/ringbuf.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class TestObject { 9 | public: 10 | explicit TestObject(quint32* count); 11 | ~TestObject(); 12 | Q_DISABLE_COPY_MOVE(TestObject); 13 | 14 | private: 15 | quint32* count; 16 | }; 17 | 18 | class TestRingBuffer: public QObject { 19 | Q_OBJECT; 20 | 21 | private slots: 22 | static void fill(); 23 | static void clearPartial(); 24 | static void move(); 25 | 26 | static void hashLookup(); 27 | }; 28 | -------------------------------------------------------------------------------- /src/wayland/session_lock/shell_integration.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class QSWaylandSessionLockIntegration: public QtWaylandClient::QWaylandShellIntegration { 9 | public: 10 | bool initialize(QtWaylandClient::QWaylandDisplay* /* display */) override { return true; } 11 | QtWaylandClient::QWaylandShellSurface* 12 | createShellSurface(QtWaylandClient::QWaylandWindow* window) override; 13 | }; 14 | -------------------------------------------------------------------------------- /src/services/greetd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-service-greetd STATIC 2 | qml.cpp 3 | connection.cpp 4 | ) 5 | 6 | qt_add_qml_module(quickshell-service-greetd 7 | URI Quickshell.Services.Greetd 8 | VERSION 0.1 9 | DEPENDENCIES QtQml 10 | ) 11 | 12 | install_qml_module(quickshell-service-greetd) 13 | 14 | # can't be Qt::Qml because generation.hpp pulls in gui types 15 | target_link_libraries(quickshell-service-greetd PRIVATE Qt::Quick) 16 | 17 | qs_module_pch(quickshell-service-greetd) 18 | 19 | target_link_libraries(quickshell PRIVATE quickshell-service-greetdplugin) 20 | -------------------------------------------------------------------------------- /src/services/pam/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-service-pam STATIC 2 | qml.cpp 3 | conversation.cpp 4 | ipc.cpp 5 | subprocess.cpp 6 | ) 7 | 8 | qt_add_qml_module(quickshell-service-pam 9 | URI Quickshell.Services.Pam 10 | VERSION 0.1 11 | DEPENDENCIES QtQml 12 | ) 13 | 14 | install_qml_module(quickshell-service-pam) 15 | 16 | target_link_libraries(quickshell-service-pam PRIVATE 17 | Qt::Qml pam ${PAM_LIBRARIES} 18 | Qt::Quick # pch 19 | ) 20 | 21 | qs_module_pch(quickshell-service-pam) 22 | 23 | target_link_libraries(quickshell PRIVATE quickshell-service-pamplugin) 24 | -------------------------------------------------------------------------------- /src/services/pipewire/connection.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "core.hpp" 4 | #include "defaults.hpp" 5 | #include "registry.hpp" 6 | 7 | namespace qs::service::pipewire { 8 | 9 | class PwConnection: public QObject { 10 | Q_OBJECT; 11 | 12 | public: 13 | explicit PwConnection(QObject* parent = nullptr); 14 | 15 | PwRegistry registry; 16 | PwDefaultTracker defaults {&this->registry}; 17 | 18 | static PwConnection* instance(); 19 | 20 | private: 21 | // init/destroy order is important. do not rearrange. 22 | PwCore core; 23 | }; 24 | 25 | } // namespace qs::service::pipewire 26 | -------------------------------------------------------------------------------- /src/wayland/buffer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(PkgConfig REQUIRED) 2 | pkg_check_modules(dmabuf-deps REQUIRED IMPORTED_TARGET libdrm gbm egl) 3 | 4 | qt_add_library(quickshell-wayland-buffer STATIC 5 | manager.cpp 6 | dmabuf.cpp 7 | shm.cpp 8 | ) 9 | 10 | wl_proto(wlp-linux-dmabuf linux-dmabuf-v1 "${WAYLAND_PROTOCOLS}/stable/linux-dmabuf") 11 | 12 | target_link_libraries(quickshell-wayland-buffer PRIVATE 13 | Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 14 | PkgConfig::dmabuf-deps 15 | wlp-linux-dmabuf 16 | ) 17 | 18 | qs_pch(quickshell-wayland-buffer SET large) 19 | -------------------------------------------------------------------------------- /src/core/iconimageprovider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class IconImageProvider: public QQuickImageProvider { 7 | public: 8 | explicit IconImageProvider(): QQuickImageProvider(QQuickImageProvider::Pixmap) {} 9 | 10 | QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override; 11 | 12 | static QPixmap missingPixmap(const QSize& size); 13 | 14 | static QString requestString( 15 | const QString& icon, 16 | const QString& path = QString(), 17 | const QString& fallback = QString() 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /src/x11/i3/ipc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-i3-ipc STATIC 2 | connection.cpp 3 | qml.cpp 4 | workspace.cpp 5 | monitor.cpp 6 | controller.cpp 7 | listener.cpp 8 | ) 9 | 10 | qt_add_qml_module(quickshell-i3-ipc 11 | URI Quickshell.I3._Ipc 12 | VERSION 0.1 13 | DEPENDENCIES QtQml 14 | ) 15 | 16 | qs_add_module_deps_light(quickshell-i3-ipc Quickshell) 17 | 18 | install_qml_module(quickshell-i3-ipc) 19 | 20 | target_link_libraries(quickshell-i3-ipc PRIVATE Qt::Quick) 21 | 22 | qs_module_pch(quickshell-i3-ipc SET large) 23 | 24 | target_link_libraries(quickshell PRIVATE quickshell-i3-ipcplugin) 25 | -------------------------------------------------------------------------------- /src/core/shell.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "qmlglobal.hpp" 9 | #include "reload.hpp" 10 | 11 | ///! Optional root config element, allowing some settings to be specified inline. 12 | class ShellRoot: public ReloadPropagator { 13 | Q_OBJECT; 14 | Q_PROPERTY(QuickshellSettings* settings READ settings CONSTANT); 15 | QML_ELEMENT; 16 | 17 | public: 18 | explicit ShellRoot(QObject* parent = nullptr): ReloadPropagator(parent) {} 19 | 20 | [[nodiscard]] QuickshellSettings* settings() const; 21 | }; 22 | -------------------------------------------------------------------------------- /src/wayland/session_lock/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-wayland-sessionlock STATIC 2 | manager.cpp 3 | surface.cpp 4 | lock.cpp 5 | shell_integration.cpp 6 | session_lock.cpp 7 | ) 8 | 9 | wl_proto(wlp-session-lock ext-session-lock-v1 "${WAYLAND_PROTOCOLS}/staging/ext-session-lock") 10 | 11 | target_link_libraries(quickshell-wayland-sessionlock PRIVATE 12 | Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 13 | wlp-session-lock 14 | ) 15 | 16 | qs_pch(quickshell-wayland-sessionlock SET large) 17 | 18 | target_link_libraries(quickshell-wayland PRIVATE quickshell-wayland-sessionlock) 19 | -------------------------------------------------------------------------------- /src/crash/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-crash STATIC 2 | main.cpp 3 | interface.cpp 4 | handler.cpp 5 | ) 6 | 7 | qs_pch(quickshell-crash SET large) 8 | 9 | find_package(PkgConfig REQUIRED) 10 | pkg_check_modules(breakpad REQUIRED IMPORTED_TARGET breakpad) 11 | # only need client?? take only includes from pkg config todo 12 | target_link_libraries(quickshell-crash PRIVATE PkgConfig::breakpad -lbreakpad_client) 13 | 14 | # quick linked for pch compat 15 | target_link_libraries(quickshell-crash PRIVATE quickshell-build Qt::Quick Qt::Widgets) 16 | 17 | target_link_libraries(quickshell PRIVATE quickshell-crash) 18 | -------------------------------------------------------------------------------- /src/wayland/screencopy/hyprland_screencopy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-wayland-screencopy-hyprland STATIC 2 | hyprland_screencopy.cpp 3 | ) 4 | 5 | wl_proto(wlp-hyprland-screencopy hyprland-toplevel-export-v1 "${CMAKE_CURRENT_SOURCE_DIR}") 6 | 7 | target_link_libraries(quickshell-wayland-screencopy-hyprland PRIVATE 8 | Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 9 | Qt::Quick # for pch 10 | ) 11 | 12 | target_link_libraries(quickshell-wayland-screencopy-hyprland PUBLIC 13 | wlp-hyprland-screencopy wlp-foreign-toplevel 14 | ) 15 | 16 | qs_pch(quickshell-wayland-screencopy-hyprland SET large) 17 | -------------------------------------------------------------------------------- /src/services/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (SERVICE_STATUS_NOTIFIER) 2 | add_subdirectory(status_notifier) 3 | endif() 4 | 5 | if (SERVICE_PIPEWIRE) 6 | add_subdirectory(pipewire) 7 | endif() 8 | 9 | if (SERVICE_MPRIS) 10 | add_subdirectory(mpris) 11 | endif() 12 | 13 | if (SERVICE_PAM) 14 | add_subdirectory(pam) 15 | endif() 16 | 17 | if (SERVICE_POLKIT) 18 | add_subdirectory(polkit) 19 | endif() 20 | 21 | if (SERVICE_GREETD) 22 | add_subdirectory(greetd) 23 | endif() 24 | 25 | if (SERVICE_UPOWER) 26 | add_subdirectory(upower) 27 | endif() 28 | 29 | if (SERVICE_NOTIFICATIONS) 30 | add_subdirectory(notifications) 31 | endif() 32 | -------------------------------------------------------------------------------- /src/core/toolsupport.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "scan.hpp" 6 | 7 | namespace qs::core { 8 | 9 | class QmlToolingSupport { 10 | public: 11 | static bool updateTooling(const QDir& configRoot, QmlScanner& scanner); 12 | 13 | private: 14 | static QString getQmllsConfig(); 15 | static bool lockTooling(); 16 | static bool updateQmllsConfig(const QDir& configRoot, bool create); 17 | static void updateToolingFs(QmlScanner& scanner, const QDir& scanDir, const QDir& linkDir); 18 | static inline bool toolingEnabled = false; 19 | static inline QFile* toolingLock = nullptr; 20 | }; 21 | 22 | } // namespace qs::core 23 | -------------------------------------------------------------------------------- /src/window/init.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../core/plugin.hpp" 5 | 6 | namespace { 7 | 8 | class WindowPlugin: public QsEnginePlugin { 9 | // _Window has to be registered before wayland or x11 modules, otherwise module overlays 10 | // will apply in the wrong order. 11 | QString name() override { return "window"; } 12 | 13 | void registerTypes() override { 14 | qmlRegisterModuleImport( 15 | "Quickshell", 16 | QQmlModuleImportModuleAny, 17 | "Quickshell._Window", 18 | QQmlModuleImportLatest 19 | ); 20 | } 21 | }; 22 | 23 | QS_REGISTER_PLUGIN(WindowPlugin); 24 | 25 | } // namespace 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quickshell 2 | See the [website](https://quickshell.outfoxxed.me) for more information 3 | and installation instructions. 4 | 5 | This repo is hosted at: 6 | - https://git.outfoxxed.me/quickshell/quickshell 7 | - https://github.com/quickshell-mirror/quickshell 8 | 9 | # Contributing / Development 10 | See [CONTRIBUTING.md](CONTRIBUTING.md) for details. 11 | 12 | #### License 13 | 14 | 15 | Licensed under the GNU LGPL 3. 16 | 17 | 18 |
19 | 20 | 21 | Unless you explicitly state otherwise, any contribution submitted 22 | for inclusion shall be licensed as above, without any additional 23 | terms or conditions. 24 | 25 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1762977756, 6 | "narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "id": "nixpkgs", 14 | "ref": "nixos-unstable", 15 | "type": "indirect" 16 | } 17 | }, 18 | "root": { 19 | "inputs": { 20 | "nixpkgs": "nixpkgs" 21 | } 22 | } 23 | }, 24 | "root": "root", 25 | "version": 7 26 | } 27 | -------------------------------------------------------------------------------- /src/core/doc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // hide a property, function, or signal from typegen 4 | #define QSDOC_HIDE 5 | 6 | // override the base class as seen by typegen 7 | #define QSDOC_BASECLASS(baseclass) 8 | 9 | // make the type visible in the docs even if not a QML_ELEMENT 10 | #define QSDOC_ELEMENT 11 | #define QSDOC_NAMED_ELEMENT(name) 12 | 13 | // unmark uncreatable (will be overlayed by other types) 14 | #define QSDOC_CREATABLE 15 | 16 | // change the cname used for this type 17 | #define QSDOC_CNAME(name) 18 | 19 | // overridden properties 20 | #define QSDOC_PROPERTY_OVERRIDE(...) 21 | 22 | // override types of properties for docs 23 | #define QSDOC_TYPE_OVERRIDE(type) 24 | -------------------------------------------------------------------------------- /src/wayland/hyprland/focus_grab/manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace qs::hyprland::focus_grab { 7 | using HyprlandFocusGrabManager = QtWayland::hyprland_focus_grab_manager_v1; 8 | class FocusGrab; 9 | 10 | class FocusGrabManager 11 | : public QWaylandClientExtensionTemplate 12 | , public HyprlandFocusGrabManager { 13 | public: 14 | explicit FocusGrabManager(); 15 | 16 | [[nodiscard]] bool available() const; 17 | [[nodiscard]] FocusGrab* createGrab(); 18 | 19 | static FocusGrabManager* instance(); 20 | }; 21 | 22 | } // namespace qs::hyprland::focus_grab 23 | -------------------------------------------------------------------------------- /src/wayland/hyprland/surface/manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "surface.hpp" 8 | 9 | namespace qs::hyprland::surface::impl { 10 | 11 | class HyprlandSurfaceManager 12 | : public QWaylandClientExtensionTemplate 13 | , public QtWayland::hyprland_surface_manager_v1 { 14 | public: 15 | explicit HyprlandSurfaceManager(); 16 | 17 | HyprlandSurface* createHyprlandExtension(QtWaylandClient::QWaylandWindow* surface); 18 | 19 | static HyprlandSurfaceManager* instance(); 20 | }; 21 | 22 | } // namespace qs::hyprland::surface::impl 23 | -------------------------------------------------------------------------------- /src/build/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(quickshell-build INTERFACE) 2 | 3 | if (NOT DEFINED GIT_REVISION) 4 | execute_process( 5 | COMMAND git rev-parse HEAD 6 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 7 | OUTPUT_VARIABLE GIT_REVISION 8 | OUTPUT_STRIP_TRAILING_WHITESPACE 9 | ) 10 | endif() 11 | 12 | if (CRASH_REPORTER) 13 | set(CRASH_REPORTER_DEF 1) 14 | else() 15 | set(CRASH_REPORTER_DEF 0) 16 | endif() 17 | 18 | if (DISTRIBUTOR_DEBUGINFO_AVAILABLE) 19 | set(DEBUGINFO_AVAILABLE 1) 20 | else() 21 | set(DEBUGINFO_AVAILABLE 0) 22 | endif() 23 | 24 | configure_file(build.hpp.in build.hpp @ONLY ESCAPE_QUOTES) 25 | 26 | target_include_directories(quickshell-build INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) 27 | -------------------------------------------------------------------------------- /src/x11/util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | xcb_connection_t* x11Connection(); 9 | 10 | class XAtom { 11 | public: 12 | [[nodiscard]] bool isValid(); 13 | [[nodiscard]] const xcb_atom_t& atom(); 14 | 15 | // NOLINTBEGIN 16 | static XAtom _NET_WM_STRUT; 17 | static XAtom _NET_WM_STRUT_PARTIAL; 18 | static XAtom _NET_WM_DESKTOP; 19 | // NOLINTEND 20 | 21 | static void initAtoms(); 22 | 23 | private: 24 | void init(const QByteArray& name); 25 | void resolve(); 26 | 27 | bool resolved = false; 28 | xcb_atom_t mAtom = XCB_ATOM_NONE; 29 | xcb_intern_atom_cookie_t cookie {}; 30 | }; 31 | -------------------------------------------------------------------------------- /changelog/v0.2.1.md: -------------------------------------------------------------------------------- 1 | ## New Features 2 | 3 | - Changes to desktop entries are now tracked in real time. 4 | 5 | ## Other Changes 6 | 7 | - Added support for Qt 6.10 8 | 9 | ## Bug Fixes 10 | 11 | - Fixed volumes getting stuck on change for pipewire devices with few volume steps. 12 | - Fixed a crash when running out of disk space to write log files. 13 | - Fixed a rare crash when disconnecting a monitor. 14 | - Fixed build issues preventing cross compilation from working. 15 | - Fixed dekstop entries with lower priority than a hidden entry not being hidden. 16 | - Fixed desktop entry keys with mismatched modifier or country not being discarded. 17 | - Fixed greetd hanging when authenticating with a fingerprint. 18 | -------------------------------------------------------------------------------- /src/core/persistentprops.cpp: -------------------------------------------------------------------------------- 1 | #include "persistentprops.hpp" 2 | 3 | #include 4 | #include 5 | 6 | void PersistentProperties::onReload(QObject* oldInstance) { 7 | if (qobject_cast(oldInstance) == nullptr) { 8 | emit this->loaded(); 9 | return; 10 | } 11 | 12 | const auto* metaObject = this->metaObject(); 13 | for (auto i = metaObject->propertyOffset(); i < metaObject->propertyCount(); i++) { 14 | const auto prop = metaObject->property(i); 15 | auto oldProp = oldInstance->property(prop.name()); 16 | 17 | if (oldProp.isValid()) { 18 | this->setProperty(prop.name(), oldProp); 19 | } 20 | } 21 | 22 | emit this->loaded(); 23 | emit this->reloaded(); 24 | } 25 | -------------------------------------------------------------------------------- /src/dbus/org.freedesktop.DBus.ObjectManager.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/x11/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(XCB REQUIRED COMPONENTS XCB) 2 | 3 | qt_add_library(quickshell-x11 STATIC 4 | util.cpp 5 | panel_window.cpp 6 | ) 7 | 8 | qt_add_qml_module(quickshell-x11 9 | URI Quickshell.X11 10 | VERSION 0.1 11 | DEPENDENCIES QtQuick 12 | ) 13 | 14 | if(I3) 15 | add_subdirectory(i3) 16 | endif() 17 | 18 | install_qml_module(quickshell-x11) 19 | 20 | add_library(quickshell-x11-init OBJECT init.cpp) 21 | 22 | target_link_libraries(quickshell-x11 PRIVATE Qt::Quick ${XCB_LIBRARIES}) 23 | target_link_libraries(quickshell-x11-init PRIVATE Qt::Quick Qt::Qml ${XCB_LIBRARIES}) 24 | 25 | qs_module_pch(quickshell-x11 SET large) 26 | 27 | target_link_libraries(quickshell PRIVATE quickshell-x11plugin quickshell-x11-init) 28 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_executable(quickshell main.cpp) 2 | 3 | install(TARGETS quickshell RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 4 | 5 | add_subdirectory(build) 6 | add_subdirectory(launch) 7 | add_subdirectory(core) 8 | add_subdirectory(debug) 9 | add_subdirectory(ipc) 10 | add_subdirectory(window) 11 | add_subdirectory(io) 12 | add_subdirectory(widgets) 13 | add_subdirectory(ui) 14 | 15 | if (CRASH_REPORTER) 16 | add_subdirectory(crash) 17 | endif() 18 | 19 | if (DBUS) 20 | add_subdirectory(dbus) 21 | endif() 22 | 23 | if (WAYLAND) 24 | add_subdirectory(wayland) 25 | endif() 26 | 27 | if (X11) 28 | add_subdirectory(x11) 29 | endif() 30 | 31 | add_subdirectory(services) 32 | 33 | if (BLUETOOTH) 34 | add_subdirectory(bluetooth) 35 | endif() 36 | -------------------------------------------------------------------------------- /src/services/mpris/org.mpris.MediaPlayer2.Player.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/core/elapsedtimer.cpp: -------------------------------------------------------------------------------- 1 | #include "elapsedtimer.hpp" 2 | 3 | #include 4 | 5 | ElapsedTimer::ElapsedTimer() { this->timer.start(); } 6 | 7 | qreal ElapsedTimer::elapsed() { return static_cast(this->elapsedNs()) / 1000000000.0; } 8 | 9 | qreal ElapsedTimer::restart() { return static_cast(this->restartNs()) / 1000000000.0; } 10 | 11 | qint64 ElapsedTimer::elapsedMs() { return this->timer.elapsed(); } 12 | 13 | qint64 ElapsedTimer::restartMs() { return this->timer.restart(); } 14 | 15 | qint64 ElapsedTimer::elapsedNs() { return this->timer.nsecsElapsed(); } 16 | 17 | qint64 ElapsedTimer::restartNs() { 18 | // see qelapsedtimer.cpp 19 | auto old = this->timer; 20 | this->timer.start(); 21 | return old.durationTo(this->timer).count(); 22 | } 23 | -------------------------------------------------------------------------------- /src/wayland/screencopy/wlr_screencopy/wlr_screencopy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../manager.hpp" 8 | 9 | namespace qs::wayland::screencopy::wlr { 10 | 11 | class WlrScreencopyManager 12 | : public QWaylandClientExtensionTemplate 13 | , public QtWayland::zwlr_screencopy_manager_v1 { 14 | public: 15 | ScreencopyContext* captureOutput(QScreen* screen, bool paintCursors, QRect region = QRect()); 16 | 17 | static WlrScreencopyManager* instance(); 18 | 19 | private: 20 | explicit WlrScreencopyManager(); 21 | 22 | friend class WlrScreencopyContext; 23 | }; 24 | 25 | } // namespace qs::wayland::screencopy::wlr 26 | -------------------------------------------------------------------------------- /src/core/singleton.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "reload.hpp" 12 | 13 | ///! The root component for reloadable singletons. 14 | /// All singletons should inherit from this type. 15 | class Singleton: public ReloadPropagator { 16 | Q_OBJECT; 17 | QML_ELEMENT; 18 | 19 | public: 20 | void componentComplete() override; 21 | }; 22 | 23 | class SingletonRegistry { 24 | public: 25 | SingletonRegistry() = default; 26 | 27 | void registerSingleton(const QUrl& url, Singleton* singleton); 28 | void onReload(SingletonRegistry* old); 29 | 30 | private: 31 | QHash registry; 32 | }; 33 | -------------------------------------------------------------------------------- /src/io/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-io STATIC 2 | datastream.cpp 3 | processcore.cpp 4 | process.cpp 5 | fileview.cpp 6 | jsonadapter.cpp 7 | ipccomm.cpp 8 | ipc.cpp 9 | ipchandler.cpp 10 | ) 11 | 12 | if (SOCKETS) 13 | target_sources(quickshell-io PRIVATE socket.cpp) 14 | endif() 15 | 16 | qt_add_qml_module(quickshell-io 17 | URI Quickshell.Io 18 | VERSION 0.1 19 | DEPENDENCIES QtQml 20 | QML_FILES 21 | FileView.qml 22 | ) 23 | 24 | qs_add_module_deps_light(quickshell-io Quickshell) 25 | install_qml_module(quickshell-io) 26 | 27 | target_link_libraries(quickshell-io PRIVATE Qt::Quick) 28 | target_link_libraries(quickshell PRIVATE quickshell-ioplugin) 29 | 30 | qs_module_pch(quickshell-io) 31 | 32 | if (BUILD_TESTING) 33 | add_subdirectory(test) 34 | endif() 35 | -------------------------------------------------------------------------------- /src/wayland/hyprland/focus_grab/manager.cpp: -------------------------------------------------------------------------------- 1 | #include "manager.hpp" 2 | 3 | #include 4 | 5 | #include "grab.hpp" 6 | 7 | namespace qs::hyprland::focus_grab { 8 | 9 | FocusGrabManager::FocusGrabManager(): QWaylandClientExtensionTemplate(1) { 10 | this->initialize(); 11 | } 12 | 13 | bool FocusGrabManager::available() const { return this->isActive(); } 14 | 15 | FocusGrab* FocusGrabManager::createGrab() { return new FocusGrab(this->create_grab()); } 16 | 17 | FocusGrabManager* FocusGrabManager::instance() { 18 | static FocusGrabManager* instance = nullptr; // NOLINT 19 | 20 | if (instance == nullptr) { 21 | instance = new FocusGrabManager(); 22 | } 23 | 24 | return instance; 25 | } 26 | 27 | } // namespace qs::hyprland::focus_grab 28 | -------------------------------------------------------------------------------- /src/wayland/hyprland/surface/manager.cpp: -------------------------------------------------------------------------------- 1 | #include "manager.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "surface.hpp" 7 | 8 | namespace qs::hyprland::surface::impl { 9 | 10 | HyprlandSurfaceManager::HyprlandSurfaceManager(): QWaylandClientExtensionTemplate(2) { 11 | this->initialize(); 12 | } 13 | 14 | HyprlandSurface* 15 | HyprlandSurfaceManager::createHyprlandExtension(QtWaylandClient::QWaylandWindow* surface) { 16 | return new HyprlandSurface(this->get_hyprland_surface(surface->surface()), surface); 17 | } 18 | 19 | HyprlandSurfaceManager* HyprlandSurfaceManager::instance() { 20 | static auto* instance = new HyprlandSurfaceManager(); 21 | return instance; 22 | } 23 | 24 | } // namespace qs::hyprland::surface::impl 25 | -------------------------------------------------------------------------------- /src/wayland/wlr_layershell/shell_integration.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace qs::wayland::layershell { 9 | 10 | class LayerShellIntegration 11 | : public QtWaylandClient::QWaylandShellIntegrationTemplate 12 | , public QtWayland::zwlr_layer_shell_v1 { 13 | public: 14 | LayerShellIntegration(); 15 | ~LayerShellIntegration() override; 16 | Q_DISABLE_COPY_MOVE(LayerShellIntegration); 17 | 18 | QtWaylandClient::QWaylandShellSurface* 19 | createShellSurface(QtWaylandClient::QWaylandWindow* window) override; 20 | }; 21 | 22 | } // namespace qs::wayland::layershell 23 | -------------------------------------------------------------------------------- /src/wayland/output_tracking.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct wl_output; 9 | 10 | namespace qs::wayland { 11 | 12 | class WlOutputTracker: public QObject { 13 | Q_OBJECT; 14 | 15 | public: 16 | [[nodiscard]] const QList& screens() const { return this->mScreens; } 17 | 18 | signals: 19 | void screenAdded(QScreen* screen); 20 | void screenRemoved(QScreen* screen); 21 | 22 | public slots: 23 | void addOutput(::wl_output* output); 24 | void removeOutput(::wl_output* output); 25 | 26 | private slots: 27 | void onQScreenAdded(QScreen* screen); 28 | 29 | private: 30 | QList mScreens; 31 | QList<::wl_output*> mOutputs; 32 | }; 33 | 34 | } // namespace qs::wayland 35 | -------------------------------------------------------------------------------- /src/wayland/screencopy/manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../buffer/manager.hpp" 8 | 9 | namespace qs::wayland::screencopy { 10 | 11 | class ScreencopyContext: public QObject { 12 | Q_OBJECT; 13 | 14 | public: 15 | [[nodiscard]] buffer::WlBufferSwapchain& swapchain() { return this->mSwapchain; } 16 | virtual void captureFrame() = 0; 17 | 18 | signals: 19 | void frameCaptured(); 20 | void stopped(); 21 | 22 | protected: 23 | ScreencopyContext() = default; 24 | 25 | buffer::WlBufferSwapchain mSwapchain; 26 | }; 27 | 28 | class ScreencopyManager { 29 | public: 30 | static ScreencopyContext* createContext(QObject* object, bool paintCursors); 31 | }; 32 | 33 | } // namespace qs::wayland::screencopy 34 | -------------------------------------------------------------------------------- /src/wayland/hyprland/surface/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-hyprland-surface-extensions STATIC 2 | qml.cpp 3 | manager.cpp 4 | surface.cpp 5 | ) 6 | 7 | qt_add_qml_module(quickshell-hyprland-surface-extensions 8 | URI Quickshell.Hyprland._SurfaceExtensions 9 | VERSION 0.1 10 | DEPENDENCIES QtQml 11 | ) 12 | 13 | install_qml_module(quickshell-hyprland-surface-extensions) 14 | 15 | wl_proto(wlp-hyprland-surface hyprland-surface-v1 "${CMAKE_CURRENT_SOURCE_DIR}") 16 | 17 | target_link_libraries(quickshell-hyprland-surface-extensions PRIVATE 18 | Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 19 | wlp-hyprland-surface 20 | ) 21 | 22 | qs_module_pch(quickshell-hyprland-surface-extensions) 23 | 24 | target_link_libraries(quickshell PRIVATE quickshell-hyprland-surface-extensionsplugin) 25 | -------------------------------------------------------------------------------- /src/wayland/wlr_layershell/shell_integration.cpp: -------------------------------------------------------------------------------- 1 | #include "shell_integration.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "surface.hpp" 8 | 9 | namespace qs::wayland::layershell { 10 | 11 | LayerShellIntegration::LayerShellIntegration() 12 | : QtWaylandClient::QWaylandShellIntegrationTemplate(4) {} 13 | 14 | LayerShellIntegration::~LayerShellIntegration() { 15 | if (this->isInitialized()) { 16 | this->destroy(); 17 | } 18 | } 19 | 20 | QtWaylandClient::QWaylandShellSurface* 21 | LayerShellIntegration::createShellSurface(QtWaylandClient::QWaylandWindow* window) { 22 | return new LayerSurface(this, window); 23 | } 24 | 25 | } // namespace qs::wayland::layershell 26 | -------------------------------------------------------------------------------- /src/core/incubator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "logcat.hpp" 8 | 9 | QS_DECLARE_LOGGING_CATEGORY(logIncubator); 10 | 11 | class QsQmlIncubator 12 | : public QObject 13 | , public QQmlIncubator { 14 | Q_OBJECT; 15 | 16 | public: 17 | explicit QsQmlIncubator(QsQmlIncubator::IncubationMode mode, QObject* parent = nullptr) 18 | : QObject(parent) 19 | , QQmlIncubator(mode) {} 20 | 21 | void statusChanged(QQmlIncubator::Status status) override; 22 | 23 | signals: 24 | void completed(); 25 | void failed(); 26 | }; 27 | 28 | class DelayedQmlIncubationController: public QQmlIncubationController { 29 | // Do nothing. 30 | // This ensures lazy loaders don't start blocking before onReload creates windows. 31 | }; 32 | -------------------------------------------------------------------------------- /src/core/module.md: -------------------------------------------------------------------------------- 1 | name = "Quickshell" 2 | description = "Core Quickshell types" 3 | headers = [ 4 | "qmlglobal.hpp", 5 | "qmlscreen.hpp", 6 | "reload.hpp", 7 | "shell.hpp", 8 | "variants.hpp", 9 | "region.hpp", 10 | "../window/proxywindow.hpp", 11 | "persistentprops.hpp", 12 | "../window/windowinterface.hpp", 13 | "../window/panelinterface.hpp", 14 | "../window/floatingwindow.hpp", 15 | "../window/popupwindow.hpp", 16 | "singleton.hpp", 17 | "lazyloader.hpp", 18 | "easingcurve.hpp", 19 | "transformwatcher.hpp", 20 | "boundcomponent.hpp", 21 | "model.hpp", 22 | "elapsedtimer.hpp", 23 | "desktopentry.hpp", 24 | "qsmenu.hpp", 25 | "retainable.hpp", 26 | "popupanchor.hpp", 27 | "types.hpp", 28 | "qsmenuanchor.hpp", 29 | "clock.hpp", 30 | "scriptmodel.hpp", 31 | "colorquantizer.hpp", 32 | ] 33 | ----- 34 | -------------------------------------------------------------------------------- /src/wayland/session_lock/shell_integration.cpp: -------------------------------------------------------------------------------- 1 | #include "shell_integration.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "session_lock.hpp" 8 | #include "surface.hpp" 9 | 10 | QtWaylandClient::QWaylandShellSurface* 11 | QSWaylandSessionLockIntegration::createShellSurface(QtWaylandClient::QWaylandWindow* window) { 12 | auto* lock = LockWindowExtension::get(window->window()); 13 | if (lock == nullptr || lock->surface == nullptr) { 14 | qFatal() << "Visibility canary failed. A window with a LockWindowExtension MUST be set to " 15 | "visible via LockWindowExtension::setVisible"; 16 | } 17 | 18 | QSWaylandSessionLockSurface* surface = lock->surface; // shut up the unused include linter 19 | return surface; 20 | } 21 | -------------------------------------------------------------------------------- /src/services/upower/org.freedesktop.UPower.Device.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/wayland/idle_notify/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-wayland-idle-notify STATIC 2 | proto.cpp 3 | monitor.cpp 4 | ) 5 | 6 | qt_add_qml_module(quickshell-wayland-idle-notify 7 | URI Quickshell.Wayland._IdleNotify 8 | VERSION 0.1 9 | DEPENDENCIES QtQuick 10 | ) 11 | 12 | install_qml_module(quickshell-wayland-idle-notify) 13 | 14 | qs_add_module_deps_light(quickshell-wayland-idle-notify Quickshell) 15 | 16 | wl_proto(wlp-idle-notify ext-idle-notify-v1 "${WAYLAND_PROTOCOLS}/staging/ext-idle-notify") 17 | 18 | target_link_libraries(quickshell-wayland-idle-notify PRIVATE 19 | Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 20 | wlp-idle-notify 21 | ) 22 | 23 | qs_module_pch(quickshell-wayland-idle-notify SET large) 24 | 25 | target_link_libraries(quickshell PRIVATE quickshell-wayland-idle-notifyplugin) 26 | -------------------------------------------------------------------------------- /src/wayland/session_lock/manager.cpp: -------------------------------------------------------------------------------- 1 | #include "manager.hpp" 2 | 3 | #include 4 | 5 | #include "lock.hpp" 6 | 7 | QSWaylandSessionLockManager::QSWaylandSessionLockManager() 8 | : QWaylandClientExtensionTemplate(1) { 9 | this->initialize(); 10 | } 11 | 12 | QSWaylandSessionLockManager::~QSWaylandSessionLockManager() { this->destroy(); } 13 | 14 | QSWaylandSessionLock* QSWaylandSessionLockManager::acquireLock() { 15 | if (this->isLocked()) return nullptr; 16 | this->active = new QSWaylandSessionLock(this, this->lock()); 17 | return this->active; 18 | } 19 | 20 | bool QSWaylandSessionLockManager::isLocked() const { return this->active != nullptr; } 21 | bool QSWaylandSessionLockManager::isSecure() const { 22 | return this->isLocked() && this->active->hasCompositorLock(); 23 | } 24 | -------------------------------------------------------------------------------- /src/wayland/hyprland/global_shortcuts/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-hyprland-global-shortcuts STATIC 2 | qml.cpp 3 | manager.cpp 4 | shortcut.cpp 5 | ) 6 | 7 | qt_add_qml_module(quickshell-hyprland-global-shortcuts 8 | URI Quickshell.Hyprland._GlobalShortcuts 9 | VERSION 0.1 10 | DEPENDENCIES QtQml 11 | ) 12 | 13 | install_qml_module(quickshell-hyprland-global-shortcuts) 14 | 15 | wl_proto(wlp-hyprland-shortcuts hyprland-global-shortcuts-v1 "${CMAKE_CURRENT_SOURCE_DIR}") 16 | 17 | target_link_libraries(quickshell-hyprland-global-shortcuts PRIVATE 18 | Qt::Qml Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 19 | Qt::Quick # pch 20 | wlp-hyprland-shortcuts 21 | ) 22 | 23 | qs_module_pch(quickshell-hyprland-global-shortcuts) 24 | 25 | target_link_libraries(quickshell PRIVATE quickshell-hyprland-global-shortcutsplugin) 26 | -------------------------------------------------------------------------------- /src/wayland/idle_inhibit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-wayland-idle-inhibit STATIC 2 | proto.cpp 3 | inhibitor.cpp 4 | ) 5 | 6 | qt_add_qml_module(quickshell-wayland-idle-inhibit 7 | URI Quickshell.Wayland._IdleInhibitor 8 | VERSION 0.1 9 | DEPENDENCIES QtQuick 10 | ) 11 | 12 | install_qml_module(quickshell-wayland-idle-inhibit) 13 | 14 | qs_add_module_deps_light(quickshell-wayland-idle-inhibit Quickshell) 15 | 16 | wl_proto(wlp-idle-inhibit idle-inhibit-unstable-v1 "${WAYLAND_PROTOCOLS}/unstable/idle-inhibit") 17 | 18 | target_link_libraries(quickshell-wayland-idle-inhibit PRIVATE 19 | Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 20 | wlp-idle-inhibit 21 | ) 22 | 23 | qs_module_pch(quickshell-wayland-idle-inhibit SET large) 24 | 25 | target_link_libraries(quickshell PRIVATE quickshell-wayland-idle-inhibitplugin) 26 | -------------------------------------------------------------------------------- /src/core/rootwrapper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "generation.hpp" 11 | 12 | class RootWrapper: public QObject { 13 | Q_OBJECT; 14 | 15 | public: 16 | explicit RootWrapper(QString rootPath, QString shellId); 17 | ~RootWrapper() override; 18 | Q_DISABLE_COPY_MOVE(RootWrapper); 19 | 20 | void reloadGraph(bool hard); 21 | 22 | private slots: 23 | void generationDestroyed(); 24 | void onWatchFilesChanged(); 25 | void onWatchedFilesChanged(); 26 | void updateTooling(); 27 | 28 | private: 29 | QString rootPath; 30 | QString shellId; 31 | EngineGeneration* generation = nullptr; 32 | QString originalWorkingDirectory; 33 | QFileSystemWatcher configDirWatcher; 34 | }; 35 | -------------------------------------------------------------------------------- /src/wayland/hyprland/focus_grab/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-hyprland-focus-grab STATIC 2 | manager.cpp 3 | grab.cpp 4 | qml.cpp 5 | ) 6 | 7 | qt_add_qml_module(quickshell-hyprland-focus-grab 8 | URI Quickshell.Hyprland._FocusGrab 9 | VERSION 0.1 10 | DEPENDENCIES QtQml 11 | ) 12 | 13 | qs_add_module_deps_light(quickshell-hyprland-focus-grab Quickshell) 14 | 15 | install_qml_module(quickshell-hyprland-focus-grab) 16 | 17 | wl_proto(wlp-hyprland-focus-grab hyprland-focus-grab-v1 "${CMAKE_CURRENT_SOURCE_DIR}") 18 | 19 | target_link_libraries(quickshell-hyprland-focus-grab PRIVATE 20 | Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 21 | wlp-hyprland-focus-grab 22 | ) 23 | 24 | qs_module_pch(quickshell-hyprland-focus-grab SET large) 25 | 26 | target_link_libraries(quickshell PRIVATE quickshell-hyprland-focus-grabplugin) 27 | -------------------------------------------------------------------------------- /src/wayland/screencopy/hyprland_screencopy/hyprland_screencopy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "../../toplevel_management/handle.hpp" 7 | #include "../manager.hpp" 8 | 9 | namespace qs::wayland::screencopy::hyprland { 10 | 11 | class HyprlandScreencopyManager 12 | : public QWaylandClientExtensionTemplate 13 | , public QtWayland::hyprland_toplevel_export_manager_v1 { 14 | public: 15 | ScreencopyContext* 16 | captureToplevel(toplevel_management::impl::ToplevelHandle* handle, bool paintCursors); 17 | 18 | static HyprlandScreencopyManager* instance(); 19 | 20 | private: 21 | explicit HyprlandScreencopyManager(); 22 | 23 | friend class HyprlandScreencopyContext; 24 | }; 25 | 26 | } // namespace qs::wayland::screencopy::hyprland 27 | -------------------------------------------------------------------------------- /src/x11/init.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../core/plugin.hpp" 7 | #include "panel_window.hpp" 8 | #include "util.hpp" 9 | 10 | namespace { 11 | 12 | class X11Plugin: public QsEnginePlugin { 13 | QList dependencies() override { return {"window"}; } 14 | 15 | bool applies() override { return QGuiApplication::platformName() == "xcb"; } 16 | 17 | void init() override { XAtom::initAtoms(); } 18 | 19 | void registerTypes() override { 20 | qmlRegisterType("Quickshell._X11Overlay", 1, 0, "PanelWindow"); 21 | 22 | qmlRegisterModuleImport( 23 | "Quickshell", 24 | QQmlModuleImportModuleAny, 25 | "Quickshell._X11Overlay", 26 | QQmlModuleImportLatest 27 | ); 28 | } 29 | }; 30 | 31 | QS_REGISTER_PLUGIN(X11Plugin); 32 | 33 | } // namespace 34 | -------------------------------------------------------------------------------- /src/core/scan.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "logcat.hpp" 10 | 11 | QS_DECLARE_LOGGING_CATEGORY(logQmlScanner); 12 | 13 | // expects canonical paths 14 | class QmlScanner { 15 | public: 16 | QmlScanner() = default; 17 | QmlScanner(const QDir& rootPath): rootPath(rootPath) {} 18 | 19 | void scanDir(const QDir& dir); 20 | void scanQmlRoot(const QString& path); 21 | 22 | QVector scannedDirs; 23 | QVector scannedFiles; 24 | QHash fileIntercepts; 25 | 26 | private: 27 | QDir rootPath; 28 | 29 | bool scanQmlFile(const QString& path, bool& singleton, bool& internal); 30 | bool scanQmlJson(const QString& path); 31 | [[nodiscard]] static QPair jsonToQml(const QJsonValue& value, int indent = 0); 32 | }; 33 | -------------------------------------------------------------------------------- /src/core/test/scriptmodel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct ModelOperation { 9 | enum Enum : quint8 { 10 | Insert, 11 | Remove, 12 | Move, 13 | }; 14 | 15 | ModelOperation(Enum operation, qint32 index, qint32 length, qint32 destIndex = -1) 16 | : operation(operation) 17 | , index(index) 18 | , length(length) 19 | , destIndex(destIndex) {} 20 | 21 | Enum operation; 22 | qint32 index = 0; 23 | qint32 length = 0; 24 | qint32 destIndex = -1; 25 | 26 | [[nodiscard]] bool operator==(const ModelOperation& other) const; 27 | }; 28 | 29 | QDebug& operator<<(QDebug& debug, const ModelOperation& op); 30 | 31 | class TestScriptModel: public QObject { 32 | Q_OBJECT; 33 | 34 | private slots: 35 | static void unique_data(); // NOLINT 36 | static void unique(); 37 | }; 38 | -------------------------------------------------------------------------------- /src/services/pipewire/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(PkgConfig REQUIRED) 2 | pkg_check_modules(pipewire REQUIRED IMPORTED_TARGET libpipewire-0.3) 3 | 4 | qt_add_library(quickshell-service-pipewire STATIC 5 | qml.cpp 6 | core.cpp 7 | connection.cpp 8 | registry.cpp 9 | node.cpp 10 | metadata.cpp 11 | link.cpp 12 | device.cpp 13 | defaults.cpp 14 | ) 15 | 16 | qt_add_qml_module(quickshell-service-pipewire 17 | URI Quickshell.Services.Pipewire 18 | VERSION 0.1 19 | DEPENDENCIES QtQml 20 | ) 21 | 22 | qs_add_module_deps_light(quickshell-service-pipewire Quickshell) 23 | 24 | install_qml_module(quickshell-service-pipewire) 25 | 26 | target_link_libraries(quickshell-service-pipewire PRIVATE 27 | Qt::Qml PkgConfig::pipewire 28 | Qt::Quick # pch 29 | ) 30 | 31 | qs_module_pch(quickshell-service-pipewire) 32 | 33 | target_link_libraries(quickshell PRIVATE quickshell-service-pipewireplugin) 34 | -------------------------------------------------------------------------------- /src/wayland/idle_notify/test/manual/idle_notify.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls 3 | import QtQuick.Layouts 4 | import Quickshell 5 | import Quickshell.Wayland 6 | 7 | FloatingWindow { 8 | color: contentItem.palette.window 9 | 10 | IdleMonitor { 11 | id: monitor 12 | enabled: enabledCb.checked 13 | timeout: timeoutSb.value 14 | respectInhibitors: respectInhibitorsCb.checked 15 | } 16 | 17 | ColumnLayout { 18 | Label { text: `Is idle? ${monitor.isIdle}` } 19 | 20 | CheckBox { 21 | id: enabledCb 22 | text: "Enabled" 23 | checked: true 24 | } 25 | 26 | CheckBox { 27 | id: respectInhibitorsCb 28 | text: "Respect Inhibitors" 29 | checked: true 30 | } 31 | 32 | RowLayout { 33 | Label { text: "Timeout" } 34 | 35 | SpinBox { 36 | id: timeoutSb 37 | editable: true 38 | from: 0 39 | to: 1000 40 | value: 5 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/wayland/session_lock/manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "lock.hpp" 8 | 9 | class QSWaylandSessionLockManager 10 | : public QWaylandClientExtensionTemplate 11 | , public QtWayland::ext_session_lock_manager_v1 { 12 | public: 13 | QSWaylandSessionLockManager(); 14 | ~QSWaylandSessionLockManager() override; 15 | Q_DISABLE_COPY_MOVE(QSWaylandSessionLockManager); 16 | 17 | // Create a new session lock if there is no currently active lock, otherwise null. 18 | QSWaylandSessionLock* acquireLock(); 19 | [[nodiscard]] bool isLocked() const; 20 | [[nodiscard]] bool isSecure() const; 21 | 22 | static bool sessionLocked(); 23 | 24 | private: 25 | QSWaylandSessionLock* active = nullptr; 26 | 27 | friend class QSWaylandSessionLock; 28 | }; 29 | -------------------------------------------------------------------------------- /src/wayland/shortcuts_inhibit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-wayland-shortcuts-inhibit STATIC 2 | proto.cpp 3 | inhibitor.cpp 4 | ) 5 | 6 | qt_add_qml_module(quickshell-wayland-shortcuts-inhibit 7 | URI Quickshell.Wayland._ShortcutsInhibitor 8 | VERSION 0.1 9 | DEPENDENCIES QtQuick 10 | ) 11 | 12 | install_qml_module(quickshell-wayland-shortcuts-inhibit) 13 | 14 | qs_add_module_deps_light(quickshell-wayland-shortcuts-inhibit Quickshell) 15 | 16 | wl_proto(wlp-shortcuts-inhibit keyboard-shortcuts-inhibit-unstable-v1 "${WAYLAND_PROTOCOLS}/unstable/keyboard-shortcuts-inhibit") 17 | 18 | target_link_libraries(quickshell-wayland-shortcuts-inhibit PRIVATE 19 | Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 20 | wlp-shortcuts-inhibit 21 | ) 22 | 23 | qs_module_pch(quickshell-wayland-shortcuts-inhibit SET large) 24 | 25 | target_link_libraries(quickshell PRIVATE quickshell-wayland-shortcuts-inhibitplugin) -------------------------------------------------------------------------------- /src/wayland/hyprland/global_shortcuts/shortcut.cpp: -------------------------------------------------------------------------------- 1 | #include "shortcut.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace qs::hyprland::global_shortcuts::impl { 8 | 9 | GlobalShortcut::GlobalShortcut(::hyprland_global_shortcut_v1* shortcut) { this->init(shortcut); } 10 | 11 | GlobalShortcut::~GlobalShortcut() { 12 | if (this->isInitialized()) { 13 | this->destroy(); 14 | } 15 | } 16 | 17 | void GlobalShortcut::hyprland_global_shortcut_v1_pressed( 18 | quint32 /*unused*/, 19 | quint32 /*unused*/, 20 | quint32 /*unused*/ 21 | ) { 22 | emit this->pressed(); 23 | } 24 | 25 | void GlobalShortcut::hyprland_global_shortcut_v1_released( 26 | quint32 /*unused*/, 27 | quint32 /*unused*/, 28 | quint32 /*unused*/ 29 | ) { 30 | emit this->released(); 31 | } 32 | 33 | } // namespace qs::hyprland::global_shortcuts::impl 34 | -------------------------------------------------------------------------------- /src/wayland/hyprland/test/manual/workspaces.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import Quickshell 4 | import Quickshell.Widgets 5 | import Quickshell.Hyprland 6 | 7 | FloatingWindow { 8 | ListView { 9 | anchors.fill: parent 10 | model: Hyprland.workspaces 11 | spacing: 5 12 | 13 | delegate: WrapperRectangle { 14 | id: wsDelegate 15 | required property HyprlandWorkspace modelData 16 | color: "lightgray" 17 | 18 | ColumnLayout { 19 | Text { text: `Workspace ${wsDelegate.modelData.id} on ${wsDelegate.modelData.monitor} | urgent: ${wsDelegate.modelData.urgent}`} 20 | 21 | ColumnLayout { 22 | Repeater { 23 | model: wsDelegate.modelData.toplevels 24 | Text { 25 | id: tDelegate 26 | required property HyprlandToplevel modelData; 27 | text: `${tDelegate.modelData}: ${tDelegate.modelData.title}` 28 | } 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/wayland/screencopy/image_copy_capture/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-wayland-screencopy-icc STATIC 2 | image_copy_capture.cpp 3 | ) 4 | 5 | wl_proto(wlp-ext-foreign-toplevel ext-foreign-toplevel-list-v1 "${WAYLAND_PROTOCOLS}/staging/ext-foreign-toplevel-list") 6 | wl_proto(wlp-image-copy-capture ext-image-copy-capture-v1 "${WAYLAND_PROTOCOLS}/staging/ext-image-copy-capture") 7 | wl_proto(wlp-image-capture-source ext-image-capture-source-v1 "${WAYLAND_PROTOCOLS}/staging/ext-image-capture-source") 8 | 9 | target_link_libraries(quickshell-wayland-screencopy-icc PRIVATE 10 | Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 11 | Qt::Quick # for pch 12 | ) 13 | 14 | target_link_libraries(quickshell-wayland-screencopy-icc PUBLIC 15 | wlp-image-copy-capture wlp-image-capture-source 16 | wlp-ext-foreign-toplevel # required for capture source to build 17 | ) 18 | 19 | qs_pch(quickshell-wayland-screencopy-icc SET large) 20 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "nixpkgs/nixos-unstable"; 4 | }; 5 | 6 | outputs = { self, nixpkgs }: let 7 | overlayPkgs = p: p.appendOverlays [ self.overlays.default ]; 8 | 9 | forEachSystem = fn: 10 | nixpkgs.lib.genAttrs 11 | nixpkgs.lib.platforms.linux 12 | (system: fn system (overlayPkgs nixpkgs.legacyPackages.${system})); 13 | in { 14 | overlays.default = import ./overlay.nix { 15 | rev = self.rev or self.dirtyRev; 16 | }; 17 | 18 | packages = forEachSystem (system: pkgs: rec { 19 | quickshell = pkgs.quickshell; 20 | default = quickshell; 21 | }); 22 | 23 | devShells = forEachSystem (system: pkgs: rec { 24 | default = import ./shell.nix { 25 | inherit pkgs; 26 | quickshell = self.packages.${system}.quickshell.override { 27 | stdenv = pkgs.clangStdenv; 28 | }; 29 | }; 30 | }); 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /src/core/desktopentrymonitor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class DesktopEntryMonitor: public QObject { 9 | Q_OBJECT 10 | 11 | public: 12 | explicit DesktopEntryMonitor(QObject* parent = nullptr); 13 | ~DesktopEntryMonitor() override = default; 14 | DesktopEntryMonitor(const DesktopEntryMonitor&) = delete; 15 | DesktopEntryMonitor& operator=(const DesktopEntryMonitor&) = delete; 16 | DesktopEntryMonitor(DesktopEntryMonitor&&) = delete; 17 | DesktopEntryMonitor& operator=(DesktopEntryMonitor&&) = delete; 18 | 19 | signals: 20 | void desktopEntriesChanged(); 21 | 22 | private slots: 23 | void onDirectoryChanged(const QString& path); 24 | void processChanges(); 25 | 26 | private: 27 | void startMonitoring(); 28 | void scanAndWatch(const QString& dirPath); 29 | 30 | QFileSystemWatcher watcher; 31 | QTimer debounceTimer; 32 | }; 33 | -------------------------------------------------------------------------------- /src/wayland/hyprland/test/manual/toplevel-association.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import Quickshell 4 | import Quickshell.Hyprland 5 | import Quickshell.Wayland 6 | 7 | FloatingWindow { 8 | ColumnLayout { 9 | anchors.fill: parent 10 | 11 | Text { text: "Hyprland -> Wayland" } 12 | 13 | ListView { 14 | Layout.fillWidth: true 15 | Layout.fillHeight: true 16 | clip: true 17 | model: Hyprland.toplevels 18 | delegate: Text { 19 | required property HyprlandToplevel modelData 20 | text: `${modelData} -> ${modelData.wayland}` 21 | } 22 | } 23 | 24 | Text { text: "Wayland -> Hyprland" } 25 | 26 | ListView { 27 | Layout.fillWidth: true 28 | Layout.fillHeight: true 29 | clip: true 30 | model: ToplevelManager.toplevels 31 | delegate: Text { 32 | required property Toplevel modelData 33 | text: `${modelData} -> ${modelData.HyprlandToplevel.handle}` 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/wayland/hyprland/test/manual/toplevels.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import Quickshell 4 | import Quickshell.Hyprland 5 | 6 | FloatingWindow { 7 | ColumnLayout { 8 | anchors.fill: parent 9 | 10 | Text { text: "Current toplevel:" } 11 | 12 | ToplevelFromHyprland { 13 | modelData: Hyprland.activeToplevel 14 | } 15 | 16 | Text { text: "\nAll toplevels:" } 17 | 18 | ListView { 19 | Layout.fillHeight: true 20 | Layout.fillWidth: true 21 | clip: true 22 | model: Hyprland.toplevels 23 | delegate: ToplevelFromHyprland {} 24 | } 25 | } 26 | 27 | component ToplevelFromHyprland: ColumnLayout { 28 | required property HyprlandToplevel modelData 29 | 30 | Text { 31 | text: `Window 0x${modelData.address}, title: ${modelData.title}, activated: ${modelData.activated}, workspace id: ${modelData.workspace.id}, monitor name: ${modelData.monitor.name}, urgent: ${modelData.urgent}` 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/window/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-window STATIC 2 | proxywindow.cpp 3 | windowinterface.cpp 4 | panelinterface.cpp 5 | floatingwindow.cpp 6 | popupwindow.cpp 7 | ) 8 | 9 | qt_add_qml_module(quickshell-window 10 | URI Quickshell._Window 11 | VERSION 0.1 12 | DEPENDENCIES QtQuick 13 | ) 14 | 15 | qs_add_module_deps_light(quickshell-window Quickshell) 16 | 17 | install_qml_module(quickshell-window) 18 | 19 | add_library(quickshell-window-init OBJECT init.cpp) 20 | 21 | target_link_libraries(quickshell-window PRIVATE 22 | Qt::Core Qt::Gui Qt::Quick Qt6::QuickPrivate 23 | ) 24 | 25 | qs_add_link_dependencies(quickshell-window quickshell-debug) 26 | 27 | target_link_libraries(quickshell-window-init PRIVATE Qt::Qml) 28 | 29 | qs_module_pch(quickshell-window SET large) 30 | 31 | target_link_libraries(quickshell PRIVATE quickshell-windowplugin quickshell-window-init) 32 | 33 | if (BUILD_TESTING) 34 | add_subdirectory(test) 35 | endif() 36 | -------------------------------------------------------------------------------- /src/core/types.cpp: -------------------------------------------------------------------------------- 1 | #include "types.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | QRect Box::qrect() const { return {this->x, this->y, this->w, this->h}; } 9 | 10 | bool Box::operator==(const Box& other) const { 11 | return this->x == other.x && this->y == other.y && this->w == other.w && this->h == other.h; 12 | } 13 | 14 | QDebug operator<<(QDebug debug, const Box& box) { 15 | auto saver = QDebugStateSaver(debug); 16 | debug.nospace() << "Box(" << box.x << ',' << box.y << ' ' << box.w << 'x' << box.h << ')'; 17 | return debug; 18 | } 19 | 20 | Qt::Edges Edges::toQt(Edges::Flags edges) { return Qt::Edges(edges.toInt()); } 21 | 22 | bool Edges::isOpposing(Edges::Flags edges) { 23 | return edges.testFlags(Edges::Top | Edges::Bottom) || edges.testFlags(Edges::Left | Edges::Right); 24 | } 25 | 26 | QMargins Margins::qmargins() const { return {this->left, this->top, this->right, this->bottom}; } 27 | -------------------------------------------------------------------------------- /src/services/pam/subprocess.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // NOLINT std::cout 4 | 5 | #include 6 | 7 | #include "ipc.hpp" 8 | 9 | // endls are intentional as it makes debugging much easier when the buffer actually flushes. 10 | // NOLINTBEGIN 11 | #define logIf(log) \ 12 | if (log) std::cout << "quickshell.service.pam.subprocess: " 13 | // NOLINTEND 14 | 15 | class PamSubprocess { 16 | public: 17 | explicit PamSubprocess(bool log, int fdIn, int fdOut): log(log), pipes(fdIn, fdOut) {} 18 | PamIpcExitCode exec(const char* configDir, const char* config, const char* user); 19 | void sendCode(PamIpcExitCode code); 20 | 21 | private: 22 | static int conversation( 23 | int msgCount, 24 | const pam_message** msgArray, 25 | pam_response** responseArray, 26 | void* appdata 27 | ); 28 | 29 | bool log; 30 | PamIpcPipes pipes; 31 | }; 32 | -------------------------------------------------------------------------------- /src/wayland/toplevel_management/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-wayland-toplevel-management STATIC 2 | manager.cpp 3 | handle.cpp 4 | qml.cpp 5 | ) 6 | 7 | qt_add_qml_module(quickshell-wayland-toplevel-management 8 | URI Quickshell.Wayland._ToplevelManagement 9 | VERSION 0.1 10 | DEPENDENCIES QtQml 11 | ) 12 | 13 | qs_add_module_deps_light(quickshell-wayland-toplevel-management 14 | Quickshell Quickshell.Wayland 15 | ) 16 | 17 | install_qml_module(quickshell-wayland-toplevel-management) 18 | 19 | wl_proto(wlp-foreign-toplevel wlr-foreign-toplevel-management-unstable-v1 "${CMAKE_CURRENT_SOURCE_DIR}") 20 | 21 | target_link_libraries(quickshell-wayland-toplevel-management PRIVATE 22 | Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 23 | wlp-foreign-toplevel 24 | ) 25 | 26 | qs_module_pch(quickshell-wayland-toplevel-management SET large) 27 | 28 | target_link_libraries(quickshell PRIVATE quickshell-wayland-toplevel-managementplugin) 29 | -------------------------------------------------------------------------------- /src/dbus/objectmanager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "dbus_objectmanager_types.hpp" 9 | 10 | class DBusObjectManagerInterface; 11 | 12 | namespace qs::dbus { 13 | 14 | class DBusObjectManager: public QObject { 15 | Q_OBJECT; 16 | 17 | public: 18 | explicit DBusObjectManager(QObject* parent = nullptr); 19 | 20 | bool setInterface( 21 | const QString& service, 22 | const QString& path, 23 | const QDBusConnection& connection = QDBusConnection::sessionBus() 24 | ); 25 | 26 | signals: 27 | void 28 | interfacesAdded(const QDBusObjectPath& objectPath, const DBusObjectManagerInterfaces& interfaces); 29 | void interfacesRemoved(const QDBusObjectPath& objectPath, const QStringList& interfaces); 30 | 31 | private: 32 | void fetchInitialObjects(); 33 | 34 | DBusObjectManagerInterface* mInterface = nullptr; 35 | }; 36 | 37 | } // namespace qs::dbus -------------------------------------------------------------------------------- /src/wayland/hyprland/surface/surface.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace qs::hyprland::surface::impl { 14 | 15 | class HyprlandSurface: public QtWayland::hyprland_surface_v1 { 16 | public: 17 | explicit HyprlandSurface(::hyprland_surface_v1* surface, QtWaylandClient::QWaylandWindow* backer); 18 | ~HyprlandSurface() override; 19 | Q_DISABLE_COPY_MOVE(HyprlandSurface); 20 | 21 | [[nodiscard]] bool surfaceEq(wl_surface* surface) const; 22 | 23 | void setOpacity(qreal opacity); 24 | void setVisibleRegion(const QRegion& region); 25 | 26 | private: 27 | wl_surface* backerSurface = nullptr; 28 | }; 29 | 30 | } // namespace qs::hyprland::surface::impl 31 | -------------------------------------------------------------------------------- /src/services/status_notifier/org.kde.StatusNotifierWatcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/wayland/hyprland/global_shortcuts/shortcut.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace qs::hyprland::global_shortcuts::impl { 10 | 11 | class GlobalShortcut 12 | : public QObject 13 | , public QtWayland::hyprland_global_shortcut_v1 { 14 | Q_OBJECT; 15 | 16 | public: 17 | explicit GlobalShortcut(::hyprland_global_shortcut_v1* shortcut); 18 | ~GlobalShortcut() override; 19 | Q_DISABLE_COPY_MOVE(GlobalShortcut); 20 | 21 | signals: 22 | void pressed(); 23 | void released(); 24 | 25 | private: 26 | // clang-format off 27 | void hyprland_global_shortcut_v1_pressed(quint32 tvSecHi, quint32 tvSecLo, quint32 tvNsec) override; 28 | void hyprland_global_shortcut_v1_released(quint32 tvSecHi, quint32 tvSecLo, quint32 tvNsec) override; 29 | // clang-format on 30 | }; 31 | 32 | } // namespace qs::hyprland::global_shortcuts::impl 33 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: [push, pull_request, workflow_dispatch] 3 | 4 | jobs: 5 | lint: 6 | name: Lint 7 | runs-on: ubuntu-latest 8 | permissions: 9 | contents: read 10 | id-token: write 11 | steps: 12 | - uses: actions/checkout@v4 13 | # Use cachix action over detsys for testing with act. 14 | # - uses: cachix/install-nix-action@v27 15 | - uses: DeterminateSystems/nix-installer-action@main 16 | - uses: DeterminateSystems/magic-nix-cache-action@main 17 | with: 18 | use-flakehub: false 19 | - uses: nicknovitski/nix-develop@v1 20 | 21 | - name: Check formatting 22 | run: clang-format -Werror --dry-run src/**/*.{cpp,hpp} 23 | 24 | # required for lint 25 | - name: Build 26 | run: | 27 | just configure debug -DNO_PCH=ON -DBUILD_TESTING=ON 28 | just build 29 | 30 | - name: Run lints 31 | run: LC_ALL=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 just lint-ci 32 | -------------------------------------------------------------------------------- /src/io/processcore.cpp: -------------------------------------------------------------------------------- 1 | #include "processcore.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../core/common.hpp" 9 | 10 | namespace qs::io::process { 11 | 12 | void setupProcessEnvironment( 13 | QProcess* process, 14 | bool clear, 15 | const QHash& envChanges 16 | ) { 17 | const auto& sysenv = qs::Common::INITIAL_ENVIRONMENT; 18 | auto env = clear ? QProcessEnvironment() : sysenv; 19 | 20 | for (auto& name: envChanges.keys()) { 21 | auto value = envChanges.value(name); 22 | if (!value.isValid()) continue; 23 | 24 | if (clear) { 25 | if (value.isNull()) { 26 | if (sysenv.contains(name)) env.insert(name, sysenv.value(name)); 27 | } else env.insert(name, value.toString()); 28 | } else { 29 | if (value.isNull()) env.remove(name); 30 | else env.insert(name, value.toString()); 31 | } 32 | } 33 | 34 | process->setProcessEnvironment(env); 35 | } 36 | 37 | } // namespace qs::io::process 38 | -------------------------------------------------------------------------------- /src/services/notifications/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_dbus_adaptor(DBUS_INTERFACES 2 | org.freedesktop.Notifications.xml 3 | server.hpp 4 | qs::service::notifications::NotificationServer 5 | dbus_notifications 6 | DBusNotificationServer 7 | ) 8 | 9 | qt_add_library(quickshell-service-notifications STATIC 10 | server.cpp 11 | notification.cpp 12 | dbusimage.cpp 13 | qml.cpp 14 | ${DBUS_INTERFACES} 15 | ) 16 | 17 | # dbus headers 18 | target_include_directories(quickshell-service-notifications PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 19 | 20 | qt_add_qml_module(quickshell-service-notifications 21 | URI Quickshell.Services.Notifications 22 | VERSION 0.1 23 | ) 24 | 25 | qs_add_module_deps_light(quickshell-service-notifications Quickshell) 26 | install_qml_module(quickshell-service-notifications) 27 | 28 | target_link_libraries(quickshell-service-notifications PRIVATE Qt::Quick Qt::DBus) 29 | target_link_libraries(quickshell PRIVATE quickshell-service-notificationsplugin) 30 | 31 | qs_module_pch(quickshell-service-notifications SET dbus) 32 | -------------------------------------------------------------------------------- /src/services/polkit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(PkgConfig REQUIRED) 2 | pkg_check_modules(glib REQUIRED IMPORTED_TARGET glib-2.0>=2.36) 3 | pkg_check_modules(gobject REQUIRED IMPORTED_TARGET gobject-2.0) 4 | pkg_check_modules(polkit_agent REQUIRED IMPORTED_TARGET polkit-agent-1) 5 | pkg_check_modules(polkit REQUIRED IMPORTED_TARGET polkit-gobject-1) 6 | 7 | qt_add_library(quickshell-service-polkit STATIC 8 | agentimpl.cpp 9 | flow.cpp 10 | identity.cpp 11 | listener.cpp 12 | session.cpp 13 | qml.cpp 14 | ) 15 | 16 | qt_add_qml_module(quickshell-service-polkit 17 | URI Quickshell.Services.Polkit 18 | VERSION 0.1 19 | DEPENDENCIES QtQml 20 | ) 21 | 22 | install_qml_module(quickshell-service-polkit) 23 | 24 | target_link_libraries(quickshell-service-polkit PRIVATE 25 | Qt::Qml 26 | Qt::Quick 27 | PkgConfig::glib 28 | PkgConfig::gobject 29 | PkgConfig::polkit_agent 30 | PkgConfig::polkit 31 | ) 32 | 33 | qs_module_pch(quickshell-service-polkit) 34 | 35 | target_link_libraries(quickshell PRIVATE quickshell-service-polkitplugin) 36 | -------------------------------------------------------------------------------- /src/wayland/wlr_layershell/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-wayland-layershell STATIC 2 | wlr_layershell.cpp 3 | shell_integration.cpp 4 | surface.cpp 5 | ) 6 | 7 | qt_add_qml_module(quickshell-wayland-layershell 8 | URI Quickshell.Wayland._WlrLayerShell 9 | VERSION 0.1 10 | DEPENDENCIES QtQuick 11 | ) 12 | 13 | qs_add_module_deps_light(quickshell-wayland-layershell Quickshell Quickshell.Wayland) 14 | 15 | install_qml_module(quickshell-wayland-layershell) 16 | 17 | wl_proto(wlp-layer-shell wlr-layer-shell-unstable-v1 "${CMAKE_CURRENT_SOURCE_DIR}") 18 | 19 | # link dependency of wlr-layer-shell's codegen 20 | wl_proto(wlp-xdg-shell xdg-shell "${WAYLAND_PROTOCOLS}/stable/xdg-shell") 21 | 22 | target_link_libraries(quickshell-wayland-layershell PRIVATE 23 | Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 24 | wlp-layer-shell wlp-xdg-shell 25 | ) 26 | 27 | qs_module_pch(quickshell-wayland-layershell SET large) 28 | 29 | target_link_libraries(quickshell-wayland PRIVATE quickshell-wayland-layershellplugin) 30 | -------------------------------------------------------------------------------- /src/services/pam/ipc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | enum class PamIpcEvent : uint8_t { 10 | Request, 11 | Exit, 12 | }; 13 | 14 | enum class PamIpcExitCode : uint8_t { 15 | Success, 16 | StartFailed, 17 | AuthFailed, 18 | MaxTries, 19 | PamError, 20 | OtherError, 21 | }; 22 | 23 | struct PamIpcRequestFlags { 24 | bool echo; 25 | bool error; 26 | bool responseRequired; 27 | }; 28 | 29 | class PamIpcPipes { 30 | public: 31 | explicit PamIpcPipes() = default; 32 | explicit PamIpcPipes(int fdIn, int fdOut): fdIn(fdIn), fdOut(fdOut) {} 33 | ~PamIpcPipes(); 34 | Q_DISABLE_COPY_MOVE(PamIpcPipes); 35 | 36 | [[nodiscard]] bool readBytes(char* buffer, size_t length) const; 37 | [[nodiscard]] bool writeBytes(const char* buffer, size_t length) const; 38 | [[nodiscard]] std::string readString(bool* ok) const; 39 | [[nodiscard]] bool writeString(const std::string& string) const; 40 | 41 | int fdIn = 0; 42 | int fdOut = 0; 43 | }; 44 | -------------------------------------------------------------------------------- /src/dbus/dbusmenu/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set_source_files_properties(com.canonical.dbusmenu.xml PROPERTIES 2 | CLASSNAME DBusMenuInterface 3 | INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/dbus_menu_types.hpp 4 | ) 5 | 6 | qt_add_dbus_interface(DBUS_INTERFACES 7 | com.canonical.dbusmenu.xml 8 | dbus_menu 9 | ) 10 | 11 | qt_add_library(quickshell-dbusmenu STATIC 12 | dbus_menu_types.cpp 13 | dbusmenu.cpp 14 | ${DBUS_INTERFACES} 15 | ) 16 | 17 | qt_add_qml_module(quickshell-dbusmenu 18 | URI Quickshell.DBusMenu 19 | VERSION 0.1 20 | DEPENDENCIES QtQml 21 | ) 22 | 23 | qs_add_module_deps_light(quickshell-dbusmenu Quickshell) 24 | 25 | install_qml_module(quickshell-dbusmenu) 26 | 27 | # dbus headers 28 | target_include_directories(quickshell-dbusmenu PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 29 | 30 | target_link_libraries(quickshell-dbusmenu PRIVATE Qt::Quick Qt::DBus) 31 | qs_add_link_dependencies(quickshell-dbusmenu quickshell-dbus) 32 | 33 | qs_module_pch(quickshell-dbusmenu SET dbus) 34 | 35 | target_link_libraries(quickshell PRIVATE quickshell-dbusmenuplugin) 36 | -------------------------------------------------------------------------------- /src/wayland/hyprland/global_shortcuts/manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "shortcut.hpp" 10 | 11 | namespace qs::hyprland::global_shortcuts::impl { 12 | 13 | class GlobalShortcutManager 14 | : public QWaylandClientExtensionTemplate 15 | , public QtWayland::hyprland_global_shortcuts_manager_v1 { 16 | public: 17 | explicit GlobalShortcutManager(); 18 | 19 | GlobalShortcut* registerShortcut( 20 | const QString& appid, 21 | const QString& name, 22 | const QString& description, 23 | const QString& triggerDescription 24 | ); 25 | 26 | void unregisterShortcut(const QString& appid, const QString& name); 27 | 28 | static GlobalShortcutManager* instance(); 29 | 30 | private: 31 | QHash, QPair> shortcuts; 32 | }; 33 | 34 | } // namespace qs::hyprland::global_shortcuts::impl 35 | -------------------------------------------------------------------------------- /src/ui/reload_popup.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../core/generation.hpp" 10 | 11 | namespace qs::ui { 12 | 13 | class ReloadPopup: public QObject { 14 | Q_OBJECT; 15 | QML_NAMED_ELEMENT(ReloadPopupInfo); 16 | QML_UNCREATABLE("") 17 | Q_PROPERTY(QString instanceId MEMBER instanceId CONSTANT); 18 | Q_PROPERTY(bool failed MEMBER failed CONSTANT); 19 | Q_PROPERTY(QString errorString MEMBER errorString CONSTANT); 20 | 21 | public: 22 | Q_INVOKABLE void closed(); 23 | 24 | static void spawnPopup(QString instanceId, bool failed, QString errorString); 25 | 26 | private: 27 | ReloadPopup(QString instanceId, bool failed, QString errorString); 28 | 29 | EngineGeneration* generation; 30 | QObject* popup = nullptr; 31 | QString instanceId; 32 | bool failed = false; 33 | bool deleting = false; 34 | QString errorString; 35 | 36 | static ReloadPopup* activePopup; 37 | }; 38 | 39 | } // namespace qs::ui 40 | -------------------------------------------------------------------------------- /src/core/plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "plugin.hpp" 2 | #include 3 | 4 | #include // NOLINT (what??) 5 | 6 | #include "generation.hpp" 7 | 8 | static QVector plugins; // NOLINT 9 | 10 | void QsEnginePlugin::registerPlugin(QsEnginePlugin& plugin) { plugins.push_back(&plugin); } 11 | 12 | void QsEnginePlugin::initPlugins() { 13 | plugins.removeIf([](QsEnginePlugin* plugin) { return !plugin->applies(); }); 14 | 15 | std::ranges::sort(plugins, [](QsEnginePlugin* a, QsEnginePlugin* b) { 16 | return b->dependencies().contains(a->name()); 17 | }); 18 | 19 | for (QsEnginePlugin* plugin: plugins) { 20 | plugin->init(); 21 | } 22 | 23 | for (QsEnginePlugin* plugin: plugins) { 24 | plugin->registerTypes(); 25 | } 26 | } 27 | 28 | void QsEnginePlugin::runConstructGeneration(EngineGeneration& generation) { 29 | for (QsEnginePlugin* plugin: plugins) { 30 | plugin->constructGeneration(generation); 31 | } 32 | } 33 | 34 | void QsEnginePlugin::runOnReload() { 35 | for (QsEnginePlugin* plugin: plugins) { 36 | plugin->onReload(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/wayland/idle_inhibit/proto.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "wayland-idle-inhibit-unstable-v1-client-protocol.h" 9 | 10 | namespace qs::wayland::idle_inhibit::impl { 11 | 12 | class IdleInhibitor; 13 | 14 | class IdleInhibitManager 15 | : public QWaylandClientExtensionTemplate 16 | , public QtWayland::zwp_idle_inhibit_manager_v1 { 17 | public: 18 | explicit IdleInhibitManager(); 19 | 20 | IdleInhibitor* createIdleInhibitor(QtWaylandClient::QWaylandWindow* surface); 21 | 22 | static IdleInhibitManager* instance(); 23 | }; 24 | 25 | class IdleInhibitor: public QtWayland::zwp_idle_inhibitor_v1 { 26 | public: 27 | explicit IdleInhibitor(::zwp_idle_inhibitor_v1* inhibitor) 28 | : QtWayland::zwp_idle_inhibitor_v1(inhibitor) {} 29 | 30 | ~IdleInhibitor() override; 31 | Q_DISABLE_COPY_MOVE(IdleInhibitor); 32 | }; 33 | 34 | } // namespace qs::wayland::idle_inhibit::impl 35 | -------------------------------------------------------------------------------- /src/core/instanceinfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct InstanceInfo { 9 | QString instanceId; 10 | QString configPath; 11 | QString shellId; 12 | QDateTime launchTime; 13 | pid_t pid = -1; 14 | QString display; 15 | 16 | static InstanceInfo CURRENT; // NOLINT 17 | }; 18 | 19 | struct RelaunchInfo { 20 | InstanceInfo instance; 21 | bool noColor = false; 22 | bool timestamp = false; 23 | bool sparseLogsOnly = false; 24 | QtMsgType defaultLogLevel = QtWarningMsg; 25 | QString logRules; 26 | }; 27 | 28 | QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info); 29 | QDataStream& operator>>(QDataStream& stream, InstanceInfo& info); 30 | 31 | QDataStream& operator<<(QDataStream& stream, const RelaunchInfo& info); 32 | QDataStream& operator>>(QDataStream& stream, RelaunchInfo& info); 33 | 34 | namespace qs::crash { 35 | 36 | struct CrashInfo { 37 | int logFd = -1; 38 | 39 | static CrashInfo INSTANCE; // NOLINT 40 | }; 41 | 42 | } // namespace qs::crash 43 | -------------------------------------------------------------------------------- /cmake/util.cmake: -------------------------------------------------------------------------------- 1 | # Adds a dependency hint to the link order, but does not block build on the dependency. 2 | function (qs_add_link_dependencies target) 3 | set_property( 4 | TARGET ${target} 5 | APPEND PROPERTY INTERFACE_LINK_LIBRARIES 6 | ${ARGN} 7 | ) 8 | endfunction() 9 | 10 | function (qs_append_qmldir target text) 11 | get_property(qmldir_content TARGET ${target} PROPERTY _qt_internal_qmldir_content) 12 | 13 | if ("${qmldir_content}" STREQUAL "") 14 | message(WARNING "qs_append_qmldir depends on private Qt cmake code, which has broken.") 15 | return() 16 | endif() 17 | 18 | set_property(TARGET ${target} APPEND_STRING PROPERTY _qt_internal_qmldir_content ${text}) 19 | endfunction() 20 | 21 | # DEPENDENCIES introduces a cmake dependency which we don't need with static modules. 22 | # This greatly improves comp speed by not introducing those dependencies. 23 | function (qs_add_module_deps_light target) 24 | foreach (dep IN LISTS ARGN) 25 | string(APPEND qmldir_extra "depends ${dep}\n") 26 | endforeach() 27 | 28 | qs_append_qmldir(${target} "${qmldir_extra}") 29 | endfunction() 30 | -------------------------------------------------------------------------------- /src/dbus/org.freedesktop.DBus.Properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/wayland/hyprland/ipc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-hyprland-ipc STATIC 2 | connection.cpp 3 | monitor.cpp 4 | workspace.cpp 5 | qml.cpp 6 | ) 7 | 8 | qt_add_qml_module(quickshell-hyprland-ipc 9 | URI Quickshell.Hyprland._Ipc 10 | VERSION 0.1 11 | DEPENDENCIES QtQuick 12 | ) 13 | 14 | qs_add_module_deps_light(quickshell-hyprland-ipc Quickshell) 15 | 16 | install_qml_module(quickshell-hyprland-ipc) 17 | 18 | target_link_libraries(quickshell-hyprland-ipc PRIVATE Qt::Quick) 19 | 20 | if (WAYLAND_TOPLEVEL_MANAGEMENT) 21 | target_sources(quickshell-hyprland-ipc PRIVATE 22 | toplevel_mapping.cpp 23 | hyprland_toplevel.cpp 24 | ) 25 | 26 | wl_proto(wlp-hyprland-toplevel-mapping hyprland-toplevel-mapping-v1 "${CMAKE_CURRENT_SOURCE_DIR}") 27 | 28 | target_link_libraries(quickshell-hyprland-ipc PRIVATE 29 | Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 30 | wlp-hyprland-toplevel-mapping 31 | wlp-foreign-toplevel 32 | ) 33 | endif() 34 | 35 | qs_module_pch(quickshell-hyprland-ipc SET large) 36 | 37 | target_link_libraries(quickshell PRIVATE quickshell-hyprland-ipcplugin) 38 | -------------------------------------------------------------------------------- /src/core/easingcurve.cpp: -------------------------------------------------------------------------------- 1 | #include "easingcurve.hpp" 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | qreal EasingCurve::valueAt(qreal x) const { return this->mCurve.valueForProgress(x); } 11 | 12 | qreal EasingCurve::interpolate(qreal x, qreal a, qreal b) const { 13 | return a + (b - a) * this->valueAt(x); 14 | } 15 | 16 | QPointF EasingCurve::interpolate(qreal x, const QPointF& a, const QPointF& b) const { 17 | return QPointF(this->interpolate(x, a.x(), b.x()), this->interpolate(x, a.y(), b.y())); 18 | } 19 | 20 | QRectF EasingCurve::interpolate(qreal x, const QRectF& a, const QRectF& b) const { 21 | return QRectF( 22 | this->interpolate(x, a.topLeft(), b.topLeft()), 23 | this->interpolate(x, a.bottomRight(), b.bottomRight()) 24 | ); 25 | } 26 | 27 | QEasingCurve EasingCurve::curve() const { return this->mCurve; } 28 | 29 | void EasingCurve::setCurve(QEasingCurve curve) { 30 | if (this->mCurve == curve) return; 31 | this->mCurve = std::move(curve); 32 | emit this->curveChanged(); 33 | } 34 | -------------------------------------------------------------------------------- /src/wayland/screencopy/image_copy_capture/image_copy_capture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../manager.hpp" 9 | 10 | namespace qs::wayland::screencopy::icc { 11 | 12 | class IccManager 13 | : public QWaylandClientExtensionTemplate 14 | , public QtWayland::ext_image_copy_capture_manager_v1 { 15 | public: 16 | ScreencopyContext* createSession(::ext_image_capture_source_v1* source, bool paintCursors); 17 | 18 | static IccManager* instance(); 19 | 20 | private: 21 | explicit IccManager(); 22 | }; 23 | 24 | class IccOutputSourceManager 25 | : public QWaylandClientExtensionTemplate 26 | , public QtWayland::ext_output_image_capture_source_manager_v1 { 27 | public: 28 | ScreencopyContext* captureOutput(QScreen* screen, bool paintCursors); 29 | 30 | static IccOutputSourceManager* instance(); 31 | 32 | private: 33 | explicit IccOutputSourceManager(); 34 | }; 35 | 36 | } // namespace qs::wayland::screencopy::icc 37 | -------------------------------------------------------------------------------- /src/bluetooth/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set_source_files_properties(org.bluez.Adapter.xml PROPERTIES 2 | CLASSNAME DBusBluezAdapterInterface 3 | ) 4 | 5 | set_source_files_properties(org.bluez.Device.xml PROPERTIES 6 | CLASSNAME DBusBluezDeviceInterface 7 | ) 8 | 9 | qt_add_dbus_interface(DBUS_INTERFACES 10 | org.bluez.Adapter.xml 11 | dbus_adapter 12 | ) 13 | 14 | qt_add_dbus_interface(DBUS_INTERFACES 15 | org.bluez.Device.xml 16 | dbus_device 17 | ) 18 | 19 | qt_add_library(quickshell-bluetooth STATIC 20 | adapter.cpp 21 | bluez.cpp 22 | device.cpp 23 | ${DBUS_INTERFACES} 24 | ) 25 | 26 | qt_add_qml_module(quickshell-bluetooth 27 | URI Quickshell.Bluetooth 28 | VERSION 0.1 29 | DEPENDENCIES QtQml 30 | ) 31 | 32 | install_qml_module(quickshell-bluetooth) 33 | 34 | # dbus headers 35 | target_include_directories(quickshell-bluetooth PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 36 | 37 | target_link_libraries(quickshell-bluetooth PRIVATE Qt::Qml Qt::DBus) 38 | qs_add_link_dependencies(quickshell-bluetooth quickshell-dbus) 39 | 40 | qs_module_pch(quickshell-bluetooth SET dbus) 41 | 42 | target_link_libraries(quickshell PRIVATE quickshell-bluetoothplugin) 43 | -------------------------------------------------------------------------------- /src/wayland/hyprland/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-hyprland STATIC) 2 | 3 | target_link_libraries(quickshell-hyprland PRIVATE ${QT_DEPS}) 4 | 5 | set(HYPRLAND_MODULES) 6 | 7 | if (HYPRLAND_IPC) 8 | add_subdirectory(ipc) 9 | list(APPEND HYPRLAND_MODULES Quickshell.Hyprland._Ipc) 10 | endif() 11 | 12 | if (HYPRLAND_FOCUS_GRAB) 13 | add_subdirectory(focus_grab) 14 | list(APPEND HYPRLAND_MODULES Quickshell.Hyprland._FocusGrab) 15 | endif() 16 | 17 | if (HYPRLAND_GLOBAL_SHORTCUTS) 18 | add_subdirectory(global_shortcuts) 19 | list(APPEND HYPRLAND_MODULES Quickshell.Hyprland._GlobalShortcuts) 20 | endif() 21 | 22 | if (HYPRLAND_SURFACE_EXTENSIONS) 23 | add_subdirectory(surface) 24 | list(APPEND HYPRLAND_MODULES Quickshell.Hyprland._SurfaceExtensions) 25 | endif() 26 | 27 | qt_add_qml_module(quickshell-hyprland 28 | URI Quickshell.Hyprland 29 | VERSION 0.1 30 | IMPORTS ${HYPRLAND_MODULES} 31 | ) 32 | 33 | qs_add_module_deps_light(quickshell-io Quickshell) 34 | install_qml_module(quickshell-hyprland) 35 | 36 | # intentionally no pch as the module is empty 37 | 38 | target_link_libraries(quickshell PRIVATE quickshell-hyprlandplugin) 39 | -------------------------------------------------------------------------------- /src/services/pipewire/metadata.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "core.hpp" 10 | #include "registry.hpp" 11 | 12 | namespace qs::service::pipewire { 13 | 14 | class PwMetadata: public PwBindable { 15 | Q_OBJECT; 16 | 17 | public: 18 | void bindHooks() override; 19 | void unbindHooks() override; 20 | void initProps(const spa_dict* props) override; 21 | 22 | [[nodiscard]] const QString& name() const; 23 | [[nodiscard]] bool hasSetPermission() const; 24 | 25 | // null value clears 26 | void setProperty(const char* key, const char* type, const char* value); 27 | 28 | signals: 29 | void propertyChanged(const char* key, const char* type, const char* value); 30 | 31 | private: 32 | static const pw_metadata_events EVENTS; 33 | static int 34 | onProperty(void* data, quint32 subject, const char* key, const char* type, const char* value); 35 | 36 | QString mName; 37 | 38 | SpaHook listener; 39 | }; 40 | 41 | } // namespace qs::service::pipewire 42 | -------------------------------------------------------------------------------- /src/wayland/session_lock/lock.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class QSWaylandSessionLockManager; 8 | 9 | class QSWaylandSessionLock 10 | : public QObject 11 | , public QtWayland::ext_session_lock_v1 { 12 | Q_OBJECT; 13 | 14 | public: 15 | QSWaylandSessionLock(QSWaylandSessionLockManager* manager, ::ext_session_lock_v1* lock); 16 | ~QSWaylandSessionLock() override; 17 | Q_DISABLE_COPY_MOVE(QSWaylandSessionLock); 18 | 19 | void unlock(); 20 | 21 | // Returns true if the lock has not finished. 22 | [[nodiscard]] bool active() const; 23 | // Returns true if the compositor considers the session to be locked. 24 | [[nodiscard]] bool hasCompositorLock() const; 25 | 26 | signals: 27 | void compositorLocked(); 28 | void unlocked(); 29 | 30 | private: 31 | void ext_session_lock_v1_locked() override; 32 | void ext_session_lock_v1_finished() override; 33 | 34 | QSWaylandSessionLockManager* manager; // static and not dealloc'd 35 | 36 | // true when the compositor determines the session is locked 37 | bool secure = false; 38 | bool finished = false; 39 | }; 40 | -------------------------------------------------------------------------------- /src/widgets/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-widgets STATIC 2 | cliprect.cpp 3 | wrapper.cpp 4 | marginwrapper.cpp 5 | ) 6 | 7 | qt_add_qml_module(quickshell-widgets 8 | URI Quickshell.Widgets 9 | VERSION 0.1 10 | QML_FILES 11 | IconImage.qml 12 | ClippingRectangle.qml 13 | WrapperItem.qml 14 | WrapperMouseArea.qml 15 | WrapperRectangle.qml 16 | ClippingWrapperRectangle.qml 17 | ClippingWrapperRectangleInternal.qml 18 | ) 19 | 20 | qt6_add_shaders(quickshell-widgets "widgets-cliprect" 21 | NOHLSL NOMSL BATCHABLE PRECOMPILE OPTIMIZED QUIET 22 | PREFIX "/Quickshell/Widgets" 23 | FILES shaders/cliprect.frag 24 | OUTPUTS shaders/cliprect.frag.qsb 25 | ) 26 | 27 | qt6_add_shaders(quickshell-widgets "widgets-cliprect-ub" 28 | NOHLSL NOMSL BATCHABLE PRECOMPILE OPTIMIZED QUIET 29 | PREFIX "/Quickshell/Widgets" 30 | FILES shaders/cliprect.frag 31 | OUTPUTS shaders/cliprect-ub.frag.qsb 32 | DEFINES CONTENT_UNDER_BORDER 33 | ) 34 | 35 | install_qml_module(quickshell-widgets) 36 | 37 | qs_module_pch(quickshell-widgets) 38 | 39 | target_link_libraries(quickshell-widgets PRIVATE Qt::Quick) 40 | target_link_libraries(quickshell PRIVATE quickshell-widgetsplugin) 41 | -------------------------------------------------------------------------------- /src/core/instanceinfo.cpp: -------------------------------------------------------------------------------- 1 | #include "instanceinfo.hpp" 2 | 3 | #include 4 | 5 | QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) { 6 | stream << info.instanceId << info.configPath << info.shellId << info.launchTime << info.pid 7 | << info.display; 8 | return stream; 9 | } 10 | 11 | QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) { 12 | stream >> info.instanceId >> info.configPath >> info.shellId >> info.launchTime >> info.pid 13 | >> info.display; 14 | return stream; 15 | } 16 | 17 | QDataStream& operator<<(QDataStream& stream, const RelaunchInfo& info) { 18 | stream << info.instance << info.noColor << info.timestamp << info.sparseLogsOnly 19 | << info.defaultLogLevel << info.logRules; 20 | 21 | return stream; 22 | } 23 | 24 | QDataStream& operator>>(QDataStream& stream, RelaunchInfo& info) { 25 | stream >> info.instance >> info.noColor >> info.timestamp >> info.sparseLogsOnly 26 | >> info.defaultLogLevel >> info.logRules; 27 | 28 | return stream; 29 | } 30 | 31 | InstanceInfo InstanceInfo::CURRENT = {}; // NOLINT 32 | 33 | namespace qs::crash { 34 | 35 | CrashInfo CrashInfo::INSTANCE = {}; // NOLINT 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/services/polkit/qml.cpp: -------------------------------------------------------------------------------- 1 | #include "qml.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../../core/logcat.hpp" 8 | #include "agentimpl.hpp" 9 | 10 | namespace { 11 | QS_LOGGING_CATEGORY(logPolkit, "quickshell.service.polkit", QtWarningMsg); 12 | } 13 | 14 | namespace qs::service::polkit { 15 | PolkitAgent::~PolkitAgent() { PolkitAgentImpl::onEndOfQmlAgent(this); }; 16 | 17 | void PolkitAgent::componentComplete() { 18 | if (this->mPath.isEmpty()) this->mPath = "/org/quickshell/PolkitAgent"; 19 | 20 | auto* impl = PolkitAgentImpl::tryTakeoverOrCreate(this); 21 | if (impl == nullptr) return; 22 | 23 | this->bFlow.setBinding([impl]() { return impl->activeFlow().value(); }); 24 | this->bIsActive.setBinding([impl]() { return impl->activeFlow().value() != nullptr; }); 25 | this->bIsRegistered.setBinding([impl]() { return impl->isRegistered().value(); }); 26 | } 27 | 28 | void PolkitAgent::setPath(const QString& path) { 29 | if (this->mPath.isEmpty()) { 30 | this->mPath = path; 31 | } else if (this->mPath != path) { 32 | qCWarning(logPolkit) << "cannot change path after it has been set."; 33 | } 34 | } 35 | } // namespace qs::service::polkit 36 | -------------------------------------------------------------------------------- /src/wayland/idle_inhibit/proto.cpp: -------------------------------------------------------------------------------- 1 | #include "proto.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../../core/logcat.hpp" 9 | 10 | namespace qs::wayland::idle_inhibit::impl { 11 | 12 | namespace { 13 | QS_LOGGING_CATEGORY(logIdleInhibit, "quickshell.wayland.idle_inhibit", QtWarningMsg); 14 | } 15 | 16 | IdleInhibitManager::IdleInhibitManager(): QWaylandClientExtensionTemplate(1) { this->initialize(); } 17 | 18 | IdleInhibitManager* IdleInhibitManager::instance() { 19 | static auto* instance = new IdleInhibitManager(); // NOLINT 20 | return instance->isInitialized() ? instance : nullptr; 21 | } 22 | 23 | IdleInhibitor* IdleInhibitManager::createIdleInhibitor(QtWaylandClient::QWaylandWindow* surface) { 24 | auto* inhibitor = new IdleInhibitor(this->create_inhibitor(surface->surface())); 25 | qCDebug(logIdleInhibit) << "Created inhibitor" << inhibitor; 26 | return inhibitor; 27 | } 28 | 29 | IdleInhibitor::~IdleInhibitor() { 30 | qCDebug(logIdleInhibit) << "Destroyed inhibitor" << this; 31 | if (this->isInitialized()) this->destroy(); 32 | } 33 | 34 | } // namespace qs::wayland::idle_inhibit::impl 35 | -------------------------------------------------------------------------------- /src/widgets/shaders/cliprect.frag: -------------------------------------------------------------------------------- 1 | #version 440 2 | layout(location = 0) in vec2 qt_TexCoord0; 3 | layout(location = 0) out vec4 fragColor; 4 | 5 | layout(std140, binding = 0) uniform buf { 6 | mat4 qt_Matrix; 7 | float qt_Opacity; 8 | vec4 backgroundColor; 9 | vec4 borderColor; 10 | }; 11 | 12 | layout(binding = 1) uniform sampler2D rect; 13 | layout(binding = 2) uniform sampler2D content; 14 | 15 | vec4 overlay(vec4 base, vec4 overlay) { 16 | if (overlay.a == 0.0) return base; 17 | if (base.a == 0.0) return overlay; 18 | 19 | vec3 rgb = overlay.rgb + base.rgb * (1.0 - overlay.a); 20 | float a = overlay.a + base.a * (1.0 - overlay.a); 21 | 22 | return vec4(rgb, a); 23 | } 24 | 25 | void main() { 26 | vec4 contentColor = texture(content, qt_TexCoord0.xy); 27 | vec4 rectColor = texture(rect, qt_TexCoord0.xy); 28 | 29 | #ifdef CONTENT_UNDER_BORDER 30 | float contentAlpha = rectColor.a; 31 | #else 32 | float contentAlpha = rectColor.r; 33 | #endif 34 | 35 | float borderAlpha = rectColor.g; 36 | 37 | vec4 innerColor = overlay(backgroundColor, contentColor) * contentAlpha; 38 | vec4 borderColor = borderColor * borderAlpha; 39 | 40 | fragColor = (innerColor * (1.0 - borderColor.a) + borderColor) * qt_Opacity; 41 | } 42 | -------------------------------------------------------------------------------- /src/dbus/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set_source_files_properties(org.freedesktop.DBus.Properties.xml PROPERTIES 2 | CLASSNAME DBusPropertiesInterface 3 | ) 4 | 5 | set_source_files_properties(org.freedesktop.DBus.ObjectManager.xml PROPERTIES 6 | CLASSNAME DBusObjectManagerInterface 7 | INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/dbus_objectmanager_types.hpp 8 | ) 9 | 10 | qt_add_dbus_interface(DBUS_INTERFACES 11 | org.freedesktop.DBus.Properties.xml 12 | dbus_properties 13 | ) 14 | 15 | qt_add_dbus_interface(DBUS_INTERFACES 16 | org.freedesktop.DBus.ObjectManager.xml 17 | dbus_objectmanager 18 | ) 19 | 20 | qt_add_library(quickshell-dbus STATIC 21 | properties.cpp 22 | objectmanager.cpp 23 | bus.cpp 24 | ${DBUS_INTERFACES} 25 | ) 26 | 27 | # dbus headers 28 | target_include_directories(quickshell-dbus PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 29 | 30 | target_link_libraries(quickshell-dbus PRIVATE Qt::Core Qt::DBus) 31 | # todo: link dbus to quickshell here instead of in modules that use it directly 32 | # linker does not like this as is 33 | 34 | qs_add_pchset(dbus 35 | DEPENDENCIES Qt::DBus 36 | HEADERS 37 | 38 | 39 | 40 | ) 41 | 42 | qs_pch(quickshell-dbus SET dbus) 43 | 44 | add_subdirectory(dbusmenu) 45 | -------------------------------------------------------------------------------- /src/io/test/process.cpp: -------------------------------------------------------------------------------- 1 | #include "process.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../process.hpp" 9 | 10 | void TestProcess::startAfterReload() { 11 | auto process = Process(); 12 | auto startedSpy = QSignalSpy(&process, &Process::started); 13 | auto exitedSpy = QSignalSpy(&process, &Process::exited); 14 | 15 | process.setCommand({"true"}); 16 | process.setRunning(true); 17 | 18 | QVERIFY(!process.isRunning()); 19 | QCOMPARE(startedSpy.count(), 0); 20 | 21 | process.postReload(); 22 | 23 | QVERIFY(process.isRunning()); 24 | QVERIFY(startedSpy.wait(100)); 25 | } 26 | 27 | void TestProcess::testExec() { 28 | auto process = Process(); 29 | auto startedSpy = QSignalSpy(&process, &Process::started); 30 | auto exitedSpy = QSignalSpy(&process, &Process::exited); 31 | 32 | process.postReload(); 33 | 34 | process.setCommand({"sleep", "30"}); 35 | process.setRunning(true); 36 | 37 | QVERIFY(process.isRunning()); 38 | QVERIFY(startedSpy.wait(100)); 39 | 40 | process.exec({"true"}); 41 | 42 | QVERIFY(exitedSpy.wait(100)); 43 | QVERIFY(startedSpy.wait(100)); 44 | QVERIFY(process.isRunning()); 45 | } 46 | 47 | QTEST_MAIN(TestProcess); 48 | -------------------------------------------------------------------------------- /src/core/logging_qtprivate.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The logging rule parser from qloggingregistry_p.h and qloggingregistry.cpp. 4 | 5 | // Was unable to properly link the functions when directly using the headers (which we depend 6 | // on anyway), so below is a slightly stripped down copy. Making the originals link would 7 | // be preferable. 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "logcat.hpp" 16 | 17 | namespace qs::log { 18 | QS_DECLARE_LOGGING_CATEGORY(logLogging); 19 | 20 | namespace qt_logging_registry { 21 | 22 | class QLoggingRule { 23 | public: 24 | QLoggingRule(); 25 | QLoggingRule(QStringView pattern, bool enabled); 26 | [[nodiscard]] int pass(QLatin1StringView categoryName, QtMsgType type) const; 27 | 28 | enum PatternFlag : quint8 { 29 | FullText = 0x1, 30 | LeftFilter = 0x2, 31 | RightFilter = 0x4, 32 | MidFilter = LeftFilter | RightFilter 33 | }; 34 | Q_DECLARE_FLAGS(PatternFlags, PatternFlag) 35 | 36 | QString category; 37 | int messageType; 38 | PatternFlags flags; 39 | bool enabled; 40 | 41 | private: 42 | void parse(QStringView pattern); 43 | }; 44 | 45 | } // namespace qt_logging_registry 46 | 47 | } // namespace qs::log 48 | -------------------------------------------------------------------------------- /src/wayland/buffer/qsg.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "manager.hpp" 13 | 14 | namespace qs::wayland::buffer { 15 | 16 | // Interact only from QSG thread. 17 | class WlBufferQSGTexture { 18 | public: 19 | virtual ~WlBufferQSGTexture() = default; 20 | Q_DISABLE_COPY_MOVE(WlBufferQSGTexture); 21 | 22 | [[nodiscard]] virtual QSGTexture* texture() const = 0; 23 | virtual void sync(const WlBuffer* /*buffer*/, QQuickWindow* /*window*/) {} 24 | 25 | protected: 26 | WlBufferQSGTexture() = default; 27 | }; 28 | 29 | // Interact only from QSG thread. 30 | class WlBufferQSGDisplayNode: public QSGTransformNode { 31 | public: 32 | explicit WlBufferQSGDisplayNode(QQuickWindow* window); 33 | 34 | void syncSwapchain(const WlBufferSwapchain& swapchain); 35 | void setRect(const QRectF& rect); 36 | 37 | private: 38 | QQuickWindow* window; 39 | QSGImageNode* imageNode; 40 | QPair> buffer1; 41 | QPair> buffer2; 42 | bool presentSecondBuffer = false; 43 | }; 44 | 45 | } // namespace qs::wayland::buffer 46 | -------------------------------------------------------------------------------- /src/services/mpris/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set_source_files_properties(org.mpris.MediaPlayer2.Player.xml PROPERTIES 2 | CLASSNAME DBusMprisPlayer 3 | NO_NAMESPACE TRUE 4 | ) 5 | 6 | qt_add_dbus_interface(DBUS_INTERFACES 7 | org.mpris.MediaPlayer2.Player.xml 8 | dbus_player 9 | ) 10 | 11 | set_source_files_properties(org.mpris.MediaPlayer2.xml PROPERTIES 12 | CLASSNAME DBusMprisPlayerApp 13 | NO_NAMESPACE TRUE 14 | ) 15 | 16 | qt_add_dbus_interface(DBUS_INTERFACES 17 | org.mpris.MediaPlayer2.xml 18 | dbus_player_app 19 | ) 20 | 21 | qt_add_library(quickshell-service-mpris STATIC 22 | player.cpp 23 | watcher.cpp 24 | ${DBUS_INTERFACES} 25 | ) 26 | 27 | # dbus headers 28 | target_include_directories(quickshell-service-mpris PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 29 | 30 | qt_add_qml_module(quickshell-service-mpris 31 | URI Quickshell.Services.Mpris 32 | VERSION 0.1 33 | DEPENDENCIES QtQml 34 | ) 35 | 36 | qs_add_module_deps_light(quickshell-service-mpris Quickshell) 37 | 38 | install_qml_module(quickshell-service-mpris) 39 | 40 | target_link_libraries(quickshell-service-mpris PRIVATE Qt::Qml Qt::DBus) 41 | qs_add_link_dependencies(quickshell-service-mpris quickshell-dbus) 42 | 43 | qs_module_pch(quickshell-service-mpris SET dbus) 44 | 45 | target_link_libraries(quickshell PRIVATE quickshell-service-mprisplugin) 46 | -------------------------------------------------------------------------------- /src/services/notifications/dbusimage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../../core/imageprovider.hpp" 8 | 9 | namespace qs::service::notifications { 10 | 11 | struct DBusNotificationImage { 12 | qint32 width = 0; 13 | qint32 height = 0; 14 | bool hasAlpha = false; 15 | QByteArray data; 16 | 17 | // valid only for the lifetime of the pixmap 18 | [[nodiscard]] QImage createImage() const; 19 | }; 20 | 21 | const QDBusArgument& operator>>(const QDBusArgument& argument, DBusNotificationImage& pixmap); 22 | const QDBusArgument& operator<<(QDBusArgument& argument, const DBusNotificationImage& pixmap); 23 | 24 | class NotificationImage: public QsIndexedImageHandle { 25 | public: 26 | explicit NotificationImage(): QsIndexedImageHandle(QQuickAsyncImageProvider::Image) {} 27 | 28 | [[nodiscard]] bool hasData() const { return !this->image.data.isEmpty(); } 29 | void clear() { this->image.data.clear(); } 30 | 31 | [[nodiscard]] DBusNotificationImage& writeImage() { 32 | this->imageChanged(); 33 | return this->image; 34 | } 35 | 36 | QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override; 37 | 38 | private: 39 | DBusNotificationImage image; 40 | }; 41 | 42 | } // namespace qs::service::notifications 43 | -------------------------------------------------------------------------------- /src/services/status_notifier/qml.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../../core/doc.hpp" 9 | #include "../../core/model.hpp" 10 | #include "item.hpp" 11 | 12 | ///! System tray 13 | /// Referencing the SystemTray singleton will make quickshell start tracking 14 | /// system tray contents, which are updated as the tray changes, and can be 15 | /// accessed via the @@items property. 16 | class SystemTray: public QObject { 17 | Q_OBJECT; 18 | /// List of all system tray icons. 19 | QSDOC_TYPE_OVERRIDE(ObjectModel*); 20 | Q_PROPERTY(UntypedObjectModel* items READ items CONSTANT); 21 | QML_ELEMENT; 22 | QML_SINGLETON; 23 | 24 | public: 25 | explicit SystemTray(QObject* parent = nullptr); 26 | 27 | [[nodiscard]] ObjectModel* items(); 28 | 29 | private slots: 30 | void onItemRegistered(qs::service::sni::StatusNotifierItem* item); 31 | void onItemUnregistered(qs::service::sni::StatusNotifierItem* item); 32 | 33 | private: 34 | static bool 35 | compareItems(qs::service::sni::StatusNotifierItem* a, qs::service::sni::StatusNotifierItem* b); 36 | 37 | ObjectModel mItems {this}; 38 | }; 39 | -------------------------------------------------------------------------------- /src/services/upower/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set_source_files_properties(org.freedesktop.UPower.xml PROPERTIES 2 | CLASSNAME DBusUPowerService 3 | NO_NAMESPACE TRUE 4 | ) 5 | 6 | qt_add_dbus_interface(DBUS_INTERFACES 7 | org.freedesktop.UPower.xml 8 | dbus_service 9 | ) 10 | 11 | set_source_files_properties(org.freedesktop.UPower.Device.xml PROPERTIES 12 | CLASSNAME DBusUPowerDevice 13 | NO_NAMESPACE TRUE 14 | ) 15 | 16 | qt_add_dbus_interface(DBUS_INTERFACES 17 | org.freedesktop.UPower.Device.xml 18 | dbus_device 19 | ) 20 | 21 | qt_add_library(quickshell-service-upower STATIC 22 | core.cpp 23 | device.cpp 24 | powerprofiles.cpp 25 | ${DBUS_INTERFACES} 26 | ) 27 | 28 | # dbus headers 29 | target_include_directories(quickshell-service-upower PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 30 | 31 | qt_add_qml_module(quickshell-service-upower 32 | URI Quickshell.Services.UPower 33 | VERSION 0.1 34 | DEPENDENCIES QtQml 35 | ) 36 | 37 | qs_add_module_deps_light(quickshell-service-upower Quickshell) 38 | 39 | install_qml_module(quickshell-service-upower) 40 | 41 | target_link_libraries(quickshell-service-upower PRIVATE Qt::Qml Qt::DBus) 42 | qs_add_link_dependencies(quickshell-service-upower quickshell-dbus) 43 | target_link_libraries(quickshell PRIVATE quickshell-service-upowerplugin) 44 | 45 | qs_module_pch(quickshell-service-upower SET dbus) 46 | -------------------------------------------------------------------------------- /src/services/status_notifier/qml.cpp: -------------------------------------------------------------------------------- 1 | #include "qml.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "../../core/model.hpp" 7 | #include "host.hpp" 8 | #include "item.hpp" 9 | 10 | using namespace qs::service::sni; 11 | 12 | SystemTray::SystemTray(QObject* parent): QObject(parent) { 13 | auto* host = StatusNotifierHost::instance(); 14 | 15 | // clang-format off 16 | QObject::connect(host, &StatusNotifierHost::itemReady, this, &SystemTray::onItemRegistered); 17 | QObject::connect(host, &StatusNotifierHost::itemUnregistered, this, &SystemTray::onItemUnregistered); 18 | // clang-format on 19 | 20 | for (auto* item: host->items()) { 21 | this->mItems.insertObjectSorted(item, &SystemTray::compareItems); 22 | } 23 | } 24 | 25 | void SystemTray::onItemRegistered(StatusNotifierItem* item) { 26 | this->mItems.insertObjectSorted(item, &SystemTray::compareItems); 27 | } 28 | 29 | void SystemTray::onItemUnregistered(StatusNotifierItem* item) { this->mItems.removeObject(item); } 30 | ObjectModel* SystemTray::items() { return &this->mItems; } 31 | 32 | bool SystemTray::compareItems(StatusNotifierItem* a, StatusNotifierItem* b) { 33 | return a->bindableCategory().value() < b->bindableCategory().value() 34 | || a->bindableId().value().compare(b->bindableId().value(), Qt::CaseInsensitive) >= 0; 35 | } 36 | -------------------------------------------------------------------------------- /src/wayland/screencopy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-wayland-screencopy STATIC 2 | manager.cpp 3 | view.cpp 4 | ) 5 | 6 | qt_add_qml_module(quickshell-wayland-screencopy 7 | URI Quickshell.Wayland._Screencopy 8 | VERSION 0.1 9 | DEPENDENCIES QtQuick 10 | ) 11 | 12 | install_qml_module(quickshell-wayland-screencopy) 13 | 14 | set(SCREENCOPY_MODULES) 15 | 16 | if (SCREENCOPY_ICC) 17 | add_subdirectory(image_copy_capture) 18 | list(APPEND SCREENCOPY_MODULES quickshell-wayland-screencopy-icc) 19 | endif() 20 | 21 | if (SCREENCOPY_WLR) 22 | add_subdirectory(wlr_screencopy) 23 | list(APPEND SCREENCOPY_MODULES quickshell-wayland-screencopy-wlr) 24 | endif() 25 | 26 | if (SCREENCOPY_HYPRLAND_TOPLEVEL) 27 | add_subdirectory(hyprland_screencopy) 28 | list(APPEND SCREENCOPY_MODULES quickshell-wayland-screencopy-hyprland) 29 | endif() 30 | 31 | configure_file(build.hpp.in build.hpp @ONLY) 32 | target_include_directories(quickshell-wayland-screencopy PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 33 | 34 | target_link_libraries(quickshell-wayland-screencopy PRIVATE 35 | Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client 36 | quickshell-wayland-buffer 37 | ${SCREENCOPY_MODULES} 38 | ) 39 | 40 | qs_module_pch(quickshell-wayland-screencopy SET large) 41 | 42 | target_link_libraries(quickshell PRIVATE quickshell-wayland-screencopyplugin) 43 | -------------------------------------------------------------------------------- /src/wayland/hyprland/focus_grab/grab.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace qs::hyprland::focus_grab { 13 | using HyprlandFocusGrab = QtWayland::hyprland_focus_grab_v1; 14 | 15 | class FocusGrab 16 | : public QObject 17 | , public HyprlandFocusGrab { 18 | using QWaylandWindow = QtWaylandClient::QWaylandWindow; 19 | 20 | Q_OBJECT; 21 | 22 | public: 23 | explicit FocusGrab(::hyprland_focus_grab_v1* grab); 24 | ~FocusGrab() override; 25 | Q_DISABLE_COPY_MOVE(FocusGrab); 26 | 27 | [[nodiscard]] bool isActive() const; 28 | void addWindow(QWindow* window); 29 | void removeWindow(QWindow* window); 30 | void sync(); 31 | void startTransaction(); 32 | void completeTransaction(); 33 | 34 | signals: 35 | void activated(); 36 | void cleared(); 37 | 38 | private: 39 | void hyprland_focus_grab_v1_cleared() override; 40 | 41 | void addWaylandWindow(QWaylandWindow* window); 42 | 43 | QList pendingAdditions; 44 | bool commitRequired = false; 45 | bool transactionActive = false; 46 | bool active = false; 47 | }; 48 | 49 | } // namespace qs::hyprland::focus_grab 50 | -------------------------------------------------------------------------------- /src/wayland/shortcuts_inhibit/test/manual/test.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls 4 | import Quickshell 5 | import Quickshell.Wayland 6 | 7 | Scope { 8 | Timer { 9 | id: toggleTimer 10 | interval: 100 11 | onTriggered: windowLoader.active = true 12 | } 13 | 14 | LazyLoader { 15 | id: windowLoader 16 | active: true 17 | 18 | property bool enabled: false 19 | 20 | FloatingWindow { 21 | id: w 22 | color: contentItem.palette.window 23 | 24 | ColumnLayout { 25 | anchors.centerIn: parent 26 | 27 | CheckBox { 28 | id: loadedCb 29 | text: "Loaded" 30 | checked: true 31 | } 32 | 33 | CheckBox { 34 | id: enabledCb 35 | text: "Enabled" 36 | checked: windowLoader.enabled 37 | onCheckedChanged: windowLoader.enabled = checked 38 | } 39 | 40 | Label { 41 | text: `Active: ${inhibitorLoader.item?.active ?? false}` 42 | } 43 | 44 | Button { 45 | text: "Toggle Window" 46 | onClicked: { 47 | windowLoader.active = false; 48 | toggleTimer.start(); 49 | } 50 | } 51 | } 52 | 53 | LazyLoader { 54 | id: inhibitorLoader 55 | active: loadedCb.checked 56 | 57 | ShortcutInhibitor { 58 | window: w 59 | enabled: enabledCb.checked 60 | onCancelled: enabledCb.checked = false 61 | } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_library(quickshell-core STATIC 2 | plugin.cpp 3 | shell.cpp 4 | variants.cpp 5 | rootwrapper.cpp 6 | reload.cpp 7 | rootwrapper.cpp 8 | qmlglobal.cpp 9 | qmlscreen.cpp 10 | region.cpp 11 | persistentprops.cpp 12 | singleton.cpp 13 | generation.cpp 14 | scan.cpp 15 | qsintercept.cpp 16 | incubator.cpp 17 | lazyloader.cpp 18 | easingcurve.cpp 19 | iconimageprovider.cpp 20 | imageprovider.cpp 21 | transformwatcher.cpp 22 | boundcomponent.cpp 23 | model.cpp 24 | elapsedtimer.cpp 25 | desktopentry.cpp 26 | desktopentrymonitor.cpp 27 | platformmenu.cpp 28 | qsmenu.cpp 29 | retainable.cpp 30 | popupanchor.cpp 31 | types.cpp 32 | qsmenuanchor.cpp 33 | clock.cpp 34 | logging.cpp 35 | paths.cpp 36 | instanceinfo.cpp 37 | common.cpp 38 | iconprovider.cpp 39 | scriptmodel.cpp 40 | colorquantizer.cpp 41 | toolsupport.cpp 42 | ) 43 | 44 | qt_add_qml_module(quickshell-core 45 | URI Quickshell 46 | VERSION 0.1 47 | DEPENDENCIES QtQuick 48 | OPTIONAL_IMPORTS Quickshell._Window 49 | DEFAULT_IMPORTS Quickshell._Window 50 | ) 51 | 52 | install_qml_module(quickshell-core) 53 | 54 | target_link_libraries(quickshell-core PRIVATE Qt::Quick Qt::Widgets) 55 | 56 | qs_module_pch(quickshell-core SET large) 57 | 58 | target_link_libraries(quickshell PRIVATE quickshell-coreplugin) 59 | 60 | if (BUILD_TESTING) 61 | add_subdirectory(test) 62 | endif() 63 | -------------------------------------------------------------------------------- /src/services/status_notifier/dbus_item_types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct DBusSniIconPixmap { 8 | qint32 width = 0; 9 | qint32 height = 0; 10 | QByteArray data; 11 | 12 | // valid only for the lifetime of the pixmap 13 | [[nodiscard]] QImage createImage() const; 14 | 15 | bool operator==(const DBusSniIconPixmap& other) const; 16 | }; 17 | 18 | using DBusSniIconPixmapList = QList; 19 | 20 | struct DBusSniTooltip { 21 | QString icon; 22 | DBusSniIconPixmapList iconPixmaps; 23 | QString title; 24 | QString description; 25 | 26 | bool operator==(const DBusSniTooltip& other) const; 27 | }; 28 | 29 | const QDBusArgument& operator>>(const QDBusArgument& argument, DBusSniIconPixmap& pixmap); 30 | const QDBusArgument& operator<<(QDBusArgument& argument, const DBusSniIconPixmap& pixmap); 31 | const QDBusArgument& operator>>(const QDBusArgument& argument, DBusSniIconPixmapList& pixmaps); 32 | const QDBusArgument& operator<<(QDBusArgument& argument, const DBusSniIconPixmapList& pixmaps); 33 | const QDBusArgument& operator>>(const QDBusArgument& argument, DBusSniTooltip& tooltip); 34 | const QDBusArgument& operator<<(QDBusArgument& argument, const DBusSniTooltip& tooltip); 35 | 36 | QDebug operator<<(QDebug debug, const DBusSniIconPixmap& pixmap); 37 | QDebug operator<<(QDebug debug, const DBusSniTooltip& tooltip); 38 | -------------------------------------------------------------------------------- /src/wayland/session_lock/lock.cpp: -------------------------------------------------------------------------------- 1 | #include "lock.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "manager.hpp" 7 | 8 | QSWaylandSessionLock::QSWaylandSessionLock( 9 | QSWaylandSessionLockManager* manager, 10 | ::ext_session_lock_v1* lock 11 | ) 12 | : manager(manager) { 13 | this->init(lock); // if isInitialized is false that means we already unlocked. 14 | } 15 | 16 | QSWaylandSessionLock::~QSWaylandSessionLock() { 17 | if (this->isInitialized()) { 18 | // This will intentionally lock the session if the lock is destroyed without calling unlock. 19 | this->destroy(); 20 | } 21 | } 22 | 23 | void QSWaylandSessionLock::unlock() { 24 | if (this->isInitialized()) { 25 | if (this->finished) this->destroy(); 26 | else this->unlock_and_destroy(); 27 | 28 | this->secure = false; 29 | this->manager->active = nullptr; 30 | 31 | emit this->unlocked(); 32 | } 33 | } 34 | 35 | bool QSWaylandSessionLock::active() const { return this->isInitialized(); } 36 | 37 | bool QSWaylandSessionLock::hasCompositorLock() const { return this->secure; } 38 | 39 | void QSWaylandSessionLock::ext_session_lock_v1_locked() { 40 | this->secure = true; 41 | emit this->compositorLocked(); 42 | } 43 | 44 | void QSWaylandSessionLock::ext_session_lock_v1_finished() { 45 | this->secure = false; 46 | this->finished = true; 47 | this->unlock(); 48 | } 49 | -------------------------------------------------------------------------------- /src/io/ipccomm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../ipc/ipc.hpp" 8 | 9 | namespace qs::io::ipc::comm { 10 | 11 | struct QueryMetadataCommand { 12 | QString target; 13 | QString name; 14 | 15 | void exec(qs::ipc::IpcServerConnection* conn) const; 16 | }; 17 | 18 | DEFINE_SIMPLE_DATASTREAM_OPS(QueryMetadataCommand, data.target, data.name); 19 | 20 | struct StringCallCommand { 21 | QString target; 22 | QString function; 23 | QVector arguments; 24 | 25 | void exec(qs::ipc::IpcServerConnection* conn) const; 26 | }; 27 | 28 | DEFINE_SIMPLE_DATASTREAM_OPS(StringCallCommand, data.target, data.function, data.arguments); 29 | 30 | void handleMsg(qs::ipc::IpcServerConnection* conn); 31 | int queryMetadata(qs::ipc::IpcClient* client, const QString& target, const QString& name); 32 | 33 | int callFunction( 34 | qs::ipc::IpcClient* client, 35 | const QString& target, 36 | const QString& function, 37 | const QVector& arguments 38 | ); 39 | 40 | struct StringPropReadCommand { 41 | QString target; 42 | QString property; 43 | 44 | void exec(qs::ipc::IpcServerConnection* conn) const; 45 | }; 46 | 47 | DEFINE_SIMPLE_DATASTREAM_OPS(StringPropReadCommand, data.target, data.property); 48 | 49 | int getProperty(qs::ipc::IpcClient* client, const QString& target, const QString& property); 50 | 51 | } // namespace qs::io::ipc::comm 52 | -------------------------------------------------------------------------------- /src/x11/i3/ipc/listener.cpp: -------------------------------------------------------------------------------- 1 | #include "listener.hpp" 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "connection.hpp" 13 | 14 | namespace qs::i3::ipc { 15 | 16 | I3IpcListener::~I3IpcListener() { this->freeI3Ipc(); } 17 | 18 | void I3IpcListener::onPostReload() { this->startListening(); } 19 | 20 | QList I3IpcListener::subscriptions() const { return this->mSubscriptions; } 21 | void I3IpcListener::setSubscriptions(QList subscriptions) { 22 | if (this->mSubscriptions == subscriptions) return; 23 | this->mSubscriptions = std::move(subscriptions); 24 | 25 | emit this->subscriptionsChanged(); 26 | this->startListening(); 27 | } 28 | 29 | void I3IpcListener::startListening() { 30 | this->freeI3Ipc(); 31 | if (this->mSubscriptions.isEmpty()) return; 32 | 33 | this->i3Ipc = new I3Ipc(this->mSubscriptions); 34 | 35 | // clang-format off 36 | QObject::connect(this->i3Ipc, &I3Ipc::rawEvent, this, &I3IpcListener::receiveEvent); 37 | // clang-format on 38 | 39 | this->i3Ipc->connect(); 40 | } 41 | 42 | void I3IpcListener::receiveEvent(I3IpcEvent* event) { emit this->ipcEvent(event); } 43 | 44 | void I3IpcListener::freeI3Ipc() { 45 | delete this->i3Ipc; 46 | this->i3Ipc = nullptr; 47 | } 48 | 49 | } // namespace qs::i3::ipc 50 | -------------------------------------------------------------------------------- /src/services/status_notifier/host.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../../core/logcat.hpp" 12 | #include "dbus_watcher_interface.h" 13 | #include "item.hpp" 14 | 15 | QS_DECLARE_LOGGING_CATEGORY(logStatusNotifierHost); 16 | 17 | namespace qs::service::sni { 18 | 19 | class StatusNotifierHost: public QObject { 20 | Q_OBJECT; 21 | 22 | public: 23 | explicit StatusNotifierHost(QObject* parent = nullptr); 24 | 25 | void connectToWatcher(); 26 | [[nodiscard]] QList items() const; 27 | [[nodiscard]] StatusNotifierItem* itemByService(const QString& service) const; 28 | 29 | static StatusNotifierHost* instance(); 30 | 31 | signals: 32 | void itemRegistered(StatusNotifierItem* item); 33 | void itemReady(StatusNotifierItem* item); 34 | void itemUnregistered(StatusNotifierItem* item); 35 | 36 | private slots: 37 | void onWatcherRegistered(); 38 | void onWatcherUnregistered(); 39 | void onItemRegistered(const QString& item); 40 | void onItemUnregistered(const QString& item); 41 | void onItemReady(); 42 | 43 | private: 44 | QString hostId; 45 | QDBusServiceWatcher serviceWatcher; 46 | DBusStatusNotifierWatcher* watcher = nullptr; 47 | QHash mItems; 48 | }; 49 | 50 | } // namespace qs::service::sni 51 | -------------------------------------------------------------------------------- /src/core/plugin.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class EngineGeneration; 8 | 9 | class QsEnginePlugin { 10 | public: 11 | QsEnginePlugin() = default; 12 | virtual ~QsEnginePlugin() = default; 13 | QsEnginePlugin(QsEnginePlugin&&) = delete; 14 | QsEnginePlugin(const QsEnginePlugin&) = delete; 15 | void operator=(QsEnginePlugin&&) = delete; 16 | void operator=(const QsEnginePlugin&) = delete; 17 | 18 | virtual QString name() { return QString(); } 19 | virtual QList dependencies() { return {}; } 20 | virtual bool applies() { return true; } 21 | virtual void init() {} 22 | virtual void registerTypes() {} 23 | virtual void constructGeneration(EngineGeneration& /*unused*/) {} // NOLINT 24 | virtual void onReload() {} 25 | 26 | static void registerPlugin(QsEnginePlugin& plugin); 27 | static void initPlugins(); 28 | static void runConstructGeneration(EngineGeneration& generation); 29 | static void runOnReload(); 30 | }; 31 | 32 | // NOLINTBEGIN 33 | #define QS_REGISTER_PLUGIN(clazz) \ 34 | [[gnu::constructor]] void qsInitPlugin() { \ 35 | static clazz plugin; \ 36 | QsEnginePlugin::registerPlugin(plugin); \ 37 | } 38 | // NOLINTEND 39 | -------------------------------------------------------------------------------- /src/core/elapsedtimer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | ///! Measures time between events 10 | /// The ElapsedTimer measures time since its last restart, and is useful 11 | /// for determining the time between events that don't supply it. 12 | class ElapsedTimer: public QObject { 13 | Q_OBJECT; 14 | QML_ELEMENT; 15 | 16 | public: 17 | explicit ElapsedTimer(); 18 | 19 | /// Return the number of seconds since the timer was last 20 | /// started or restarted, with nanosecond precision. 21 | Q_INVOKABLE qreal elapsed(); 22 | 23 | /// Restart the timer, returning the number of seconds since 24 | /// the timer was last started or restarted, with nanosecond precision. 25 | Q_INVOKABLE qreal restart(); 26 | 27 | /// Return the number of milliseconds since the timer was last 28 | /// started or restarted. 29 | Q_INVOKABLE qint64 elapsedMs(); 30 | 31 | /// Restart the timer, returning the number of milliseconds since 32 | /// the timer was last started or restarted. 33 | Q_INVOKABLE qint64 restartMs(); 34 | 35 | /// Return the number of nanoseconds since the timer was last 36 | /// started or restarted. 37 | Q_INVOKABLE qint64 elapsedNs(); 38 | 39 | /// Restart the timer, returning the number of nanoseconds since 40 | /// the timer was last started or restarted. 41 | Q_INVOKABLE qint64 restartNs(); 42 | 43 | private: 44 | QElapsedTimer timer; 45 | }; 46 | -------------------------------------------------------------------------------- /src/wayland/toplevel_management/manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../../core/logcat.hpp" 10 | #include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" 11 | 12 | namespace qs::wayland::toplevel_management::impl { 13 | 14 | class ToplevelHandle; 15 | 16 | QS_DECLARE_LOGGING_CATEGORY(logToplevelManagement); 17 | 18 | class ToplevelManager 19 | : public QWaylandClientExtensionTemplate 20 | , public QtWayland::zwlr_foreign_toplevel_manager_v1 { 21 | Q_OBJECT; 22 | 23 | public: 24 | [[nodiscard]] bool available() const; 25 | [[nodiscard]] const QVector& readyToplevels() const; 26 | [[nodiscard]] ToplevelHandle* handleFor(::zwlr_foreign_toplevel_handle_v1* toplevel); 27 | 28 | static ToplevelManager* instance(); 29 | 30 | signals: 31 | void toplevelReady(ToplevelHandle* toplevel); 32 | 33 | protected: 34 | explicit ToplevelManager(); 35 | 36 | void 37 | zwlr_foreign_toplevel_manager_v1_toplevel(::zwlr_foreign_toplevel_handle_v1* toplevel) override; 38 | 39 | private slots: 40 | void onToplevelReady(); 41 | void onToplevelClosed(); 42 | 43 | private: 44 | QVector mToplevels; 45 | QVector mReadyToplevels; 46 | }; 47 | 48 | } // namespace qs::wayland::toplevel_management::impl 49 | -------------------------------------------------------------------------------- /src/wayland/session_lock/surface.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "session_lock.hpp" 13 | 14 | class QSWaylandSessionLockSurface 15 | : public QtWaylandClient::QWaylandShellSurface 16 | , public QtWayland::ext_session_lock_surface_v1 { 17 | public: 18 | QSWaylandSessionLockSurface(QtWaylandClient::QWaylandWindow* window); 19 | ~QSWaylandSessionLockSurface() override; 20 | Q_DISABLE_COPY_MOVE(QSWaylandSessionLockSurface); 21 | 22 | [[nodiscard]] bool isExposed() const override; 23 | void applyConfigure() override; 24 | 25 | #if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) 26 | [[nodiscard]] bool commitSurfaceRole() const override; 27 | #else 28 | bool handleExpose(const QRegion& region) override; 29 | #endif 30 | 31 | void setExtension(LockWindowExtension* ext); 32 | void setVisible(); 33 | 34 | private: 35 | void 36 | ext_session_lock_surface_v1_configure(quint32 serial, quint32 width, quint32 height) override; 37 | 38 | #if QT_VERSION < QT_VERSION_CHECK(6, 10, 0) 39 | void initVisible(); 40 | bool visible = false; 41 | QtWaylandClient::QWaylandShmBuffer* initBuf = nullptr; 42 | #endif 43 | 44 | LockWindowExtension* ext = nullptr; 45 | QSize size; 46 | bool configured = false; 47 | }; 48 | -------------------------------------------------------------------------------- /src/core/easingcurve.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | ///! Easing curve. 11 | /// Directly accessible easing curve as used in property animations. 12 | class EasingCurve: public QObject { 13 | Q_OBJECT; 14 | /// Easing curve settings. Works exactly the same as 15 | /// [PropertyAnimation.easing](https://doc.qt.io/qt-6/qml-qtquick-propertyanimation.html#easing-prop). 16 | Q_PROPERTY(QEasingCurve curve READ curve WRITE setCurve NOTIFY curveChanged); 17 | QML_ELEMENT; 18 | 19 | public: 20 | EasingCurve(QObject* parent = nullptr): QObject(parent) {} 21 | 22 | /// Returns the Y value for the given X value on the curve 23 | /// from 0.0 to 1.0. 24 | Q_INVOKABLE [[nodiscard]] qreal valueAt(qreal x) const; 25 | /// Interpolates between two values using the given X coordinate. 26 | Q_INVOKABLE [[nodiscard]] qreal interpolate(qreal x, qreal a, qreal b) const; 27 | /// Interpolates between two points using the given X coordinate. 28 | Q_INVOKABLE [[nodiscard]] QPointF interpolate(qreal x, const QPointF& a, const QPointF& b) const; 29 | /// Interpolates two rects using the given X coordinate. 30 | Q_INVOKABLE [[nodiscard]] QRectF interpolate(qreal x, const QRectF& a, const QRectF& b) const; 31 | 32 | [[nodiscard]] QEasingCurve curve() const; 33 | void setCurve(QEasingCurve curve); 34 | 35 | signals: 36 | void curveChanged(); 37 | 38 | private: 39 | QEasingCurve mCurve; 40 | }; 41 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import {}, 3 | stdenv ? pkgs.clangStdenv, # faster compiles than gcc 4 | quickshell ? pkgs.callPackage ./default.nix { inherit stdenv; }, 5 | ... 6 | }: let 7 | tidyfox = import (pkgs.fetchFromGitea { 8 | domain = "git.outfoxxed.me"; 9 | owner = "outfoxxed"; 10 | repo = "tidyfox"; 11 | rev = "9d85d7e7dea2602aa74ec3168955fee69967e92f"; 12 | hash = "sha256-77ERiweF6lumonp2c/124rAoVG6/o9J+Aajhttwtu0w="; 13 | }) { inherit pkgs; }; 14 | in pkgs.mkShell.override { stdenv = quickshell.stdenv; } { 15 | inputsFrom = [ quickshell ]; 16 | 17 | nativeBuildInputs = with pkgs; [ 18 | just 19 | clang-tools 20 | parallel 21 | makeWrapper 22 | ]; 23 | 24 | TIDYFOX = "${tidyfox}/lib/libtidyfox.so"; 25 | 26 | shellHook = '' 27 | export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc) 28 | 29 | # Add Qt-related environment variables. 30 | # https://discourse.nixos.org/t/qt-development-environment-on-a-flake-system/23707/5 31 | setQtEnvironment=$(mktemp) 32 | random=$(openssl rand -base64 20 | sed "s/[^a-zA-Z0-9]//g") 33 | makeShellWrapper "$(type -p sh)" "$setQtEnvironment" "''${qtWrapperArgs[@]}" --argv0 "$random" 34 | sed "/$random/d" -i "$setQtEnvironment" 35 | source "$setQtEnvironment" 36 | 37 | # qmlls does not account for the import path and bases its search off qtbase's path. 38 | # The actual imports come from qtdeclarative. This directs qmlls to the correct imports. 39 | export QMLLS_BUILD_DIRS=$(pwd)/build:$QML2_IMPORT_PATH 40 | ''; 41 | } 42 | -------------------------------------------------------------------------------- /src/wayland/idle_notify/proto.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../../core/logcat.hpp" 13 | 14 | namespace qs::wayland::idle_notify { 15 | QS_DECLARE_LOGGING_CATEGORY(logIdleNotify); 16 | } 17 | 18 | namespace qs::wayland::idle_notify::impl { 19 | 20 | class IdleNotification; 21 | 22 | class IdleNotificationManager 23 | : public QWaylandClientExtensionTemplate 24 | , public QtWayland::ext_idle_notifier_v1 { 25 | public: 26 | explicit IdleNotificationManager(); 27 | IdleNotification* createIdleNotification(quint32 timeout, bool respectInhibitors); 28 | 29 | static IdleNotificationManager* instance(); 30 | }; 31 | 32 | class IdleNotification 33 | : public QObject 34 | , public QtWayland::ext_idle_notification_v1 { 35 | Q_OBJECT; 36 | 37 | public: 38 | explicit IdleNotification(::ext_idle_notification_v1* notification) 39 | : QtWayland::ext_idle_notification_v1(notification) {} 40 | 41 | ~IdleNotification() override; 42 | Q_DISABLE_COPY_MOVE(IdleNotification); 43 | 44 | Q_OBJECT_BINDABLE_PROPERTY(IdleNotification, bool, bIsIdle); 45 | 46 | protected: 47 | void ext_idle_notification_v1_idled() override; 48 | void ext_idle_notification_v1_resumed() override; 49 | }; 50 | 51 | } // namespace qs::wayland::idle_notify::impl 52 | -------------------------------------------------------------------------------- /src/services/pipewire/core.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace qs::service::pipewire { 16 | 17 | class SpaHook { 18 | public: 19 | explicit SpaHook(); 20 | 21 | void remove(); 22 | spa_hook hook; 23 | }; 24 | 25 | class PwCore: public QObject { 26 | Q_OBJECT; 27 | 28 | public: 29 | explicit PwCore(QObject* parent = nullptr); 30 | ~PwCore() override; 31 | Q_DISABLE_COPY_MOVE(PwCore); 32 | 33 | [[nodiscard]] bool isValid() const; 34 | [[nodiscard]] qint32 sync(quint32 id) const; 35 | 36 | pw_loop* loop = nullptr; 37 | pw_context* context = nullptr; 38 | pw_core* core = nullptr; 39 | 40 | signals: 41 | void polled(); 42 | void synced(quint32 id, qint32 seq); 43 | 44 | private slots: 45 | void poll(); 46 | 47 | private: 48 | static const pw_core_events EVENTS; 49 | 50 | static void onSync(void* data, quint32 id, qint32 seq); 51 | 52 | QSocketNotifier notifier; 53 | SpaHook listener; 54 | }; 55 | 56 | template 57 | class PwObject { 58 | public: 59 | explicit PwObject(T* object = nullptr): object(object) {} 60 | ~PwObject() { pw_proxy_destroy(reinterpret_cast(this->object)); } 61 | 62 | Q_DISABLE_COPY_MOVE(PwObject); 63 | 64 | T* object; 65 | }; 66 | 67 | } // namespace qs::service::pipewire 68 | -------------------------------------------------------------------------------- /src/x11/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | xcb_connection_t* x11Connection() { 10 | static xcb_connection_t* conn = nullptr; // NOLINT 11 | 12 | if (conn == nullptr) { 13 | if (auto* x11Application = dynamic_cast(QGuiApplication::instance()) 14 | ->nativeInterface()) 15 | { 16 | conn = x11Application->connection(); 17 | } 18 | } 19 | 20 | return conn; 21 | } 22 | 23 | // NOLINTBEGIN 24 | XAtom XAtom::_NET_WM_STRUT {}; 25 | XAtom XAtom::_NET_WM_STRUT_PARTIAL {}; 26 | XAtom XAtom::_NET_WM_DESKTOP {}; 27 | // NOLINTEND 28 | 29 | void XAtom::initAtoms() { 30 | _NET_WM_STRUT.init("_NET_WM_STRUT"); 31 | _NET_WM_STRUT_PARTIAL.init("_NET_WM_STRUT_PARTIAL"); 32 | _NET_WM_DESKTOP.init("_NET_WM_DESKTOP"); 33 | } 34 | 35 | void XAtom::init(const QByteArray& name) { 36 | this->cookie = xcb_intern_atom(x11Connection(), 0, name.length(), name.data()); 37 | } 38 | 39 | bool XAtom::isValid() { 40 | this->resolve(); 41 | return this->mAtom != XCB_ATOM_NONE; 42 | } 43 | 44 | const xcb_atom_t& XAtom::atom() { 45 | this->resolve(); 46 | return this->mAtom; 47 | } 48 | 49 | void XAtom::resolve() { 50 | if (!this->resolved) { 51 | this->resolved = true; 52 | 53 | auto* reply = xcb_intern_atom_reply(x11Connection(), this->cookie, nullptr); 54 | if (reply != nullptr) this->mAtom = reply->atom; 55 | free(reply); // NOLINT 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/core/imageprovider.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class QsImageProvider: public QQuickImageProvider { 12 | public: 13 | explicit QsImageProvider(): QQuickImageProvider(QQuickImageProvider::Image) {} 14 | QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override; 15 | }; 16 | 17 | class QsPixmapProvider: public QQuickImageProvider { 18 | public: 19 | explicit QsPixmapProvider(): QQuickImageProvider(QQuickImageProvider::Pixmap) {} 20 | QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override; 21 | }; 22 | 23 | class QsImageHandle { 24 | public: 25 | explicit QsImageHandle(QQmlImageProviderBase::ImageType type); 26 | virtual ~QsImageHandle(); 27 | Q_DISABLE_COPY_MOVE(QsImageHandle); 28 | 29 | [[nodiscard]] virtual QString url() const; 30 | 31 | virtual QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize); 32 | virtual QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize); 33 | 34 | private: 35 | QQmlImageProviderBase::ImageType type; 36 | QString id; 37 | }; 38 | 39 | class QsIndexedImageHandle: public QsImageHandle { 40 | public: 41 | explicit QsIndexedImageHandle(QQmlImageProviderBase::ImageType type): QsImageHandle(type) {} 42 | 43 | [[nodiscard]] QString url() const override; 44 | void imageChanged(); 45 | 46 | private: 47 | quint32 changeIndex = 0; 48 | }; 49 | -------------------------------------------------------------------------------- /src/wayland/idle_notify/monitor.cpp: -------------------------------------------------------------------------------- 1 | #include "monitor.hpp" 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "proto.hpp" 9 | 10 | namespace qs::wayland::idle_notify { 11 | 12 | IdleMonitor::~IdleMonitor() { delete this->bNotification.value(); } 13 | 14 | void IdleMonitor::onPostReload() { 15 | this->bParams.setBinding([this] { 16 | return Params { 17 | .enabled = this->bEnabled.value(), 18 | .timeout = this->bTimeout.value(), 19 | .respectInhibitors = this->bRespectInhibitors.value() 20 | }; 21 | }); 22 | 23 | this->bIsIdle.setBinding([this] { 24 | auto* notification = this->bNotification.value(); 25 | return notification ? notification->bIsIdle.value() : false; 26 | }); 27 | } 28 | 29 | void IdleMonitor::updateNotification() { 30 | auto* notification = this->bNotification.value(); 31 | delete notification; 32 | notification = nullptr; 33 | 34 | auto guard = qScopeGuard([&, this] { this->bNotification = notification; }); 35 | 36 | auto params = this->bParams.value(); 37 | 38 | if (params.enabled) { 39 | auto* manager = impl::IdleNotificationManager::instance(); 40 | 41 | if (!manager) { 42 | qWarning() << "Cannot create idle monitor as ext-idle-notify-v1 is not supported by the " 43 | "current compositor."; 44 | return; 45 | } 46 | 47 | auto timeout = static_cast(std::max(0, static_cast(params.timeout * 1000))); 48 | notification = manager->createIdleNotification(timeout, params.respectInhibitors); 49 | } 50 | } 51 | 52 | } // namespace qs::wayland::idle_notify 53 | -------------------------------------------------------------------------------- /src/services/status_notifier/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | qt_add_dbus_adaptor(DBUS_INTERFACES 2 | org.kde.StatusNotifierWatcher.xml 3 | watcher.hpp 4 | qs::service::sni::StatusNotifierWatcher 5 | dbus_watcher 6 | StatusNotifierWatcherAdaptor 7 | ) 8 | 9 | set_source_files_properties(org.kde.StatusNotifierItem.xml PROPERTIES 10 | CLASSNAME DBusStatusNotifierItem 11 | INCLUDE dbus_item_types.hpp 12 | ) 13 | 14 | qt_add_dbus_interface(DBUS_INTERFACES 15 | org.kde.StatusNotifierItem.xml 16 | dbus_item 17 | ) 18 | 19 | set_source_files_properties(org.kde.StatusNotifierWatcher.xml PROPERTIES 20 | CLASSNAME DBusStatusNotifierWatcher 21 | ) 22 | 23 | qt_add_dbus_interface(DBUS_INTERFACES 24 | org.kde.StatusNotifierWatcher.xml 25 | dbus_watcher_interface 26 | ) 27 | 28 | qt_add_library(quickshell-service-statusnotifier STATIC 29 | qml.cpp 30 | 31 | watcher.cpp 32 | host.cpp 33 | item.cpp 34 | dbus_item_types.cpp 35 | ${DBUS_INTERFACES} 36 | ) 37 | 38 | # dbus headers 39 | target_include_directories(quickshell-service-statusnotifier PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 40 | 41 | qt_add_qml_module(quickshell-service-statusnotifier 42 | URI Quickshell.Services.SystemTray 43 | VERSION 0.1 44 | DEPENDENCIES QtQml 45 | ) 46 | 47 | qs_add_module_deps_light(quickshell-service-statusnotifier Quickshell Quickshell.DBusMenu) 48 | 49 | install_qml_module(quickshell-service-statusnotifier) 50 | 51 | target_link_libraries(quickshell-service-statusnotifier PRIVATE Qt::Quick Qt::DBus) 52 | target_link_libraries(quickshell PRIVATE quickshell-service-statusnotifierplugin) 53 | 54 | qs_module_pch(quickshell-service-statusnotifier SET dbus) 55 | -------------------------------------------------------------------------------- /Justfile: -------------------------------------------------------------------------------- 1 | builddir := 'build' 2 | 3 | fmt: 4 | find src -type f \( -name "*.cpp" -o -name "*.hpp" \) -print0 | xargs -0 clang-format -i 5 | 6 | lint: 7 | find src -type f -name "*.cpp" -print0 | parallel -j$(nproc) -q0 --no-notice --will-cite --tty --bar clang-tidy --load={{ env_var("TIDYFOX") }} 8 | 9 | lint-ci: 10 | find src -type f -name "*.cpp" -print0 | parallel -j$(nproc) -q0 --no-notice --will-cite --tty clang-tidy --load={{ env_var("TIDYFOX") }} 11 | 12 | lint-changed: 13 | git diff --name-only HEAD | grep "^.*\.cpp\$" | parallel -j$(nproc) --no-notice --will-cite --tty --bar clang-tidy --load={{ env_var("TIDYFOX") }} 14 | 15 | lint-staged: 16 | git diff --staged --name-only HEAD | grep "^.*\.cpp\$" | parallel -j$(nproc) --no-notice --will-cite --tty --bar clang-tidy --load={{ env_var("TIDYFOX") }} 17 | 18 | configure target='debug' *FLAGS='': 19 | cmake -GNinja -B {{builddir}} \ 20 | -DCMAKE_BUILD_TYPE={{ if target == "debug" { "Debug" } else { "RelWithDebInfo" } }} \ 21 | -DCMAKE_EXPORT_COMPILE_COMMANDS=ON {{FLAGS}} 22 | 23 | ln -sf {{builddir}}/compile_commands.json compile_commands.json 24 | 25 | _configure_if_clean: 26 | @if ! [ -d {{builddir}} ]; then just configure; fi 27 | 28 | build: _configure_if_clean 29 | cmake --build {{builddir}} 30 | 31 | release: (configure "release") build 32 | 33 | clean: 34 | rm -f compile_commands.json 35 | rm -rf {{builddir}} 36 | 37 | run *ARGS='': build 38 | {{builddir}}/src/quickshell {{ARGS}} 39 | 40 | test *ARGS='': build 41 | ctest --test-dir {{builddir}} --output-on-failure {{ARGS}} 42 | 43 | install *ARGS='': 44 | cmake --install {{builddir}} {{ARGS}} 45 | -------------------------------------------------------------------------------- /src/core/platformmenu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "../core/popupanchor.hpp" 15 | #include "qsmenu.hpp" 16 | 17 | namespace qs::menu::platform { 18 | 19 | class PlatformMenuQMenu; 20 | 21 | class PlatformMenuEntry: public QObject { 22 | Q_OBJECT; 23 | 24 | public: 25 | explicit PlatformMenuEntry(QsMenuEntry* menu); 26 | ~PlatformMenuEntry() override; 27 | Q_DISABLE_COPY_MOVE(PlatformMenuEntry); 28 | 29 | bool display(QObject* parentWindow, int relativeX, int relativeY); 30 | bool display(PopupAnchor* anchor); 31 | 32 | static void registerCreationHook(std::function hook); 33 | 34 | signals: 35 | void closed(); 36 | void relayoutParent(); 37 | 38 | public slots: 39 | void relayout(); 40 | 41 | private slots: 42 | void onAboutToShow(); 43 | void onAboutToHide(); 44 | void onActionTriggered(); 45 | void onChildDestroyed(); 46 | void onEnabledChanged(); 47 | void onTextChanged(); 48 | void onIconChanged(); 49 | void onButtonTypeChanged(); 50 | void onCheckStateChanged(); 51 | 52 | private: 53 | void clearChildren(); 54 | void addToQMenu(PlatformMenuQMenu* menu); 55 | 56 | QsMenuEntry* menu; 57 | PlatformMenuQMenu* qmenu = nullptr; 58 | QAction* qaction = nullptr; 59 | QActionGroup* qactiongroup = nullptr; 60 | QVector childEntries; 61 | }; 62 | 63 | } // namespace qs::menu::platform 64 | -------------------------------------------------------------------------------- /src/wayland/screencopy/manager.cpp: -------------------------------------------------------------------------------- 1 | #include "manager.hpp" 2 | 3 | #include 4 | 5 | #include "build.hpp" 6 | 7 | #if SCREENCOPY_ICC || SCREENCOPY_WLR 8 | #include "../../core/qmlscreen.hpp" 9 | #endif 10 | 11 | #if SCREENCOPY_ICC 12 | #include "image_copy_capture/image_copy_capture.hpp" 13 | #endif 14 | 15 | #if SCREENCOPY_WLR 16 | #include "wlr_screencopy/wlr_screencopy.hpp" 17 | #endif 18 | 19 | #if SCREENCOPY_HYPRLAND_TOPLEVEL 20 | #include "../toplevel_management/qml.hpp" 21 | #include "hyprland_screencopy/hyprland_screencopy.hpp" 22 | #endif 23 | 24 | namespace qs::wayland::screencopy { 25 | 26 | ScreencopyContext* ScreencopyManager::createContext(QObject* object, bool paintCursors) { 27 | if (auto* screen = qobject_cast(object)) { 28 | #if SCREENCOPY_ICC 29 | { 30 | auto* manager = icc::IccOutputSourceManager::instance(); 31 | if (manager->isActive()) { 32 | return manager->captureOutput(screen->screen, paintCursors); 33 | } 34 | } 35 | #endif 36 | #if SCREENCOPY_WLR 37 | { 38 | auto* manager = wlr::WlrScreencopyManager::instance(); 39 | if (manager->isActive()) { 40 | return manager->captureOutput(screen->screen, paintCursors); 41 | } 42 | } 43 | #endif 44 | #if SCREENCOPY_HYPRLAND_TOPLEVEL 45 | } else if (auto* toplevel = qobject_cast(object)) { 46 | auto* manager = hyprland::HyprlandScreencopyManager::instance(); 47 | if (manager->isActive()) { 48 | return manager->captureToplevel(toplevel->implHandle(), paintCursors); 49 | } 50 | #endif 51 | } 52 | 53 | return nullptr; 54 | } 55 | 56 | } // namespace qs::wayland::screencopy 57 | -------------------------------------------------------------------------------- /src/services/mpris/watcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "../../core/doc.hpp" 14 | #include "../../core/model.hpp" 15 | #include "player.hpp" 16 | 17 | namespace qs::service::mpris { 18 | 19 | ///! Provides access to MprisPlayers. 20 | class MprisWatcher: public QObject { 21 | Q_OBJECT; 22 | 23 | public: 24 | [[nodiscard]] ObjectModel* players(); 25 | 26 | static MprisWatcher* instance(); 27 | 28 | private slots: 29 | void onServiceRegistered(const QString& service); 30 | void onServiceUnregistered(const QString& service); 31 | void onPlayerReady(); 32 | void onPlayerDestroyed(QObject* object); 33 | 34 | private: 35 | explicit MprisWatcher(); 36 | 37 | void registerExisting(); 38 | void registerPlayer(const QString& address); 39 | 40 | QDBusServiceWatcher serviceWatcher; 41 | QHash mPlayers; 42 | ObjectModel readyPlayers {this}; 43 | }; 44 | 45 | class MprisQml: public QObject { 46 | Q_OBJECT; 47 | QML_NAMED_ELEMENT(Mpris); 48 | QML_SINGLETON; 49 | /// All connected MPRIS players. 50 | QSDOC_TYPE_OVERRIDE(ObjectModel*); 51 | Q_PROPERTY(UntypedObjectModel* players READ players CONSTANT); 52 | 53 | public: 54 | explicit MprisQml(QObject* parent = nullptr): QObject(parent) {} 55 | 56 | [[nodiscard]] ObjectModel* players(); 57 | }; 58 | 59 | } // namespace qs::service::mpris 60 | -------------------------------------------------------------------------------- /src/core/singleton.cpp: -------------------------------------------------------------------------------- 1 | #include "singleton.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "generation.hpp" 10 | #include "reload.hpp" 11 | 12 | void Singleton::componentComplete() { 13 | auto* context = QQmlEngine::contextForObject(this); 14 | 15 | if (context == nullptr) { 16 | qWarning() << "Not registering singleton not created in the qml context:" << this; 17 | return; 18 | } 19 | 20 | auto url = context->baseUrl(); 21 | 22 | if (this->parent() != nullptr || context->contextObject() != this) { 23 | qWarning() << "Tried to register singleton" << this 24 | << "which is not the root component of its file" << url; 25 | return; 26 | } 27 | 28 | auto* generation = EngineGeneration::findObjectGeneration(this); 29 | 30 | if (generation == nullptr) { 31 | qWarning() << "Tried to register singleton" << this 32 | << "which has no associated engine generation" << url; 33 | return; 34 | } 35 | 36 | generation->singletonRegistry.registerSingleton(url, this); 37 | this->ReloadPropagator::componentComplete(); 38 | } 39 | 40 | void SingletonRegistry::registerSingleton(const QUrl& url, Singleton* singleton) { 41 | if (this->registry.contains(url)) { 42 | qWarning() << "Tried to register singleton twice for the same file" << url; 43 | return; 44 | } 45 | 46 | this->registry.insert(url, singleton); 47 | } 48 | 49 | void SingletonRegistry::onReload(SingletonRegistry* old) { 50 | for (auto [url, singleton]: this->registry.asKeyValueRange()) { 51 | singleton->reload(old == nullptr ? nullptr : old->registry.value(url)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/services/pam/ipc.cpp: -------------------------------------------------------------------------------- 1 | #include "ipc.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | PamIpcPipes::~PamIpcPipes() { 9 | if (this->fdIn != 0) close(this->fdIn); 10 | if (this->fdOut != 0) close(this->fdOut); 11 | } 12 | 13 | bool PamIpcPipes::readBytes(char* buffer, size_t length) const { 14 | size_t i = 0; 15 | 16 | while (i < length) { 17 | auto count = read(this->fdIn, buffer + i, length - i); // NOLINT 18 | 19 | if (count == -1 || count == 0) { 20 | return false; 21 | } 22 | 23 | i += count; 24 | } 25 | 26 | return true; 27 | } 28 | 29 | bool PamIpcPipes::writeBytes(const char* buffer, size_t length) const { 30 | size_t i = 0; 31 | while (i < length) { 32 | auto count = write(this->fdOut, buffer + i, length - i); // NOLINT 33 | 34 | if (count == -1 || count == 0) { 35 | return false; 36 | } 37 | 38 | i += count; 39 | } 40 | 41 | return true; 42 | } 43 | 44 | std::string PamIpcPipes::readString(bool* ok) const { 45 | if (ok != nullptr) *ok = false; 46 | 47 | uint32_t length = 0; 48 | if (!this->readBytes(reinterpret_cast(&length), sizeof(length))) { 49 | return ""; 50 | } 51 | 52 | char data[length]; // NOLINT 53 | if (!this->readBytes(data, length)) { 54 | return ""; 55 | } 56 | 57 | if (ok != nullptr) *ok = true; 58 | 59 | return std::string(data, length); 60 | } 61 | 62 | bool PamIpcPipes::writeString(const std::string& string) const { 63 | uint32_t length = string.length(); 64 | if (!this->writeBytes(reinterpret_cast(&length), sizeof(length))) { 65 | return false; 66 | } 67 | 68 | return this->writeBytes(string.data(), string.length()); 69 | } 70 | -------------------------------------------------------------------------------- /changelog/next.md: -------------------------------------------------------------------------------- 1 | ## Breaking Changes 2 | 3 | ### Config paths are no longer canonicalized 4 | 5 | This fixes nix configs changing shell-ids on rebuild as the shell id is now derived from 6 | the symlink path. Configs with a symlink in their path will have a different shell id. 7 | 8 | Shell ids are used to derive the default config / state / cache folders, so those files 9 | will need to be manually moved if using a config behind a symlinked path without an explicitly 10 | set shell id. 11 | 12 | ## New Features 13 | 14 | - Added support for creating Polkit agents. 15 | - Added support for creating wayland idle inhibitors. 16 | - Added support for wayland idle timeouts. 17 | - Added support for inhibiting wayland compositor shortcuts for focused windows. 18 | - Added the ability to override Quickshell.cacheDir with a custom path. 19 | - Added minimized, maximized, and fullscreen properties to FloatingWindow. 20 | - Added the ability to handle move and resize events to FloatingWindow. 21 | 22 | ## Other Changes 23 | 24 | - IPC operations filter available instances to the current display connection by default. 25 | 26 | ## Bug Fixes 27 | 28 | - Fixed volume control breaking with pipewire pro audio mode. 29 | - Fixed escape sequence handling in desktop entries. 30 | - Fixed volumes not initializing if a pipewire device was already loaded before its node. 31 | - Fixed hyprland active toplevel not resetting after window closes. 32 | - Fixed hyprland ipc window names and titles being reversed. 33 | - Fixed missing signals for system tray item title and description updates. 34 | 35 | ## Packaging Changes 36 | 37 | `glib` and `polkit` have been added as dependencies when compiling with polkit agent support. 38 | -------------------------------------------------------------------------------- /src/dbus/bus.cpp: -------------------------------------------------------------------------------- 1 | #include "bus.hpp" // NOLINT 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "../core/logcat.hpp" 16 | 17 | namespace qs::dbus { 18 | 19 | namespace { 20 | QS_LOGGING_CATEGORY(logDbus, "quickshell.dbus", QtWarningMsg); 21 | } 22 | 23 | void tryLaunchService( 24 | QObject* parent, 25 | QDBusConnection& connection, 26 | const QString& serviceName, 27 | const std::function& callback 28 | ) { 29 | qCDebug(logDbus) << "Attempting to launch service" << serviceName; 30 | 31 | auto message = QDBusMessage::createMethodCall( 32 | "org.freedesktop.DBus", 33 | "/org/freedesktop/DBus", 34 | "org.freedesktop.DBus", 35 | "StartServiceByName" 36 | ); 37 | 38 | message << serviceName << 0u; 39 | auto pendingCall = connection.asyncCall(message); 40 | 41 | auto* call = new QDBusPendingCallWatcher(pendingCall, parent); 42 | 43 | auto responseCallback = [callback, serviceName](QDBusPendingCallWatcher* call) { 44 | const QDBusPendingReply reply = *call; 45 | 46 | if (reply.isError()) { 47 | qCWarning(logDbus).noquote().nospace() 48 | << "Could not launch service " << serviceName << ": " << reply.error(); 49 | callback(false); 50 | } else { 51 | qCDebug(logDbus) << "Service launch successful for" << serviceName; 52 | callback(true); 53 | } 54 | 55 | delete call; 56 | }; 57 | 58 | QObject::connect(call, &QDBusPendingCallWatcher::finished, parent, responseCallback); 59 | } 60 | 61 | } // namespace qs::dbus 62 | -------------------------------------------------------------------------------- /src/wayland/hyprland/global_shortcuts/manager.cpp: -------------------------------------------------------------------------------- 1 | #include "manager.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "shortcut.hpp" 7 | 8 | namespace qs::hyprland::global_shortcuts::impl { 9 | 10 | GlobalShortcutManager::GlobalShortcutManager() 11 | : QWaylandClientExtensionTemplate(1) { 12 | this->initialize(); 13 | } 14 | 15 | GlobalShortcut* GlobalShortcutManager::registerShortcut( 16 | const QString& appid, 17 | const QString& name, 18 | const QString& description, 19 | const QString& triggerDescription 20 | ) { 21 | auto shortcut = this->shortcuts.value({appid, name}); 22 | 23 | if (shortcut.second != nullptr) { 24 | this->shortcuts.insert({appid, name}, {shortcut.first + 1, shortcut.second}); 25 | return shortcut.second; 26 | } else { 27 | auto* shortcutObj = this->register_shortcut(name, appid, description, triggerDescription); 28 | auto* managedObj = new GlobalShortcut(shortcutObj); 29 | this->shortcuts.insert({appid, name}, {1, managedObj}); 30 | return managedObj; 31 | } 32 | } 33 | 34 | void GlobalShortcutManager::unregisterShortcut(const QString& appid, const QString& name) { 35 | auto shortcut = this->shortcuts.value({appid, name}); 36 | 37 | if (shortcut.first > 1) { 38 | this->shortcuts.insert({appid, name}, {shortcut.first - 1, shortcut.second}); 39 | } else { 40 | delete shortcut.second; 41 | this->shortcuts.remove({appid, name}); 42 | } 43 | } 44 | 45 | GlobalShortcutManager* GlobalShortcutManager::instance() { 46 | static GlobalShortcutManager* instance = nullptr; // NOLINT 47 | 48 | if (instance == nullptr) { 49 | instance = new GlobalShortcutManager(); 50 | } 51 | 52 | return instance; 53 | } 54 | 55 | } // namespace qs::hyprland::global_shortcuts::impl 56 | -------------------------------------------------------------------------------- /src/core/logcat.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace qs::log { 7 | void initLogCategoryLevel(const char* name, QtMsgType defaultLevel = QtDebugMsg); 8 | } 9 | 10 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 11 | #define QS_DECLARE_LOGGING_CATEGORY(name) \ 12 | namespace qslogcat { \ 13 | Q_DECLARE_LOGGING_CATEGORY(name); \ 14 | } \ 15 | const QLoggingCategory& name() 16 | 17 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 18 | #define QS_LOGGING_CATEGORY(name, category, ...) \ 19 | namespace qslogcat { \ 20 | Q_LOGGING_CATEGORY(name, category __VA_OPT__(, __VA_ARGS__)); \ 21 | } \ 22 | const QLoggingCategory& name() { \ 23 | static auto* init = []() { \ 24 | qs::log::initLogCategoryLevel(category __VA_OPT__(, __VA_ARGS__)); \ 25 | return &qslogcat::name; \ 26 | }(); \ 27 | return (init) (); \ 28 | } 29 | -------------------------------------------------------------------------------- /src/dbus/dbusmenu/dbus_menu_types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct DBusMenuLayout { 10 | qint32 id = 0; 11 | QVariantMap properties; 12 | QList children; 13 | }; 14 | 15 | using DBusMenuIdList = QList; 16 | 17 | struct DBusMenuItemProperties { 18 | qint32 id = 0; 19 | QVariantMap properties; 20 | }; 21 | 22 | using DBusMenuItemPropertiesList = QList; 23 | 24 | struct DBusMenuItemPropertyNames { 25 | qint32 id = 0; 26 | QStringList properties; 27 | }; 28 | 29 | using DBusMenuItemPropertyNamesList = QList; 30 | 31 | const QDBusArgument& operator>>(const QDBusArgument& argument, DBusMenuLayout& layout); 32 | const QDBusArgument& operator<<(QDBusArgument& argument, const DBusMenuLayout& layout); 33 | const QDBusArgument& operator>>(const QDBusArgument& argument, DBusMenuItemProperties& item); 34 | const QDBusArgument& operator<<(QDBusArgument& argument, const DBusMenuItemProperties& item); 35 | const QDBusArgument& operator>>(const QDBusArgument& argument, DBusMenuItemPropertyNames& names); 36 | const QDBusArgument& operator<<(QDBusArgument& argument, const DBusMenuItemPropertyNames& names); 37 | 38 | QDebug operator<<(QDebug debug, const DBusMenuLayout& layout); 39 | QDebug operator<<(QDebug debug, const DBusMenuItemProperties& item); 40 | QDebug operator<<(QDebug debug, const DBusMenuItemPropertyNames& names); 41 | 42 | Q_DECLARE_METATYPE(DBusMenuLayout); 43 | Q_DECLARE_METATYPE(DBusMenuIdList); 44 | Q_DECLARE_METATYPE(DBusMenuItemProperties); 45 | Q_DECLARE_METATYPE(DBusMenuItemPropertiesList); 46 | Q_DECLARE_METATYPE(DBusMenuItemPropertyNames); 47 | Q_DECLARE_METATYPE(DBusMenuItemPropertyNamesList); 48 | -------------------------------------------------------------------------------- /src/wayland/screencopy/hyprland_screencopy/hyprland_screencopy_p.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "../../toplevel_management/handle.hpp" 7 | #include "../manager.hpp" 8 | 9 | namespace qs::wayland::screencopy::hyprland { 10 | 11 | class HyprlandScreencopyManager; 12 | 13 | class HyprlandScreencopyContext 14 | : public ScreencopyContext 15 | , public QtWayland::hyprland_toplevel_export_frame_v1 { 16 | public: 17 | explicit HyprlandScreencopyContext( 18 | HyprlandScreencopyManager* manager, 19 | toplevel_management::impl::ToplevelHandle* handle, 20 | bool paintCursors 21 | ); 22 | 23 | ~HyprlandScreencopyContext() override; 24 | Q_DISABLE_COPY_MOVE(HyprlandScreencopyContext); 25 | 26 | void captureFrame() override; 27 | 28 | protected: 29 | // clang-format off 30 | void hyprland_toplevel_export_frame_v1_buffer(uint32_t format, uint32_t width, uint32_t height, uint32_t stride) override; 31 | void hyprland_toplevel_export_frame_v1_linux_dmabuf(uint32_t format, uint32_t width, uint32_t height) override; 32 | void hyprland_toplevel_export_frame_v1_flags(uint32_t flags) override; 33 | void hyprland_toplevel_export_frame_v1_buffer_done() override; 34 | void hyprland_toplevel_export_frame_v1_ready(uint32_t tvSecHi, uint32_t tvSecLo, uint32_t tvNsec) override; 35 | void hyprland_toplevel_export_frame_v1_failed() override; 36 | // clang-format on 37 | 38 | private slots: 39 | void onToplevelDestroyed(); 40 | 41 | private: 42 | HyprlandScreencopyManager* manager; 43 | buffer::WlBufferRequest request; 44 | bool copiedFirstFrame = false; 45 | 46 | toplevel_management::impl::ToplevelHandle* handle; 47 | bool paintCursors; 48 | }; 49 | 50 | } // namespace qs::wayland::screencopy::hyprland 51 | -------------------------------------------------------------------------------- /src/services/polkit/gobjectref.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace qs::service::polkit { 6 | 7 | struct GObjectNoRefTag {}; 8 | constexpr GObjectNoRefTag G_OBJECT_NO_REF; 9 | 10 | template 11 | class GObjectRef { 12 | public: 13 | explicit GObjectRef(T* ptr = nullptr): ptr(ptr) { 14 | if (this->ptr) { 15 | g_object_ref(this->ptr); 16 | } 17 | } 18 | 19 | explicit GObjectRef(T* ptr, GObjectNoRefTag /*tag*/): ptr(ptr) {} 20 | 21 | ~GObjectRef() { 22 | if (this->ptr) { 23 | g_object_unref(this->ptr); 24 | } 25 | } 26 | 27 | // We do handle self-assignment in a more general case by checking the 28 | // included pointers rather than the wrapper objects themselves. 29 | // NOLINTBEGIN(bugprone-unhandled-self-assignment) 30 | 31 | GObjectRef(const GObjectRef& other): GObjectRef(other.ptr) {} 32 | GObjectRef& operator=(const GObjectRef& other) { 33 | if (*this == other) return *this; 34 | if (this->ptr) { 35 | g_object_unref(this->ptr); 36 | } 37 | this->ptr = other.ptr; 38 | if (this->ptr) { 39 | g_object_ref(this->ptr); 40 | } 41 | return *this; 42 | } 43 | 44 | GObjectRef(GObjectRef&& other) noexcept: ptr(other.ptr) { other.ptr = nullptr; } 45 | GObjectRef& operator=(GObjectRef&& other) noexcept { 46 | if (*this == other) return *this; 47 | if (this->ptr) { 48 | g_object_unref(this->ptr); 49 | } 50 | this->ptr = other.ptr; 51 | other.ptr = nullptr; 52 | return *this; 53 | } 54 | 55 | // NOLINTEND(bugprone-unhandled-self-assignment) 56 | 57 | [[nodiscard]] T* get() const { return this->ptr; } 58 | T* operator->() const { return this->ptr; } 59 | 60 | bool operator==(const GObjectRef& other) const { return this->ptr == other.ptr; } 61 | 62 | private: 63 | T* ptr; 64 | }; 65 | } // namespace qs::service::polkit -------------------------------------------------------------------------------- /src/ui/reload_popup.cpp: -------------------------------------------------------------------------------- 1 | #include "reload_popup.hpp" 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../core/generation.hpp" 12 | 13 | namespace qs::ui { 14 | 15 | ReloadPopup::ReloadPopup(QString instanceId, bool failed, QString errorString) 16 | : generation(new EngineGeneration()) 17 | , instanceId(std::move(instanceId)) 18 | , failed(failed) 19 | , errorString(std::move(errorString)) { 20 | auto component = QQmlComponent( 21 | this->generation->engine, 22 | "qrc:/qt/qml/Quickshell/_InternalUi/ReloadPopup.qml", 23 | this 24 | ); 25 | 26 | this->popup = component.createWithInitialProperties({{"reloadInfo", QVariant::fromValue(this)}}); 27 | 28 | if (!this->popup) { 29 | qCritical() << "Failed to open reload popup:" << component.errorString(); 30 | } 31 | 32 | this->generation->onReload(nullptr); 33 | } 34 | 35 | void ReloadPopup::closed() { 36 | if (ReloadPopup::activePopup == this) ReloadPopup::activePopup = nullptr; 37 | 38 | if (!this->deleting) { 39 | this->deleting = true; 40 | 41 | QTimer::singleShot(0, [this]() { 42 | if (this->popup) this->popup->deleteLater(); 43 | this->generation->destroy(); 44 | this->deleteLater(); 45 | }); 46 | } 47 | } 48 | 49 | void ReloadPopup::spawnPopup(QString instanceId, bool failed, QString errorString) { 50 | if (qEnvironmentVariableIsSet("QS_NO_RELOAD_POPUP")) return; 51 | 52 | if (ReloadPopup::activePopup) ReloadPopup::activePopup->closed(); 53 | ReloadPopup::activePopup = new ReloadPopup(std::move(instanceId), failed, std::move(errorString)); 54 | } 55 | 56 | ReloadPopup* ReloadPopup::activePopup = nullptr; 57 | 58 | } // namespace qs::ui 59 | -------------------------------------------------------------------------------- /src/wayland/hyprland/surface/surface.cpp: -------------------------------------------------------------------------------- 1 | #include "surface.hpp" 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace qs::hyprland::surface::impl { 15 | 16 | HyprlandSurface::HyprlandSurface( 17 | ::hyprland_surface_v1* surface, 18 | QtWaylandClient::QWaylandWindow* backer 19 | ) 20 | : QtWayland::hyprland_surface_v1(surface) 21 | , backerSurface(backer->surface()) {} 22 | 23 | HyprlandSurface::~HyprlandSurface() { this->destroy(); } 24 | 25 | bool HyprlandSurface::surfaceEq(wl_surface* surface) const { 26 | return surface == this->backerSurface; 27 | } 28 | 29 | void HyprlandSurface::setOpacity(qreal opacity) { 30 | this->set_opacity(wl_fixed_from_double(opacity)); 31 | } 32 | 33 | void HyprlandSurface::setVisibleRegion(const QRegion& region) { 34 | if (this->version() < HYPRLAND_SURFACE_V1_SET_VISIBLE_REGION_SINCE_VERSION) { 35 | qWarning() << "Cannot set hyprland surface visible region: compositor does not support " 36 | "hyprland_surface_v1.set_visible_region"; 37 | return; 38 | } 39 | 40 | if (region.isEmpty()) { 41 | this->set_visible_region(nullptr); 42 | } else { 43 | static const auto* waylandIntegration = QtWaylandClient::QWaylandIntegration::instance(); 44 | auto* display = waylandIntegration->display(); 45 | 46 | auto* wlRegion = display->createRegion(region); 47 | this->set_visible_region(wlRegion); 48 | wl_region_destroy(wlRegion); // NOLINT(misc-include-cleaner) 49 | } 50 | } 51 | 52 | } // namespace qs::hyprland::surface::impl 53 | -------------------------------------------------------------------------------- /src/x11/i3/ipc/workspace.cpp: -------------------------------------------------------------------------------- 1 | #include "workspace.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "controller.hpp" 11 | #include "monitor.hpp" 12 | 13 | namespace qs::i3::ipc { 14 | 15 | I3Workspace::I3Workspace(I3IpcController* ipc): QObject(ipc), ipc(ipc) { 16 | Qt::beginPropertyUpdateGroup(); 17 | 18 | this->bActive.setBinding([this]() { 19 | return this->bMonitor.value() && this->bMonitor->bindableActiveWorkspace().value() == this; 20 | }); 21 | 22 | this->bFocused.setBinding([this]() { 23 | return this->ipc->bindableFocusedWorkspace().value() == this; 24 | }); 25 | 26 | Qt::endPropertyUpdateGroup(); 27 | } 28 | 29 | QVariantMap I3Workspace::lastIpcObject() const { return this->mLastIpcObject; } 30 | 31 | void I3Workspace::updateFromObject(const QVariantMap& obj) { 32 | if (obj != this->mLastIpcObject) { 33 | this->mLastIpcObject = obj; 34 | emit this->lastIpcObjectChanged(); 35 | } 36 | 37 | Qt::beginPropertyUpdateGroup(); 38 | 39 | this->bId = obj.value("id").value(); 40 | this->bName = obj.value("name").value(); 41 | this->bNumber = obj.value("num").value(); 42 | this->bUrgent = obj.value("urgent").value(); 43 | 44 | auto monitorName = obj.value("output").value(); 45 | 46 | if (!this->bMonitor || monitorName != this->bMonitor->bindableName().value()) { 47 | if (monitorName.isEmpty()) { 48 | this->bMonitor = nullptr; 49 | } else { 50 | this->bMonitor = this->ipc->findMonitorByName(monitorName, true); 51 | } 52 | } 53 | 54 | Qt::endPropertyUpdateGroup(); 55 | } 56 | 57 | void I3Workspace::activate() { 58 | this->ipc->dispatch(QString("workspace number %1").arg(this->bNumber.value())); 59 | } 60 | 61 | } // namespace qs::i3::ipc 62 | -------------------------------------------------------------------------------- /assets/quickshell.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/services/pipewire/device.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "core.hpp" 15 | #include "node.hpp" 16 | #include "registry.hpp" 17 | 18 | namespace qs::service::pipewire { 19 | 20 | class PwDevice; 21 | 22 | class PwDevice: public PwBindable { 23 | Q_OBJECT; 24 | 25 | public: 26 | void bindHooks() override; 27 | void unbindHooks() override; 28 | 29 | bool setVolumes(qint32 routeDevice, const QVector& volumes); 30 | bool setMuted(qint32 routeDevice, bool muted); 31 | 32 | void waitForDevice(); 33 | [[nodiscard]] bool waitingForDevice() const; 34 | 35 | [[nodiscard]] bool tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps); 36 | 37 | signals: 38 | void deviceReady(); 39 | void routeVolumesChanged(qint32 routeDevice, const PwVolumeProps& volumeProps); 40 | 41 | private slots: 42 | void polled(); 43 | 44 | private: 45 | static const pw_device_events EVENTS; 46 | static void onInfo(void* data, const pw_device_info* info); 47 | static void 48 | onParam(void* data, qint32 seq, quint32 id, quint32 index, quint32 next, const spa_pod* param); 49 | 50 | QHash routeDeviceIndexes; 51 | QHash routeDeviceVolumes; 52 | QList stagingIndexes; 53 | void addDeviceIndexPairs(const spa_pod* param); 54 | 55 | bool 56 | setRouteProps(qint32 routeDevice, const std::function& propsCallback); 57 | 58 | bool mWaitingForDevice = false; 59 | bool deviceResponded = false; 60 | SpaHook listener; 61 | }; 62 | 63 | } // namespace qs::service::pipewire 64 | -------------------------------------------------------------------------------- /src/x11/i3/ipc/listener.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // NOLINT 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "../../../core/doc.hpp" 19 | #include "../../../core/generation.hpp" 20 | #include "../../../core/qmlglobal.hpp" 21 | #include "../../../core/reload.hpp" 22 | #include "connection.hpp" 23 | 24 | namespace qs::i3::ipc { 25 | 26 | ///! I3/Sway IPC event listener 27 | /// #### Example 28 | /// ```qml 29 | /// I3IpcListener { 30 | /// subscriptions: ["input"] 31 | /// onIpcEvent: function (event) { 32 | /// handleInputEvent(event.data) 33 | /// } 34 | /// } 35 | /// ``` 36 | class I3IpcListener: public PostReloadHook { 37 | Q_OBJECT; 38 | // clang-format off 39 | /// List of [I3/Sway events](https://man.archlinux.org/man/sway-ipc.7.en#EVENTS) to subscribe to. 40 | Q_PROPERTY(QList subscriptions READ subscriptions WRITE setSubscriptions NOTIFY subscriptionsChanged); 41 | // clang-format on 42 | QML_ELEMENT; 43 | 44 | public: 45 | explicit I3IpcListener(QObject* parent = nullptr): PostReloadHook(parent) {} 46 | ~I3IpcListener() override; 47 | Q_DISABLE_COPY_MOVE(I3IpcListener); 48 | 49 | void onPostReload() override; 50 | 51 | [[nodiscard]] QList subscriptions() const; 52 | void setSubscriptions(QList subscriptions); 53 | 54 | signals: 55 | void ipcEvent(I3IpcEvent* event); 56 | void subscriptionsChanged(); 57 | 58 | private: 59 | void startListening(); 60 | void receiveEvent(I3IpcEvent* event); 61 | 62 | void freeI3Ipc(); 63 | 64 | QList mSubscriptions; 65 | I3Ipc* i3Ipc = nullptr; 66 | }; 67 | 68 | } // namespace qs::i3::ipc 69 | -------------------------------------------------------------------------------- /src/core/persistentprops.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "reload.hpp" 7 | 8 | ///! Object that holds properties that can persist across a config reload. 9 | /// PersistentProperties holds properties declated in it across a reload, which is 10 | /// often useful for things like keeping expandable popups open and styling them. 11 | /// 12 | /// Below is an example of using `PersistentProperties` to keep track of the state 13 | /// of an expandable panel. When the configuration is reloaded, the `expanderOpen` property 14 | /// will be saved and the expandable panel will stay in the open/closed state. 15 | /// 16 | /// ```qml 17 | /// PersistentProperties { 18 | /// id: persist 19 | /// reloadableId: "persistedStates" 20 | /// 21 | /// property bool expanderOpen: false 22 | /// } 23 | /// 24 | /// Button { 25 | /// id: expanderButton 26 | /// anchors.centerIn: parent 27 | /// text: "toggle expander" 28 | /// onClicked: persist.expanderOpen = !persist.expanderOpen 29 | /// } 30 | /// 31 | /// Rectangle { 32 | /// anchors.top: expanderButton.bottom 33 | /// anchors.left: expanderButton.left 34 | /// anchors.right: expanderButton.right 35 | /// height: 100 36 | /// 37 | /// color: "lightblue" 38 | /// visible: persist.expanderOpen 39 | /// } 40 | /// ``` 41 | class PersistentProperties: public Reloadable { 42 | Q_OBJECT; 43 | QML_ELEMENT; 44 | 45 | public: 46 | PersistentProperties(QObject* parent = nullptr): Reloadable(parent) {} 47 | 48 | void onReload(QObject* oldInstance) override; 49 | 50 | signals: 51 | /// Called every time the reload stage completes. 52 | /// Will be called every time, including when nothing was loaded from an old instance. 53 | void loaded(); 54 | /// Called every time the properties are reloaded. 55 | /// Will not be called if no old instance was loaded. 56 | void reloaded(); 57 | }; 58 | -------------------------------------------------------------------------------- /src/wayland/screencopy/image_copy_capture/image_copy_capture_p.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../manager.hpp" 10 | 11 | namespace qs::wayland::screencopy::icc { 12 | 13 | class IccScreencopyContext 14 | : public ScreencopyContext 15 | , public QtWayland::ext_image_copy_capture_session_v1 16 | , public QtWayland::ext_image_copy_capture_frame_v1 { 17 | 18 | public: 19 | IccScreencopyContext(::ext_image_copy_capture_session_v1* session); 20 | ~IccScreencopyContext() override; 21 | Q_DISABLE_COPY_MOVE(IccScreencopyContext); 22 | 23 | void captureFrame() override; 24 | 25 | protected: 26 | // clang-format off 27 | void ext_image_copy_capture_session_v1_buffer_size(uint32_t width, uint32_t height) override; 28 | void ext_image_copy_capture_session_v1_shm_format(uint32_t format) override; 29 | void ext_image_copy_capture_session_v1_dmabuf_device(wl_array* device) override; 30 | void ext_image_copy_capture_session_v1_dmabuf_format(uint32_t format, wl_array* modifiers) override; 31 | void ext_image_copy_capture_session_v1_done() override; 32 | void ext_image_copy_capture_session_v1_stopped() override; 33 | 34 | void ext_image_copy_capture_frame_v1_transform(uint32_t transform) override; 35 | void ext_image_copy_capture_frame_v1_damage(int32_t x, int32_t y, int32_t width, int32_t height) override; 36 | void ext_image_copy_capture_frame_v1_ready() override; 37 | void ext_image_copy_capture_frame_v1_failed(uint32_t reason) override; 38 | // clang-format on 39 | 40 | private: 41 | void clearOldState(); 42 | void doCapture(); 43 | 44 | buffer::WlBufferRequest request; 45 | bool statePending = true; 46 | bool capturePending = false; 47 | QRect damage; 48 | QRect lastDamage; 49 | }; 50 | 51 | } // namespace qs::wayland::screencopy::icc 52 | -------------------------------------------------------------------------------- /src/services/polkit/session.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // _PolkitIdentity and _PolkitAgentSession are considered reserved identifiers, 6 | // but I am specifically forward declaring those reserved names. 7 | 8 | // NOLINTBEGIN(bugprone-reserved-identifier) 9 | using PolkitIdentity = struct _PolkitIdentity; 10 | using PolkitAgentSession = struct _PolkitAgentSession; 11 | // NOLINTEND(bugprone-reserved-identifier) 12 | 13 | namespace qs::service::polkit { 14 | //! Represents an authentication session for a specific identity. 15 | class Session: public QObject { 16 | Q_OBJECT; 17 | Q_DISABLE_COPY_MOVE(Session); 18 | 19 | public: 20 | explicit Session(PolkitIdentity* identity, const QString& cookie, QObject* parent = nullptr); 21 | ~Session() override; 22 | 23 | /// Call this after connecting to the relevant signals. 24 | void initiate(); 25 | /// Call this to abort a running authentication session. 26 | void cancel(); 27 | /// Provide a response to an input request. 28 | void respond(const QString& response); 29 | 30 | Q_SIGNALS: 31 | /// Emitted when the session wants to request input from the user. 32 | /// 33 | /// The message is a prompt to present to the user. 34 | /// If echo is false, the user's response should not be displayed (e.g. for passwords). 35 | void request(const QString& message, bool echo); 36 | 37 | /// Emitted when the authentication session completes. 38 | /// 39 | /// If success is true, authentication was successful. 40 | /// Otherwise it failed (e.g. wrong password). 41 | void completed(bool success); 42 | 43 | /// Emitted when an error message should be shown to the user. 44 | void showError(const QString& message); 45 | 46 | /// Emitted when an informational message should be shown to the user. 47 | void showInfo(const QString& message); 48 | 49 | private: 50 | PolkitAgentSession* session = nullptr; 51 | }; 52 | } // namespace qs::service::polkit 53 | -------------------------------------------------------------------------------- /src/services/notifications/org.freedesktop.Notifications.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/services/status_notifier/watcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../../core/logcat.hpp" 13 | 14 | QS_DECLARE_LOGGING_CATEGORY(logStatusNotifierWatcher); 15 | 16 | namespace qs::service::sni { 17 | 18 | class StatusNotifierWatcher 19 | : public QObject 20 | , protected QDBusContext { 21 | Q_OBJECT; 22 | Q_PROPERTY(qint32 ProtocolVersion READ protocolVersion); 23 | Q_PROPERTY(bool IsStatusNotifierHostRegistered READ isHostRegistered); 24 | Q_PROPERTY(QList RegisteredStatusNotifierItems READ registeredItems); 25 | 26 | public: 27 | explicit StatusNotifierWatcher(QObject* parent = nullptr); 28 | 29 | void tryRegister(); 30 | 31 | [[nodiscard]] qint32 protocolVersion() const { return 0; } // NOLINT 32 | [[nodiscard]] bool isHostRegistered() const; 33 | [[nodiscard]] QList registeredItems() const; 34 | [[nodiscard]] bool isRegistered() const; 35 | 36 | // NOLINTBEGIN 37 | void RegisterStatusNotifierHost(const QString& host); 38 | void RegisterStatusNotifierItem(const QString& item); 39 | // NOLINTEND 40 | 41 | static StatusNotifierWatcher* instance(); 42 | 43 | signals: 44 | // NOLINTBEGIN 45 | void StatusNotifierHostRegistered(); 46 | void StatusNotifierHostUnregistered(); 47 | void StatusNotifierItemRegistered(const QString& service); 48 | void StatusNotifierItemUnregistered(const QString& service); 49 | // NOLINTEND 50 | 51 | private slots: 52 | void onServiceUnregistered(const QString& service); 53 | 54 | private: 55 | QString qualifiedItem(const QString& item); 56 | 57 | QDBusServiceWatcher serviceWatcher; 58 | QList hosts; 59 | QList items; 60 | bool registered = false; 61 | }; 62 | 63 | } // namespace qs::service::sni 64 | -------------------------------------------------------------------------------- /cmake/pch.cmake: -------------------------------------------------------------------------------- 1 | # pch breaks clang-tidy..... somehow 2 | if (NOT NO_PCH) 3 | file(GENERATE 4 | OUTPUT ${CMAKE_BINARY_DIR}/pchstub.cpp 5 | CONTENT "// intentionally empty" 6 | ) 7 | endif() 8 | 9 | function (qs_pch target) 10 | if (NO_PCH) 11 | return() 12 | endif() 13 | 14 | cmake_parse_arguments(PARSE_ARGV 1 arg "" "SET" "") 15 | 16 | if ("${arg_SET}" STREQUAL "") 17 | set(arg_SET "common") 18 | endif() 19 | 20 | target_precompile_headers(${target} REUSE_FROM "qs-pchset-${arg_SET}") 21 | endfunction() 22 | 23 | function (qs_module_pch target) 24 | qs_pch(${target} ${ARGN}) 25 | qs_pch("${target}plugin" SET plugin) 26 | qs_pch("${target}plugin_init" SET plugin) 27 | endfunction() 28 | 29 | function (qs_add_pchset SETNAME) 30 | if (NO_PCH) 31 | return() 32 | endif() 33 | 34 | cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "HEADERS;DEPENDENCIES") 35 | 36 | set(LIBNAME "qs-pchset-${SETNAME}") 37 | 38 | add_library(${LIBNAME} ${CMAKE_BINARY_DIR}/pchstub.cpp) 39 | target_link_libraries(${LIBNAME} ${arg_DEPENDENCIES}) 40 | target_precompile_headers(${LIBNAME} PUBLIC ${arg_HEADERS}) 41 | endfunction() 42 | 43 | set(COMMON_PCH_SET 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | ) 55 | 56 | qs_add_pchset(common 57 | DEPENDENCIES Qt::Quick 58 | HEADERS ${COMMON_PCH_SET} 59 | ) 60 | 61 | qs_add_pchset(large 62 | DEPENDENCIES Qt::Quick 63 | HEADERS 64 | ${COMMON_PCH_SET} 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | ) 76 | 77 | 78 | # including qplugin.h directly will cause required symbols to disappear 79 | qs_add_pchset(plugin 80 | DEPENDENCIES Qt::Qml 81 | HEADERS 82 | 83 | 84 | 85 | ) 86 | -------------------------------------------------------------------------------- /src/wayland/buffer/shm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "manager.hpp" 13 | #include "qsg.hpp" 14 | 15 | namespace qs::wayland::buffer::shm { 16 | 17 | class WlShmBuffer: public WlBuffer { 18 | public: 19 | ~WlShmBuffer() override; 20 | Q_DISABLE_COPY_MOVE(WlShmBuffer); 21 | 22 | [[nodiscard]] wl_buffer* buffer() const override { return this->shmBuffer->buffer(); } 23 | [[nodiscard]] QSize size() const override { return this->shmBuffer->size(); } 24 | [[nodiscard]] bool isCompatible(const WlBufferRequest& request) const override; 25 | [[nodiscard]] WlBufferQSGTexture* createQsgTexture(QQuickWindow* window) const override; 26 | 27 | private: 28 | WlShmBuffer(QtWaylandClient::QWaylandShmBuffer* shmBuffer, uint32_t format) 29 | : shmBuffer(shmBuffer) 30 | , format(format) {} 31 | 32 | std::shared_ptr shmBuffer; 33 | uint32_t format; 34 | 35 | friend class WlShmBufferQSGTexture; 36 | friend class ShmbufManager; 37 | friend QDebug& operator<<(QDebug& debug, const WlShmBuffer* buffer); 38 | }; 39 | 40 | QDebug& operator<<(QDebug& debug, const WlShmBuffer* buffer); 41 | 42 | class WlShmBufferQSGTexture: public WlBufferQSGTexture { 43 | public: 44 | [[nodiscard]] QSGTexture* texture() const override { return this->qsgTexture.get(); } 45 | void sync(const WlBuffer* buffer, QQuickWindow* window) override; 46 | 47 | private: 48 | WlShmBufferQSGTexture() = default; 49 | 50 | std::shared_ptr shmBuffer; 51 | std::unique_ptr qsgTexture; 52 | 53 | friend class WlShmBuffer; 54 | }; 55 | 56 | class ShmbufManager { 57 | public: 58 | [[nodiscard]] static WlBuffer* createShmbuf(const WlBufferRequest& request); 59 | }; 60 | 61 | } // namespace qs::wayland::buffer::shm 62 | -------------------------------------------------------------------------------- /src/services/greetd/qml.cpp: -------------------------------------------------------------------------------- 1 | #include "qml.hpp" 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "connection.hpp" 8 | 9 | Greetd::Greetd(QObject* parent): QObject(parent) { 10 | auto* connection = GreetdConnection::instance(); 11 | 12 | QObject::connect(connection, &GreetdConnection::authMessage, this, &Greetd::authMessage); 13 | QObject::connect(connection, &GreetdConnection::authFailure, this, &Greetd::authFailure); 14 | QObject::connect(connection, &GreetdConnection::readyToLaunch, this, &Greetd::readyToLaunch); 15 | QObject::connect(connection, &GreetdConnection::launched, this, &Greetd::launched); 16 | QObject::connect(connection, &GreetdConnection::error, this, &Greetd::error); 17 | 18 | QObject::connect(connection, &GreetdConnection::stateChanged, this, &Greetd::stateChanged); 19 | QObject::connect(connection, &GreetdConnection::userChanged, this, &Greetd::userChanged); 20 | } 21 | 22 | void Greetd::createSession(QString user) { 23 | GreetdConnection::instance()->createSession(std::move(user)); 24 | } 25 | 26 | void Greetd::cancelSession() { GreetdConnection::instance()->cancelSession(); } 27 | 28 | void Greetd::respond(QString response) { 29 | GreetdConnection::instance()->respond(std::move(response)); 30 | } 31 | 32 | void Greetd::launch(const QList& command) { 33 | GreetdConnection::instance()->launch(command, {}, true); 34 | } 35 | 36 | void Greetd::launch(const QList& command, const QList& environment) { 37 | GreetdConnection::instance()->launch(command, environment, true); 38 | } 39 | 40 | void Greetd::launch(const QList& command, const QList& environment, bool quit) { 41 | GreetdConnection::instance()->launch(command, environment, quit); 42 | } 43 | 44 | bool Greetd::isAvailable() { return GreetdConnection::instance()->isAvailable(); } 45 | GreetdState::Enum Greetd::state() { return GreetdConnection::instance()->state(); } 46 | QString Greetd::user() { return GreetdConnection::instance()->user(); } 47 | -------------------------------------------------------------------------------- /src/core/desktopentrymonitor.cpp: -------------------------------------------------------------------------------- 1 | #include "desktopentrymonitor.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "desktopentry.hpp" 11 | 12 | namespace { 13 | void addPathAndParents(QFileSystemWatcher& watcher, const QString& path) { 14 | watcher.addPath(path); 15 | 16 | auto p = QFileInfo(path).absolutePath(); 17 | while (!p.isEmpty()) { 18 | watcher.addPath(p); 19 | const auto parent = QFileInfo(p).dir().absolutePath(); 20 | if (parent == p) break; 21 | p = parent; 22 | } 23 | } 24 | } // namespace 25 | 26 | DesktopEntryMonitor::DesktopEntryMonitor(QObject* parent): QObject(parent) { 27 | this->debounceTimer.setSingleShot(true); 28 | this->debounceTimer.setInterval(100); 29 | 30 | QObject::connect( 31 | &this->watcher, 32 | &QFileSystemWatcher::directoryChanged, 33 | this, 34 | &DesktopEntryMonitor::onDirectoryChanged 35 | ); 36 | QObject::connect( 37 | &this->debounceTimer, 38 | &QTimer::timeout, 39 | this, 40 | &DesktopEntryMonitor::processChanges 41 | ); 42 | 43 | this->startMonitoring(); 44 | } 45 | 46 | void DesktopEntryMonitor::startMonitoring() { 47 | for (const auto& path: DesktopEntryManager::desktopPaths()) { 48 | if (!QDir(path).exists()) continue; 49 | addPathAndParents(this->watcher, path); 50 | this->scanAndWatch(path); 51 | } 52 | } 53 | 54 | void DesktopEntryMonitor::scanAndWatch(const QString& dirPath) { 55 | auto dir = QDir(dirPath); 56 | if (!dir.exists()) return; 57 | 58 | this->watcher.addPath(dirPath); 59 | 60 | auto subdirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); 61 | for (const auto& subdir: subdirs) this->watcher.addPath(subdir.absoluteFilePath()); 62 | } 63 | 64 | void DesktopEntryMonitor::onDirectoryChanged(const QString& /*path*/) { 65 | this->debounceTimer.start(); 66 | } 67 | 68 | void DesktopEntryMonitor::processChanges() { emit this->desktopEntriesChanged(); } --------------------------------------------------------------------------------