diff --git a/.gitignore b/.gitignore
index 50b4af4b..e4e69e73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
*.tar.zst
-pkg/
\ No newline at end of file
+pkg/
+.vscode/
\ No newline at end of file
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
new file mode 100644
index 00000000..34a94537
--- /dev/null
+++ b/.vscode/c_cpp_properties.json
@@ -0,0 +1,20 @@
+{
+ "configurations": [
+ {
+ "name": "Linux",
+ "includePath": [
+ "${workspaceFolder}/**",
+ "/usr/include/qt/QtCore",
+ "/usr/include/qt",
+ "/usr/include/qt/QtQml",
+ "/usr/include/qt/QtGui"
+ ],
+ "defines": [],
+ "compilerPath": "/usr/bin/clang",
+ "cStandard": "c17",
+ "cppStandard": "c++17",
+ "intelliSenseMode": "linux-clang-x64"
+ }
+ ],
+ "version": 4
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..9ddf6b28
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "cmake.ignoreCMakeListsMissing": true
+}
\ No newline at end of file
diff --git a/PKGBUILD b/PKGBUILD
index a41a63ca..c1efda87 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,21 +1,21 @@
pkgname="sleex"
-pkgver="1.31"
-pkgrel="4"
+pkgver="2.0_beta.1"
+pkgrel="1"
pkgdesc="Desktop environment focused on aesthetics and performance"
arch=("x86_64")
depends=(
# Audio
'cava' 'pavucontrol-qt' 'wireplumber' 'libdbusmenu-gtk3' 'playerctl'
# Backlight
- 'hyprsunset' 'geoclue' 'brightnessctl' 'ddcutil'
+ 'gammastep' 'geoclue' 'brightnessctl' 'ddcutil'
# Basic
"axel" "bc" "coreutils" "cliphist" "cmake" "curl" "rsync" "wget" "ripgrep" "jq" "meson" "xdg-user-dirs" "foot" "power-profiles-daemon" "mission-center" "kvantum" "inotify-tools" "lm_sensors" "qt5ct" "qt6ct" "sqlite"
# Cursor
"bibata-cursor-theme-bin"
# Fonts & Themes
'adw-gtk-theme' 'breeze-plus' 'eza' 'fish' 'fontconfig' 'kde-material-you-colors' 'matugen-bin' 'starship' 'ttf-gabarito-git' 'ttf-jetbrains-mono-nerd' 'ttf-material-design-icons-extended' 'ttf-material-symbols-variable' 'ttf-readex-pro' 'ttf-rubik-vf' 'ttf-twemoji'
- # Hyprland dependencies
- 'hyprutils' 'hyprpicker' 'hyprlang' 'hyprland-qt-support' 'hyprland-guiutils' 'hyprcursor' 'hyprwayland-scanner' 'hyprland' 'xdg-desktop-portal-hyprland' 'wl-clipboard' 'hyprlock'
+ # Fhtc dependencies
+ 'fht-compositor-git' 'fht-share-picker-git' 'wl-clipboard'
# QT/KDE dependencies
'bluedevil' 'gnome-keyring' 'networkmanager' 'polkit-kde-agent' 'pcmanfm-qt' 'kwrite' "libnm" "gio-qt" "qt6-connectivity"
# Microtex
@@ -23,9 +23,9 @@ depends=(
# Portal
'xdg-desktop-portal'
# Python deps
- 'clang' 'uv' 'gtk4' 'libadwaita' 'libsoup3' 'libportal-gtk4' 'gobject-introspection' 'sassc' 'python-setproctitle' 'python-pywayland'
+ 'clang' 'uv' 'gtk4' 'libadwaita' 'libsoup3' 'libportal-gtk4' 'gobject-introspection' 'sassc' 'python-setproctitle' 'python-pywayland' 'python-toml'
# Screencast/Screenrecord
- 'hyprshot' 'ksnip' 'wf-recorder' 'slurp' 'grim' 'tesseract' 'tesseract-data-eng'
+ 'ksnip' 'wf-recorder' 'slurp' 'grim' 'tesseract' 'tesseract-data-eng'
# Tools
'kdialog' 'qt6-5compat' 'qt6-avif-image-plugin' 'qt6-base' 'qt6-declarative' 'qt6-imageformats' 'qt6-multimedia' 'qt6-positioning' 'qt6-quicktimeline' 'qt6-sensors' 'qt6-svg' 'qt6-tools' 'qt6-translations' 'qt6-virtualkeyboard' 'qt6-wayland' 'syntax-highlighting' 'upower' 'wtype' 'ydotool' 'fprintd' 'khal' 'vdirsyncer' 'python-aiohttp-oauthlib' 'swappy' 'hypnos' 'bluez-utils'
# Widgets
@@ -36,7 +36,6 @@ depends=(
"sleex-artworks"
)
optdepends=(
- "hyprwayland-scanner: Wayland protocol scanner for Hyprland"
"neofetch: Fancy system info in your terminal"
"firefox: Web browser"
"pipewire-pulse: PulseAudio replacement via PipeWire"
@@ -76,7 +75,4 @@ package() {
mkdir -p "$pkgdir/usr/share/wayland-sessions"
cp -r "$srcdir/share/wayland-sessions/"* "$pkgdir/usr/share/wayland-sessions/"
-
- mkdir -p "$pkgdir/usr/libalpm/hooks"
- cp -r "$srcdir/share/libalpm/hooks/"* "$pkgdir/usr/libalpm/hooks/"
}
diff --git a/README.md b/README.md
index 0bafbfa7..7688cd0c 100644
--- a/README.md
+++ b/README.md
@@ -2,41 +2,12 @@
-Sleex is the third desktop environement of AxOS. It is based on Hyprland with Quickshell.
+# [WIP] Sleex with the Fht compositor
-## Features
-- AI chat integration with external providers (Gemini, OpenAI...)
-- Smooth animations
-- Tiling window management for seamless multitasking
-- Adaptative color scheme based on the wallpaper
-- Ready to use
-- Multiple available built in tools
-- Looks good
+If you want to test this very very unstable branch, you can build and install the package:
-| |
-|------|
-|  |
-
-## Installation
-
-> [!IMPORTANT]
-> If you already have an hyprland config, this will erase it.
-
-If you are using AxOS, you can simply use epsilon:
-```
-epsi i sleex
+```bash
+makepkg -si
```
-### Other arch-based distributions
-
-Sleex is available on the AUR as `sleex-git`. [See the page](https://aur.archlinux.org/packages/sleex-git).
-You can install it using an aur helper such as `yay` or `paru`.
-
-Then, you can start sleex by selecting the sleex session on your greeter
-
-## License
-Sleex is licensed under the GNU General Public License v3.0
-
-## Special thanks
-- [@end-4](https://github.com/end-4/): Great inspiration | Sleex is based on his work
-- [@xHyperVoid](https://github.com/xHyperVoid): Designer | Made the logo and the wallpapers
+[Fht compositor](https://nferhat.github.io/fht-compositor/) is a new Wayland compositor written in Rust. It is designed to be fast, lightweight, and easy to use. It is also highly customizable, allowing users to create their own unique desktop experience.
\ No newline at end of file
diff --git a/sleex-user-config/PKGBUILD b/sleex-user-config/PKGBUILD
index feabad3b..b23824d4 100644
--- a/sleex-user-config/PKGBUILD
+++ b/sleex-user-config/PKGBUILD
@@ -5,10 +5,6 @@ pkgdesc="User configuration for Sleex desktop environment"
arch=("x86_64")
depend=("sleex")
-provides=("axskel-hypr")
-replaces=("axskel-hypr")
-conflicts=("axskel-hypr")
-
package(){
mkdir -p ${pkgdir}/etc/skel/
cp -r ${srcdir}/.config/ ${pkgdir}/etc/skel/
diff --git a/sleex-user-config/README.md b/sleex-user-config/README.md
deleted file mode 100644
index 93a38383..00000000
--- a/sleex-user-config/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# axskel-hypr
-
-`axskel-hypr` is a custom desktop environment setup, featuring the **Hyprland** window manager and pre-configured settings. This package includes configurations for **Hyprland**, alongside several enhancements for usability, aesthetics, and functionality.
-
-
-
-After installation, the package deploys configuration files to `/etc/skel`. Customize these files as needed to tailor your desktop environment.
-
diff --git a/sleex-user-config/src/.config/fht/apps.toml b/sleex-user-config/src/.config/fht/apps.toml
new file mode 100644
index 00000000..bc76b458
--- /dev/null
+++ b/sleex-user-config/src/.config/fht/apps.toml
@@ -0,0 +1,16 @@
+# Terminal
+Super-Return = { action = "run-command", arg = "foot" }
+# Code
+Super-c = { action = "run-command", arg = "code --enable-features=UseOzonePlatform --ozone-platform=wayland" }
+# Browser
+Super-w = { action = "run-command", arg = "firefox" }
+# File manager
+Super-e = { action = "run-command", arg = "pcmanfm-qt" }
+# Text editor
+Super-x = { action = "run-command", arg = "kwrite" }
+# Sleex settings
+Super-i = { action = "run-command", arg = "qs -p /usr/share/sleex/settings.qml" }
+# Volume mixer
+Ctrl-Super-v = { action = "run-command", arg = "pavucontrol" }
+# System monitor
+Ctrl-Shift-Esc = { action = "run-command", arg = "missioncenter" }
\ No newline at end of file
diff --git a/sleex-user-config/src/.config/hypr/custom/env.conf b/sleex-user-config/src/.config/fht/custom/env.toml
similarity index 58%
rename from sleex-user-config/src/.config/hypr/custom/env.conf
rename to sleex-user-config/src/.config/fht/custom/env.toml
index bc21f238..b6a2a503 100644
--- a/sleex-user-config/src/.config/hypr/custom/env.conf
+++ b/sleex-user-config/src/.config/fht/custom/env.toml
@@ -1,3 +1,3 @@
# This is a part of the Sleex's compositor user configuration
# You can put extra environment variables here
-# https://wiki.hyprland.org/Configuring/Environment-variables/
+# https://nferhat.github.io/fht-compositor/configuration/introduction.html#env
\ No newline at end of file
diff --git a/sleex-user-config/src/.config/fht/custom/execs.toml b/sleex-user-config/src/.config/fht/custom/execs.toml
new file mode 100644
index 00000000..b452a4c0
--- /dev/null
+++ b/sleex-user-config/src/.config/fht/custom/execs.toml
@@ -0,0 +1,3 @@
+# This is a part of the Sleex's compositor user configuration
+# You can put extra executables here
+# https://nferhat.github.io/fht-compositor/configuration/introduction.html#autostart
\ No newline at end of file
diff --git a/sleex-user-config/src/.config/hypr/custom/general.conf b/sleex-user-config/src/.config/fht/custom/general.toml
similarity index 100%
rename from sleex-user-config/src/.config/hypr/custom/general.conf
rename to sleex-user-config/src/.config/fht/custom/general.toml
diff --git a/sleex-user-config/src/.config/hypr/custom/keybinds.conf b/sleex-user-config/src/.config/fht/custom/keybinds.toml
similarity index 58%
rename from sleex-user-config/src/.config/hypr/custom/keybinds.conf
rename to sleex-user-config/src/.config/fht/custom/keybinds.toml
index 107ff446..affa2121 100644
--- a/sleex-user-config/src/.config/hypr/custom/keybinds.conf
+++ b/sleex-user-config/src/.config/fht/custom/keybinds.toml
@@ -1,4 +1,4 @@
# This is a part of the Sleex's compositor user configuration
# You can put your preferred keybinds here
-# https://wiki.hyprland.org/Configuring/Binds/
+# https://nferhat.github.io/fht-compositor/configuration/keybindings.html
diff --git a/sleex-user-config/src/.config/fht/custom/rules.toml b/sleex-user-config/src/.config/fht/custom/rules.toml
new file mode 100644
index 00000000..b65778c7
--- /dev/null
+++ b/sleex-user-config/src/.config/fht/custom/rules.toml
@@ -0,0 +1,4 @@
+# This is a part of the Sleex's compositor user configuration
+# You can put custom rules here
+# https://nferhat.github.io/fht-compositor/configuration/window-rules.html
+# https://nferhat.github.io/fht-compositor/configuration/layer-rules.html
\ No newline at end of file
diff --git a/sleex-user-config/src/.config/fht/monitors.toml b/sleex-user-config/src/.config/fht/monitors.toml
new file mode 100644
index 00000000..78917d63
--- /dev/null
+++ b/sleex-user-config/src/.config/fht/monitors.toml
@@ -0,0 +1,9 @@
+# Output configuration (examples)
+# https://nferhat.github.io/fht-compositor/configuration/outputs.html
+
+# [outputs.HDMI-A-1]
+# mode = "1920x1080@144"
+
+# [outputs.DP-1]
+# mode = "1920x1080@75"
+# position.x = 1920
\ No newline at end of file
diff --git a/sleex-user-config/src/.config/fish/auto-Hypr.fish b/sleex-user-config/src/.config/fish/auto-Hypr.fish
deleted file mode 100644
index c3c1890f..00000000
--- a/sleex-user-config/src/.config/fish/auto-Hypr.fish
+++ /dev/null
@@ -1,5 +0,0 @@
-# Auto start Hyprland on tty1
-if test -z "$DISPLAY" ;and test "$XDG_VTNR" -eq 1
- mkdir -p ~/.cache
- exec Hyprland > ~/.cache/hyprland.log ^&1
-end
diff --git a/sleex-user-config/src/.config/hypr/apps.conf b/sleex-user-config/src/.config/hypr/apps.conf
deleted file mode 100644
index 716a58d1..00000000
--- a/sleex-user-config/src/.config/hypr/apps.conf
+++ /dev/null
@@ -1,15 +0,0 @@
-# This is a part of the Sleex's compositor user configuration
-# This file is permanent, you can edit it safely to add your preferred app keybinds
-# Be just careful to not duplicate existing keybinds, including those in other config files
-
-#!
-##! Apps
-bind = Super, Return, exec, # Launch foot (terminal)
-bind = Super, C, exec, code --password-store=gnome --enable-features=UseOzonePlatform --ozone-platform=wayland # Launch VSCode (editor)
-bind = Super, E, exec, pcmanfm-qt # Launch file manager
-bind = Super, W, exec, firefox # launch Firefox
-bind = Super, X, exec, kwrite # Launch Text Editor
-bind = Super, I, exec, qs -p /usr/share/sleex/settings.qml # Launch Sleex Settings
-bind = Ctrl+Super, V, exec, pavucontrol # Launch pavucontrol (volume mixer)
-bind = Ctrl+Super+Shift, V, exec, easyeffects # Launch EasyEffects (equalizer & other audio effects)
-bind = Ctrl+Shift, Escape, exec, missioncenter # Launch System monitor
diff --git a/sleex-user-config/src/.config/hypr/custom/execs.conf b/sleex-user-config/src/.config/hypr/custom/execs.conf
deleted file mode 100644
index 5969a35c..00000000
--- a/sleex-user-config/src/.config/hypr/custom/execs.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-# This is a part of the Sleex's compositor user configuration
-# You can make apps auto-start here
-# Relevant Hyprland wiki section: https://wiki.hyprland.org/Configuring/Keywords/#executing
diff --git a/sleex-user-config/src/.config/hypr/custom/rules.conf b/sleex-user-config/src/.config/hypr/custom/rules.conf
deleted file mode 100644
index c4bda6b4..00000000
--- a/sleex-user-config/src/.config/hypr/custom/rules.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-# This is a part of the Sleex's compositor user configuration
-# You can put custom rules here
-# Window/layer rules: https://wiki.hyprland.org/Configuring/Window-Rules/
-# Workspace rules: https://wiki.hyprland.org/Configuring/Workspace-Rules/
-
diff --git a/sleex-user-config/src/.config/hypr/custom/scripts/__restore_video_wallpaper.sh b/sleex-user-config/src/.config/hypr/custom/scripts/__restore_video_wallpaper.sh
deleted file mode 100644
index ea4b4fbd..00000000
--- a/sleex-user-config/src/.config/hypr/custom/scripts/__restore_video_wallpaper.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-# The content of this script will be generated by switchwall.sh - Don't modify it by yourself.
diff --git a/sleex-user-config/src/.config/hypr/hypridle.conf b/sleex-user-config/src/.config/hypr/hypridle.conf
deleted file mode 100755
index 98f11f27..00000000
--- a/sleex-user-config/src/.config/hypr/hypridle.conf
+++ /dev/null
@@ -1,36 +0,0 @@
-general {
- lock_cmd = qs -p /usr/share/sleex ipc call lock lock
- before_sleep_cmd = qs -p /usr/share/sleex ipc call lock lock # lock before suspend.
- after_sleep_cmd = hyprctl dispatch dpms on # to avoid having to press a key twice to turn on the display.
-}
-
-listener {
- timeout = 120 # 2min.
- on-timeout = brightnessctl -s set 10 # set monitor backlight to minimum, avoid 0 on OLED monitor.
- on-resume = brightnessctl -r # monitor backlight restore.
-}
-
-# turn off keyboard backlight if present (auto-detect device)
-listener {
- timeout = 300 # 5min.
- on-timeout = KBD=$(ls /sys/class/leds | grep -i kbd_backlight | head -n1) && \
- [ -n "$KBD" ] && brightnessctl -sd "$KBD" set 0
- on-resume = KBD=$(ls /sys/class/leds | grep -i kbd_backlight | head -n1) && \
- [ -n "$KBD" ] && brightnessctl -rd "$KBD"
-}
-
-listener {
- timeout = 120 # 2min.
- on-timeout = qs -p /usr/share/sleex ipc call lock lock # lock screen when timeout has passed
-}
-
-# listener {
-# timeout = 220 # 3min, 40sec
-# on-timeout = hyprctl dispatch dpms off # screen off when timeout has passed
-# on-resume = hyprctl dispatch dpms on && brightnessctl -r # screen on when activity is detected after timeout has fired.
-# }
-
-listener {
- timeout = 600 # 10min
- on-timeout = systemctl suspend # suspend pc
-}
diff --git a/sleex-user-config/src/.config/hypr/monitors.conf b/sleex-user-config/src/.config/hypr/monitors.conf
deleted file mode 100644
index a9851bb1..00000000
--- a/sleex-user-config/src/.config/hypr/monitors.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-# Generated by nwg-displays on 2025-06-09 at 12:05:41. Do not edit manually, use `nwg-displays` instead.
-
-monitor=,preferred,auto,1
diff --git a/sleex-user-config/src/.config/hypr/workspaces.conf b/sleex-user-config/src/.config/hypr/workspaces.conf
deleted file mode 100644
index e69de29b..00000000
diff --git a/sleex-user-config/src/.config/matugen/config.toml b/sleex-user-config/src/.config/matugen/config.toml
index 0f081295..eec40b84 100644
--- a/sleex-user-config/src/.config/matugen/config.toml
+++ b/sleex-user-config/src/.config/matugen/config.toml
@@ -5,14 +5,6 @@ version_check = false
input_path = '~/.config/matugen/templates/colors.json'
output_path = '~/.local/state/sleex/user/generated/colors.json'
-[templates.hyprland]
-input_path = '~/.config/matugen/templates/hyprland/colors.conf'
-output_path = '~/.config/hypr/hyprland/colors.conf'
-
-[templates.hyprlock]
-input_path = '~/.config/matugen/templates/hyprland/hyprlock.conf'
-output_path = '~/.config/hypr/hyprlock.conf'
-
[templates.fuzzel]
input_path = '~/.config/matugen/templates/fuzzel/fuzzel_theme.ini'
output_path = '~/.config/fuzzel/fuzzel_theme.ini'
diff --git a/sleex-user-config/src/.config/matugen/templates/hyprland/colors.conf b/sleex-user-config/src/.config/matugen/templates/hyprland/colors.conf
deleted file mode 100644
index 67fdaea7..00000000
--- a/sleex-user-config/src/.config/matugen/templates/hyprland/colors.conf
+++ /dev/null
@@ -1,32 +0,0 @@
-general {
- col.active_border = rgba({{colors.on_surface.default.hex_stripped}}39)
- col.inactive_border = rgba({{colors.outline.default.hex_stripped}}30)
-}
-
-misc {
- background_color = rgba({{colors.surface.dark.hex_stripped}}FF)
-}
-
-plugin {
- hyprbars {
- # Honestly idk if it works like css, but well, why not
- bar_text_font = Rubik, Geist, AR One Sans, Reddit Sans, Inter, Roboto, Ubuntu, Noto Sans, sans-serif
- bar_height = 30
- bar_padding = 10
- bar_button_padding = 5
- bar_precedence_over_border = true
- bar_part_of_window = true
-
- bar_color = rgba({{colors.background.default.hex_stripped}}FF)
- col.text = rgba({{colors.on_background.default.hex_stripped}}FF)
-
-
- # example buttons (R -> L)
- # hyprbars-button = color, size, on-click
- hyprbars-button = rgb({{colors.on_background.default.hex_stripped}}), 13, , hyprctl dispatch killactive
- hyprbars-button = rgb({{colors.on_background.default.hex_stripped}}), 13, , hyprctl dispatch fullscreen 1
- hyprbars-button = rgb({{colors.on_background.default.hex_stripped}}), 13, , hyprctl dispatch movetoworkspacesilent special
- }
-}
-
-windowrulev2 = bordercolor rgba({{colors.primary.default.hex_stripped}}AA) rgba({{colors.primary.default.hex_stripped}}77),pinned:1
diff --git a/sleex-user-config/src/.config/matugen/templates/hyprland/hyprlock.conf b/sleex-user-config/src/.config/matugen/templates/hyprland/hyprlock.conf
deleted file mode 100644
index eab16719..00000000
--- a/sleex-user-config/src/.config/matugen/templates/hyprland/hyprlock.conf
+++ /dev/null
@@ -1,93 +0,0 @@
-$text_color = rgba({{colors.primary_fixed.default.hex_stripped}}FF)
-$entry_background_color = rgba({{colors.on_primary_fixed.default.hex_stripped}}11)
-$entry_border_color = rgba({{colors.outline.default.hex_stripped}}55)
-$entry_color = rgba({{colors.primary_fixed.default.hex_stripped}}FF)
-$font_family = Rubik Light
-$font_family_clock = Rubik Light
-$font_material_symbols = Material Symbols Rounded
-
-background {
- color = rgba(181818FF)
-
- path = {{image}}
- blur_size = 15
- blur_passes = 4
- brightness = 0.33
-}
-input-field {
- monitor =
- size = 250, 50
- outline_thickness = 2
- dots_size = 0.1
- dots_spacing = 0.3
- outer_color = $entry_border_color
- inner_color = $entry_background_color
- font_color = $entry_color
- fade_on_empty = true
-
- position = 0, 20
- halign = center
- valign = center
-}
-
-label { # Caps Lock Warning
- monitor =
- text = cmd[update:250] ${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hyprlock/check-capslock.sh
- color = $text_color
- font_size = 13
- font_family = $font_family
- position = 0, -25
- halign = center
- valign = center
-}
-
-
-label { # Clock
- monitor =
- text = $TIME
- color = $text_color
- font_size = 65
- font_family = $font_family_clock
-
- position = 0, 300
- halign = center
- valign = center
-}
-label { # Date
- monitor =
- text = cmd[update:5000] date +"%A, %B %d"
- color = $text_color
- font_size = 17
- font_family = $font_family
-
- position = 0, 240
- halign = center
- valign = center
-}
-
-label { # User
- monitor =
- text = $USER
- color = $text_color
- outline_thickness = 2
- dots_size = 0.2 # Scale of input-field height, 0.2 - 0.8
- dots_spacing = 0.2 # Scale of dots' absolute size, 0.0 - 1.0
- dots_center = true
- font_size = 20
- font_family = $font_family
- position = 0, 50
- halign = center
- valign = bottom
-}
-
-label { # Status
- monitor =
- text = cmd[update:5000] ${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hyprlock/status.sh
- color = $text_color
- font_size = 14
- font_family = $font_family
-
- position = 30, -30
- halign = left
- valign = top
-}
\ No newline at end of file
diff --git a/sleex-user-config/src/.config/xdg-desktop-portal/hyprland-portals.conf b/sleex-user-config/src/.config/xdg-desktop-portal/hyprland-portals.conf
deleted file mode 100644
index 62f3d568..00000000
--- a/sleex-user-config/src/.config/xdg-desktop-portal/hyprland-portals.conf
+++ /dev/null
@@ -1,3 +0,0 @@
-[preferred]
-default = hyprland;gtk
-org.freedesktop.impl.portal.FileChooser = kde
diff --git a/sleex-user-config/src/.config/zshrc.d/auto-Hypr.sh b/sleex-user-config/src/.config/zshrc.d/auto-Hypr.sh
deleted file mode 100644
index 55eca5e0..00000000
--- a/sleex-user-config/src/.config/zshrc.d/auto-Hypr.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-# Auto start Hyprland on tty1
-if [ -z "$DISPLAY" ] && [ "$XDG_VTNR" -eq 1 ]; then
- mkdir -p ~/.cache
- exec Hyprland > ~/.cache/hyprland.log 2>&1
-fi
diff --git a/src/bin/rubyshot b/src/bin/rubyshot
deleted file mode 100755
index 8431bd69..00000000
--- a/src/bin/rubyshot
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/bash
-
-WORKSPACES="$(hyprctl monitors -j | jq -r 'map(.activeWorkspace.id)')"
-WINDOWS="$(hyprctl clients -j | jq -r --argjson workspaces "$WORKSPACES" 'map(select([.workspace.id] | inside($workspaces)))' )"
-GEOM=$(echo "$WINDOWS" | jq -r '.[] | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | slurp -f '%x %y %w %h')
-wayshot -s "$GEOM" --stdout ${#:+"$@"}
\ No newline at end of file
diff --git a/src/bin/sleex b/src/bin/sleex
index ee2218d0..3640c400 100755
--- a/src/bin/sleex
+++ b/src/bin/sleex
@@ -1,10 +1,9 @@
#!/bin/sh
export XDG_CURRENT_DESKTOP="Sleex"
-export HYPRLAND_INSTANCE_SIGNATURE=""
systemctl --user import-environment XDG_CURRENT_DESKTOP
# Check if config dirs/files exist, if not copy them from /etc/skel
-for dir in fish hypr anyrun fuzzel Kvantum matugen vdirsyncer kde-material-you-colors mpv wlogout qt5ct xdg-desktop-portal fontconfig khal qt6ct zshrc.d foot; do
+for dir in fish fht anyrun fuzzel Kvantum matugen vdirsyncer kde-material-you-colors mpv wlogout qt5ct xdg-desktop-portal fontconfig khal qt6ct zshrc.d foot; do
if [ ! -d ~/.config/$dir ]; then
cp -r /etc/skel/.config/$dir ~/.config/$dir
chown -R $USER:$USER ~/.config/$dir
@@ -12,9 +11,9 @@ for dir in fish hypr anyrun fuzzel Kvantum matugen vdirsyncer kde-material-you-c
done
# Create Sleex custom files
-if [ ! -d ~/.config/hypr/custom ]; then
- mkdir -p ~/.config/hypr/custom
- cp -r /etc/skel/.config/hypr/custom/* ~/.config/hypr/custom
+if [ ! -d ~/.config/fht/custom ]; then
+ mkdir -p ~/.config/fht/custom
+ cp -r /etc/skel/.config/fht/custom/* ~/.config/fht/custom
fi
if [ ! -d ~/.local/share/khal/calendars/default ]; then
@@ -32,17 +31,19 @@ done
# First run setup
if [ ! -f ~/.local/state/sleex/user/first_run.txt ]; then
# Sets up the user's environment to use Sleex.
- CONFIG_FILE="$HOME/.config/hypr/custom/general.conf"
+ CONFIG_FILE="$HOME/.config/fht/custom/general.conf"
VC_KEYMAP=$(grep -i KEYMAP /etc/vconsole.conf | cut -d= -f2 | tr -d '"')
LAYOUT=$(echo "$VC_KEYMAP" | awk '{print tolower($0)}')
echo "Setting keyboard layout to '$LAYOUT' in $CONFIG_FILE."
- if grep -q "^input:kb_layout" "$CONFIG_FILE"; then
- sed -i "s/^input:kb_layout.*/input:kb_layout = $LAYOUT/" "$CONFIG_FILE"
+ if grep -q "^\[input\.keyboard\]" "$CONFIG_FILE"; then
+ sed -i "/^\[input\.keyboard\]/,/^\[/ s/^layout = .*/layout = \"$LAYOUT\"/" "$CONFIG_FILE"
else
- echo "input:kb_layout = $LAYOUT" >> "$CONFIG_FILE"
+ echo "" >> "$CONFIG_FILE"
+ echo "[input.keyboard]" >> "$CONFIG_FILE"
+ echo "layout = \"$LAYOUT\"" >> "$CONFIG_FILE"
fi
if lsmod | grep -q '^nvidia'; then
@@ -50,7 +51,7 @@ if [ ! -f ~/.local/state/sleex/user/first_run.txt ]; then
{
echo "env = LIBVA_DRIVER_NAME,nvidia"
echo "env = __GLX_VENDOR_LIBRARY_NAME,nvidia"
- } >> ~/.config/hypr/custom/env.conf
+ } >> ~/.config/fht/custom/env.conf
fi
fi
@@ -60,5 +61,5 @@ if [ ! -f ~/.config/user-dirs.dirs ]; then
fi
-# Start Hyprland with the Sleex config
-start-hyprland -- --config /etc/sleex/hyprland.conf > /dev/null 2>&1
+# Start with the Sleex config
+fht-compositor -c /etc/sleex/compositor.toml > /dev/null 2>&1
\ No newline at end of file
diff --git a/src/etc/sleex/compositor.toml b/src/etc/sleex/compositor.toml
new file mode 100644
index 00000000..457348a5
--- /dev/null
+++ b/src/etc/sleex/compositor.toml
@@ -0,0 +1,10 @@
+imports = [
+ "/etc/sleex/compositor/general.toml",
+ "/etc/sleex/compositor/keybinds.toml",
+ "/etc/sleex/compositor/rules.toml",
+ "/etc/sleex/compositor/execs.toml",
+ "/etc/sleex/compositor/envs.toml",
+
+ "~/.config/fht/monitors.toml",
+ "~/.config/fht/apps.toml",
+]
diff --git a/src/etc/sleex/compositor/envs.toml b/src/etc/sleex/compositor/envs.toml
new file mode 100644
index 00000000..bf8bf27e
--- /dev/null
+++ b/src/etc/sleex/compositor/envs.toml
@@ -0,0 +1,19 @@
+[env]
+# Themes
+QT_QPA_PLATFORM = "wayland"
+QT_QPA_PLATFORMTHEME = "kde"
+QT_STYLE_OVERRIDE = "kvantum"
+WLR_NO_HARDWARE_CURSORS = "1"
+
+XDG_SESSION_TYPE = "wayland"
+GDK_BACKEND = "wayland"
+SDL_VIDEODRIVER = "wayland"
+CLUTTER_BACKEND = "wayland"
+MOZ_ENABLE_WAYLAND = "1"
+
+# Screen tearing
+# WLR_DRM_NO_ATOMIC, 1
+
+# Others
+
+SLEEX_VIRTUAL_ENV = "~/.local/state/sleex/.venv"
diff --git a/src/etc/sleex/compositor/execs.toml b/src/etc/sleex/compositor/execs.toml
new file mode 100644
index 00000000..9cc51af4
--- /dev/null
+++ b/src/etc/sleex/compositor/execs.toml
@@ -0,0 +1,12 @@
+# Autostart programs.
+autostart = [
+ #"swww-daemon",
+ "qs -p /usr/share/sleex/",
+ "/usr/lib/geoclue-2.0/demos/agent & gammastep",
+ "gnome-keyring-daemon --start --components=secrets",
+ "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 || /usr/libexec/polkit-gnome-authentication-agent-1",
+ "dbus-update-activation-environment --all",
+ "sleep 1 && dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP",
+ "wl-paste --type text --watch cliphist store",
+ "wl-paste --type image --watch cliphist store"
+]
diff --git a/src/etc/sleex/compositor/general.toml b/src/etc/sleex/compositor/general.toml
new file mode 100644
index 00000000..d7a189ed
--- /dev/null
+++ b/src/etc/sleex/compositor/general.toml
@@ -0,0 +1,64 @@
+# You can find everything you want by reading the wiki!
+# https://nferhat.github.io/fht-compositor
+
+# Input configuration.
+# SEE: man 7 xkeyboard-config
+[input.keyboard]
+layout = "us"
+rules = ""
+repeat-rate = 50
+repeat-delay = 250
+options = "numpad:mac" # This makes the numpad keys work as expected when numlock is off.
+
+[input.touchpad]
+natural-scrolling = true
+
+[cursor]
+name = "Bibata-Modern-Classic"
+size = 24
+
+# ---------------------------------------------------------
+
+# General compositor behaviour
+[general]
+cursor-warps = true # Move the cursor to newly opened windows
+focus-new-windows = true # Give newly inserted windows keyboard focus
+focus-follows-mouse = true # When true, hovering over a window will focus it without clicking
+
+layouts = ["tile", "floating"]
+nmaster = 1 # Only one master client
+mwfact = 0.5 # And share the screen size between the master and slave equally
+inner-gaps = 7 # gaps between tiles
+outer-gaps = 7 # gaps around the screen
+
+# ---------------------------------------------------------
+
+# Decorations: I.E the pretty
+# All the decorations can be overriden on a per-window basis using window rules.
+[decorations]
+decoration-mode = "force-server-side"
+
+# Border around windows!
+[decorations.border]
+thickness = 0
+radius = 10
+focused-color = "#ffffff"
+normal-color = "#222230"
+
+# Shadows! Very useful for floating windows.
+[decorations.shadow]
+color = "black" # by default its rgba(black, 0.5)
+floating-only = false # Up to you, I don't judge!
+
+# Blur, because yes!
+[decorations.blur]
+disable = false
+passes = 4
+radius = 5
+noise = 0.05
+
+# ---------------------------------------------------------
+
+# Animations. Makes everything smooooooooooooooth!
+[animations]
+disable = false
diff --git a/src/etc/sleex/compositor/keybinds.toml b/src/etc/sleex/compositor/keybinds.toml
new file mode 100644
index 00000000..ca356d53
--- /dev/null
+++ b/src/etc/sleex/compositor/keybinds.toml
@@ -0,0 +1,106 @@
+# Lines ending with `# [hidden]` won't be shown on cheatsheet
+# Lines starting with #! are section headings
+
+[keybinds]
+
+#!
+##! Basic
+Super-Shift-s = { action = "run-command", arg = 'grim -g "$(slurp -o)" - | wl-copy --type image/png' } # Screenshot a selected area and copy to clipboard
+
+#!
+##! Multimedia
+XF86AudioRaiseVolume = { action = "run-command", arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+", allow-while-locked = true } # Increase volume by 5%
+XF86AudioLowerVolume = { action = "run-command", arg = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%-", allow-while-locked = true } # Decrease volume by 5%
+XF86AudioMute = { action = "run-command", arg = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle", allow-while-locked = true } # Toggle mute
+
+#!
+##! Focus and window management
+Super-Right = "focus-next-window" # Focus the next window in the current workspace
+Super-Left = "focus-previous-window" # Focus the previous window in the current workspace
+
+Super-m = "maximize-focused-window" # Toggle maximize the focused window
+Super-f = "fullscreen-focused-window" # Toggle fullscreen the focused window
+Super-q = "close-focused-window" # Close the focused window
+Super-Ctrl-Space = "float-focused-window" # Toggle floating the focused window
+Super-Shift-Right = "swap-with-next-window" # Swap the focused window with the next window in the current workspace
+Super-Shift-Left = "swap-with-previous-window" # Swap the focused window with the previous window in the current workspace
+# Super-Ctrl-p = "pin-focused-window"
+
+# Transient layout changes.
+Super-Space = "select-next-layout" # Cycle to the next layout in the current workspace
+Super-Ctrl-Left = { action = "change-mwfact", arg = -0.1 } # Decrease the master area size by 10%
+Super-Ctrl-Right = { action = "change-mwfact", arg = 0.1 } # Increase the master area size by 10%
+Super-Shift-h = { action = "change-nmaster", arg = 1 } # Increase the number of windows in the master area by 1
+Super-Shift-l = { action = "change-nmaster", arg = -1 } # Decrease the number of windows in the master area by 1
+Super-i = { action = "change-window-proportion", arg = 0.5 } # Increase the size of the focused window
+Super-o = { action = "change-window-proportion", arg = -0.5 } # Decrease the size of the focused window
+
+# Workspaces
+#/# Super-[1-9] = { action = "focus-workspace", arg = N } # Focus workspace N (0-indexed)
+Super-ampersand = { action = "focus-workspace", arg = 0 } # [hidden]
+Super-eacute = { action = "focus-workspace", arg = 1 } # [hidden]
+Super-quotedbl = { action = "focus-workspace", arg = 2 } # [hidden]
+Super-apostrophe = { action = "focus-workspace", arg = 3 } # [hidden]
+Super-parenleft = { action = "focus-workspace", arg = 4 } # [hidden]
+Super-minus = { action = "focus-workspace", arg = 5 } # [hidden]
+Super-egrave = { action = "focus-workspace", arg = 6 } # [hidden]
+Super-underscore = { action = "focus-workspace", arg = 7 } # [hidden]
+Super-agrave = { action = "focus-workspace", arg = 8 } # [hidden]
+# For QWERTY users
+Super-1 = { action = "focus-workspace", arg = 0 } # [hidden]
+Super-2 = { action = "focus-workspace", arg = 1 } # [hidden]
+Super-3 = { action = "focus-workspace", arg = 2 } # [hidden]
+Super-4 = { action = "focus-workspace", arg = 3 } # [hidden]
+Super-5 = { action = "focus-workspace", arg = 4 } # [hidden]
+Super-6 = { action = "focus-workspace", arg = 5 } # [hidden]
+Super-7 = { action = "focus-workspace", arg = 6 } # [hidden]
+Super-8 = { action = "focus-workspace", arg = 7 } # [hidden]
+Super-9 = { action = "focus-workspace", arg = 8 } # [hidden]
+
+# Sending windows to workspaces
+#/#! Super-Shift-[1-9] = { action = "send-to-workspace", arg = N } # Send the focused window to workspace N (0-indexed)
+Super-Ctrl-ampersand = { action = "send-to-workspace", arg = 0 } # [hidden]
+Super-Ctrl-eacute = { action = "send-to-workspace", arg = 1 } # [hidden]
+Super-Ctrl-quotedbl = { action = "send-to-workspace", arg = 2 } # [hidden]
+Super-Ctrl-apostrophe = { action = "send-to-workspace", arg = 3 } # [hidden]
+Super-Ctrl-parenleft = { action = "send-to-workspace", arg = 4 } # [hidden]
+Super-Ctrl-minus = { action = "send-to-workspace", arg = 5 } # [hidden]
+Super-Ctrl-egrave = { action = "send-to-workspace", arg = 6 } # [hidden]
+Super-Ctrl-underscore = { action = "send-to-workspace", arg = 7 } # [hidden]
+Super-Ctrl-agrave = { action = "send-to-workspace", arg = 8 } # [hidden]
+# For QWERTY users
+Super-Ctrl-1 = { action = "send-to-workspace", arg = 0 } # [hidden]
+Super-Ctrl-2 = { action = "send-to-workspace", arg = 1 } # [hidden]
+Super-Ctrl-3 = { action = "send-to-workspace", arg = 2 } # [hidden]
+Super-Ctrl-4 = { action = "send-to-workspace", arg = 3 } # [hidden]
+Super-Ctrl-5 = { action = "send-to-workspace", arg = 4 } # [hidden]
+Super-Ctrl-6 = { action = "send-to-workspace", arg = 5 } # [hidden]
+Super-Ctrl-7 = { action = "send-to-workspace", arg = 6 } # [hidden]
+Super-Ctrl-8 = { action = "send-to-workspace", arg = 7 } # [hidden]
+Super-Ctrl-9 = { action = "send-to-workspace", arg = 8 } # [hidden]
+
+#!
+##! Shell
+Super-d = { action = "global-shortcut", arg = "quickshell:dashboardToggle" } # Toggle the dashboard
+Super-Super_L = { action = "global-shortcut", arg = "quickshell:overviewToggleReleaseInterrupt" } # Toggle the overview
+Super-v = { action = "global-shortcut", arg = "quickshell:overviewClipboardToggle" } # Toggle the clipboard overview
+Super-semicolon = { action = "global-shortcut", arg = "quickshell:overviewEmojiToggle" } # Toggle the emoji overview
+Super-f1 = { action = "global-shortcut", arg = "quickshell:cheatsheetToggle" } # Toggle the cheatsheet
+Ctrl-Alt-Delete = { action = "global-shortcut", arg = "quickshell:sessionToggle" } # Toggle the session overview
+Super-t = { action = "global-shortcut", arg = "quickshell:wppselectorToggle" } # Toggle the wpp selector
+Super-l = { action = "global-shortcut", arg = "quickshell:lockScreen" } # Lock the screen
+
+# ---------------------------------------------------------
+
+## [hidden]
+[mousebinds]
+Super-Left = "swap-tile"
+Super-Right = "resize-tile"
+Super-scrollup = "focus-next-workspace"
+Super-scrolldown = "focus-previous-workspace"
+
+[gesturebinds]
+focus-next-workspace = { fingers = 4, direction = "left" }
+focus-previous-workspace = { fingers = 4, direction = "right" }
+fullscreen-focused-window = { fingers = 4, direction = "up" }
+float-focused-window = { fingers = 4, direction = "down" }
diff --git a/src/etc/sleex/compositor/rules.toml b/src/etc/sleex/compositor/rules.toml
new file mode 100644
index 00000000..38feb13e
--- /dev/null
+++ b/src/etc/sleex/compositor/rules.toml
@@ -0,0 +1,52 @@
+# Window rules
+[[rules]]
+match-app-id = [
+ "blueberry.py",
+ "com.axos-project.axinstall",
+ "steam",
+ "org.pulseaudio.pavucontrol",
+ "cheese",
+ "timeshift-gtk",
+ "org.gnome.PowerStats",
+ "lstopo",
+ "org.gnome.ColorProfileViewer",
+ "qt5ct",
+ "org.kde.plasmawindowed",
+ "kcm.networkmanagement",
+ "gcr-prompter"
+ ]
+match-title = [
+ "Open File",
+ "Select a File",
+ "Choose wallpaper",
+ "Open Folder",
+ "Save File",
+ "Save As",
+ "Library",
+ "File Upload",
+ "Sleex Settings",
+ "Calculator",
+ "BoxBuddy",
+ "Popsicle",
+ "Trayscale",
+ "Choose Files",
+ "Save Image",
+ "Save File"
+]
+floating = true
+centered = true
+
+[[rules]]
+is-floating = true
+ontop = true
+
+[[rules]]
+match-app-id = ["org.gnome.Calculator"]
+# pinned = true
+floating = true
+centered = true
+
+# Layer-shell rules
+#[[layer-rules]]
+#match-namespace = ["quickshell:*"]
+#blur = { disable = false, noise = 0, passes = 4, radius = 1 }
diff --git a/src/etc/sleex/hyprland.conf b/src/etc/sleex/hyprland.conf
deleted file mode 100644
index ef63b2f2..00000000
--- a/src/etc/sleex/hyprland.conf
+++ /dev/null
@@ -1,23 +0,0 @@
-# This file sources other files in `hyprland` and `custom` folders
-# You wanna add your stuff in file in `custom`
-exec = hyprctl dispatch submap global
-submap = global
-# Defaults
-source=/etc/sleex/hyprland/env.conf
-source=/etc/sleex/hyprland/execs.conf
-source=/etc/sleex/hyprland/general.conf
-source=/etc/sleex/hyprland/rules.conf
-source=/etc/sleex/hyprland/colors.conf
-source=/etc/sleex/hyprland/keybinds.conf
-
-# Custom
-source=~/.config/hypr/custom/env.conf
-source=~/.config/hypr/custom/execs.conf
-source=~/.config/hypr/custom/general.conf
-source=~/.config/hypr/custom/rules.conf
-source=~/.config/hypr/custom/keybinds.conf
-
-source=~/.config/hypr/monitors.conf
-
-# Applications bindings
-source=~/.config/hypr/apps.conf
\ No newline at end of file
diff --git a/src/etc/sleex/hyprland/colors.conf b/src/etc/sleex/hyprland/colors.conf
deleted file mode 100644
index eb97b9e5..00000000
--- a/src/etc/sleex/hyprland/colors.conf
+++ /dev/null
@@ -1,32 +0,0 @@
-general {
- col.active_border = rgba(dfe3e739)
- col.inactive_border = rgba(8b919830)
-}
-
-misc {
- background_color = rgba(101417FF)
-}
-
-plugin {
- hyprbars {
- # Honestly idk if it works like css, but well, why not
- bar_text_font = Rubik, Geist, AR One Sans, Reddit Sans, Inter, Roboto, Ubuntu, Noto Sans, sans-serif
- bar_height = 30
- bar_padding = 10
- bar_button_padding = 5
- bar_precedence_over_border = true
- bar_part_of_window = true
-
- bar_color = rgba(101417FF)
- col.text = rgba(dfe3e7FF)
-
-
- # example buttons (R -> L)
- # hyprbars-button = color, size, on-click
- hyprbars-button = rgb(dfe3e7), 13, , hyprctl dispatch killactive
- hyprbars-button = rgb(dfe3e7), 13, , hyprctl dispatch fullscreen 1
- hyprbars-button = rgb(dfe3e7), 13, , hyprctl dispatch movetoworkspacesilent special
- }
-}
-
-windowrule = border_color rgba(93cdf6AA) rgba(93cdf677), match:pin true
diff --git a/src/etc/sleex/hyprland/env.conf b/src/etc/sleex/hyprland/env.conf
deleted file mode 100644
index 938e0605..00000000
--- a/src/etc/sleex/hyprland/env.conf
+++ /dev/null
@@ -1,28 +0,0 @@
-# ######### Input method ##########
-# See https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland
-env = QT_IM_MODULE, fcitx
-env = XMODIFIERS, @im=fcitx
-# env = GTK_IM_MODULE, wayland # Crashes electron apps in xwayland
-env = SDL_IM_MODULE, fcitx
-env = GLFW_IM_MODULE, ibus
-env = INPUT_METHOD, fcitx
-
-# ############ Themes #############
-env = QT_QPA_PLATFORM, wayland
-env = QT_QPA_PLATFORMTHEME, qt6ct
-env = QT_STYLE_OVERRIDE, kvantum
-# env = WLR_NO_HARDWARE_CURSORS, 1
-
-env = XDG_SESSION_TYPE, wayland
-env = GDK_BACKEND, wayland
-env = QT_QPA_PLATFORM, wayland
-env = SDL_VIDEODRIVER, wayland
-env = CLUTTER_BACKEND, wayland
-env = MOZ_ENABLE_WAYLAND, 1
-
-# ######## Screen tearing #########
-# env = WLR_DRM_NO_ATOMIC, 1
-
-# ############ Others #############
-
-env = SLEEX_VIRTUAL_ENV, ~/.local/state/sleex/.venv
diff --git a/src/etc/sleex/hyprland/execs.conf b/src/etc/sleex/hyprland/execs.conf
deleted file mode 100644
index b8119262..00000000
--- a/src/etc/sleex/hyprland/execs.conf
+++ /dev/null
@@ -1,18 +0,0 @@
-# Bar, wallpaper
-# exec-once = swww-daemon
-exec-once = qs -p /usr/share/sleex &
-
-# Core components (authentication, lock screen, notification daemon)
-exec-once = dbus-update-activation-environment --all
-exec-once = sleep 1 && dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP # Some fix idk
-
-# Audio
-exec-once = easyeffects --gapplication-service
-
-# Clipboard: history
-# exec-once = wl-paste --watch cliphist store &
-exec-once = wl-paste --type text --watch cliphist store
-exec-once = wl-paste --type image --watch cliphist store
-
-# Cursor
-exec-once = hyprctl setcursor Bibata-Modern-Classic 24
diff --git a/src/etc/sleex/hyprland/general.conf b/src/etc/sleex/hyprland/general.conf
deleted file mode 100644
index e1cbeb17..00000000
--- a/src/etc/sleex/hyprland/general.conf
+++ /dev/null
@@ -1,144 +0,0 @@
-input {
- # Keyboard: Add a layout and uncomment kb_options for Win+Space switching shortcut
- kb_layout = us
- # kb_options = grp:win_space_toggle
- numlock_by_default = true
- repeat_delay = 250
- repeat_rate = 35
-
- touchpad {
- natural_scroll = yes
- disable_while_typing = true
- clickfinger_behavior = true
- scroll_factor = 0.5
- }
- special_fallthrough = true
- follow_mouse = 1
-}
-
-binds {
- # focus_window_on_workspace_c# For Auto-run stuff see execs.confhange = true
- scroll_event_delay = 0
-}
-
-gestures {
- workspace_swipe_distance = 700
- workspace_swipe_cancel_ratio = 0.2
- workspace_swipe_min_speed_to_force = 5
- workspace_swipe_direction_lock = true
- workspace_swipe_direction_lock_threshold = 10
- workspace_swipe_create_new = true
-}
-
-gesture = 4, horizontal, workspace
-gesture = 4, vertical, fullscreen
-
-general {
- # Gaps and border
- gaps_in = 2
- gaps_out = 4
- gaps_workspaces = 50
- border_size = 1
-
- # Fallback colors
- col.active_border = rgba(0DB7D4FF)
- col.inactive_border = rgba(31313600)
-
- resize_on_border = true
- no_focus_fallback = true
- layout = dwindle
-
- #focus_to_other_workspaces = true # ahhhh i still haven't properly implemented this
- allow_tearing = true # This just allows the `immediate` window rule to work
-}
-
-dwindle {
- preserve_split = true
- # no_gaps_when_only = 1
- smart_split = false
- smart_resizing = false
-}
-
-decoration {
- rounding = 7
-
- blur {
- enabled = true
- xray = true
- special = false
- new_optimizations = true
- size = 5
- passes = 4
- brightness = 1
- noise = 0.01
- contrast = 1
- popups = true
- popups_ignorealpha = 0.6
- }
-
- # Dim
- dim_inactive = false
- dim_strength = 0.1
- dim_special = 0
-}
-
-animations {
- enabled = true
- # Animation curves
-
- bezier = linear, 0, 0, 1, 1
- bezier = md3_standard, 0.2, 0, 0, 1
- bezier = md3_decel, 0.05, 0.7, 0.1, 1
- bezier = md3_accel, 0.3, 0, 0.8, 0.15
- bezier = overshot, 0.05, 0.9, 0.1, 1.1
- bezier = crazyshot, 0.1, 1.5, 0.76, 0.92
- bezier = hyprnostretch, 0.05, 0.9, 0.1, 1.0
- bezier = menu_decel, 0.1, 1, 0, 1
- bezier = menu_accel, 0.38, 0.04, 1, 0.07
- bezier = easeInOutCirc, 0.85, 0, 0.15, 1
- bezier = easeOutCirc, 0, 0.55, 0.45, 1
- bezier = easeOutExpo, 0.16, 1, 0.3, 1
- bezier = softAcDecel, 0.26, 0.26, 0.15, 1
- bezier = md2, 0.4, 0, 0.2, 1 # use with .2s duration
- # Animation configs
- animation = windows, 1, 3, md3_decel, popin 60%
- animation = windowsIn, 1, 3, md3_decel, popin 60%
- animation = windowsOut, 1, 3, md3_accel, popin 60%
- animation = border, 1, 10, default
- animation = fade, 1, 3, md3_decel
- # animation = layers, 1, 2, md3_decel, slide
- animation = layersIn, 1, 3, menu_decel, slide
- animation = layersOut, 1, 1.6, menu_accel
- animation = fadeLayersIn, 1, 2, menu_decel
- animation = fadeLayersOut, 1, 4.5, menu_accel
- animation = workspaces, 1, 7, menu_decel, slide
- # animation = workspaces, 1, 2.5, softAcDecel, slide
- # animation = workspaces, 1, 7, menu_decel, slidefade 15%
- # animation = specialWorkspace, 1, 3, md3_decel, slidefadevert 15%
- animation = specialWorkspace, 1, 3, md3_decel, slidevert
-}
-
-misc {
- vfr = 1
- vrr = 1
- animate_manual_resizes = false
- animate_mouse_windowdragging = false
- enable_swallow = false
- swallow_regex = (foot|kitty|allacritty|Alacritty)
-
- disable_hyprland_logo = true
- force_default_wallpaper = 0
- on_focus_under_fullscreen = 2
- allow_session_lock_restore = true
-
- initial_workspace_tracking = false
-
- disable_xdg_env_checks = true # Because Hyprland doesn't seems to like Sleex
-
- session_lock_xray = true
-}
-
-ecosystem {
- no_update_news = true
- no_donation_nag = true
-}
diff --git a/src/etc/sleex/hyprland/keybinds.conf b/src/etc/sleex/hyprland/keybinds.conf
deleted file mode 100644
index 5a87f549..00000000
--- a/src/etc/sleex/hyprland/keybinds.conf
+++ /dev/null
@@ -1,155 +0,0 @@
-# Lines ending with `# [hidden]` won't be shown on cheatsheet
-# Lines starting with #! are section headings
-
-
-#!
-##! Shell
-# These absolutely need to be on top, or they won't work consistently
-bindid = Super, Super_L, Toggle overview, global, quickshell:overviewToggleRelease # Toggle overview/launcher
-binditn = Super, catchall, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Ctrl, Super_L, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:272, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:273, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:274, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:275, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:276, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse:277, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse_up, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-bind = Super, mouse_down,global, quickshell:overviewToggleReleaseInterrupt # [hidden]
-
-bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden],
-bindd = Super, V, Clipboard history >> clipboard, global, quickshell:overviewClipboardToggle # Clipboard history >> clipboard
-bindd = Super, semicolon, Emoji >> clipboard, global, quickshell:overviewEmojiToggle # Emoji >> clipboard
-bindd = Super, D, Toggle dashboard, global, quickshell:dashboardToggle # Toggle dashboard
-bindd = Super, F1, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet
-bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu
-bindd = Super, T, Toggle wallpaper selector, global, quickshell:wppselectorToggle # Toggle wallpaper selector
-
-binde = Ctrl+Super, Minus, exec, qs -p /usr/share/sleex/ ipc call zoom zoomOut # Zoom out
-binde = Ctrl+Super, Equal, exec, qs -p /usr/share/sleex/ ipc call zoom zoomIn # Zoom in
-
-# Debug
-bind = Super+Alt, F12, exec, notify-send 'Test notification' "Here's a really long message to test truncation and wrapping\nYou can middle click or flick this notification to dismiss it!" -a 'Shell' -A "Test1=I got it!" -A "Test2=Another action" -t 5000 # [hidden]
-bind = Super+Alt, Equal, exec, notify-send "Urgent notification" "Ah hell no" -u critical -a 'Hyprland keybind' # [hidden]
-
-bindl = Super ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # Mute the mic
-bindl = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle # Mute the sink
-bindle=, XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+ # Increase the volume
-bindle=, XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- # Decrease the volume
-bindle=, XF86MonBrightnessUp, exec, qs -p /usr/share/sleex/ ipc call brightness increment # [hidden]
-bindle=, XF86MonBrightnessDown, exec, qs -p /usr/share/sleex/ ipc call brightness decrement # [hidden]
-
-# bindle = , XF86KbdBrightnessUp, exec, brightnessctl -d 'white:kbd_backlight' set '1+'
-# bindle = , XF86KbdBrightnessDown, exec, brightnessctl -d 'white:kbd_backlight' set '1-'
-# bindl = , XF86KbLightOnOff, exec, brightnessctl -d 'white:kbd_backlight' set 0
-
-#!
-##! Essentials for beginners
-
-bind = Super, Return, exec, foot # Launch foot (terminal)
-bind = , Super, exec, true # Open app launcher
-bind = Ctrl+Super, T, exec, /usr/share/sleex/scripts/colors/switchwall.sh # Change wallpaper
-
-##! Actions
-
-# OCR
-bind = Super+Shift,T,exec,grim -g "$(slurp $SLURP_ARGS)" "tmp.png" && tesseract "tmp.png" - | wl-copy && rm "tmp.png" # Screen snip to text >> clipboard
-
-# Color picker
-bind = Super+Shift, X, exec, hyprpicker -a # Pick color (Hex) >> clipboard
-
-# Screenshot
-bindl=,Print,exec,grim - | wl-copy # Screenshot >> clipboard
-bindl= Ctrl,Print, exec, mkdir -p ~/Pictures/Screenshots && /usr/share/sleex/scripts/grimblast.sh copysave screen ~/Pictures/Screenshots/Screenshot_"$(date '+%Y-%m-%d_%H.%M.%S')".png # Screenshot >> clipboard & file
-bind = Super+Shift+Alt, S, exec, grim -g "$(slurp)" - | swappy -f - # Screen snip >> edit
-bind = Super+Shift, S, exec, /usr/share/sleex/scripts/grimblast.sh --freeze copy area # Screen snip
-
-# Recording stuff
-bind = Super+Alt, R, exec, /usr/share/sleex/scripts/record-script.sh # Record region (no sound)
-bind = Ctrl+Alt, R, exec, /usr/share/sleex/scripts/record-script.sh --fullscreen # Record screen (no sound)
-bind = Super+Shift+Alt, R, exec, /usr/share/sleex/scripts/record-script.sh --fullscreen-sound # Record screen (with sound)
-
-##! Session
-# bind = Super, L, exec, hyprlock # Lock session
-bind = Super, L, global, quickshell:lockScreen # Lock session
-bind = Super, End, exec, pkill qs && qs -p /usr/share/sleex/shell.qml # Restart Shell
-bind = Super+Ctrl, End, exec, qs -p /usr/share/sleex/shell.qml # Start Shell
-
-#!
-##! Window management
-# Focusing
-#/# bind = Super, ←/↑/→/↓,, # Move focus in direction
-bind = Super, Left, movefocus, l # [hidden]
-bind = Super, Right, movefocus, r # [hidden]
-bind = Super, Up, movefocus, u # [hidden]
-bind = Super, Down, movefocus, d # [hidden]
-bind = Super, Q, killactive, # Close an app
-bind = Super+Shift+Alt, Q, exec, hyprctl kill # Pick and kill a window
-
-##! Window arrangement
-# Positioning mode
-bind = Super+Alt, Space, togglefloating, # Toggle floating
-bind = Super, F, fullscreen,
-bindm = Super, mouse:272, movewindow
-bindm = Super, mouse:273, resizewindow
-
-# Toggle focused window split
-$d=[$wm]
-bindd = Super, J, $d toggle split, togglesplit # Toggle split
-
-#!
-##! Workspace navigation
-# Switching
-#/# bind = Super, Hash,, # Focus workspace # (1, 2, 3, 4, ...)
-bind = Super, code:10, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 1 # [hidden]
-bind = Super, code:11, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 2 # [hidden]
-bind = Super, code:12, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 3 # [hidden]
-bind = Super, code:13, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 4 # [hidden]
-bind = Super, code:14, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 5 # [hidden]
-bind = Super, code:15, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 6 # [hidden]
-bind = Super, code:16, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 7 # [hidden]
-bind = Super, code:17, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 8 # [hidden]
-bind = Super, code:18, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 9 # [hidden]
-bind = Super, code:19, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh workspace 10 # [hidden]
-
-
-#/# bind = Ctrl+Super, ←/→,, # Workspace: focus left/right
-bind = Ctrl+Super, Right, workspace, +1 # [hidden]
-bind = Ctrl+Super, Left, workspace, -1 # [hidden]
-
-#/# bind = Super, Scroll ↑/↓,, # Workspace: focus left/right
-bind = Super, mouse_up, workspace, +1 # [hidden]
-bind = Super, mouse_down, workspace, -1 # [hidden]
-
-#/# bind = Super, Page_↑/↓,, # Workspace: focus left/right
-bind = Super, Page_Down, workspace, +1 # [hidden]
-bind = Super, Page_Up, workspace, -1 # [hidden]
-
-##! Workspace management
-# Move window to workspace Super + Ctrl + [0-9]
-#/# bind = Super+Ctrl, Hash,, # Window: move to workspace # (1, 2, 3, 4, ...)
-bind = Super+Ctrl, code:10, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 1 # [hidden]
-bind = Super+Ctrl, code:11, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 2 # [hidden]
-bind = Super+Ctrl, code:12, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 3 # [hidden]
-bind = Super+Ctrl, code:13, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 4 # [hidden]
-bind = Super+Ctrl, code:14, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 5 # [hidden]
-bind = Super+Ctrl, code:15, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 6 # [hidden]
-bind = Super+Ctrl, code:16, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 7 # [hidden]
-bind = Super+Ctrl, code:17, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 8 # [hidden]
-bind = Super+Ctrl, code:18, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 9 # [hidden]
-bind = Super+Ctrl, code:19, exec, /usr/share/sleex/scripts/hyprland/workspace_action.sh movetoworkspacesilent 10 # [hidden]
-
-
-bind = Ctrl+Super+Shift, Right, movetoworkspace, +1 # [hidden]
-bind = Ctrl+Super+Shift, Left, movetoworkspace, -1 # [hidden]
-
-#/# bind = Super+Shift, Scroll ↑/↓,, # Window: move to workspace left/right
-bind = Super+Shift, mouse_down, movetoworkspace, -1 # [hidden]
-bind = Super+Shift, mouse_up, movetoworkspace, +1 # [hidden]
-
-
-##! Media
-bindl= ,XF86AudioNext, exec, playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` # Next track
-bindl= ,XF86AudioPrev, exec, playerctl previous # Previous track
-bindl= ,XF86AudioPlay, exec, playerctl play-pause # Media play/pause
-bindl= ,XF86AudioPause, exec, playerctl play-pause # [hidden]
diff --git a/src/etc/sleex/hyprland/rules.conf b/src/etc/sleex/hyprland/rules.conf
deleted file mode 100644
index 73214511..00000000
--- a/src/etc/sleex/hyprland/rules.conf
+++ /dev/null
@@ -1,104 +0,0 @@
-# ######## Window rules ########
-# windowrule = noblur,.*
-windowrule = match:class ^(blueberry.py)$, float on
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, float on
-windowrule = match:class ^(com.axos-project.axinstall)$, float on
-windowrule = match:title ^(Open File)(.*)$, center on
-windowrule = match:title ^(Select a File)(.*)$, center on
-windowrule = match:title ^(Choose wallpaper)(.*)$, center on
-windowrule = match:title ^(Open Folder)(.*)$, center on
-windowrule = match:title ^(Save As)(.*)$, center on
-windowrule = match:title ^(Library)(.*)$, center on
-windowrule = match:title ^(File Upload)(.*)$, center on
-
-# Dialogs
-windowrule=match:title ^(Open File)(.*)$, float on
-windowrule=match:title ^(Select a File)(.*)$, float on
-windowrule=match:title ^(Choose wallpaper)(.*)$, float on
-windowrule=match:title ^(Open Folder)(.*)$, float on
-windowrule=match:title ^(Save As)(.*)$, float on
-windowrule=match:title ^(Library)(.*)$, float on
-windowrule=match:title ^(File Upload)(.*)$, float on
-#windowrule=match:title ^(Sleex Settings)$, float on
-windowrule=match:title ^(Calculator)$, float on
-windowrule=match:title ^(BoxBuddy)$, float on
-windowrule=match:title ^(Popsicle)$, float on
-windowrule=match:title ^(Trayscale)$, float on
-windowrule=match:class ^(org.pulseaudio.pavucontrol)$, float on
-windowrule=match:class ^(org.pulseaudio.pavucontrol)$, size (monitor_w*.45) (monitor_h*.45)
-windowrule=match:class ^(org.pulseaudio.pavucontrol)$, center on
-windowrule=match:class ^(cheese)$, float on
-windowrule=match:class ^(timeshift-gtk)$, float on
-windowrule=match:class ^(lstopo)$, float on
-windowrule=match:class ^(org.gnome.ColorProfileViewer)$, float on
-windowrule=match:class ^(qt5ct)$, float on
-windowrule=match:class ^(rustdesk)$, float on
-windowrule=match:title ^(Choose Files)$, float on
-windowrule=match:title ^(Save Image)$, float on
-windowrule=match:title ^(Save File)$, float on
-windowrule=match:class ^(org.kde.plasmawindowed)$, float on
-windowrule=match:class ^(kcm.networkmanagement)$, float on
-
-# Tearing
-windowrule = match:title .*\.exe, immediate on
-windowrule = match:title .*minecraft.*, immediate on
-windowrule = match:class ^(steam_app).*, immediate on
-
-# No shadow for tiled windows
-windowrule = match:float 0, no_shadow on
-
-# ######## Layer rules ########
-layerrule = match:namespace .*, xray on
-# layerrule = match:namespace .*, no_anim on
-layerrule = match:namespace walker, no_anim on
-layerrule = match:namespace selection, no_anim on
-layerrule = match:namespace overview, no_anim on
-layerrule = match:namespace anyrun, no_anim on
-layerrule = match:namespace indicator.*, no_anim on
-layerrule = match:namespace osk, no_anim on
-layerrule = match:namespace hyprpicker, no_anim on
-
-layerrule = match:namespace noanim, no_anim on
-layerrule = match:namespace gtk-layer-shell, blur on
-layerrule = match:namespace gtk-layer-shell, ignore_alpha 0
-layerrule = match:namespace launcher, blur on
-layerrule = match:namespace launcher, ignore_alpha 0.5
-layerrule = match:namespace notifications, blur on
-layerrule = match:namespace notifications, ignore_alpha 0.69
-layerrule = match:namespace logout_dialog # wlogout, blur on
-
-# Picture-in-Picture
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, float on
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, keep_aspect_ratio on
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, move (monitor_w*.73) (monitor_h*.72)
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, size (monitor_w*.25) (monitor_h*.25)
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, float on
-windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, pin on
-
-
-# Shell
-layerrule = match:namespace quickshell:.*, blur_popups on
-layerrule = match:namespace quickshell:.*, blur on
-layerrule = match:namespace quickshell:.*, ignore_alpha 0.2
-layerrule = match:namespace quickshell:bar, animation slide
-layerrule = match:namespace quickshell:cheatsheet, animation slide bottom
-layerrule = match:namespace quickshell:dock, animation slide bottom
-layerrule = match:namespace quickshell:lockWindowPusher, no_anim on
-layerrule = match:namespace quickshell:notificationPopup, animation fade
-layerrule = match:namespace quickshell:overlay, no_anim on
-layerrule = match:namespace quickshell:overlay, ignore_alpha 1
-layerrule = match:namespace quickshell:overview, animation slide top
-layerrule = match:namespace quickshell:osk, animation slide bottom
-layerrule = match:namespace quickshell:polkit, no_anim on
-layerrule = match:namespace quickshell:popup, xray off # No weird color for bar tooltips (this in theory should suffice)
-layerrule = match:namespace quickshell:popup, ignore_alpha 1 # No weird color for bar tooltips (but somehow this is necessary)
-layerrule = match:namespace quickshell:reloadPopup, animation slide
-layerrule = match:namespace quickshell:regionSelector, no_anim on
-layerrule = match:namespace quickshell:screenshot, no_anim on
-layerrule = match:namespace quickshell:session, blur on
-layerrule = match:namespace quickshell:session, ignore_alpha 0
-layerrule = match:namespace quickshell:dashboard, animation slide bottom
-layerrule = match:namespace quickshell:wallpaperSelector, animation slide top
-layerrule = match:namespace quickshell:batterywarning, blur off
-layerrule = match:namespace quickshell:batterywarning, ignore_alpha 1
-layerrule = match:namespace quickshell:batterywarning, no_anim on
diff --git a/src/share/libalpm/hooks/sleex-hyprctl-reload.hook b/src/share/libalpm/hooks/sleex-hyprctl-reload.hook
deleted file mode 100644
index a1351b4d..00000000
--- a/src/share/libalpm/hooks/sleex-hyprctl-reload.hook
+++ /dev/null
@@ -1,12 +0,0 @@
-## Copyright (C) 2025 Ardox
-
-[Trigger]
-Operation = Install
-Operation = Upgrade
-Type = Package
-Target = sleex
-
-[Action]
-Description = reloading hyprctl configuration
-When = PostTransaction
-Exec = /usr/bin/sleep 5 && /usr/bin/hyprctl reload &
diff --git a/src/share/sleex/GlobalStates.qml b/src/share/sleex/GlobalStates.qml
index 68c1a47a..779cf39b 100644
--- a/src/share/sleex/GlobalStates.qml
+++ b/src/share/sleex/GlobalStates.qml
@@ -18,13 +18,13 @@ Singleton {
property bool wppselectorOpen: false
property bool screenLocked: false
- property real screenZoom: 1
- onScreenZoomChanged: {
- Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", `${root.screenZoom.toString()}`]);
- }
- Behavior on screenZoom {
- animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
- }
+ // property real screenZoom: 1
+ // onScreenZoomChanged: {
+ // Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", `${root.screenZoom.toString()}`]);
+ // }
+ // Behavior on screenZoom {
+ // animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
+ // }
// When user is not reluctant while pressing super, they probably don't need to see workspace numbers
onSuperReleaseMightTriggerChanged: {
@@ -54,17 +54,17 @@ Singleton {
}
}
- IpcHandler {
- target: "zoom"
+ // IpcHandler {
+ // target: "zoom"
- function zoomIn() {
- screenZoom = Math.min(screenZoom + 0.4, 3.0)
- }
+ // function zoomIn() {
+ // screenZoom = Math.min(screenZoom + 0.4, 3.0)
+ // }
- function zoomOut() {
- screenZoom = Math.max(screenZoom - 0.4, 1)
- }
- }
+ // function zoomOut() {
+ // screenZoom = Math.max(screenZoom - 0.4, 1)
+ // }
+ // }
IpcHandler {
diff --git a/src/share/sleex/greetd.qml b/src/share/sleex/greetd.qml
index 71ebd7f5..5f09972b 100755
--- a/src/share/sleex/greetd.qml
+++ b/src/share/sleex/greetd.qml
@@ -100,18 +100,17 @@ ShellRoot {
color: "transparent"
WlrLayershell.exclusionMode: ExclusionMode.Ignore
- Image {
+ WallpaperDisplay {
id: backgroundImage
anchors.fill: parent
source: Config.options.background.wallpaperPath
- fillMode: Image.PreserveAspectCrop
- GaussianBlur {
- anchors.fill: parent
- source: backgroundImage
- radius: 10
- opacity: parent.opacity
- }
+ // GaussianBlur {
+ // anchors.fill: parent
+ // source: backgroundImage
+ // radius: 10
+ // opacity: parent.opacity
+ // }
}
RippleButton {
diff --git a/src/share/sleex/modules/background/Background.qml b/src/share/sleex/modules/background/Background.qml
index 37a2870a..c9955149 100644
--- a/src/share/sleex/modules/background/Background.qml
+++ b/src/share/sleex/modules/background/Background.qml
@@ -10,6 +10,7 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
+import Sleex.Utils
Scope {
id: root
@@ -184,6 +185,8 @@ Scope {
Watermark { visibleWatermark: Config.options.background.showWatermark }
Quote { visibleQuote: Config.options.background.enableQuote }
+ PostIt { id: postItManager; z: 50 }
+
}
}
}
diff --git a/src/share/sleex/modules/background/BackgroundContextMenu.qml b/src/share/sleex/modules/background/BackgroundContextMenu.qml
index 58b1d3fb..7efb9713 100755
--- a/src/share/sleex/modules/background/BackgroundContextMenu.qml
+++ b/src/share/sleex/modules/background/BackgroundContextMenu.qml
@@ -2,7 +2,6 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
-import Quickshell.Hyprland
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
@@ -112,7 +111,7 @@ Item {
}
onClicked: {
- Hyprland.dispatch("global quickshell:sessionOpen")
+ Quickshell.execDetached(["qs", "-p", "/usr/share/sleex", "ipc", "call", "session", "toggle"])
bgMenu.close()
}
}
@@ -142,6 +141,32 @@ Item {
bgMenu.close()
}
}
+
+ Rectangle {
+ Layout.fillWidth: true
+ implicitHeight: 1
+ color: Appearance.m3colors.m3outlineVariant
+ Layout.topMargin: 4
+ Layout.bottomMargin: 4
+ }
+
+ RippleButton {
+ Layout.fillWidth: true
+ buttonRadius: popupBackground.radius - popupBackground.padding
+
+ contentItem: RowLayout {
+ spacing: 8
+ anchors.fill: parent
+ anchors.margins: 12
+ MaterialSymbol { text: "note_add"; iconSize: 20 }
+ StyledText { text: "New Post-it"; Layout.fillWidth: true }
+ }
+
+ onClicked: {
+ postItManager.createNote(bgMenu.menuX, bgMenu.menuY)
+ bgMenu.close()
+ }
+ }
}
}
diff --git a/src/share/sleex/modules/background/DesktopIcons.qml b/src/share/sleex/modules/background/DesktopIcons.qml
index 10cbe8c7..14ef13c6 100644
--- a/src/share/sleex/modules/background/DesktopIcons.qml
+++ b/src/share/sleex/modules/background/DesktopIcons.qml
@@ -24,6 +24,7 @@ Item {
DesktopModel {
id: desktopModel
+ rows: Math.max(1, Math.floor(gridArea.height / cellHeight))
Component.onCompleted: loadDirectory(FileUtils.trimFileProtocol(Directories.desktop))
}
diff --git a/src/share/sleex/modules/background/PostIt.qml b/src/share/sleex/modules/background/PostIt.qml
new file mode 100644
index 00000000..c3ef1fe2
--- /dev/null
+++ b/src/share/sleex/modules/background/PostIt.qml
@@ -0,0 +1,71 @@
+import QtQuick
+import Sleex.Utils
+import qs.services
+
+Item {
+ id: postItManager
+ anchors.fill: parent
+ z: 50
+
+ ListModel {
+ id: postItModel
+ Component.onCompleted: {
+ let notes = DesktopStateManager.getPostIts()
+ for (let i = 0; i < notes.length; i++) {
+ postItModel.append(notes[i])
+ }
+ }
+ }
+
+ function saveNotes() {
+ let arr = []
+ for(let i = 0; i < postItModel.count; i++) {
+ let item = postItModel.get(i)
+ arr.push({
+ posX: item.posX,
+ posY: item.posY,
+ text: item.text
+ })
+ }
+ DesktopStateManager.savePostIts(arr)
+ }
+
+ function createNote(spawnX, spawnY) {
+ postItModel.append({
+ "posX": spawnX,
+ "posY": spawnY,
+ "text": ""
+ })
+ saveNotes()
+ }
+
+ Repeater {
+ model: postItModel
+ delegate: PostItDelegate {
+ required property real posX
+ required property real posY
+ required property string text
+ required property int index
+
+ x: posX
+ y: posY
+ noteText: text
+
+ onDeleteRequested: {
+ postItModel.remove(index)
+ postItManager.saveNotes()
+ }
+
+ onPositionUpdated: (newX, newY) => {
+ postItModel.setProperty(index, "posX", newX)
+ postItModel.setProperty(index, "posY", newY)
+ postItManager.saveNotes()
+ }
+
+ onTextUpdated: (newText) => {
+ postItModel.setProperty(index, "text", newText)
+ postItManager.saveNotes()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/share/sleex/modules/background/PostItDelegate.qml b/src/share/sleex/modules/background/PostItDelegate.qml
new file mode 100644
index 00000000..6a3cfab5
--- /dev/null
+++ b/src/share/sleex/modules/background/PostItDelegate.qml
@@ -0,0 +1,78 @@
+import QtQuick
+import QtQuick.Controls
+import qs.modules.common.widgets
+import qs.modules.common
+
+Rectangle {
+ id: postitRoot
+ width: 200
+ height: 200
+ color: "#fdfd96"
+ border.color: "#d9d97c"
+ border.width: 1
+ radius: 4
+
+ property string noteText: ""
+ signal deleteRequested()
+ signal textUpdated(string newText)
+ signal positionUpdated(real newX, real newY)
+
+ DragHandler {
+ target: postitRoot
+ cursorShape: active ? Qt.ClosedHandCursor : Qt.OpenHandCursor
+
+ onActiveChanged: {
+ if (!active) {
+ postitRoot.positionUpdated(postitRoot.x, postitRoot.y)
+ }
+ }
+ }
+
+ TextEdit {
+ id: editor
+ anchors.fill: parent
+ anchors.margins: 12
+ text: postitRoot.noteText
+ wrapMode: TextEdit.Wrap
+ font.pointSize: 12
+ color: "#222"
+ readOnly: true
+
+ Keys.onPressed: function (event) {
+ if (event.key === Qt.Key_Escape) {
+ readOnly = true
+ postitRoot.textUpdated(text)
+ readOnly = true
+ event.accepted = true
+ }
+ }
+ }
+
+ RippleButton {
+ id: deleteButton
+ anchors.top: parent.top
+ anchors.right: parent.right
+ anchors.margins: 4
+ width: 24
+ height: 24
+ buttonRadius: width / 2
+ z: 100
+
+ MaterialSymbol {
+ anchors.centerIn: parent;
+ text: "close";
+ iconSize: 16
+ color: Appearance.colors.colOnError
+ }
+
+ onClicked: postitRoot.deleteRequested()
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onDoubleClicked: {
+ editor.readOnly = false
+ editor.forceActiveFocus()
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/share/sleex/modules/bar/ActiveWindow.qml b/src/share/sleex/modules/bar/ActiveWindow.qml
index 8262dbfb..dd93452c 100644
--- a/src/share/sleex/modules/bar/ActiveWindow.qml
+++ b/src/share/sleex/modules/bar/ActiveWindow.qml
@@ -1,15 +1,14 @@
import qs.modules.common
import qs.modules.common.widgets
+import qs.services
import QtQuick
import QtQuick.Layouts
import Quickshell.Wayland
-import Quickshell.Hyprland
+import Sleex.Fhtc
Item {
id: root
required property var bar
- readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
- readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
implicitWidth: colLayout.implicitWidth
@@ -26,7 +25,7 @@ Item {
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
elide: Text.ElideRight
- text: root.activeWindow?.activated ? root.activeWindow?.appId : qsTr("Desktop")
+ text: FhtcWorkspaces.focusedWindow !== undefined ? FhtcWorkspaces.focusedWindow['app-id'] : qsTr("Desktop")
}
StyledText {
@@ -34,7 +33,7 @@ Item {
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnLayer0
elide: Text.ElideRight
- text: root.activeWindow?.activated ? root.activeWindow?.title : `${qsTr("Workspace")} ${monitor.activeWorkspace?.id}`
+ text: FhtcWorkspaces.focusedWindow !== undefined ? FhtcWorkspaces.focusedWindow.title : `${qsTr("Workspace")} ${FhtcWorkspaces.activeWorkspaceId + 1}`
}
}
diff --git a/src/share/sleex/modules/bar/AudioOsd.qml b/src/share/sleex/modules/bar/AudioOsd.qml
index d1a909de..b310f3e7 100644
--- a/src/share/sleex/modules/bar/AudioOsd.qml
+++ b/src/share/sleex/modules/bar/AudioOsd.qml
@@ -1,5 +1,4 @@
import Quickshell
-import Quickshell.Hyprland
import QtQuick.Layouts
import QtQuick
import qs.modules.common
diff --git a/src/share/sleex/modules/bar/Bar.qml b/src/share/sleex/modules/bar/Bar.qml
index 60f1e109..ddc0aaa5 100644
--- a/src/share/sleex/modules/bar/Bar.qml
+++ b/src/share/sleex/modules/bar/Bar.qml
@@ -507,10 +507,10 @@ Scope {
NotifIndicator {}
}
- HyprlandXkbIndicator {
- Layout.alignment: Qt.AlignVCenter
- Layout.rightMargin: indicatorsRowLayout.realSpacing
- }
+ // HyprlandXkbIndicator {
+ // Layout.alignment: Qt.AlignVCenter
+ // Layout.rightMargin: indicatorsRowLayout.realSpacing
+ // }
Revealer {
diff --git a/src/share/sleex/modules/bar/BrightnessOsd.qml b/src/share/sleex/modules/bar/BrightnessOsd.qml
index 952cd601..4ffccde2 100644
--- a/src/share/sleex/modules/bar/BrightnessOsd.qml
+++ b/src/share/sleex/modules/bar/BrightnessOsd.qml
@@ -1,16 +1,16 @@
import Quickshell
-import Quickshell.Hyprland
import QtQuick
import qs.modules.common
import qs.modules.common.widgets
import qs.services
+import Sleex.Fhtc
Revealer {
id: root
reveal: showOsdValues
property bool showOsdValues: false
- property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
+ property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName)
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
Connections {
diff --git a/src/share/sleex/modules/bar/SysTray.qml b/src/share/sleex/modules/bar/SysTray.qml
index 4b911182..58f63943 100644
--- a/src/share/sleex/modules/bar/SysTray.qml
+++ b/src/share/sleex/modules/bar/SysTray.qml
@@ -3,7 +3,6 @@ import qs.modules.common.widgets
import QtQuick
import QtQuick.Layouts
import Quickshell
-import Quickshell.Hyprland
import Quickshell.Services.SystemTray
Item {
@@ -26,20 +25,20 @@ Item {
}
function grabFocus() {
- focusGrab.active = true;
+ // focusGrab.active = true;
}
function setExtraWindowAndGrabFocus(window) {
root.activeMenu = window;
- root.grabFocus();
+ // root.grabFocus();
}
function releaseFocus() {
- focusGrab.active = false;
+ // focusGrab.active = false;
}
function closeOverflowMenu() {
- focusGrab.active = false;
+ // focusGrab.active = false;
}
onTrayOverflowOpenChanged: {
@@ -48,19 +47,6 @@ Item {
}
}
- HyprlandFocusGrab {
- id: focusGrab
- active: false
- windows: [trayOverflowLayout.QsWindow?.window, root.activeMenu]
- onCleared: {
- root.trayOverflowOpen = false;
- if (root.activeMenu) {
- root.activeMenu.close();
- root.activeMenu = null;
- }
- }
- }
-
GridLayout {
id: gridLayout
columns: root.vertical ? 1 : -1
diff --git a/src/share/sleex/modules/bar/Workspaces.qml b/src/share/sleex/modules/bar/Workspaces.qml
index 75c0806a..bc923bb8 100644
--- a/src/share/sleex/modules/bar/Workspaces.qml
+++ b/src/share/sleex/modules/bar/Workspaces.qml
@@ -8,28 +8,41 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
-import Quickshell.Hyprland
import Quickshell.Io
import Quickshell.Widgets
import Qt5Compat.GraphicalEffects
+import Sleex.Fhtc
Item {
id: root
required property var bar
- property bool borderless: Config.options.bar.borderless
- readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
- readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
+ readonly property string screenName: bar.screen?.name ?? ""
+
+ // Get workspaces for this screen only, sorted by ID
+ readonly property var screenWorkspaces: {
+ return Object.values(FhtcWorkspaces.workspaces)
+ .filter(ws => ws.output === screenName)
+ .sort((a, b) => a.id - b.id);
+ }
+
+ // Active workspace index within this screen (0-based)
+ readonly property int activeWorkspaceIndex: {
+ if (!FhtcWorkspaces.activeWorkspace) return -1;
+ if (FhtcWorkspaces.activeWorkspace.output !== screenName) return -1;
+ // Find the index of the active workspace in our sorted screen workspaces
+ const idx = screenWorkspaces.findIndex(ws => ws.id === FhtcWorkspaces.activeWorkspace.id);
+ // Return -1 if the workspace is beyond the shown limit
+ if (idx >= Config.options.bar.workspaces.shown) return -1;
+ return idx;
+ }
- readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / Config.options.bar.workspaces.shown)
property list workspaceOccupied: []
- property int widgetPadding: 0
property int horizontalPadding: 5
property int workspaceButtonWidth: 30
property real workspaceIconSize: workspaceButtonWidth * 0.6
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
property real workspaceIconOpacityShrinked: 1
property real workspaceIconMarginShrinked: -4
- property int workspaceIndexInGroup: (monitor.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown
property bool useMaterialIcons: Config.options.bar.workspaces.useMaterialIcons
@@ -37,17 +50,27 @@ Item {
// Function to update workspaceOccupied
function updateWorkspaceOccupied() {
workspaceOccupied = Array.from({ length: Config.options.bar.workspaces.shown }, (_, i) => {
- return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * Config.options.bar.workspaces.shown + i + 1);
+ // Get the workspace at this index for this screen
+ const ws = screenWorkspaces[i];
+ if (!ws) return false;
+ // Check if the workspace has any windows
+ return ws.windows && ws.windows.length > 0;
})
}
// Initialize workspaceOccupied when the component is created
Component.onCompleted: updateWorkspaceOccupied()
- // Listen for changes in Hyprland.workspaces.values
+ // Listen for changes in Fhtc.workspaces and windows
Connections {
- target: Hyprland.workspaces
- function onValuesChanged() {
+ target: FhtcWorkspaces
+ function onWorkspacesChanged() {
+ updateWorkspaceOccupied();
+ }
+ function onWindowsChanged() {
+ updateWorkspaceOccupied();
+ }
+ function onActiveWorkspaceChanged() {
updateWorkspaceOccupied();
}
}
@@ -59,24 +82,15 @@ Item {
// Scroll to switch workspaces
WheelHandler {
onWheel: (event) => {
- if (event.angleDelta.y < 0)
- Hyprland.dispatch(`workspace +1`);
- else if (event.angleDelta.y > 0)
- Hyprland.dispatch(`workspace -1`);
+ if (event.angleDelta.y < 0) {
+ FhtcIpc.dispatch("focus-next-workspace", {});
+ } else if (event.angleDelta.y > 0) {
+ FhtcIpc.dispatch("focus-previous-workspace", {});
+ }
}
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
}
- MouseArea {
- anchors.fill: parent
- acceptedButtons: Qt.BackButton
- onPressed: (event) => {
- if (event.button === Qt.BackButton) {
- Hyprland.dispatch(`togglespecialworkspace`);
- }
- }
- }
-
Item {
anchors.fill: parent
anchors.leftMargin: horizontalPadding
@@ -99,8 +113,8 @@ Item {
implicitWidth: workspaceButtonWidth
implicitHeight: workspaceButtonWidth
radius: Appearance.rounding.full
- property var leftOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index))
- property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+2))
+ property var leftOccupied: (workspaceOccupied[index-1])
+ property var rightOccupied: (workspaceOccupied[index+1])
property var radiusLeft: leftOccupied ? 0 : Appearance.rounding.full
property var radiusRight: rightOccupied ? 0 : Appearance.rounding.full
@@ -110,7 +124,7 @@ Item {
bottomRightRadius: radiusRight
color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4)
- opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+1)) ? 1 : 0
+ opacity: (workspaceOccupied[index]) ? 1 : 0
Behavior on opacity {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
@@ -132,6 +146,7 @@ Item {
// Active workspace
Rectangle {
z: 2
+ visible: activeWorkspaceIndex >= 0
// Make active ws indicator, which has a brighter color, smaller to look like it is of the same size as ws occupied highlight
property real activeWorkspaceMargin: 2
implicitHeight: workspaceButtonWidth - activeWorkspaceMargin * 2
@@ -139,8 +154,8 @@ Item {
color: Appearance.colors.colPrimary
anchors.verticalCenter: parent.verticalCenter
- property real idx1: workspaceIndexInGroup
- property real idx2: workspaceIndexInGroup
+ property real idx1: activeWorkspaceIndex >= 0 ? activeWorkspaceIndex : 0
+ property real idx2: activeWorkspaceIndex >= 0 ? activeWorkspaceIndex : 0
x: Math.min(idx1, idx2) * workspaceButtonWidth + activeWorkspaceMargin
implicitWidth: Math.abs(idx1 - idx2) * workspaceButtonWidth + workspaceButtonWidth - activeWorkspaceMargin * 2
@@ -175,29 +190,38 @@ Item {
Button {
id: button
- property int workspaceValue: workspaceGroup * Config.options.bar.workspaces.shown + index + 1
+ property var workspace: screenWorkspaces[index] ?? null
+ property int workspaceId: workspace?.id ?? -1
Layout.fillHeight: true
- onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`)
+ onPressed: {
+ if (button.workspaceId >= 0) {
+ FhtcIpc.dispatch("focus-workspace-by-index", { "workspace_idx": button.workspaceId });
+ }
+ }
width: workspaceButtonWidth
background: Item {
id: workspaceButtonBackground
implicitWidth: workspaceButtonWidth
implicitHeight: workspaceButtonWidth
+
+ // Get the biggest window from the workspace's window list
property var biggestWindow: {
- const windowsInThisWorkspace = HyprlandData.windowList.filter(w => w.workspace.id == button.workspaceValue)
+ if (!button.workspace || !button.workspace.windows || button.workspace.windows.length === 0) return null;
+ const windowIds = button.workspace.windows;
+ const windowsInThisWorkspace = windowIds.map(id => FhtcWorkspaces.windows[id]).filter(w => w != null);
return windowsInThisWorkspace.reduce((maxWin, win) => {
const maxArea = (maxWin?.size?.[0] ?? 0) * (maxWin?.size?.[1] ?? 0)
const winArea = (win?.size?.[0] ?? 0) * (win?.size?.[1] ?? 0)
return winArea > maxArea ? win : maxWin
}, null)
}
- property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
+ property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.["app-id"]), "image-missing")
property string materialIconName: {
if (!biggestWindow) return ""
- const winClass = biggestWindow.class.toLowerCase()
+ const winClass = biggestWindow?.["app-id"]
const map = {
"language": ["firefox", "chromium", "google-chrome", "brave", "edge", "vivaldi", "qutebrowser", "librewolf", "zen-browser"],
"terminal": ["foot", "kitty", "alacritty", "wezterm", "gnome-terminal", "konsole", "xfce4-terminal", "xterm"],
@@ -230,9 +254,9 @@ Item {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2)
- text: `${button.workspaceValue}`
+ text: `${index + 1}`
elide: Text.ElideRight
- color: (monitor.activeWorkspace?.id == button.workspaceValue) ?
+ color: (activeWorkspaceIndex == index) ?
Appearance.m3colors.m3onPrimary :
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
Appearance.colors.colOnLayer1Inactive)
@@ -252,9 +276,9 @@ Item {
width: workspaceButtonWidth * 0.18
height: width
radius: width / 2
- color: (monitor.activeWorkspace?.id == button.workspaceValue) ?
- Appearance.m3colors.m3onPrimary :
- (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
+ color: (activeWorkspaceIndex == index) ?
+ Appearance.m3colors.m3onPrimary :
+ (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
Appearance.colors.colOnLayer1Inactive)
Behavior on opacity {
@@ -268,7 +292,7 @@ Item {
opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 :
(workspaceButtonBackground.biggestWindow && !GlobalStates.workspaceShowNumbers && Config.options?.bar.workspaces.showAppIcons) ?
1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0
- visible: opacity > 0
+ visible: opacity > 0
IconImage {
id: mainAppIcon
anchors.bottom: parent.bottom
@@ -308,8 +332,8 @@ Item {
(workspaceButtonWidth - workspaceIconSize) / 2 - 2 : workspaceIconMarginShrinked
anchors.rightMargin: (!GlobalStates.workspaceShowNumbers) ?
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
- color: (monitor.activeWorkspace?.id == button.workspaceValue) ?
- Appearance.m3colors.m3onPrimary : Appearance.m3colors.m3onSecondaryContainer
+ color: (activeWorkspaceIndex == index) ?
+ Appearance.m3colors.m3onPrimary : Appearance.colors.colOnSecondaryContainer
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
diff --git a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
index 2146fb23..e07a06b1 100644
--- a/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
+++ b/src/share/sleex/modules/cheatsheet/Cheatsheet.qml
@@ -38,23 +38,13 @@ Scope { // Scope
implicitWidth: cheatsheetBackground.width + Appearance.sizes.elevationMargin * 2
implicitHeight: cheatsheetBackground.height + Appearance.sizes.elevationMargin * 2
WlrLayershell.namespace: "quickshell:cheatsheet"
- // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
- // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
color: "transparent"
mask: Region {
item: cheatsheetBackground
}
- HyprlandFocusGrab { // Click outside to close
- id: grab
- windows: [ cheatsheetRoot ]
- active: cheatsheetRoot.visible
- onCleared: () => {
- if (!active) cheatsheetRoot.hide()
- }
- }
-
// Background
StyledRectangularShadow {
@@ -161,5 +151,4 @@ Scope { // Scope
cheatsheetLoader.active = false;
}
}
-
}
diff --git a/src/share/sleex/modules/cheatsheet/CheatsheetKeybinds.qml b/src/share/sleex/modules/cheatsheet/CheatsheetKeybinds.qml
index f39e27ab..97fbf480 100644
--- a/src/share/sleex/modules/cheatsheet/CheatsheetKeybinds.qml
+++ b/src/share/sleex/modules/cheatsheet/CheatsheetKeybinds.qml
@@ -9,11 +9,10 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
-import Quickshell.Hyprland
Item {
id: root
- readonly property var keybinds: HyprlandKeybinds.keybinds
+ readonly property var keybinds: FhtcKeybinds.keybinds
property real spacing: 16
property real titleSpacing: 8
width: 800
diff --git a/src/share/sleex/modules/common/Appearance.qml b/src/share/sleex/modules/common/Appearance.qml
index c65992bc..ee38636a 100644
--- a/src/share/sleex/modules/common/Appearance.qml
+++ b/src/share/sleex/modules/common/Appearance.qml
@@ -346,7 +346,7 @@ Singleton {
sizes: QtObject {
property real baseBarHeight: 40
property real barHeight: Config.options.bar.cornerStyle === 1 ?
- (baseBarHeight + root.sizes.hyprlandGapsOut * 2) : baseBarHeight
+ (baseBarHeight + root.sizes.gapsOut * 2) : baseBarHeight
property real barCenterSideModuleWidth: Config.options?.bar.verbose ? 360 : 140
property real barCenterSideModuleWidthShortened: 280
property real barCenterSideModuleWidthHellaShortened: 190
@@ -355,7 +355,7 @@ Singleton {
property real elevationMargin: 10
property real fabShadowRadius: 5
property real fabHoveredShadowRadius: 7
- property real hyprlandGapsOut: 5
+ property real gapsOut: 5
property real mediaControlsWidth: 440
property real mediaControlsHeight: 160
property real notificationPopupWidth: 410
@@ -366,7 +366,7 @@ Singleton {
property real sidebarWidthExtended: 750
property real baseVerticalBarWidth: 46
property real verticalBarWidth: Config.options.bar.cornerStyle === 1 ?
- (baseVerticalBarWidth + root.sizes.hyprlandGapsOut * 2) : baseVerticalBarWidth
+ (baseVerticalBarWidth + root.sizes.gapsOut * 2) : baseVerticalBarWidth
property real wallpaperSelectorWidth: 1200
property real wallpaperSelectorHeight: 690
property real wallpaperSelectorItemMargins: 8
diff --git a/src/share/sleex/modules/common/Config.qml b/src/share/sleex/modules/common/Config.qml
index e385a7a3..5518d91b 100644
--- a/src/share/sleex/modules/common/Config.qml
+++ b/src/share/sleex/modules/common/Config.qml
@@ -156,7 +156,7 @@ Singleton {
property bool background: false
property bool borderless: false // true for no grouping of items
property bool verbose: true
- property list screenList: [] // List of names, like "eDP-1", find out with 'hyprctl monitors' command
+ property list screenList: [] // List of names, like "eDP-1"
property JsonObject workspaces: JsonObject {
property int shown: 10
property bool showAppIcons: false
diff --git a/src/share/sleex/modules/common/Directories.qml b/src/share/sleex/modules/common/Directories.qml
index 5f68e9cc..8651bbdd 100644
--- a/src/share/sleex/modules/common/Directories.qml
+++ b/src/share/sleex/modules/common/Directories.qml
@@ -5,7 +5,6 @@ import qs.modules.common.functions
import Qt.labs.platform
import QtQuick
import Quickshell
-import Quickshell.Hyprland
Singleton {
// XDG Dirs, with "file://"
diff --git a/src/share/sleex/modules/common/widgets/ConfigSelectionArray.qml b/src/share/sleex/modules/common/widgets/ConfigSelectionArray.qml
index b1c279f7..04f6480b 100644
--- a/src/share/sleex/modules/common/widgets/ConfigSelectionArray.qml
+++ b/src/share/sleex/modules/common/widgets/ConfigSelectionArray.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import qs.services
import qs.modules.common
import qs.modules.common.widgets
diff --git a/src/share/sleex/modules/common/widgets/Favicon.qml b/src/share/sleex/modules/common/widgets/Favicon.qml
index 7eae7fb6..29cb7689 100644
--- a/src/share/sleex/modules/common/widgets/Favicon.qml
+++ b/src/share/sleex/modules/common/widgets/Favicon.qml
@@ -10,7 +10,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Io
import Quickshell.Widgets
-import Quickshell.Hyprland
IconImage {
id: root
diff --git a/src/share/sleex/modules/common/widgets/LightDarkPreferenceButton.qml b/src/share/sleex/modules/common/widgets/LightDarkPreferenceButton.qml
index d380aaf3..dad9d367 100644
--- a/src/share/sleex/modules/common/widgets/LightDarkPreferenceButton.qml
+++ b/src/share/sleex/modules/common/widgets/LightDarkPreferenceButton.qml
@@ -7,7 +7,6 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Io
-import Quickshell.Hyprland
GroupButton {
id: lightDarkButtonRoot
diff --git a/src/share/sleex/modules/common/widgets/NotificationGroup.qml b/src/share/sleex/modules/common/widgets/NotificationGroup.qml
index 826ba294..310c49f4 100644
--- a/src/share/sleex/modules/common/widgets/NotificationGroup.qml
+++ b/src/share/sleex/modules/common/widgets/NotificationGroup.qml
@@ -11,7 +11,6 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
-import Quickshell.Hyprland
import Quickshell.Services.Notifications
/**
diff --git a/src/share/sleex/modules/common/widgets/NotificationItem.qml b/src/share/sleex/modules/common/widgets/NotificationItem.qml
index 4e526ae5..007e855f 100644
--- a/src/share/sleex/modules/common/widgets/NotificationItem.qml
+++ b/src/share/sleex/modules/common/widgets/NotificationItem.qml
@@ -11,7 +11,6 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
-import Quickshell.Hyprland
import Quickshell.Services.Notifications
Item { // Notification item area
diff --git a/src/share/sleex/modules/common/widgets/NotificationListView.qml b/src/share/sleex/modules/common/widgets/NotificationListView.qml
index 6727b33c..4a3d18c8 100644
--- a/src/share/sleex/modules/common/widgets/NotificationListView.qml
+++ b/src/share/sleex/modules/common/widgets/NotificationListView.qml
@@ -7,7 +7,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
-import Quickshell.Hyprland
StyledListView { // Scrollable window
id: root
diff --git a/src/share/sleex/modules/common/widgets/SelectionGroupButton.qml b/src/share/sleex/modules/common/widgets/SelectionGroupButton.qml
index d6ba9091..d7a1196c 100644
--- a/src/share/sleex/modules/common/widgets/SelectionGroupButton.qml
+++ b/src/share/sleex/modules/common/widgets/SelectionGroupButton.qml
@@ -2,7 +2,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import qs.services
import qs.modules.common
import qs.modules.common.widgets
diff --git a/src/share/sleex/modules/common/widgets/WindowDialog.qml b/src/share/sleex/modules/common/widgets/WindowDialog.qml
index 58bb577c..54529331 100644
--- a/src/share/sleex/modules/common/widgets/WindowDialog.qml
+++ b/src/share/sleex/modules/common/widgets/WindowDialog.qml
@@ -33,7 +33,7 @@ Rectangle {
dialogBackground.implicitHeight = show ? backgroundHeight : 0
}
- radius: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
+ radius: Appearance.rounding.screenRounding - Appearance.sizes.gapsOut + 1
MouseArea { // Clicking outside the dialog should dismiss
anchors.fill: parent
diff --git a/src/share/sleex/modules/dashboard/Dashboard.qml b/src/share/sleex/modules/dashboard/Dashboard.qml
index a0fd60a7..8c4b32b1 100644
--- a/src/share/sleex/modules/dashboard/Dashboard.qml
+++ b/src/share/sleex/modules/dashboard/Dashboard.qml
@@ -14,6 +14,7 @@ import Quickshell.Widgets
import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Bluetooth
+import Sleex.Fhtc
Scope {
id: dashboardScope
@@ -28,8 +29,8 @@ Scope {
PanelWindow {
id: dashboardRoot
required property var modelData
- readonly property HyprlandMonitor monitor: Hyprland.monitorFor(screen)
- property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id)
+ required property ShellScreen screen
+ property bool monitorIsFocused: (FhtcMonitors.activeMonitorName === screen.name)
screen: modelData
visible: GlobalStates.dashboardOpen && monitorIsFocused
@@ -38,20 +39,13 @@ Scope {
implicitHeight: Screen.height
WlrLayershell.namespace: "quickshell:dashboard"
WlrLayershell.layer: WlrLayer.Overlay
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
color: "transparent"
mask: GlobalStates.dashboardOpen ? null : emptyRegion
Region { id: emptyRegion }
- HyprlandFocusGrab {
- id: grab
- windows: [ dashboardRoot ]
- property bool canBeActive: dashboardRoot.monitorIsFocused
- active: false
- onCleared: () => { if (!active) ipc.close() }
- }
-
Connections {
target: GlobalStates
function onDashboardOpenChanged() {
@@ -158,10 +152,6 @@ Scope {
bottom: parent.bottom
right: parent.right
left: parent.left
- topMargin: Appearance.sizes.hyprlandGapsOut
- rightMargin: Appearance.sizes.hyprlandGapsOut
- bottomMargin: Appearance.sizes.hyprlandGapsOut
- leftMargin: Appearance.sizes.elevationMargin
}
focus: GlobalStates.dashboardOpen && dashboardRoot.monitorIsFocused
Keys.onPressed: (event) => {
@@ -180,10 +170,10 @@ Scope {
Rectangle {
id: dashboardBackground
anchors.fill: parent
- implicitHeight: parent.height - Appearance.sizes.hyprlandGapsOut * 2
- implicitWidth: dashboardWidth - Appearance.sizes.hyprlandGapsOut * 2
+ implicitHeight: parent.height
+ implicitWidth: dashboardWidth
color: Appearance.colors.colLayer0
- radius: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
+ radius: Appearance.rounding.screenRounding
ColumnLayout {
spacing: dashboardPadding
@@ -229,10 +219,10 @@ Scope {
toggled: false
buttonIcon: "restart_alt"
onClicked: {
- Hyprland.dispatch("reload")
+ FhtcIpc.dispatch("reload-config")
Quickshell.reload(true)
}
- StyledToolTip { text: qsTr("Reload Hyprland & Quickshell") }
+ StyledToolTip { text: qsTr("Reload Fhtc & Quickshell") }
}
QuickToggleButton {
toggled: false
@@ -246,7 +236,10 @@ Scope {
QuickToggleButton {
toggled: false
buttonIcon: "power_settings_new"
- onClicked: Hyprland.dispatch("global quickshell:sessionOpen")
+ onClicked: () => {
+ Quickshell.execDetached(["qs", "-p", "/usr/share/sleex/", "ipc", "call", "session", "open"])
+ ipc.close()
+ }
StyledToolTip { text: qsTr("Session") }
}
}
diff --git a/src/share/sleex/modules/dashboard/ai/aiChat/AnnotationSourceButton.qml b/src/share/sleex/modules/dashboard/ai/aiChat/AnnotationSourceButton.qml
index bd75a5d4..8746a59c 100644
--- a/src/share/sleex/modules/dashboard/ai/aiChat/AnnotationSourceButton.qml
+++ b/src/share/sleex/modules/dashboard/ai/aiChat/AnnotationSourceButton.qml
@@ -4,7 +4,6 @@ import qs.modules.common.functions
import qs.services
import QtQuick
import QtQuick.Layouts
-import Quickshell.Hyprland
RippleButton {
id: root
diff --git a/src/share/sleex/modules/dashboard/ai/aiChat/MessageTextBlock.qml b/src/share/sleex/modules/dashboard/ai/aiChat/MessageTextBlock.qml
index d68a260c..e74b45d7 100644
--- a/src/share/sleex/modules/dashboard/ai/aiChat/MessageTextBlock.qml
+++ b/src/share/sleex/modules/dashboard/ai/aiChat/MessageTextBlock.qml
@@ -8,7 +8,6 @@ import qs.modules.common.functions
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
-import Quickshell.Hyprland
ColumnLayout {
id: root
diff --git a/src/share/sleex/modules/dashboard/ai/aiChat/SearchQueryButton.qml b/src/share/sleex/modules/dashboard/ai/aiChat/SearchQueryButton.qml
index 4ad60ac9..699f8a03 100644
--- a/src/share/sleex/modules/dashboard/ai/aiChat/SearchQueryButton.qml
+++ b/src/share/sleex/modules/dashboard/ai/aiChat/SearchQueryButton.qml
@@ -5,7 +5,6 @@ import qs.services
import qs.modules.common.functions
import QtQuick
import QtQuick.Layouts
-import Quickshell.Hyprland
RippleButton {
id: root
diff --git a/src/share/sleex/modules/dashboard/quickToggles/BluetoothToggle.qml b/src/share/sleex/modules/dashboard/quickToggles/BluetoothToggle.qml
index 17591002..5c6c4fbf 100644
--- a/src/share/sleex/modules/dashboard/quickToggles/BluetoothToggle.qml
+++ b/src/share/sleex/modules/dashboard/quickToggles/BluetoothToggle.qml
@@ -8,8 +8,6 @@ import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Bluetooth
-import Quickshell.Hyprland
-
import Sleex.Services
QuickToggleButton {
diff --git a/src/share/sleex/modules/dashboard/quickToggles/GameMode.qml b/src/share/sleex/modules/dashboard/quickToggles/GameMode.qml
deleted file mode 100644
index bc0f5497..00000000
--- a/src/share/sleex/modules/dashboard/quickToggles/GameMode.qml
+++ /dev/null
@@ -1,26 +0,0 @@
-import qs.modules.common
-import qs.modules.common.widgets
-import "../"
-import Quickshell
-import Quickshell.Io
-import Quickshell.Hyprland
-
-QuickToggleButton {
- property bool enabled: false
- buttonIcon: "gamepad"
- toggled: enabled
-
- onClicked: {
- enabled = !enabled
- if (enabled) {
- // gameModeOn.running = true
- Quickshell.execDetached(["hyprctl", "--batch", "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"])
- } else {
- Quickshell.execDetached(["hyprctl reload"])
- }
- }
-
- StyledToolTip {
- text: qsTr("Game mode")
- }
-}
diff --git a/src/share/sleex/modules/dashboard/quickToggles/NetworkToggle.qml b/src/share/sleex/modules/dashboard/quickToggles/NetworkToggle.qml
index b99ec9be..4ef764ca 100644
--- a/src/share/sleex/modules/dashboard/quickToggles/NetworkToggle.qml
+++ b/src/share/sleex/modules/dashboard/quickToggles/NetworkToggle.qml
@@ -7,7 +7,6 @@ import "../"
import QtQuick
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Sleex.Services
diff --git a/src/share/sleex/modules/dock/Dock.qml b/src/share/sleex/modules/dock/Dock.qml
index d96b82eb..f0aaa850 100644
--- a/src/share/sleex/modules/dock/Dock.qml
+++ b/src/share/sleex/modules/dock/Dock.qml
@@ -10,7 +10,6 @@ import Quickshell.Io
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
-import Quickshell.Hyprland
Scope { // Scope
id: root
@@ -27,7 +26,7 @@ Scope { // Scope
property bool reveal: root.pinned
|| (Config.options?.dock.hoverToReveal && dockMouseArea.containsMouse)
|| dockApps.requestDockShow
- || (!ToplevelManager.activeToplevel?.activated)
+ || (ToplevelManager.toplevels?.length === 0)
anchors {
bottom: true
@@ -36,14 +35,14 @@ Scope { // Scope
}
exclusiveZone: root.pinned ? implicitHeight
- - (Appearance.sizes.hyprlandGapsOut)
- - (Appearance.sizes.elevationMargin - Appearance.sizes.hyprlandGapsOut) : 0
+ - (Appearance.sizes.gapsOut)
+ - (Appearance.sizes.elevationMargin - Appearance.sizes.gapsOut) : 0
implicitWidth: dockBackground.implicitWidth
WlrLayershell.namespace: "quickshell:dock"
color: "transparent"
- implicitHeight: (Config.options?.dock.height ?? 70) + Appearance.sizes.elevationMargin + Appearance.sizes.hyprlandGapsOut
+ implicitHeight: (Config.options?.dock.height ?? 70) + Appearance.sizes.elevationMargin + Appearance.sizes.gapsOut
mask: Region {
item: dockMouseArea
@@ -80,7 +79,7 @@ Scope { // Scope
}
implicitWidth: dockRow.implicitWidth + 5 * 2
- height: parent.height - Appearance.sizes.elevationMargin - Appearance.sizes.hyprlandGapsOut
+ height: parent.height - Appearance.sizes.elevationMargin - Appearance.sizes.gapsOut
StyledRectangularShadow {
target: dockVisualBackground
@@ -90,7 +89,7 @@ Scope { // Scope
property real margin: Appearance.sizes.elevationMargin
anchors.fill: parent
anchors.topMargin: Appearance.sizes.elevationMargin
- anchors.bottomMargin: Appearance.sizes.hyprlandGapsOut
+ anchors.bottomMargin: Appearance.sizes.gapsOut
color: Appearance.colors.colLayer0
border.width: 1
border.color: Appearance.m3colors.m3outlineVariant
@@ -106,7 +105,7 @@ Scope { // Scope
property real padding: 5
VerticalButtonGroup {
- Layout.topMargin: Appearance.sizes.hyprlandGapsOut // why does this work
+ Layout.topMargin: Appearance.sizes.gapsOut // why does this work
GroupButton { // Pin button
baseWidth: 35
baseHeight: 35
diff --git a/src/share/sleex/modules/dock/DockApps.qml b/src/share/sleex/modules/dock/DockApps.qml
index d8bb3351..dbe5244d 100644
--- a/src/share/sleex/modules/dock/DockApps.qml
+++ b/src/share/sleex/modules/dock/DockApps.qml
@@ -24,7 +24,7 @@ Item {
property bool requestDockShow: previewPopup.show
Layout.fillHeight: true
- Layout.topMargin: Appearance.sizes.hyprlandGapsOut // why does this work
+ Layout.topMargin: Appearance.sizes.gapsOut // why does this work
implicitWidth: listView.implicitWidth
StyledListView {
diff --git a/src/share/sleex/modules/dock/DockButton.qml b/src/share/sleex/modules/dock/DockButton.qml
index 61655782..e7ec6a8f 100644
--- a/src/share/sleex/modules/dock/DockButton.qml
+++ b/src/share/sleex/modules/dock/DockButton.qml
@@ -7,10 +7,10 @@ import QtQuick.Layouts
RippleButton {
Layout.fillHeight: true
- Layout.topMargin: Appearance.sizes.elevationMargin - Appearance.sizes.hyprlandGapsOut
+ Layout.topMargin: Appearance.sizes.elevationMargin - Appearance.sizes.gapsOut
implicitWidth: implicitHeight - topInset - bottomInset
buttonRadius: Appearance.rounding.normal
- topInset: Appearance.sizes.hyprlandGapsOut + dockRow.padding
- bottomInset: Appearance.sizes.hyprlandGapsOut + dockRow.padding
+ topInset: Appearance.sizes.gapsOut + dockRow.padding
+ bottomInset: Appearance.sizes.gapsOut + dockRow.padding
}
diff --git a/src/share/sleex/modules/dock/DockSeparator.qml b/src/share/sleex/modules/dock/DockSeparator.qml
index 419b0fed..48460e9f 100644
--- a/src/share/sleex/modules/dock/DockSeparator.qml
+++ b/src/share/sleex/modules/dock/DockSeparator.qml
@@ -6,7 +6,7 @@ import QtQuick.Layouts
Rectangle {
Layout.topMargin: Appearance.sizes.elevationMargin + dockRow.padding + Appearance.rounding.normal
- Layout.bottomMargin: Appearance.sizes.hyprlandGapsOut + dockRow.padding + Appearance.rounding.normal
+ Layout.bottomMargin: Appearance.sizes.gapsOut + dockRow.padding + Appearance.rounding.normal
Layout.fillHeight: true
implicitWidth: 1
color: Appearance.colors.colOutlineVariant
diff --git a/src/share/sleex/modules/lockscreen/LockSurface.qml b/src/share/sleex/modules/lockscreen/LockSurface.qml
index 4659be66..8ace81b4 100644
--- a/src/share/sleex/modules/lockscreen/LockSurface.qml
+++ b/src/share/sleex/modules/lockscreen/LockSurface.qml
@@ -44,11 +44,10 @@ FocusScope {
}
// Wallpaper & Scrim
- Image {
+ WallpaperDisplay {
id: wallpaper
anchors.fill: parent
source: Config.options.background.wallpaperPath
- fillMode: Image.PreserveAspectCrop
opacity: root.visualsReady ? 1 : 0
Behavior on opacity {
@@ -64,7 +63,7 @@ FocusScope {
radius: 15
opacity: parent.opacity
}
-
+
MouseArea {
anchors.fill: parent
onClicked: passwordInput.forceActiveFocus()
diff --git a/src/share/sleex/modules/mediaControls/MediaControls.qml b/src/share/sleex/modules/mediaControls/MediaControls.qml
index 0f31b25e..0417b3e8 100644
--- a/src/share/sleex/modules/mediaControls/MediaControls.qml
+++ b/src/share/sleex/modules/mediaControls/MediaControls.qml
@@ -12,7 +12,6 @@ import Quickshell.Io
import Quickshell.Services.Mpris
import Quickshell.Widgets
import Quickshell.Wayland
-import Quickshell.Hyprland
Item {
id: root
diff --git a/src/share/sleex/modules/mediaControls/PlayerControl.qml b/src/share/sleex/modules/mediaControls/PlayerControl.qml
index 94ad7577..d0aa6c10 100644
--- a/src/share/sleex/modules/mediaControls/PlayerControl.qml
+++ b/src/share/sleex/modules/mediaControls/PlayerControl.qml
@@ -12,7 +12,6 @@ import Quickshell.Io
import Quickshell.Services.Mpris
import Quickshell.Widgets
import Quickshell.Wayland
-import Quickshell.Hyprland
Item { // Player instance
id: playerController
diff --git a/src/share/sleex/modules/mediaControls/PlayerControlBlank.qml b/src/share/sleex/modules/mediaControls/PlayerControlBlank.qml
index 493b8623..7d0f92e6 100644
--- a/src/share/sleex/modules/mediaControls/PlayerControlBlank.qml
+++ b/src/share/sleex/modules/mediaControls/PlayerControlBlank.qml
@@ -14,7 +14,6 @@ import Quickshell.Io
import Quickshell.Services.Mpris
import Quickshell.Widgets
import Quickshell.Wayland
-import Quickshell.Hyprland
Item {
id: playerControllerBlank
diff --git a/src/share/sleex/modules/notificationPopup/NotificationPopup.qml b/src/share/sleex/modules/notificationPopup/NotificationPopup.qml
index a302a45c..01aeec19 100644
--- a/src/share/sleex/modules/notificationPopup/NotificationPopup.qml
+++ b/src/share/sleex/modules/notificationPopup/NotificationPopup.qml
@@ -7,7 +7,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
-import Quickshell.Hyprland
+import Sleex.Fhtc
Scope {
id: notificationPopup
@@ -17,7 +17,7 @@ Scope {
PanelWindow {
id: root
visible: (Notifications.popupList.length > 0)
- screen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) ?? null
+ screen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName) ?? null
WlrLayershell.namespace: "quickshell:notificationPopup"
WlrLayershell.layer: WlrLayer.Overlay
diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
index af6115cb..f396b3c7 100644
--- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
+++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayBrightness.qml
@@ -8,11 +8,12 @@ import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import Quickshell.Wayland
+import Sleex.Fhtc
Scope {
id: root
property bool showOsdValues: false
- property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
+ property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName === screen.name)
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
function triggerOsd() {
@@ -148,5 +149,4 @@ Scope {
root.showOsdValues = false
}
}
-
}
\ No newline at end of file
diff --git a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
index 5598f2ef..75ac9209 100644
--- a/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
+++ b/src/share/sleex/modules/onScreenDisplay/OnScreenDisplayVolume.qml
@@ -8,12 +8,13 @@ import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
+import Sleex.Fhtc
Scope {
id: root
property bool showOsdValues: false
property string protectionMessage: ""
- property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
+ property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName === screen.name)
function triggerOsd() {
showOsdValues = true
@@ -200,5 +201,4 @@ Scope {
root.showOsdValues = false
}
}
-
}
\ No newline at end of file
diff --git a/src/share/sleex/modules/overview/Overview.qml b/src/share/sleex/modules/overview/Overview.qml
index 5687357e..8a645eda 100644
--- a/src/share/sleex/modules/overview/Overview.qml
+++ b/src/share/sleex/modules/overview/Overview.qml
@@ -9,6 +9,7 @@ import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
+import Sleex.Fhtc
Scope {
id: overviewScope
@@ -20,23 +21,19 @@ Scope {
id: root
required property var modelData
property string searchingText: ""
- readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
- property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id)
+ required property ShellScreen screen
+ property bool monitorIsFocused: (FhtcMonitors.activeMonitorName === screen.name)
screen: modelData
visible: GlobalStates.overviewOpen && monitorIsFocused
WlrLayershell.namespace: "quickshell:overview"
WlrLayershell.layer: WlrLayer.Overlay
- // WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
+ WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
color: "transparent"
mask: Region {
item: GlobalStates.overviewOpen ? columnLayout : null
}
- HyprlandWindow.visibleMask: Region {
- item: GlobalStates.overviewOpen ? columnLayout : null
- }
-
anchors {
top: true
@@ -45,16 +42,6 @@ Scope {
bottom: true
}
- HyprlandFocusGrab {
- id: grab
- windows: [ root ]
- property bool canBeActive: root.monitorIsFocused
- active: false
- onCleared: () => {
- if (!active) GlobalStates.overviewOpen = false
- }
- }
-
Connections {
target: GlobalStates
function onOverviewOpenChanged() {
@@ -65,21 +52,10 @@ Scope {
if (!overviewScope.dontAutoCancelSearch) {
searchWidget.cancelSearch()
}
- delayedGrabTimer.start()
}
}
}
- Timer {
- id: delayedGrabTimer
- interval: Config.options.hacks.arbitraryRaceConditionDelay
- repeat: false
- onTriggered: {
- if (!grab.canBeActive) return
- grab.active = GlobalStates.overviewOpen
- }
- }
-
implicitWidth: columnLayout.implicitWidth
implicitHeight: columnLayout.implicitHeight
@@ -101,9 +77,9 @@ Scope {
if (event.key === Qt.Key_Escape) {
GlobalStates.overviewOpen = false;
} else if (event.key === Qt.Key_Left) {
- if (!root.searchingText) Hyprland.dispatch("workspace -1");
+ if (!root.searchingText) FhtcIpc.dispatch("focus-previous-workspace");
} else if (event.key === Qt.Key_Right) {
- if (!root.searchingText) Hyprland.dispatch("workspace +1");
+ if (!root.searchingText) FhtcIpc.dispatch("focus-next-workspace");
}
}
@@ -203,7 +179,7 @@ Scope {
}
for (let i = 0; i < overviewVariants.instances.length; i++) {
let panelWindow = overviewVariants.instances[i];
- if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
+ if (panelWindow.modelData.name == FhtcMonitors.activeMonitorName) {
overviewScope.dontAutoCancelSearch = true;
panelWindow.setSearchingText(
Config.options.search.prefix.clipboard
@@ -226,7 +202,7 @@ Scope {
}
for (let i = 0; i < overviewVariants.instances.length; i++) {
let panelWindow = overviewVariants.instances[i];
- if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
+ if (panelWindow.modelData.name == FhtcMonitors.activeMonitorName) {
overviewScope.dontAutoCancelSearch = true;
panelWindow.setSearchingText(
Config.options.search.prefix.emojis
@@ -237,5 +213,4 @@ Scope {
}
}
}
-
}
diff --git a/src/share/sleex/modules/overview/OverviewWidget.qml b/src/share/sleex/modules/overview/OverviewWidget.qml
index 0b8d73ca..a79e188b 100644
--- a/src/share/sleex/modules/overview/OverviewWidget.qml
+++ b/src/share/sleex/modules/overview/OverviewWidget.qml
@@ -10,32 +10,26 @@ import Quickshell
import Quickshell.Io
import Quickshell.Widgets
import Quickshell.Wayland
-import Quickshell.Hyprland
+import Sleex.Fhtc
Item {
id: root
required property var panelWindow
- readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen)
- readonly property var toplevels: ToplevelManager.toplevels
+ readonly property var monitor: FhtcMonitors.activeMonitor
+ // readonly property var toplevels: ToplevelManager.toplevels
readonly property int workspacesShown: Config.options.overview.numOfRows * Config.options.overview.numOfCols
- readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / workspacesShown)
- property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor.id)
- property var windows: HyprlandData.windowList
- property var windowByAddress: HyprlandData.windowByAddress
- property var windowAddresses: HyprlandData.addresses
- property var monitorData: HyprlandData.monitors.find(m => m.id === root.monitor.id)
+ readonly property int workspaceGroup: Math.floor(((monitor["active-workspace-idx"] ?? 0)) / workspacesShown)
+ property bool monitorIsFocused: (FhtcMonitors.activeMonitorName === screen.name)
+ readonly property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName)
property real scale: Config.options.overview.scale
property color activeBorderColor: Appearance.colors.colSecondary
- property real workspaceImplicitWidth: (monitorData?.transform % 2 === 1) ?
- ((monitor.height - monitorData?.reserved[0] - monitorData?.reserved[2]) * root.scale / monitor.scale) :
- ((monitor.width - monitorData?.reserved[0] - monitorData?.reserved[2]) * root.scale / monitor.scale)
- property real workspaceImplicitHeight: (monitorData?.transform % 2 === 1) ?
- ((monitor.width - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale / monitor.scale) :
- ((monitor.height - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale / monitor.scale)
+ property real workspaceImplicitWidth: (focusedScreen?.width ?? 0) * root.scale
+ property real workspaceImplicitHeight: ((focusedScreen?.height ?? 0) - Appearance.sizes.barHeight) * root.scale
property real workspaceNumberMargin: 80
- property real workspaceNumberSize: Math.min(workspaceImplicitHeight, workspaceImplicitWidth) * monitor.scale
+ property real workspaceNumberSize: Math.min(workspaceImplicitHeight, workspaceImplicitWidth) * (panelWindow.screen.devicePixelRatio ?? 1)
+
property int workspaceZ: 0
property int windowZ: 1
property int windowDraggingZ: 99999
@@ -82,7 +76,7 @@ Item {
Rectangle { // Workspace
id: workspace
property int colIndex: index
- property int workspaceValue: root.workspaceGroup * workspacesShown + rowIndex * Config.options.overview.numOfCols + colIndex + 1
+ property int workspaceValue: root.workspaceGroup * workspacesShown + rowIndex * Config.options.overview.numOfCols + colIndex
property color defaultWorkspaceColor: Appearance.colors.colLayer1 // TODO: reconsider this color for a cleaner look
property color hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1)
property color hoveredBorderColor: Appearance.colors.colLayer2Hover
@@ -111,9 +105,8 @@ Item {
acceptedButtons: Qt.LeftButton
onClicked: {
if (root.draggingTargetWorkspace === -1) {
- // Hyprland.dispatch(`exec qs ipc call overview close`)
GlobalStates.overviewOpen = false
- Hyprland.dispatch(`workspace ${workspaceValue}`)
+ FhtcIpc.dispatch("focus-workspace-by-index", { "workspace_idx": workspaceValue })
}
}
}
@@ -146,36 +139,29 @@ Item {
Repeater { // Window repeater
model: ScriptModel {
values: {
- // console.log(JSON.stringify(ToplevelManager.toplevels.values.map(t => t), null, 2))
- return ToplevelManager.toplevels.values.filter((toplevel) => {
- const address = `0x${toplevel.HyprlandToplevel.address}`
- // console.log(`Checking window with address: ${address}`)
- var win = windowByAddress[address]
- return (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown)
+ return Object.values(FhtcWorkspaces.windows).filter((win) => {
+ const wsId = win?.["workspace-id"] ?? -1
+ return wsId >= root.workspaceGroup * root.workspacesShown &&
+ wsId < (root.workspaceGroup + 1) * root.workspacesShown
})
}
}
delegate: OverviewWindow {
id: window
required property var modelData
- property var address: `0x${modelData.HyprlandToplevel.address}`
- windowData: windowByAddress[address]
- toplevel: modelData
- monitorData: root.monitorData
+ windowData: modelData
+ // toplevel: not yet available with fhtc compositor
+ monitorData: root.focusedScreen
scale: root.scale
availableWorkspaceWidth: root.workspaceImplicitWidth
availableWorkspaceHeight: root.workspaceImplicitHeight
- property int monitorId: windowData?.monitor
- property var monitor: HyprlandData.monitors[monitorId]
-
property bool atInitPosition: (initX == x && initY == y)
- restrictToWorkspace: Drag.active || atInitPosition
- property int workspaceColIndex: (windowData?.workspace.id - 1) % Config.options.overview.numOfCols
- property int workspaceRowIndex: Math.floor((windowData?.workspace.id - 1) % root.workspacesShown / Config.options.overview.numOfCols)
- xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex - (monitor?.x * root.scale)
- yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex - (monitor?.y * root.scale)
+ property int workspaceColIndex: (modelData?.["workspace-id"] ?? 0) % Config.options.overview.numOfCols
+ property int workspaceRowIndex: Math.floor((modelData?.["workspace-id"] ?? 0) % root.workspacesShown / Config.options.overview.numOfCols)
+ xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex
+ yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex
Timer {
id: updateWindowPosition
@@ -183,12 +169,13 @@ Item {
repeat: false
running: false
onTriggered: {
- window.x = Math.round(Math.max((windowData?.at[0] - monitorData?.reserved[0]) * root.scale, 0) + xOffset)
- window.y = Math.round(Math.max((windowData?.at[1] - monitorData?.reserved[1]) * root.scale, 0) + yOffset)
- // console.log(`[OverviewWindow] Updated position for window ${windowData?.address} to (${window.x}, ${window.y})`)
+ window.x = Math.round(Math.max(modelData?.location[0] * root.scale, 0) + xOffset)
+ window.y = Math.round(Math.max((modelData?.location[1] - Appearance.sizes.barHeight) * root.scale, 0) + yOffset)
}
}
+ Component.onCompleted: updateWindowPosition.restart()
+
z: atInitPosition ? root.windowZ : root.windowDraggingZ
Drag.hotSpot.x: targetWindowWidth / 2
Drag.hotSpot.y: targetWindowHeight / 2
@@ -201,35 +188,32 @@ Item {
acceptedButtons: Qt.LeftButton | Qt.MiddleButton
drag.target: parent
onPressed: {
- root.draggingFromWorkspace = windowData?.workspace.id
+ root.draggingFromWorkspace = modelData?.["workspace-id"]
window.pressed = true
window.Drag.active = true
window.Drag.source = window
- // console.log(`[OverviewWindow] Dragging window ${windowData?.address} from position (${window.x}, ${window.y})`)
}
onReleased: {
const targetWorkspace = root.draggingTargetWorkspace
window.pressed = false
window.Drag.active = false
root.draggingFromWorkspace = -1
- if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) {
- Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`)
+ if (targetWorkspace !== -1 && targetWorkspace !== modelData?.["workspace-id"]) {
+ FhtcIpc.dispatch("send-window-to-workspace", { "window-id": modelData?.id, "workspace-id": targetWorkspace })
updateWindowPosition.restart()
- }
- else {
+ } else {
window.x = window.initX
window.y = window.initY
}
}
onClicked: (event) => {
- if (!windowData) return;
-
+ if (!modelData) return;
if (event.button === Qt.LeftButton) {
GlobalStates.overviewOpen = false
- Hyprland.dispatch(`focuswindow address:${windowData.address}`)
+ FhtcIpc.dispatch("focus-window", { "window-id": modelData.id })
event.accepted = true
} else if (event.button === Qt.MiddleButton) {
- Hyprland.dispatch(`closewindow address:${windowData.address}`)
+ FhtcIpc.dispatch("close-window", { "window-id": modelData.id })
event.accepted = true
}
}
@@ -237,7 +221,7 @@ Item {
StyledToolTip {
extraVisibleCondition: false
alternativeVisibleCondition: dragArea.containsMouse && !window.Drag.active
- text: `${windowData.title}\n[${windowData.class}] ${windowData.xwayland ? "[XWayland] " : ""}\n`
+ text: `${modelData.title}\n[${modelData["app-id"]}]\n`
}
}
}
@@ -245,9 +229,9 @@ Item {
Rectangle { // Focused workspace indicator
id: focusedWorkspaceIndicator
- property int activeWorkspaceInGroup: monitor.activeWorkspace?.id - (root.workspaceGroup * root.workspacesShown)
- property int activeWorkspaceRowIndex: Math.floor((activeWorkspaceInGroup - 1) / Config.options.overview.numOfCols)
- property int activeWorkspaceColIndex: (activeWorkspaceInGroup - 1) % Config.options.overview.numOfCols
+ property int activeWorkspaceInGroup: (monitor["active-workspace-idx"] ?? 0) - (root.workspaceGroup * root.workspacesShown)
+ property int activeWorkspaceRowIndex: Math.floor(activeWorkspaceInGroup / Config.options.overview.numOfCols)
+ property int activeWorkspaceColIndex: activeWorkspaceInGroup % Config.options.overview.numOfCols
x: (root.workspaceImplicitWidth + workspaceSpacing) * activeWorkspaceColIndex
y: (root.workspaceImplicitHeight + workspaceSpacing) * activeWorkspaceRowIndex
z: root.windowZ
diff --git a/src/share/sleex/modules/overview/OverviewWindow.qml b/src/share/sleex/modules/overview/OverviewWindow.qml
index e75a6487..c8c6caf9 100644
--- a/src/share/sleex/modules/overview/OverviewWindow.qml
+++ b/src/share/sleex/modules/overview/OverviewWindow.qml
@@ -11,7 +11,6 @@ import Quickshell
import Quickshell.Widgets
import Quickshell.Io
import Quickshell.Wayland
-import Quickshell.Hyprland
Item { // Window
id: root
@@ -22,8 +21,8 @@ Item { // Window
property var availableWorkspaceWidth
property var availableWorkspaceHeight
property bool restrictToWorkspace: true
- property real initX: Math.max((windowData?.at[0] - monitorData?.reserved[0]) * root.scale, 0) + xOffset
- property real initY: Math.max((windowData?.at[1] - monitorData?.reserved[1]) * root.scale, 0) + yOffset
+ property real initX: Math.max(windowData?.location[0] * root.scale, 0) + xOffset
+ property real initY: Math.max((windowData?.location[1] - Appearance.sizes.barHeight) * root.scale, 0) + yOffset
property real xOffset: 0
property real yOffset: 0
@@ -35,10 +34,10 @@ Item { // Window
property var iconToWindowRatio: 0.35
property var xwaylandIndicatorToIconRatio: 0.35
property var iconToWindowRatioCompact: 0.6
- property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.class), "image-missing")
+ property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.["app-id"]), "image-missing")
property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth
- property bool indicateXWayland: (Config.options.overview.showXwaylandIndicator && windowData?.xwayland) ?? false
+ // property bool indicateXWayland: (Config.options.overview.showXwaylandIndicator && windowData?.xwayland) ?? false
x: initX
y: initY
@@ -70,7 +69,7 @@ Item { // Window
ScreencopyView {
id: windowPreview
anchors.fill: parent
- captureSource: GlobalStates.overviewOpen ? root.toplevel : null
+ captureSource: null // GlobalStates.overviewOpen ? root.toplevel : null
live: true
Rectangle {
diff --git a/src/share/sleex/modules/overview/SearchItem.qml b/src/share/sleex/modules/overview/SearchItem.qml
index 272907fd..f14f5430 100644
--- a/src/share/sleex/modules/overview/SearchItem.qml
+++ b/src/share/sleex/modules/overview/SearchItem.qml
@@ -8,7 +8,6 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
-import Quickshell.Hyprland
RippleButton {
id: root
diff --git a/src/share/sleex/modules/polkit/Polkit.qml b/src/share/sleex/modules/polkit/Polkit.qml
index 6a8ac3bf..85cbfac0 100644
--- a/src/share/sleex/modules/polkit/Polkit.qml
+++ b/src/share/sleex/modules/polkit/Polkit.qml
@@ -6,7 +6,6 @@ import qs.modules.common.functions
import QtQuick
import Quickshell
import Quickshell.Wayland
-import Quickshell.Hyprland
Scope {
id: root
@@ -29,7 +28,7 @@ Scope {
color: "transparent"
WlrLayershell.namespace: "quickshell:polkit"
- WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
WlrLayershell.layer: WlrLayer.Overlay
exclusionMode: ExclusionMode.Ignore
diff --git a/src/share/sleex/modules/screenCorners/ScreenCorners.qml b/src/share/sleex/modules/screenCorners/ScreenCorners.qml
index 91aeb21e..f487b412 100644
--- a/src/share/sleex/modules/screenCorners/ScreenCorners.qml
+++ b/src/share/sleex/modules/screenCorners/ScreenCorners.qml
@@ -5,7 +5,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
-import Quickshell.Hyprland
Scope {
id: screenCorners
@@ -26,20 +25,6 @@ Scope {
mask: Region {
item: null
}
- HyprlandWindow.visibleMask: Region {
- Region {
- item: topLeftCorner
- }
- Region {
- item: topRightCorner
- }
- Region {
- item: bottomLeftCorner
- }
- Region {
- item: bottomRightCorner
- }
- }
WlrLayershell.namespace: "quickshell:screenCorners"
WlrLayershell.layer: WlrLayer.Overlay
color: "transparent"
diff --git a/src/share/sleex/modules/session/Session.qml b/src/share/sleex/modules/session/Session.qml
index 9987efca..0c937600 100644
--- a/src/share/sleex/modules/session/Session.qml
+++ b/src/share/sleex/modules/session/Session.qml
@@ -10,10 +10,11 @@ import Quickshell.Io
import Quickshell.Widgets
import Quickshell.Wayland
import Quickshell.Hyprland
+import Sleex.Fhtc
Scope {
id: root
- property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
+ property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName)
Loader {
id: sessionLoader
@@ -111,7 +112,7 @@ Scope {
id: sessionLogout
buttonIcon: "logout"
buttonText: qsTr("Logout")
- onClicked: { Quickshell.execDetached(["pkill", "Hyprland"]); sessionRoot.hide() }
+ onClicked: { Quickshell.execDetached(["pkill", "sleex"]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.left: sessionSleep
KeyNavigation.right: sessionTaskManager
@@ -224,5 +225,4 @@ Scope {
sessionLoader.active = true;
}
}
-
}
diff --git a/src/share/sleex/modules/settings/Display.qml b/src/share/sleex/modules/settings/Display.qml
index c06df541..a4dc4c63 100644
--- a/src/share/sleex/modules/settings/Display.qml
+++ b/src/share/sleex/modules/settings/Display.qml
@@ -5,14 +5,14 @@ import Quickshell
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-import Quickshell.Hyprland
+import Sleex.Fhtc
import Sleex.Services
import "displaySettings" as DS
ContentPage {
id: root
- property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
+ property var focusedScreen: Quickshell.screens.find(s => s.name === FhtcMonitors.activeMonitorName === screen.name)
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
property int nlStartHour: parseInt(Config.options.display.nightLightFrom?.split(":")[0] ?? "20")
diff --git a/src/share/sleex/modules/settings/Style.qml b/src/share/sleex/modules/settings/Style.qml
index f7815cce..69990498 100644
--- a/src/share/sleex/modules/settings/Style.qml
+++ b/src/share/sleex/modules/settings/Style.qml
@@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Quickshell.Widgets
import Qt5Compat.GraphicalEffects
import QtMultimedia
diff --git a/src/share/sleex/modules/settings/displaySettings/DisplaySettings.qml b/src/share/sleex/modules/settings/displaySettings/DisplaySettings.qml
index e154e08e..13164aa8 100644
--- a/src/share/sleex/modules/settings/displaySettings/DisplaySettings.qml
+++ b/src/share/sleex/modules/settings/displaySettings/DisplaySettings.qml
@@ -267,7 +267,7 @@ Item {
target === "None" ? "" : target
)
- // If un-mirroring, give Hyprland more time to re-expose the monitor
+ // If un-mirroring, give the compositor more time to re-expose the monitor
if (target === "None") {
Qt.callLater(() => {
Qt.createQmlObject('import QtQuick 2.0; Timer { interval: 1000; running: true; onTriggered: Monitors.refresh() }', root)
diff --git a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml b/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml
deleted file mode 100644
index fbdfe662..00000000
--- a/src/share/sleex/modules/sidebarLeft/SidebarLeft.qml
+++ /dev/null
@@ -1,203 +0,0 @@
-import qs
-import qs.services
-import qs.modules.common
-import qs.modules.common.widgets
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Effects
-import Qt5Compat.GraphicalEffects
-import Quickshell.Io
-import Quickshell
-import Quickshell.Widgets
-import Quickshell.Wayland
-import Quickshell.Hyprland
-
-Scope { // Scope
- id: root
- property int sidebarPadding: 15
- property bool detach: false
- property Component contentComponent: SidebarLeftContent {}
- property Item sidebarContent
-
- Component.onCompleted: {
- root.sidebarContent = contentComponent.createObject(null, {
- "scopeRoot": root,
- });
- sidebarLoader.item.contentParent.children = [root.sidebarContent];
- }
-
- onDetachChanged: {
- if (root.detach) {
- sidebarContent.parent = null; // Detach content from sidebar
- sidebarLoader.active = false; // Unload sidebar
- detachedSidebarLoader.active = true; // Load detached window
- detachedSidebarLoader.item.contentParent.children = [sidebarContent];
- } else {
- sidebarContent.parent = null; // Detach content from window
- detachedSidebarLoader.active = false; // Unload detached window
- sidebarLoader.active = true; // Load sidebar
- sidebarLoader.item.contentParent.children = [sidebarContent];
- }
- }
-
- Loader {
- id: sidebarLoader
- active: true
-
- sourceComponent: PanelWindow { // Window
- id: sidebarRoot
- visible: GlobalStates.sidebarLeftOpen
-
- property bool extend: false
- property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth
- property var contentParent: sidebarLeftBackground
-
- function hide() {
- GlobalStates.sidebarLeftOpen = false
- }
-
- exclusiveZone: 0
- implicitWidth: Appearance.sizes.sidebarWidthExtended + Appearance.sizes.elevationMargin
- WlrLayershell.namespace: "quickshell:sidebarLeft"
- // Hyprland 0.49: OnDemand is Exclusive, Exclusive just breaks click-outside-to-close
- // WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
- color: "transparent"
-
- anchors {
- top: true
- left: true
- bottom: true
- }
-
- mask: Region {
- item: sidebarLeftBackground
- }
-
- HyprlandFocusGrab { // Click outside to close
- id: grab
- windows: [ sidebarRoot ]
- active: sidebarRoot.visible
- onCleared: () => {
- if (!active) sidebarRoot.hide()
- }
- }
-
- // Content
- StyledRectangularShadow {
- target: sidebarLeftBackground
- radius: sidebarLeftBackground.radius
- visible: Config.options.appearance.transparency
- }
- Rectangle {
- id: sidebarLeftBackground
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.topMargin: Appearance.sizes.hyprlandGapsOut
- anchors.leftMargin: Appearance.sizes.hyprlandGapsOut
- width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
- height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
- color: Appearance.colors.colLayer0
- radius: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
-
- Behavior on width {
- animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
- }
-
- Keys.onPressed: (event) => {
- if (event.key === Qt.Key_Escape) {
- sidebarRoot.hide();
- }
- if (event.modifiers === Qt.ControlModifier) {
- if (event.key === Qt.Key_O) {
- sidebarRoot.extend = !sidebarRoot.extend;
- }
- else if (event.key === Qt.Key_P) {
- root.detach = !root.detach;
- }
- event.accepted = true;
- }
- }
- }
- }
- }
-
- Loader {
- id: detachedSidebarLoader
- active: false
-
- sourceComponent: FloatingWindow {
- id: detachedSidebarRoot
- visible: GlobalStates.sidebarLeftOpen
- property var contentParent: detachedSidebarBackground
-
- Rectangle {
- id: detachedSidebarBackground
- anchors.fill: parent
- color: Appearance.colors.colLayer0
-
- Keys.onPressed: (event) => {
- if (event.modifiers === Qt.ControlModifier) {
- if (event.key === Qt.Key_P) {
- root.detach = !root.detach;
- }
- event.accepted = true;
- }
- }
- }
- }
- }
-
- IpcHandler {
- target: "sidebarLeft"
-
- function toggle(): void {
- GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen
- }
-
- function close(): void {
- GlobalStates.sidebarLeftOpen = false
- }
-
- function open(): void {
- GlobalStates.sidebarLeftOpen = true
- }
- }
-
- GlobalShortcut {
- name: "sidebarLeftToggle"
- description: qsTr("Toggles left sidebar on press")
-
- onPressed: {
- GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
- }
- }
-
- GlobalShortcut {
- name: "sidebarLeftOpen"
- description: qsTr("Opens left sidebar on press")
-
- onPressed: {
- GlobalStates.sidebarLeftOpen = true;
- }
- }
-
- GlobalShortcut {
- name: "sidebarLeftClose"
- description: qsTr("Closes left sidebar on press")
-
- onPressed: {
- GlobalStates.sidebarLeftOpen = false;
- }
- }
-
- GlobalShortcut {
- name: "sidebarLeftToggleDetach"
- description: qsTr("Detach left sidebar into a window/Attach it back")
-
- onPressed: {
- root.detach = !root.detach;
- }
- }
-
-}
diff --git a/src/share/sleex/modules/sidebarLeft/SidebarLeftContent.qml b/src/share/sleex/modules/sidebarLeft/SidebarLeftContent.qml
deleted file mode 100644
index a2559978..00000000
--- a/src/share/sleex/modules/sidebarLeft/SidebarLeftContent.qml
+++ /dev/null
@@ -1,44 +0,0 @@
-import qs
-import qs.services
-import qs.modules.common
-import qs.modules.common.widgets
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Effects
-import Qt5Compat.GraphicalEffects
-import Quickshell.Io
-import Quickshell
-import Quickshell.Widgets
-import Quickshell.Wayland
-import Quickshell.Hyprland
-
-Item {
- id: root
- required property var scopeRoot
- anchors.fill: parent
-
- ColumnLayout {
- anchors.fill: parent
- anchors.margins: sidebarPadding
-
- spacing: sidebarPadding
-
- SwipeView { // Content pages
- id: swipeView
- Layout.topMargin: 5
- Layout.fillWidth: true
- Layout.fillHeight: true
- spacing: 10
-
- contentChildren: [
- aiChat.createObject()
- ]
- }
-
- Component {
- id: aiChat
- AiChat {}
- }
- }
-}
\ No newline at end of file
diff --git a/src/share/sleex/modules/sidebarLeft/aiChat/AnnotationSourceButton.qml b/src/share/sleex/modules/sidebarLeft/aiChat/AnnotationSourceButton.qml
index bd75a5d4..8746a59c 100644
--- a/src/share/sleex/modules/sidebarLeft/aiChat/AnnotationSourceButton.qml
+++ b/src/share/sleex/modules/sidebarLeft/aiChat/AnnotationSourceButton.qml
@@ -4,7 +4,6 @@ import qs.modules.common.functions
import qs.services
import QtQuick
import QtQuick.Layouts
-import Quickshell.Hyprland
RippleButton {
id: root
diff --git a/src/share/sleex/modules/sidebarLeft/aiChat/MessageTextBlock.qml b/src/share/sleex/modules/sidebarLeft/aiChat/MessageTextBlock.qml
index d68a260c..e74b45d7 100644
--- a/src/share/sleex/modules/sidebarLeft/aiChat/MessageTextBlock.qml
+++ b/src/share/sleex/modules/sidebarLeft/aiChat/MessageTextBlock.qml
@@ -8,7 +8,6 @@ import qs.modules.common.functions
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
-import Quickshell.Hyprland
ColumnLayout {
id: root
diff --git a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
index 4630d489..adbf63ed 100644
--- a/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
+++ b/src/share/sleex/modules/wallpaperSelector/WallpaperSelector.qml
@@ -30,19 +30,9 @@ Scope {
implicitWidth: 1920
implicitHeight: 200
WlrLayershell.namespace: "quickshell:wppselector"
- // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
- // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
color: "transparent"
- HyprlandFocusGrab {
- id: grab
- windows: [ wppselectorRoot ]
- active: GlobalStates.wppselectorOpen
- onCleared: () => {
- if (!active) wppselectorRoot.hide()
- }
- }
-
anchors {
top: true
}
@@ -54,13 +44,13 @@ Scope {
bottom: parent.bottom
right: parent.right
left: parent.left
- topMargin: Appearance.sizes.hyprlandGapsOut
- rightMargin: Appearance.sizes.hyprlandGapsOut
- bottomMargin: Appearance.sizes.hyprlandGapsOut
+ topMargin: Appearance.sizes.gapsOut
+ rightMargin: Appearance.sizes.gapsOut
+ bottomMargin: Appearance.sizes.gapsOut
leftMargin: Appearance.sizes.elevationMargin
}
- width: wppselectorWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
- height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
+ width: wppselectorWidth - Appearance.sizes.gapsOut - Appearance.sizes.elevationMargin
+ height: parent.height - Appearance.sizes.gapsOut * 2
focus: GlobalStates.wppselectorOpen
Keys.onPressed: (event) => {
@@ -77,10 +67,10 @@ Scope {
id: wppselectorBackground
anchors.fill: parent
- implicitHeight: parent.height - Appearance.sizes.hyprlandGapsOut * 2
- implicitWidth: wppselectorWidth - Appearance.sizes.hyprlandGapsOut * 2
+ implicitHeight: parent.height - Appearance.sizes.gapsOut * 2
+ implicitWidth: wppselectorWidth - Appearance.sizes.gapsOut * 2
color: Appearance.colors.colLayer0
- radius: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
+ radius: Appearance.rounding.screenRounding - Appearance.sizes.gapsOut + 1
Rectangle {
id: flickableBg
@@ -193,5 +183,4 @@ Scope {
GlobalStates.wppselectorOpen = false;
}
}
-
}
diff --git a/src/share/sleex/plugins/src/Sleex/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/CMakeLists.txt
index ef3fd398..4c51fca7 100644
--- a/src/share/sleex/plugins/src/Sleex/CMakeLists.txt
+++ b/src/share/sleex/plugins/src/Sleex/CMakeLists.txt
@@ -36,4 +36,5 @@ endfunction()
add_subdirectory(services)
add_subdirectory(core)
add_subdirectory(utils)
-add_subdirectory(widgets)
\ No newline at end of file
+add_subdirectory(widgets)
+add_subdirectory(fhtc)
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
new file mode 100644
index 00000000..50cb6f15
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/CMakeLists.txt
@@ -0,0 +1,16 @@
+
+qml_module(sleex-fhtc
+ URI Sleex.Fhtc
+ SOURCES
+ ipc.hpp ipc.cpp
+ workspaces.hpp workspaces.cpp
+ monitors.hpp monitors.cpp
+ plugin.cpp
+)
+
+target_link_libraries(sleex-fhtc
+ PRIVATE
+ Qt6::Core
+ Qt6::Qml
+ Qt6::Network
+)
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp
new file mode 100644
index 00000000..a2b4454f
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.cpp
@@ -0,0 +1,220 @@
+#include "ipc.hpp"
+#include
+#include
+#include
+#include
+#include
+
+Ipc::Ipc(QObject *parent)
+: QObject(parent),
+m_requestSocket(new QLocalSocket(this)),
+m_eventSocket(new QLocalSocket(this))
+{
+ m_socketPath = qEnvironmentVariable("FHTC_SOCKET_PATH");
+ if (m_socketPath.isEmpty()) {
+ emit error("The environment variable FHTC_SOCKET_PATH is not set.");
+ qWarning() << "Ipc: FHTC_SOCKET_PATH is not set.";
+ }
+
+ // Connections for the request socket
+ connect(m_requestSocket, &QLocalSocket::connected, this, &Ipc::onRequestConnected);
+ connect(m_requestSocket, &QLocalSocket::readyRead, this, &Ipc::onRequestReadyRead);
+ connect(m_requestSocket, &QLocalSocket::errorOccurred, this, &Ipc::onRequestError);
+ connect(m_requestSocket, &QLocalSocket::disconnected, this, &Ipc::onRequestDisconnected);
+
+ // Connections for the event socket
+ connect(m_eventSocket, &QLocalSocket::connected, this, &Ipc::onEventConnected);
+ connect(m_eventSocket, &QLocalSocket::readyRead, this, &Ipc::onEventReadyRead);
+ connect(m_eventSocket, &QLocalSocket::errorOccurred, this, &Ipc::onEventError);
+ connect(m_eventSocket, &QLocalSocket::disconnected, this, &Ipc::onEventDisconnected);
+}
+
+Ipc::~Ipc()
+{
+ // Sockets are automatically destroyed because 'this' is their parent
+}
+
+
+void Ipc::subscribe()
+{
+ if (m_socketPath.isEmpty()) return;
+ if (m_eventSocket->state() == QLocalSocket::UnconnectedState) {
+ qDebug() << "Ipc: Connecting to the event socket...";
+ m_eventSocket->connectToServer(m_socketPath);
+ }
+}
+
+void Ipc::dispatch(const QString &name, const QVariantMap &args)
+{
+ if (!args.isEmpty() || name.contains("workspace")) {
+ QVariantMap actionBody;
+ actionBody.insert(name, args);
+ sendAction(actionBody);
+ }
+ else {
+ sendAction(name);
+ }
+}
+
+void Ipc::sendRequest(const QVariant &request)
+{
+ if (m_socketPath.isEmpty()) return;
+
+ // qDebug() << "Ipc: Queuing request:" << request;
+
+ m_pendingRequests.enqueue(request);
+ if (m_requestSocket->state() == QLocalSocket::UnconnectedState) {
+ qDebug() << "Ipc: Connecting to the request socket...";
+ m_requestSocket->connectToServer(m_socketPath);
+ } else if (m_requestSocket->state() == QLocalSocket::ConnectedState) {
+ // If already connected, send the pending request
+ writeJson(m_requestSocket, m_pendingRequests.dequeue());
+ }
+ // If 'Connecting', the request will be sent in onRequestConnected
+}
+
+void Ipc::sendAction(const QVariant &action)
+{
+ QVariantMap request;
+ request.insert("action", action);
+ sendRequest(request);
+}
+
+
+void Ipc::onRequestConnected()
+{
+ qDebug() << "Ipc: Request socket connected.";
+ // Send the first pending request (if any)
+ if (!m_pendingRequests.isEmpty()) {
+ writeJson(m_requestSocket, m_pendingRequests.dequeue());
+ }
+}
+
+void Ipc::onRequestReadyRead()
+{
+ m_requestBuffer.append(m_requestSocket->readAll());
+
+ // The fhtc IPC guarantees one response per request on this socket.
+ // We do not loop, we just wait for a complete line.
+ QVariant response = parseLine(m_requestBuffer);
+ if (!response.isNull()) {
+ // qDebug() << "Ipc: Received response:" << response;
+
+ emit requestResponse(response);
+
+ // If there are other requests, send them
+ if (!m_pendingRequests.isEmpty()) {
+ writeJson(m_requestSocket, m_pendingRequests.dequeue());
+ }
+ }
+}
+
+void Ipc::onRequestError(QLocalSocket::LocalSocketError socketError)
+{
+ Q_UNUSED(socketError);
+ QString msg = "Ipc (Request): " + m_requestSocket->errorString();
+ qWarning() << msg;
+ emit error(msg);
+ m_pendingRequests.clear(); // Clear pending requests
+}
+
+void Ipc::onRequestDisconnected()
+{
+ qDebug() << "Ipc: Request socket disconnected.";
+ if (!m_requestBuffer.isEmpty()) {
+ qWarning() << "Ipc (Request): Disconnected with data in buffer:" << m_requestBuffer;
+ m_requestBuffer.clear();
+ }
+}
+
+
+void Ipc::onEventConnected()
+{
+ qDebug() << "Ipc: Event socket connected. Sending 'subscribe' request.";
+ emit subscribed();
+
+ // Send the subscribe request
+ QVariantMap subRequest;
+ subRequest.insert("subscribe", QVariant()); // "subscribe": null
+ writeJson(m_eventSocket, subRequest);
+}
+
+void Ipc::onEventReadyRead()
+{
+ m_eventBuffer.append(m_eventSocket->readAll());
+
+ // There may be multiple JSON events in the buffer
+ while (true) {
+ QVariant event = parseLine(m_eventBuffer);
+ if (event.isNull()) {
+ break; // Incomplete line, wait for more data
+ }
+ emit newEvent(event);
+ }
+}
+
+void Ipc::onEventError(QLocalSocket::LocalSocketError socketError)
+{
+ Q_UNUSED(socketError);
+ QString msg = "Ipc (Event): " + m_eventSocket->errorString();
+ qWarning() << msg;
+ emit error(msg);
+}
+
+void Ipc::onEventDisconnected()
+{
+ QString msg = "Ipc: Event socket disconnected. Attempting to reconnect in 5s...";
+ qWarning() << msg;
+ emit error(msg);
+
+ // Attempt automatic reconnection for the event socket
+ QTimer::singleShot(5000, this, &Ipc::subscribe);
+}
+
+// --- Utility functions ---
+void Ipc::writeJson(QLocalSocket *socket, const QVariant &data)
+{
+ if (socket->state() != QLocalSocket::ConnectedState) {
+ qWarning() << "Ipc: Attempt to write to a socket that is not connected.";
+ return;
+ }
+
+ QJsonDocument doc = QJsonDocument::fromVariant(data);
+ if (doc.isNull()) {
+ qWarning() << "Ipc: Failed to convert QVariant to JSON.";
+ return;
+ }
+
+ QByteArray json = doc.toJson(QJsonDocument::Compact);
+ json.append('\n'); // fhtc IPC is delimited by newlines
+
+ socket->write(json);
+}
+
+QVariant Ipc::parseLine(QByteArray &buffer)
+{
+ int newlineIdx = buffer.indexOf('\n');
+ if (newlineIdx == -1) {
+ return QVariant(); // Incomplete line
+ }
+
+ // Extract the line and remove it from the buffer
+ QByteArray line = buffer.left(newlineIdx);
+ buffer = buffer.mid(newlineIdx + 1);
+
+ if (line.isEmpty()) {
+ return QVariant(); // Empty line, ignore
+ }
+
+ QJsonParseError parseError;
+ QJsonDocument doc = QJsonDocument::fromJson(line, &parseError);
+
+ if (parseError.error != QJsonParseError::NoError) {
+ QString msg = "Ipc: JSON parse error: " + parseError.errorString() + " | Line: " + QString(line);
+ qWarning() << msg;
+ emit error(msg);
+ return QVariant();
+ }
+
+ return doc.toVariant();
+}
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp
new file mode 100644
index 00000000..ca5abb6f
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/ipc.hpp
@@ -0,0 +1,56 @@
+#ifndef IPC_H
+#define IPC_H
+
+#include
+#include
+#include
+#include
+#include
+
+class Ipc : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(FhtcIpc)
+ QML_SINGLETON
+
+public:
+ explicit Ipc(QObject *parent = nullptr);
+ ~Ipc() override;
+
+signals:
+ void error(const QString &message);
+ void requestResponse(const QVariant &response);
+ void subscribed();
+ void newEvent(const QVariant &event);
+
+public slots:
+ void subscribe();
+ void sendRequest(const QVariant &request);
+ void sendAction(const QVariant &action);
+ void dispatch(const QString &name, const QVariantMap &args = {});
+
+private slots:
+ void onRequestConnected();
+ void onRequestReadyRead();
+ void onRequestError(QLocalSocket::LocalSocketError socketError);
+ void onRequestDisconnected();
+
+ void onEventConnected();
+ void onEventReadyRead();
+ void onEventError(QLocalSocket::LocalSocketError socketError);
+ void onEventDisconnected();
+
+private:
+ void writeJson(QLocalSocket* socket, const QVariant &data);
+ QVariant parseLine(QByteArray &buffer);
+
+ QString m_socketPath;
+ QLocalSocket *m_requestSocket;
+ QLocalSocket *m_eventSocket;
+
+ QByteArray m_eventBuffer;
+ QByteArray m_requestBuffer;
+ QQueue m_pendingRequests;
+};
+
+#endif // IPC_H
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/monitors.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.cpp
new file mode 100644
index 00000000..05db8968
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.cpp
@@ -0,0 +1,46 @@
+#include "monitors.hpp"
+#include
+
+Monitors::Monitors(QObject *parent) : QObject(parent)
+{
+ m_ipc = new Ipc(this);
+
+ connect(m_ipc, &Ipc::newEvent, this, &Monitors::handleEvent);
+
+ m_ipc->subscribe();
+}
+
+void Monitors::handleEvent(const QVariant &eventVar)
+{
+ QVariantMap event = eventVar.toMap();
+ QString type = event.value("event").toString();
+ QVariant data = event.value("data");
+
+ if (type == "space") {
+ QVariant data = event.value("data");
+ QVariantMap monitorsMap = data.toMap().value("monitors").toMap();
+
+ m_monitors = monitorsMap;
+
+ bool activeFound = false;
+ QMapIterator i(m_monitors);
+ while (i.hasNext()) {
+ i.next();
+ QVariantMap monitor = i.value().toMap();
+ if (monitor.value("active").toBool()) {
+ m_activeMonitor = monitor;
+ m_activeMonitorName = monitor.value("output").toString();
+ activeFound = true;
+ break;
+ }
+ }
+
+ if (!activeFound) {
+ m_activeMonitor = QVariant();
+ }
+
+ emit monitorsChanged();
+ emit activeMonitorChanged();
+ emit activeMonitorNameChanged();
+ }
+}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/monitors.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.hpp
new file mode 100644
index 00000000..fd0a3fc6
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/monitors.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include "ipc.hpp"
+
+class Monitors : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(FhtcMonitors)
+ QML_SINGLETON
+
+ Q_PROPERTY(QVariantMap monitors READ monitors NOTIFY monitorsChanged)
+ Q_PROPERTY(QVariant activeMonitor READ activeMonitor NOTIFY activeMonitorChanged)
+ Q_PROPERTY(QString activeMonitorName READ activeMonitorName NOTIFY activeMonitorNameChanged)
+
+public:
+ explicit Monitors(QObject *parent = nullptr);
+
+ QVariantMap monitors() const { return m_monitors; }
+ QVariant activeMonitor() const { return m_activeMonitor; }
+ QString activeMonitorName() const { return m_activeMonitorName; }
+
+signals:
+ void monitorsChanged();
+ void activeMonitorChanged();
+ void activeMonitorNameChanged();
+
+private slots:
+ void handleEvent(const QVariant &eventVar);
+
+private:
+ Ipc *m_ipc;
+
+ QVariantMap m_monitors;
+ QVariant m_activeMonitor;
+ QString m_activeMonitorName;
+};
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/plugin.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/plugin.cpp
new file mode 100644
index 00000000..aff6826b
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/plugin.cpp
@@ -0,0 +1,14 @@
+#include
+
+class SleexFhtcPlugin : public QQmlExtensionPlugin {
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+
+public:
+ void registerTypes(const char *uri) override {
+ // QML_ELEMENT and QML_SINGLETON macros handle registration automatically
+ Q_UNUSED(uri)
+ }
+};
+
+#include "plugin.moc"
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
new file mode 100644
index 00000000..145f2d5a
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.cpp
@@ -0,0 +1,94 @@
+#include "workspaces.hpp"
+#include
+
+Workspaces::Workspaces(QObject *parent) : QObject(parent)
+{
+ m_ipc = new Ipc(this);
+
+ connect(m_ipc, &Ipc::newEvent, this, &Workspaces::handleEvent);
+
+ m_ipc->subscribe();
+}
+
+void Workspaces::handleEvent(const QVariant &eventVar)
+{
+ QVariantMap event = eventVar.toMap();
+ QString type = event.value("event").toString();
+ QVariant data = event.value("data");
+
+ if (type == "windows") {
+ m_windows = data.toMap();
+ emit windowsChanged();
+ }
+ else if (type == "focused-window-changed") {
+ QVariantMap dataMap = data.toMap();
+ QVariant idVar = dataMap.value("id");
+
+ if (idVar.isNull() || !idVar.isValid()) {
+ m_focusedWindowId = -1;
+ m_focusedWindow = QVariant();
+ } else {
+ m_focusedWindowId = idVar.toInt();
+ m_focusedWindow = m_windows.value(QString::number(m_focusedWindowId));
+ }
+ emit focusedWindowIdChanged();
+ emit focusedWindowChanged();
+ }
+ else if (type == "window-closed") {
+ int id = data.toMap().value("id").toInt();
+ m_windows.remove(QString::number(id));
+ emit windowsChanged();
+ }
+ else if (type == "window-changed") {
+ QVariantMap win = data.toMap();
+ int id = win.value("id").toInt();
+
+ m_windows.insert(QString::number(id), win);
+ emit windowsChanged();
+
+ if (id == m_focusedWindowId) {
+ m_focusedWindow = win;
+ emit focusedWindowChanged();
+ }
+ }
+ else if (type == "workspaces") {
+ m_workspaces = data.toMap();
+ emit workspacesChanged();
+
+ if (m_activeWorkspaceId != -1) {
+ m_activeWorkspace = m_workspaces.value(QString::number(m_activeWorkspaceId));
+ emit activeWorkspaceChanged();
+ }
+ }
+ else if (type == "active-workspace-changed") {
+ QVariantMap dataMap = data.toMap();
+ QVariant idVar = dataMap.value("id");
+
+ if (idVar.isNull() || !idVar.isValid()) {
+ m_activeWorkspaceId = -1;
+ m_activeWorkspace = QVariant();
+ } else {
+ m_activeWorkspaceId = idVar.toInt();
+ m_activeWorkspace = m_workspaces.value(QString::number(m_activeWorkspaceId));
+ }
+ emit activeWorkspaceIdChanged();
+ emit activeWorkspaceChanged();
+ }
+ else if (type == "workspace-changed") {
+ QVariantMap ws = data.toMap();
+ int id = ws.value("id").toInt();
+
+ m_workspaces.insert(QString::number(id), ws);
+ emit workspacesChanged();
+
+ if (id == m_activeWorkspaceId) {
+ m_activeWorkspace = ws;
+ emit activeWorkspaceChanged();
+ }
+ }
+ else if (type == "workspace-removed") {
+ int id = data.toMap().value("id").toInt();
+ m_workspaces.remove(QString::number(id));
+ emit workspacesChanged();
+ }
+}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
new file mode 100644
index 00000000..72fa77bc
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/fhtc/workspaces.hpp
@@ -0,0 +1,54 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include "ipc.hpp"
+
+class Workspaces : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(FhtcWorkspaces)
+ QML_SINGLETON
+
+ Q_PROPERTY(QVariantMap windows READ windows NOTIFY windowsChanged)
+ Q_PROPERTY(QVariantMap workspaces READ workspaces NOTIFY workspacesChanged)
+
+ Q_PROPERTY(int focusedWindowId READ focusedWindowId NOTIFY focusedWindowIdChanged)
+ Q_PROPERTY(QVariant focusedWindow READ focusedWindow NOTIFY focusedWindowChanged)
+
+ Q_PROPERTY(int activeWorkspaceId READ activeWorkspaceId NOTIFY activeWorkspaceIdChanged)
+ Q_PROPERTY(QVariant activeWorkspace READ activeWorkspace NOTIFY activeWorkspaceChanged)
+
+public:
+ explicit Workspaces(QObject *parent = nullptr);
+
+ QVariantMap windows() const { return m_windows; }
+ QVariantMap workspaces() const { return m_workspaces; }
+ int focusedWindowId() const { return m_focusedWindowId; }
+ QVariant focusedWindow() const { return m_focusedWindow; }
+ int activeWorkspaceId() const { return m_activeWorkspaceId; }
+ QVariant activeWorkspace() const { return m_activeWorkspace; }
+
+signals:
+ void windowsChanged();
+ void workspacesChanged();
+ void focusedWindowIdChanged();
+ void focusedWindowChanged();
+ void activeWorkspaceIdChanged();
+ void activeWorkspaceChanged();
+
+private slots:
+ void handleEvent(const QVariant &eventVar);
+
+private:
+ Ipc *m_ipc;
+
+ QVariantMap m_windows;
+ QVariantMap m_workspaces;
+ int m_focusedWindowId = -1;
+ QVariant m_focusedWindow;
+ int m_activeWorkspaceId = -1;
+ QVariant m_activeWorkspace;
+};
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp
index a28f5a64..b87dbb66 100644
--- a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp
+++ b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.cpp
@@ -2,6 +2,7 @@
#include "DesktopStateManager.hpp"
#include
#include
+#include
DesktopModel::DesktopModel(QObject *parent) : QAbstractListModel(parent) {}
@@ -34,7 +35,29 @@ QHash DesktopModel::roleNames() const {
return roles;
}
+QPoint DesktopModel::getEmptySpot(const QSet &occupied) const {
+ for (int x = 0; ; ++x) {
+ for (int y = 0; y < m_rows; ++y) {
+ QString key = QString::number(x) + "," + QString::number(y);
+ if (!occupied.contains(key)) {
+ return QPoint(x, y);
+ }
+ }
+ }
+}
+
void DesktopModel::loadDirectory(const QString &path) {
+ m_watchedPath = path;
+
+ if (!m_watcher.directories().isEmpty())
+ m_watcher.removePaths(m_watcher.directories());
+
+ m_watcher.addPath(path);
+
+ connect(&m_watcher, &QFileSystemWatcher::directoryChanged,
+ this, &DesktopModel::onDirectoryChanged,
+ Qt::UniqueConnection);
+
beginResetModel();
m_items.clear();
@@ -45,6 +68,14 @@ void DesktopModel::loadDirectory(const QString &path) {
DesktopStateManager sm;
QVariantMap savedLayout = sm.getLayout();
+ QSet occupied;
+ for (const QFileInfo &fileInfo : list) {
+ if (savedLayout.contains(fileInfo.fileName())) {
+ QVariantMap pos = savedLayout[fileInfo.fileName()].toMap();
+ occupied.insert(QString::number(pos["x"].toInt()) + "," + QString::number(pos["y"].toInt()));
+ }
+ }
+
for (const QFileInfo &fileInfo : list) {
DesktopItem item;
item.fileName = fileInfo.fileName();
@@ -56,15 +87,20 @@ void DesktopModel::loadDirectory(const QString &path) {
item.gridX = pos["x"].toInt();
item.gridY = pos["y"].toInt();
} else {
- // TODO: make getEmptySpot in C++ and call it here to get the initial position for new icons
- item.gridX = 0;
- item.gridY = 0;
+ QPoint spot = getEmptySpot(occupied);
+ item.gridX = spot.x();
+ item.gridY = spot.y();
+ occupied.insert(QString::number(item.gridX) + "," + QString::number(item.gridY));
}
m_items.append(item);
}
endResetModel();
}
+void DesktopModel::onDirectoryChanged() {
+ loadDirectory(m_watchedPath);
+}
+
void DesktopModel::moveIcon(int index, int newX, int newY) {
if (index < 0 || index >= m_items.size()) return;
diff --git a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp
index eeebb2cf..a4090c34 100644
--- a/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp
+++ b/src/share/sleex/plugins/src/Sleex/utils/DesktopModel.hpp
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
struct DesktopItem {
QString fileName;
@@ -36,7 +37,23 @@ class DesktopModel : public QAbstractListModel {
Q_INVOKABLE void moveIcon(int index, int newX, int newY);
Q_INVOKABLE void massMove(const QVariantList &selectedPathsList, const QString &leaderPath, int targetX, int targetY, int maxCol, int maxRow);
+ Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
+
+public:
+ int rows() const { return m_rows; }
+ void setRows(int r) {
+ if (m_rows != r) { m_rows = r; emit rowsChanged(); }
+ }
+
+signals:
+ void rowsChanged();
+
private:
+ int m_rows = 1;
QList m_items;
+ QString m_watchedPath;
+ QFileSystemWatcher m_watcher;
void saveCurrentLayout();
+ QPoint getEmptySpot(const QSet &occupied) const;
+ void onDirectoryChanged();
};
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.cpp b/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.cpp
index be6993c4..88ef6ea2 100644
--- a/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.cpp
+++ b/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.cpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
DesktopStateManager::DesktopStateManager(QObject *parent) : QObject(parent) {}
@@ -26,7 +27,7 @@ void DesktopStateManager::saveLayout(const QVariantMap& layout) {
file.write(doc.toJson(QJsonDocument::Indented));
file.close();
} else {
- qWarning() << "Sleex: Impossible de sauvegarder le layout du bureau dans" << getConfigFilePath();
+ qWarning() << "Sleex: Cannot save desktop layout to" << getConfigFilePath();
}
}
@@ -46,4 +47,43 @@ QVariantMap DesktopStateManager::getLayout() {
}
return QVariantMap();
+}
+
+QString DesktopStateManager::getPostItsFilePath() const {
+ QString configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/sleex";
+ QDir dir(configDir);
+ if (!dir.exists()) dir.mkpath(".");
+
+ return configDir + "/postits.json";
+}
+
+void DesktopStateManager::savePostIts(const QVariantList& postits) {
+ QJsonArray jsonArr = QJsonArray::fromVariantList(postits);
+ QJsonDocument doc(jsonArr);
+ QFile file(getPostItsFilePath());
+
+ if (file.open(QIODevice::WriteOnly)) {
+ file.write(doc.toJson(QJsonDocument::Indented));
+ file.close();
+ } else {
+ qWarning() << "Sleex: Cannot save post-its to" << getPostItsFilePath();
+ }
+}
+
+QVariantList DesktopStateManager::getPostIts() {
+ QFile file(getPostItsFilePath());
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ return QVariantList();
+ }
+
+ QByteArray data = file.readAll();
+ file.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(data);
+ if (doc.isArray()) {
+ return doc.array().toVariantList();
+ }
+
+ return QVariantList();
}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.hpp b/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.hpp
index a83271f9..55af1740 100644
--- a/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.hpp
+++ b/src/share/sleex/plugins/src/Sleex/utils/DesktopStateManager.hpp
@@ -2,6 +2,7 @@
#include
#include
+#include
#include
class DesktopStateManager : public QObject {
@@ -14,7 +15,11 @@ class DesktopStateManager : public QObject {
Q_INVOKABLE void saveLayout(const QVariantMap& layout);
Q_INVOKABLE QVariantMap getLayout();
+
+ Q_INVOKABLE void savePostIts(const QVariantList& postits);
+ Q_INVOKABLE QVariantList getPostIts();
private:
QString getConfigFilePath() const;
+ QString getPostItsFilePath() const;
};
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/CMakeLists.txt b/src/share/sleex/plugins/src/Sleex/widgets/CMakeLists.txt
index b0931ed5..eac681a7 100755
--- a/src/share/sleex/plugins/src/Sleex/widgets/CMakeLists.txt
+++ b/src/share/sleex/plugins/src/Sleex/widgets/CMakeLists.txt
@@ -1,13 +1,65 @@
+enable_language(C)
+
pkg_check_modules(GLIB REQUIRED glib-2.0 gobject-2.0 gio-2.0)
+find_package(Qt6 REQUIRED COMPONENTS GuiPrivate)
+
+pkg_check_modules(WAYLAND_CLIENT REQUIRED IMPORTED_TARGET wayland-client)
+pkg_check_modules(WAYLAND_PROTOS REQUIRED IMPORTED_TARGET wayland-protocols)
+pkg_check_modules(GBM REQUIRED IMPORTED_TARGET gbm)
+pkg_check_modules(DRM REQUIRED IMPORTED_TARGET libdrm)
+pkg_check_modules(EGL REQUIRED IMPORTED_TARGET egl)
+pkg_check_modules(GLESv2 REQUIRED IMPORTED_TARGET glesv2)
+
+find_program(WAYLAND_SCANNER wayland-scanner REQUIRED)
+
+pkg_get_variable(WL_PROTO_DIR wayland-protocols pkgdatadir)
+set(PROTO_DIR "${CMAKE_SOURCE_DIR}/protocols")
+
+set(WAYLAND_PROTO_SOURCES "")
+
+macro(wayland_add_protocol_sources _xml)
+ get_filename_component(_base "${_xml}" NAME_WE)
+ set(_header "${CMAKE_CURRENT_BINARY_DIR}/${_base}-client-protocol.h")
+ set(_source "${CMAKE_CURRENT_BINARY_DIR}/${_base}-client-protocol.c")
+
+ add_custom_command(
+ OUTPUT "${_header}"
+ COMMAND "${WAYLAND_SCANNER}" client-header "${_xml}" "${_header}"
+ DEPENDS "${_xml}"
+ VERBATIM
+ )
+ add_custom_command(
+ OUTPUT "${_source}"
+ COMMAND "${WAYLAND_SCANNER}" private-code "${_xml}" "${_source}"
+ DEPENDS "${_xml}"
+ VERBATIM
+ )
+ list(APPEND WAYLAND_PROTO_SOURCES "${_header}" "${_source}")
+endmacro()
+
+wayland_add_protocol_sources("${WL_PROTO_DIR}/staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml")
+wayland_add_protocol_sources("${WL_PROTO_DIR}/staging/ext-image-capture-source/ext-image-capture-source-v1.xml")
+wayland_add_protocol_sources("${WL_PROTO_DIR}/staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml")
+wayland_add_protocol_sources("${WL_PROTO_DIR}/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml")
+
+add_library(sleex_wayland_protocols STATIC ${WAYLAND_PROTO_SOURCES})
+target_link_libraries(sleex_wayland_protocols PUBLIC PkgConfig::WAYLAND_CLIENT)
+target_include_directories(sleex_wayland_protocols PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
+set_target_properties(sleex_wayland_protocols PROPERTIES POSITION_INDEPENDENT_CODE ON)
+
qml_module(sleex-widgets
URI Sleex.Widgets
SOURCES
ContributionCalendar.cpp ContributionCalendar.hpp
+ ExtCaptureItem.cpp ExtCaptureItem.hpp
+ GbmBuffer.cpp GbmBuffer.hpp
plugin.cpp
)
target_include_directories(sleex-widgets PRIVATE
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
${LIBNM_INCLUDE_DIRS}
${GLIB_INCLUDE_DIRS}
)
@@ -15,6 +67,7 @@ target_include_directories(sleex-widgets PRIVATE
target_link_libraries(sleex-widgets PRIVATE
${LIBNM_LIBRARIES}
${GLIB_LIBRARIES}
+ sleex_wayland_protocols
)
target_compile_options(sleex-widgets PRIVATE
@@ -25,6 +78,13 @@ target_compile_options(sleex-widgets PRIVATE
target_link_libraries(sleex-widgets
PRIVATE
Qt6::Core
+ Qt6::Gui
+ Qt6::GuiPrivate
Qt6::Qml
Qt6::Quick
+ PkgConfig::WAYLAND_CLIENT
+ PkgConfig::GBM
+ PkgConfig::DRM
+ PkgConfig::EGL
+ PkgConfig::GLESv2
)
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/ContributionCalendar.cpp b/src/share/sleex/plugins/src/Sleex/widgets/ContributionCalendar.cpp
old mode 100644
new mode 100755
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/ContributionCalendar.hpp b/src/share/sleex/plugins/src/Sleex/widgets/ContributionCalendar.hpp
old mode 100644
new mode 100755
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.cpp b/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.cpp
new file mode 100644
index 00000000..8c40b4b8
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.cpp
@@ -0,0 +1,575 @@
+#include "ExtCaptureItem.hpp"
+#include "GbmBuffer.hpp"
+
+#include "ext-image-copy-capture-v1-client-protocol.h"
+#include "ext-image-capture-source-v1-client-protocol.h"
+#include "ext-foreign-toplevel-list-v1-client-protocol.h"
+#include "linux-dmabuf-unstable-v1-client-protocol.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+static const wl_registry_listener s_registryListener = {
+ .global = &ExtCaptureItem::onRegistryGlobal,
+ .global_remove = &ExtCaptureItem::onRegistryGlobalRemove,
+};
+
+static const ext_foreign_toplevel_list_v1_listener s_toplevelListListener = {
+ .toplevel = &ExtCaptureItem::onToplevelListToplevel,
+ .finished = &ExtCaptureItem::onToplevelListFinished,
+};
+
+static const ext_foreign_toplevel_handle_v1_listener s_toplevelHandleListener = {
+ .closed = &ExtCaptureItem::onToplevelClosed,
+ .done = &ExtCaptureItem::onToplevelDone,
+ .title = &ExtCaptureItem::onToplevelTitle,
+ .app_id = &ExtCaptureItem::onToplevelAppId,
+ .identifier = &ExtCaptureItem::onToplevelIdentifier,
+};
+
+static const ext_image_copy_capture_session_v1_listener s_sessionListener = {
+ .buffer_size = &ExtCaptureItem::onSessionBufferSize,
+ .shm_format = &ExtCaptureItem::onSessionShmFormat,
+ .dmabuf_device = &ExtCaptureItem::onSessionDmabufDevice,
+ .dmabuf_format = &ExtCaptureItem::onSessionDmabufFormat,
+ .done = &ExtCaptureItem::onSessionDone,
+ .stopped = &ExtCaptureItem::onSessionStopped,
+};
+
+static const ext_image_copy_capture_frame_v1_listener s_frameListener = {
+ .transform = &ExtCaptureItem::onFrameTransform,
+ .damage = &ExtCaptureItem::onFrameDamage,
+ .presentation_time = &ExtCaptureItem::onFramePresentationTime,
+ .ready = &ExtCaptureItem::onFrameReady,
+ .failed = &ExtCaptureItem::onFrameFailed,
+};
+
+ExtCaptureItem::ExtCaptureItem(QQuickItem* parent) : QQuickItem(parent)
+{
+ setFlag(ItemHasContents, true);
+}
+
+ExtCaptureItem::~ExtCaptureItem()
+{
+ stopCapture();
+
+ // Destroy protocol objects
+ if (m_toplevelList) ext_foreign_toplevel_list_v1_destroy(m_toplevelList);
+ if (m_sourceManager) ext_foreign_toplevel_image_capture_source_manager_v1_destroy(m_sourceManager);
+ if (m_captureManager) ext_image_copy_capture_manager_v1_destroy(m_captureManager);
+ if (m_dmabuf) zwp_linux_dmabuf_v1_destroy(m_dmabuf);
+
+ // release GBM buffers before the device
+ for (auto& buf : m_buffers) buf.reset();
+
+ if (m_gbmDev) gbm_device_destroy(m_gbmDev);
+ if (m_drmFd >= 0) close(m_drmFd);
+}
+
+void ExtCaptureItem::componentComplete()
+{
+ QQuickItem::componentComplete();
+ initWayland();
+ initGbm();
+ if (m_active) tryStartCapture();
+}
+
+void ExtCaptureItem::setActive(bool active)
+{
+ if (m_active == active) return;
+ m_active = active;
+ emit activeChanged();
+
+ if (m_waylandReady) {
+ if (active) tryStartCapture();
+ else stopCapture();
+ }
+}
+
+void ExtCaptureItem::initWayland()
+{
+ // QPlatformNativeInterface is available on all Qt 5/6 Wayland QPA backends
+ // without pulling in any QtWaylandClient private headers.
+ QPlatformNativeInterface* ni = qGuiApp->platformNativeInterface();
+ if (!ni) {
+ qWarning() << "[ExtCapture] Not running on a Wayland QPA backend";
+ return;
+ }
+ m_display = static_cast(
+ ni->nativeResourceForIntegration(QByteArrayLiteral("wl_display")));
+ if (!m_display) {
+ qWarning() << "[ExtCapture] wl_display not available via QPlatformNativeInterface";
+ return;
+ }
+
+ // registry
+ m_registry = wl_display_get_registry(m_display);
+ wl_registry_add_listener(m_registry, &s_registryListener, this);
+
+ // Blocking roundtrip so that all globals are bound synchronously before
+ // we proceed. A production implementation may prefer a deferred approach.
+ wl_display_roundtrip(m_display);
+ wl_display_roundtrip(m_display); // second roundtrip picks up any late events
+
+ m_waylandReady = true;
+}
+
+// Sometimes, even I don't understand my own code
+void ExtCaptureItem::onRegistryGlobal(void* data, wl_registry* registry, uint32_t name, const char* iface, uint32_t version)
+{
+ auto* self = static_cast(data);
+
+ if (strcmp(iface, ext_image_copy_capture_manager_v1_interface.name) == 0) {
+ self->m_captureManager = static_cast(wl_registry_bind(registry, name, &ext_image_copy_capture_manager_v1_interface, qMin(version, 1u)));
+ }
+ else if (strcmp(iface, ext_foreign_toplevel_image_capture_source_manager_v1_interface.name) == 0) {
+ self->m_sourceManager = static_cast( wl_registry_bind(registry, name, &ext_foreign_toplevel_image_capture_source_manager_v1_interface, qMin(version, 1u)));
+ }
+ else if (strcmp(iface, ext_foreign_toplevel_list_v1_interface.name) == 0) {
+ self->m_toplevelList = static_cast(wl_registry_bind(registry, name, &ext_foreign_toplevel_list_v1_interface, qMin(version, 1u)));
+ ext_foreign_toplevel_list_v1_add_listener(self->m_toplevelList, &s_toplevelListListener, self);
+ }
+ else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0) {
+ if (version >= 3) {
+ self->m_dmabuf = static_cast(wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, qMin(version, 4u)));
+ }
+ }
+}
+
+void ExtCaptureItem::onRegistryGlobalRemove(void* /*data*/, wl_registry*, uint32_t /*name*/) {}
+
+
+void ExtCaptureItem::onToplevelListToplevel(void* data, ext_foreign_toplevel_list_v1*, ext_foreign_toplevel_handle_v1* handle)
+{
+ auto* self = static_cast(data);
+ ToplevelInfo info;
+ info.handle = handle;
+ self->m_pendingToplevels.append(info);
+ ext_foreign_toplevel_handle_v1_add_listener(handle, &s_toplevelHandleListener, self);
+}
+
+void ExtCaptureItem::onToplevelListFinished(void* data, ext_foreign_toplevel_list_v1*) {
+ // The list won't send more toplevels. We're watching ongoing events via
+ // onToplevelDone / onToplevelClosed.
+ Q_UNUSED(data)
+}
+
+void ExtCaptureItem::onToplevelAppId(void* data, ext_foreign_toplevel_handle_v1* handle, const char* app_id)
+{
+ auto* self = static_cast(data);
+ for (auto& t : self->m_pendingToplevels) {
+ if (t.handle == handle) {
+ t.appId = QString::fromUtf8(app_id);
+ return;
+ }
+ }
+}
+
+void ExtCaptureItem::onToplevelTitle(void* data, ext_foreign_toplevel_handle_v1* handle, const char* title)
+{
+ auto* self = static_cast(data);
+ for (auto& t : self->m_pendingToplevels) {
+ if (t.handle == handle) {
+ t.title = QString::fromUtf8(title);
+ emit self->windowsChanged();
+ return;
+ }
+ }
+}
+
+void ExtCaptureItem::onToplevelIdentifier(void* data, ext_foreign_toplevel_handle_v1* handle, const char* identifier)
+{
+ auto* self = static_cast(data);
+ for (auto& t : self->m_pendingToplevels) {
+ if (t.handle == handle) {
+ t.identifier = QString::fromUtf8(identifier);
+ emit self->windowsChanged();
+ return;
+ }
+ }
+}
+
+void ExtCaptureItem::setWindowId(const QString& id)
+{
+ if (m_windowId == id) return;
+ m_windowId = id;
+ emit windowIdChanged();
+
+ if (m_waylandReady) {
+ stopCapture();
+ m_targetHandle = nullptr;
+
+ for (const auto& t : m_pendingToplevels) {
+ if (t.identifier == m_windowId) {
+ m_targetHandle = t.handle;
+ break;
+ }
+ }
+
+ if (m_active && m_targetHandle) tryStartCapture();
+ }
+}
+
+void ExtCaptureItem::onToplevelDone(void* data, ext_foreign_toplevel_handle_v1* handle)
+{
+ auto* self = static_cast(data);
+ for (auto& t : self->m_pendingToplevels) {
+ if (t.handle == handle && !t.matched) {
+ if (!self->m_windowId.isEmpty() && t.identifier == self->m_windowId) {
+ t.matched = true;
+ self->m_targetHandle = handle;
+ if (self->m_active && self->m_waylandReady)
+ self->tryStartCapture();
+ }
+ return;
+ }
+ }
+}
+
+void ExtCaptureItem::onToplevelClosed(void* data, ext_foreign_toplevel_handle_v1* handle)
+{
+ auto* self = static_cast(data);
+ if (self->m_targetHandle == handle) {
+ self->stopCapture();
+ self->m_targetHandle = nullptr;
+ }
+ // Remove from pending list
+ for (int i = self->m_pendingToplevels.size() - 1; i >= 0; --i) {
+ if (self->m_pendingToplevels[i].handle == handle) {
+ self->m_pendingToplevels.removeAt(i);
+ }
+ }
+}
+
+void ExtCaptureItem::tryStartCapture()
+{
+ if (!m_captureManager || !m_sourceManager || !m_targetHandle) return;
+ if (m_session) return; // already running
+
+ m_source = ext_foreign_toplevel_image_capture_source_manager_v1_create_source(m_sourceManager, m_targetHandle);
+
+ m_session = ext_image_copy_capture_manager_v1_create_session(m_captureManager, m_source, 0u);
+
+ ext_image_copy_capture_session_v1_add_listener(m_session, &s_sessionListener, this);
+
+ // The session will fire buffer_size + dmabuf_format* + done events in a batch. Flush and dispatch.
+ wl_display_flush(m_display);
+}
+
+void ExtCaptureItem::stopCapture()
+{
+ if (m_frame) {
+ ext_image_copy_capture_frame_v1_destroy(m_frame);
+ m_frame = nullptr;
+ }
+ if (m_session) {
+ ext_image_copy_capture_session_v1_destroy(m_session);
+ m_session = nullptr;
+ }
+ if (m_source) {
+ ext_image_capture_source_v1_destroy(m_source);
+ m_source = nullptr;
+ }
+ m_formatsReceived = false;
+
+ if (m_running) {
+ m_running = false;
+ emit runningChanged();
+ }
+}
+
+void ExtCaptureItem::onSessionBufferSize(void* data, ext_image_copy_capture_session_v1*, uint32_t w, uint32_t h)
+{
+ auto* self = static_cast(data);
+ self->m_captureSize = QSize(static_cast(w), static_cast(h));
+}
+
+void ExtCaptureItem::onSessionShmFormat(void*, ext_image_copy_capture_session_v1*, uint32_t) {
+ // We only use DMA-BUF. ignore SHM formats.
+}
+
+void ExtCaptureItem::onSessionDmabufDevice(void*, ext_image_copy_capture_session_v1*, wl_array* /*device*/) {
+ // The device hint is optional; we already opened the render node.
+}
+
+void ExtCaptureItem::onSessionDmabufFormat(void* data, ext_image_copy_capture_session_v1*, uint32_t fmt, wl_array* modifiers)
+{
+ auto* self = static_cast(data);
+ const auto* modifierValues = static_cast(modifiers->data);
+ const int modifierCount = static_cast(modifiers->size / sizeof(uint64_t));
+ for (int i = 0; i < modifierCount; ++i) {
+ self->m_dmabufFormats.append({ fmt, modifierValues[i] });
+ }
+}
+
+void ExtCaptureItem::onSessionDone(void* data, ext_image_copy_capture_session_v1*)
+{
+ // All constraints received. Allocate buffers and request the first frame.
+ auto* self = static_cast(data);
+ self->m_formatsReceived = true;
+ self->allocateBuffers();
+ self->requestNextFrame();
+}
+
+void ExtCaptureItem::onSessionStopped(void* data, ext_image_copy_capture_session_v1*)
+{
+ auto* self = static_cast(data);
+ qWarning() << "[ExtCapture] Session stopped by compositor";
+ self->stopCapture();
+}
+
+void ExtCaptureItem::initGbm()
+{
+ // Open the first available render node
+ for (int i = 128; i < 138 && m_drmFd < 0; ++i) {
+ QString path = QStringLiteral("/dev/dri/renderD%1").arg(i);
+ m_drmFd = open(path.toLocal8Bit().constData(), O_RDWR | O_CLOEXEC);
+ }
+ if (m_drmFd < 0) {
+ qWarning() << "[ExtCapture] Failed to open DRM render node";
+ return;
+ }
+ m_gbmDev = gbm_create_device(m_drmFd);
+ if (!m_gbmDev) {
+ qWarning() << "[ExtCapture] gbm_create_device failed";
+ close(m_drmFd);
+ m_drmFd = -1;
+ }
+}
+
+void ExtCaptureItem::allocateBuffers()
+{
+ if (!m_gbmDev || m_captureSize.isEmpty()) return;
+
+ uint32_t fmt = DRM_FORMAT_XRGB8888;
+ uint64_t modifier = DRM_FORMAT_MOD_LINEAR;
+
+ for (const auto& fi : m_dmabufFormats) {
+ if (fi.format == DRM_FORMAT_XRGB8888) {
+ fmt = fi.format;
+ modifier = fi.modifier;
+ break;
+ }
+ }
+
+ for (int i = 0; i < POOL; ++i) {
+ auto buf = std::make_unique();
+ if (!buf->allocate(m_gbmDev,
+ m_captureSize.width(),
+ m_captureSize.height(),
+ fmt, modifier)) {
+ qWarning() << "[ExtCapture] Failed to allocate GBM buffer" << i;
+ return;
+ }
+ createWaylandBuffer(buf.get());
+ m_buffers[i] = std::move(buf);
+ }
+}
+
+void ExtCaptureItem::createWaylandBuffer(GbmBuffer* buf)
+{
+ if (!m_dmabuf) return;
+
+ zwp_linux_buffer_params_v1* params = zwp_linux_dmabuf_v1_create_params(m_dmabuf);
+
+ // Add the single plane
+ zwp_linux_buffer_params_v1_add(params,
+ buf->fd(),
+ 0, // plane index
+ buf->offset(),
+ buf->stride(),
+ static_cast(buf->modifier() >> 32),
+ static_cast(buf->modifier() & 0xFFFFFFFF)
+ );
+
+ wl_buffer* wlBuf = zwp_linux_buffer_params_v1_create_immed(params, buf->width(), buf->height(), buf->format(), 0 /* flags */);
+
+ zwp_linux_buffer_params_v1_destroy(params);
+ buf->setWaylandBuffer(wlBuf);
+}
+
+GbmBuffer* ExtCaptureItem::acquireCaptureBuffer()
+{
+ // Return a buffer that Qt is not currently reading from.
+ for (int i = 0; i < POOL; ++i) {
+ if (m_buffers[i] && !m_buffers[i]->inUse) {
+ m_captureIdx = i;
+ return m_buffers[i].get();
+ }
+ }
+ return nullptr;
+}
+
+void ExtCaptureItem::requestNextFrame() {
+ if (!m_session || m_frame || !m_formatsReceived) return;
+
+ int target = -1;
+ for (int i = 0; i < POOL; ++i) {
+ if (!m_buffers[i]->inUse && !m_buffers[i]->ready && !m_buffers[i]->isCapturing) {
+ target = i;
+ break;
+ }
+ }
+
+ if (target == -1) return;
+
+ m_captureIdx = target;
+ m_buffers[target]->isCapturing = true;
+
+ m_frame = ext_image_copy_capture_session_v1_create_frame(m_session);
+ ext_image_copy_capture_frame_v1_add_listener(m_frame, &s_frameListener, this);
+ ext_image_copy_capture_frame_v1_attach_buffer(m_frame, m_buffers[target]->waylandBuffer());
+ ext_image_copy_capture_frame_v1_capture(m_frame);
+ wl_display_flush(m_display);
+}
+
+void ExtCaptureItem::onFrameTransform(void*, ext_image_copy_capture_frame_v1*, uint32_t) {}
+
+void ExtCaptureItem::onFrameDamage(void*, ext_image_copy_capture_frame_v1*, int32_t, int32_t, int32_t, int32_t) {}
+
+void ExtCaptureItem::onFramePresentationTime(void*, ext_image_copy_capture_frame_v1*, uint32_t, uint32_t, uint32_t) {}
+
+void ExtCaptureItem::onFrameReady(void* data, ext_image_copy_capture_frame_v1* frame) {
+ auto* self = static_cast(data);
+ int idx = self->m_captureIdx;
+
+ ext_image_copy_capture_frame_v1_destroy(frame);
+ self->m_frame = nullptr;
+
+ self->m_buffers[idx]->isCapturing = false;
+ self->m_buffers[idx]->ready = true;
+
+ {
+ QMutexLocker lk(&self->m_sgMutex);
+ self->m_pendingIdx = idx;
+ }
+
+ self->update();
+}
+
+void ExtCaptureItem::onFrameFailed(void* data, ext_image_copy_capture_frame_v1* frame, uint32_t reason)
+{
+ auto* self = static_cast(data);
+ qWarning() << "[ExtCapture] Frame capture failed, reason:" << reason;
+ ext_image_copy_capture_frame_v1_destroy(frame);
+ self->m_frame = nullptr;
+ // Retry after a brief pause (avoid tight loop on persistent error)
+ QTimer::singleShot(200, self, &ExtCaptureItem::requestNextFrame);
+}
+
+QSGNode* ExtCaptureItem::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*)
+{
+ auto* node = static_cast(oldNode);
+ int readyIdx = -1;
+
+ {
+ QMutexLocker lk(&m_sgMutex);
+ readyIdx = m_pendingIdx;
+ m_pendingBuffer = nullptr;
+ }
+
+ if (m_displayIdx >= 0) {
+ m_buffers[m_displayIdx]->inUse = false;
+ }
+
+ if (readyIdx != -1) {
+ m_displayIdx = readyIdx;
+ m_buffers[m_displayIdx]->ready = false;
+ m_buffers[m_displayIdx]->inUse = true;
+ }
+
+ if (!m_frame && m_active) {
+ QMetaObject::invokeMethod(this, &ExtCaptureItem::requestNextFrame, Qt::QueuedConnection);
+ }
+
+ if (m_displayIdx == -1) return node;
+
+ GbmBuffer* buf = m_buffers[m_displayIdx].get();
+ if (!buf->qtTexture()) {
+ if (!m_eglCreateImage) {
+ QPlatformNativeInterface* ni = qGuiApp->platformNativeInterface();
+ m_eglDisplay = static_cast(ni->nativeResourceForIntegration(QByteArrayLiteral("egldisplay")));
+ if (m_eglDisplay == EGL_NO_DISPLAY) m_eglDisplay = eglGetCurrentDisplay();
+
+ m_eglCreateImage = reinterpret_cast(eglGetProcAddress("eglCreateImageKHR"));
+ m_eglDestroyImage = reinterpret_cast(eglGetProcAddress("eglDestroyImageKHR"));
+ m_glEGLImageTarget = reinterpret_cast(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
+ }
+
+ auto fnCreateImage = reinterpret_cast(m_eglCreateImage);
+ auto fnEGLTarget = reinterpret_cast(m_glEGLImageTarget);
+
+ EGLint attribs[] = {
+ EGL_WIDTH, buf->width(),
+ EGL_HEIGHT, buf->height(),
+ EGL_LINUX_DRM_FOURCC_EXT, static_cast(buf->format()),
+ EGL_DMA_BUF_PLANE0_FD_EXT, buf->fd(),
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, static_cast(buf->stride()),
+ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, static_cast(buf->modifier() & 0xFFFFFFFF),
+ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, static_cast(buf->modifier() >> 32),
+ EGL_NONE
+ };
+
+ buf->eglImage = fnCreateImage(m_eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs);
+
+ if (buf->eglImage == EGL_NO_IMAGE_KHR) {
+ qWarning() << "[ExtCapture] eglCreateImageKHR failed:" << eglGetError();
+ return node;
+ }
+
+ GLuint texId = 0;
+ glGenTextures(1, &texId);
+ glBindTexture(GL_TEXTURE_2D, texId);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ fnEGLTarget(GL_TEXTURE_2D, static_cast(buf->eglImage));
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // On crée la texture Qt qui "enveloppe" notre texture native
+ // On ne la détruira que lors de la destruction du GbmBuffer
+ QSGTexture* sgTex = QNativeInterface::QSGOpenGLTexture::fromNative(
+ texId,
+ window(),
+ QSize(buf->width(), buf->height()),
+ QQuickWindow::TextureHasAlphaChannel);
+
+ buf->setQtTexture(sgTex);
+ }
+
+ if (!node) node = new QSGSimpleTextureNode;
+ node->setTexture(buf->qtTexture());
+ node->setRect(boundingRect());
+ return node;
+}
+
+QVariantList ExtCaptureItem::windows() const
+{
+ QVariantList list;
+ for (const auto& t : m_pendingToplevels) {
+ if (t.identifier.isEmpty()) continue;
+ QVariantMap map;
+ map["id"] = t.identifier;
+ map["appId"] = t.appId;
+ map["title"] = t.title.isEmpty() ? t.appId : t.title;
+ list << map;
+ }
+ return list;
+}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.hpp b/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.hpp
new file mode 100644
index 00000000..0f6228d4
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/widgets/ExtCaptureItem.hpp
@@ -0,0 +1,189 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+struct ext_image_copy_capture_manager_v1;
+struct ext_image_copy_capture_session_v1;
+struct ext_image_copy_capture_frame_v1;
+struct ext_image_capture_source_v1;
+struct ext_image_copy_capture_source_v1;
+struct ext_foreign_toplevel_list_v1;
+struct ext_foreign_toplevel_handle_v1;
+struct ext_foreign_toplevel_image_capture_source_manager_v1;
+struct zwp_linux_dmabuf_v1;
+struct zwp_linux_buffer_params_v1;
+
+class GbmBuffer;
+class QSGTexture;
+
+// ---------------------------------------------------------------------------
+// ExtCaptureItem
+//
+// QML usage:
+// import Sleex.ExtCapture
+//
+// ExtCaptureItem {
+// anchors.fill: parent
+// appId: "firefox"
+// active: true
+// }
+// ---------------------------------------------------------------------------
+class ExtCaptureItem : public QQuickItem
+{
+ Q_OBJECT
+ QML_ELEMENT
+
+ // The identifier of the window to capture, typically in the form "appId:instanceId". Updated when the session is active.
+ Q_PROPERTY(QString windowId READ windowId WRITE setWindowId NOTIFY windowIdChanged)
+ // App title
+ Q_PROPERTY(QVariantList windows READ windows NOTIFY windowsChanged)
+ // Set to true to start capturing.
+ Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
+ // Read-only: true once the session is live and frames are flowing.
+ Q_PROPERTY(bool running READ running NOTIFY runningChanged)
+
+public:
+ explicit ExtCaptureItem(QQuickItem* parent = nullptr);
+ ~ExtCaptureItem() override;
+
+ QString windowId() const { return m_windowId; }
+ void setWindowId(const QString& id);
+ QVariantList windows() const;
+ QStringList availableAppIds() const;
+ bool active() const { return m_active; }
+ bool running() const { return m_running; }
+
+ void setAppId(const QString& id);
+ void setActive(bool active);
+
+protected:
+ // called by the Scene Graph render thread.
+ QSGNode* updatePaintNode(QSGNode* old, UpdatePaintNodeData*) override;
+ void componentComplete() override;
+
+signals:
+ void windowIdChanged();
+ void windowsChanged();
+ void activeChanged();
+ void runningChanged();
+
+private:
+ void initWayland();
+ void initGbm();
+ void tryStartCapture();
+ void stopCapture();
+ void requestNextFrame();
+ void allocateBuffers();
+ void createWaylandBuffer(GbmBuffer* buf);
+
+ // Returns the buffer that is not currently in use by Qt.
+ GbmBuffer* acquireCaptureBuffer();
+
+ QString m_windowId;
+ bool m_active = false;
+ bool m_running = false;
+ bool m_waylandReady = false;
+
+ // Capture geometry (set by session::buffer_size event)
+ QSize m_captureSize;
+
+ // Supported DMA-BUF formats reported by the session
+ struct FormatInfo { uint32_t format; uint64_t modifier; };
+ QList m_dmabufFormats;
+ bool m_formatsReceived = false;
+
+ wl_display* m_display = nullptr;
+ wl_registry* m_registry = nullptr;
+
+ ext_image_copy_capture_manager_v1* m_captureManager = nullptr;
+ ext_foreign_toplevel_image_capture_source_manager_v1* m_sourceManager = nullptr;
+ ext_foreign_toplevel_list_v1* m_toplevelList = nullptr;
+ zwp_linux_dmabuf_v1* m_dmabuf = nullptr;
+
+ ext_image_capture_source_v1* m_source = nullptr;
+ ext_image_copy_capture_session_v1* m_session = nullptr;
+ ext_image_copy_capture_frame_v1* m_frame = nullptr;
+
+ // The toplevel handle we want to capture
+ ext_foreign_toplevel_handle_v1* m_targetHandle = nullptr;
+ // Temporary per-toplevel state during discovery
+ struct ToplevelInfo {
+ ext_foreign_toplevel_handle_v1* handle = nullptr;
+ QString appId;
+ QString title;
+ QString identifier;
+ bool matched = false;
+ };
+ QList m_pendingToplevels;
+
+ int m_drmFd = -1;
+ gbm_device* m_gbmDev = nullptr;
+
+ int m_pendingIdx = -1;
+
+ // Double-buffering: one buffer captured by compositor, one displayed by Qt.
+ static constexpr int POOL = 2;
+ std::array, POOL> m_buffers;
+ int m_captureIdx = 0; // index of the buffer we last sent to the compositor
+ int m_displayIdx = -1;// index of the buffer ready for Qt to display
+
+ // Qt Scene Graph (render thread side)
+ // Protected by m_sgMutex.
+ QMutex m_sgMutex;
+ GbmBuffer* m_pendingBuffer = nullptr; // written by Wayland CB, read by SG
+ EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
+ // EGL/GL extension function pointers, resolved once (stored as void* to
+ // avoid pulling gl2ext.h into a MOC-parsed header).
+ void* m_eglCreateImage = nullptr; // PFNEGLCREATEIMAGEKHRPROC
+ void* m_eglDestroyImage = nullptr; // PFNEGLDESTROYIMAGEKHRPROC
+ void* m_glEGLImageTarget = nullptr; // PFNGLEGLIMAGETARGETTEXTURE2DOESPROC
+
+public:
+ static void onRegistryGlobal(void*, wl_registry*, uint32_t name,
+ const char* iface, uint32_t version);
+ static void onRegistryGlobalRemove(void*, wl_registry*, uint32_t name);
+
+ // ext_foreign_toplevel_list
+ static void onToplevelListToplevel(void*, ext_foreign_toplevel_list_v1*,
+ ext_foreign_toplevel_handle_v1* handle);
+ static void onToplevelListFinished(void*, ext_foreign_toplevel_list_v1*);
+
+ // ext_foreign_toplevel_handle
+ static void onToplevelAppId(void*, ext_foreign_toplevel_handle_v1*, const char* app_id);
+ static void onToplevelTitle(void*, ext_foreign_toplevel_handle_v1*, const char* title);
+ static void onToplevelIdentifier(void*, ext_foreign_toplevel_handle_v1*, const char* id);
+ static void onToplevelDone(void*, ext_foreign_toplevel_handle_v1*);
+ static void onToplevelClosed(void*, ext_foreign_toplevel_handle_v1*);
+
+ // ext_image_copy_capture_session
+ static void onSessionBufferSize(void*, ext_image_copy_capture_session_v1*,
+ uint32_t w, uint32_t h);
+ static void onSessionShmFormat(void*, ext_image_copy_capture_session_v1*, uint32_t fmt);
+ static void onSessionDmabufDevice(void*, ext_image_copy_capture_session_v1*,
+ wl_array* device);
+ static void onSessionDmabufFormat(void*, ext_image_copy_capture_session_v1*,
+ uint32_t fmt, wl_array* modifiers);
+ static void onSessionDone(void*, ext_image_copy_capture_session_v1*);
+ static void onSessionStopped(void*, ext_image_copy_capture_session_v1*);
+
+ // ext_image_copy_capture_frame
+ static void onFrameTransform(void*, ext_image_copy_capture_frame_v1*, uint32_t transform);
+ static void onFrameDamage(void*, ext_image_copy_capture_frame_v1*,
+ int32_t x, int32_t y, int32_t w, int32_t h);
+ static void onFramePresentationTime(void*, ext_image_copy_capture_frame_v1*,
+ uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec);
+ static void onFrameReady(void*, ext_image_copy_capture_frame_v1*);
+ static void onFrameFailed(void*, ext_image_copy_capture_frame_v1*, uint32_t reason);
+};
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.cpp b/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.cpp
new file mode 100644
index 00000000..13c8617b
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.cpp
@@ -0,0 +1,98 @@
+#include "GbmBuffer.hpp"
+#include
+#include
+#include
+#include
+#include
+
+bool GbmBuffer::allocate(gbm_device* dev,
+ int width,
+ int height,
+ uint32_t format,
+ uint64_t modifier)
+{
+ release();
+
+ m_width = width;
+ m_height = height;
+ m_format = format;
+ m_modifier = modifier;
+
+ if (modifier != DRM_FORMAT_MOD_INVALID && modifier != DRM_FORMAT_MOD_LINEAR) {
+ const uint64_t mods[] = { modifier };
+ m_bo = gbm_bo_create_with_modifiers2(dev, width, height, format, mods, 1,
+ GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
+ }
+ if (!m_bo) {
+ m_bo = gbm_bo_create(dev, width, height, format,
+ GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
+ }
+ if (!m_bo) return false;
+
+ m_fd = gbm_bo_get_fd(m_bo);
+ m_stride = gbm_bo_get_stride(m_bo);
+
+ return (m_fd >= 0);
+}
+
+GbmBuffer::~GbmBuffer()
+{
+ release();
+}
+
+GbmBuffer::GbmBuffer(GbmBuffer&& o) noexcept
+ : m_bo(o.m_bo), m_fd(o.m_fd), m_stride(o.m_stride),
+ m_format(o.m_format), m_modifier(o.m_modifier),
+ m_width(o.m_width), m_height(o.m_height),
+ m_wlBuffer(o.m_wlBuffer), m_qtTexture(o.m_qtTexture),
+ eglImage(o.eglImage), inUse(o.inUse), ready(o.ready)
+{
+ o.m_bo = nullptr;
+ o.m_fd = -1;
+ o.m_wlBuffer = nullptr;
+ o.m_qtTexture = nullptr;
+ o.eglImage = EGL_NO_IMAGE_KHR;
+}
+
+GbmBuffer& GbmBuffer::operator=(GbmBuffer&& o) noexcept
+{
+ if (this != &o) {
+ release();
+ m_bo = o.m_bo; o.m_bo = nullptr;
+ m_fd = o.m_fd; o.m_fd = -1;
+ m_stride = o.m_stride;
+ m_format = o.m_format;
+ m_modifier = o.m_modifier;
+ m_width = o.m_width;
+ m_height = o.m_height;
+ m_wlBuffer = o.m_wlBuffer; o.m_wlBuffer = nullptr;
+ m_qtTexture = o.m_qtTexture; o.m_qtTexture = nullptr;
+ eglImage = o.eglImage; o.eglImage = EGL_NO_IMAGE_KHR;
+ inUse = o.inUse;
+ ready = o.ready;
+ }
+ return *this;
+}
+
+void GbmBuffer::release()
+{
+ if (m_qtTexture) {
+ delete m_qtTexture;
+ m_qtTexture = nullptr;
+ }
+
+ if (m_wlBuffer) {
+ wl_buffer_destroy(m_wlBuffer);
+ m_wlBuffer = nullptr;
+ }
+ if (m_fd >= 0) {
+ close(m_fd);
+ m_fd = -1;
+ }
+ if (m_bo) {
+ gbm_bo_destroy(m_bo);
+ m_bo = nullptr;
+ }
+ inUse = false;
+ ready = false;
+}
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.hpp b/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.hpp
new file mode 100644
index 00000000..1cee75fd
--- /dev/null
+++ b/src/share/sleex/plugins/src/Sleex/widgets/GbmBuffer.hpp
@@ -0,0 +1,61 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+struct wl_buffer;
+class QSGTexture; // Forward declaration pour éviter d'inclure tout Qt ici
+
+class GbmBuffer {
+public:
+ GbmBuffer() = default;
+
+ bool allocate(gbm_device* dev,
+ int width,
+ int height,
+ uint32_t format,
+ uint64_t modifier);
+
+ ~GbmBuffer();
+
+ // Non-copyable, moveable
+ GbmBuffer(const GbmBuffer&) = delete;
+ GbmBuffer& operator=(const GbmBuffer&) = delete;
+ GbmBuffer(GbmBuffer&&) noexcept;
+ GbmBuffer& operator=(GbmBuffer&&) noexcept;
+
+ bool isValid() const { return m_bo != nullptr; }
+ int fd() const { return m_fd; }
+ uint32_t stride() const { return m_stride; }
+ uint32_t offset() const { return 0; }
+ uint32_t format() const { return m_format; }
+ uint64_t modifier() const { return m_modifier; }
+ int width() const { return m_width; }
+ int height() const { return m_height; }
+
+ wl_buffer* waylandBuffer() const { return m_wlBuffer; }
+ void setWaylandBuffer(wl_buffer* b) { m_wlBuffer = b; }
+
+ QSGTexture* qtTexture() const { return m_qtTexture; }
+ void setQtTexture(QSGTexture* tex) { m_qtTexture = tex; }
+ EGLImage eglImage = EGL_NO_IMAGE_KHR;
+
+ // Ping-pong flags
+ bool isCapturing = false;
+ bool ready = false;
+ bool inUse = false;
+private:
+ void release();
+
+ gbm_bo* m_bo = nullptr;
+ int m_fd = -1;
+ uint32_t m_stride = 0;
+ uint32_t m_format = 0;
+ uint64_t m_modifier = 0;
+ int m_width = 0;
+ int m_height = 0;
+ wl_buffer* m_wlBuffer = nullptr;
+ QSGTexture* m_qtTexture = nullptr;
+};
\ No newline at end of file
diff --git a/src/share/sleex/plugins/src/Sleex/widgets/plugin.cpp b/src/share/sleex/plugins/src/Sleex/widgets/plugin.cpp
old mode 100644
new mode 100755
index 1114bd49..c67f993d
--- a/src/share/sleex/plugins/src/Sleex/widgets/plugin.cpp
+++ b/src/share/sleex/plugins/src/Sleex/widgets/plugin.cpp
@@ -1,6 +1,6 @@
#include
-class SleexUtilsPlugin : public QQmlExtensionPlugin {
+class SleexWidgetsPlugin : public QQmlExtensionPlugin {
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
diff --git a/src/share/sleex/scripts/colors/switchwall.sh b/src/share/sleex/scripts/colors/switchwall.sh
index 0b206cdd..0fa5f7b1 100755
--- a/src/share/sleex/scripts/colors/switchwall.sh
+++ b/src/share/sleex/scripts/colors/switchwall.sh
@@ -51,8 +51,8 @@ post_process() {
check_and_prompt_upscale() {
local img="$1"
- min_width_desired="$(hyprctl monitors -j | jq '([.[].width] | max)' | xargs)"
- min_height_desired="$(hyprctl monitors -j | jq '([.[].height] | max)' | xargs)"
+ min_width_desired="$(fht-compositor ipc -j outputs | jq '[.[] | .size[0]] | max' | xargs)"
+ min_height_desired="$(fht-compositor ipc -j outputs | jq '[.[] | .size[1]] | max' | xargs)"
if command -v identify &>/dev/null && [ -f "$img" ]; then
local img_width img_height
@@ -79,15 +79,15 @@ check_and_prompt_upscale() {
is_video() {
local extension="${1##*.}"
- [[ "$extension" == "mp4" || "$extension" == "mkv" || "$extension" == "webm" ]] && return 0 || return 1 [cite: 4, 5]
+ [[ "$extension" == "mp4" || "$extension" == "mkv" || "$extension" == "webm" ]] && return 0 || return 1
}
update_wallpaper_config() {
local wallpaper_path="$1"
if [[ -f "$DB_CONFIG" ]]; then
- sqlite3 "$DB_CONFIG" "UPDATE sleex_settings SET config_json = json_set(config_json, '$.wallpaperPath', '$wallpaper_path') WHERE module='background';" [cite: 11]
- qs -p /usr/share/sleex/ ipc call background forceWallpaperReload "$wallpaper_path" [cite: 21, 53]
+ sqlite3 "$DB_CONFIG" "UPDATE sleex_settings SET config_json = json_set(config_json, '$.wallpaperPath', '$wallpaper_path') WHERE module='background';"
+ qs -p /usr/share/sleex/ ipc call background forceWallpaperReload "$wallpaper_path"
qs -p /usr/share/sleex/settings.qml ipc call settings reloadWallpaper "$wallpaper_path"
fi
}
@@ -100,12 +100,7 @@ switch() {
local color="$5"
local actual_wallpaper_path="$imgpath"
- read scale screenx screeny screensizey < <(hyprctl monitors -j | jq '.[] | select(.focused) | .scale, .x, .y, .height' | xargs)
- cursorposx=$(hyprctl cursorpos -j | jq '.x' 2>/dev/null) || cursorposx=960
- cursorposx=$(bc <<< "scale=0; ($cursorposx - $screenx) * $scale / 1")
- cursorposy=$(hyprctl cursorpos -j | jq '.y' 2>/dev/null) || cursorposy=540
- cursorposy=$(bc <<< "scale=0; ($cursorposy - $screeny) * $scale / 1")
- cursorposy_inverted=$((screensizey - cursorposy))
+ read scale screenx screeny screensizey < <(fht-compositor ipc -j outputs | jq --arg act "$(fht-compositor ipc -j space | jq -r '.monitors[] | select(.active).output')" '.[$act] | .scale, .position[0], .position[1], .size[1]' | xargs)
if [[ "$color_flag" == "1" ]]; then
matugen_args=(color hex "$color")
@@ -163,8 +158,8 @@ switch() {
> "$STATE_DIR"/user/generated/material_colors.scss
"$SCRIPT_DIR"/applycolor.sh
- max_width_desired="$(hyprctl monitors -j | jq '([.[].width] | min)' | xargs)"
- max_height_desired="$(hyprctl monitors -j | jq '([.[].height] | min)' | xargs)"
+ max_width_desired="$(fht-compositor ipc -j outputs | jq '[.[] | .size[0]] | min' | xargs)"
+ max_height_desired="$(fht-compositor ipc -j outputs | jq '[.[] | .size[1]] | min' | xargs)"
post_process "$max_width_desired" "$max_height_desired" "$actual_wallpaper_path"
}
diff --git a/src/share/sleex/scripts/fht/get_keybinds.py b/src/share/sleex/scripts/fht/get_keybinds.py
new file mode 100755
index 00000000..fff53775
--- /dev/null
+++ b/src/share/sleex/scripts/fht/get_keybinds.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env -S _/bin/sh _-c "source $(eval echo $SLEEX_VIRTUAL_ENV)/bin/activate && exec python -E "$0" "$@""
+import argparse
+import re
+import os
+import json
+import toml
+from typing import Dict, List, Any
+
+TITLE_REGEX = r"^#+!"
+HIDE_COMMENT = "[hidden]"
+
+parser = argparse.ArgumentParser(description='Sleex TOML keybind reader')
+parser.add_argument('--path', type=str, default="/etc/sleex/compositor/keybinds.toml", help='path to config file')
+args = parser.parse_args()
+
+class Section(dict):
+ def __init__(self, name: str):
+ self["name"] = name
+ self["children"] = []
+ self["keybinds"] = []
+
+def parse_toml_config(path: str):
+ expanded_path = os.path.expanduser(os.path.expandvars(path))
+ if not os.access(expanded_path, os.R_OK):
+ return "error"
+
+ with open(expanded_path, "r") as f:
+ lines = f.readlines()
+
+ with open(expanded_path, "r") as f:
+ full_data = toml.load(f)
+
+ keybinds_data = full_data.get("keybinds", {})
+
+ root = Section("")
+ stack = [(0, root)] # (level, section_object)
+
+ for line in lines:
+ clean_line = line.strip()
+
+ if re.match(TITLE_REGEX, clean_line):
+ level = clean_line.count('#')
+ name = clean_line.replace('!', '').replace('#', '').strip()
+ new_section = Section(name)
+
+ while stack and stack[-1][0] >= level:
+ stack.pop()
+
+ stack[-1][1]["children"].append(new_section)
+ stack.append((level, new_section))
+ continue
+
+ if HIDE_COMMENT in line or not clean_line or clean_line.startswith("["):
+ continue
+
+ if "=" in clean_line:
+ key_part = clean_line.split("=")[0].strip()
+ base_key = key_part.split(".")[0]
+
+ if base_key in keybinds_data:
+ data = keybinds_data[base_key]
+ if not any(kb["key"] == base_key for kb in stack[-1][1]["keybinds"]):
+
+ comment = ""
+ if "#" in line and HIDE_COMMENT not in line:
+ comment = line.split("#")[-1].strip()
+
+ mods = base_key.split("-")
+ actual_key = mods.pop()
+
+ if isinstance(data, dict):
+ dispatcher = data.get("action", "")
+ params = str(data.get("arg", ""))
+ else:
+ dispatcher = data
+ params = ""
+
+ stack[-1][1]["keybinds"].append({
+ "mods": mods,
+ "key": actual_key,
+ "dispatcher": dispatcher,
+ "params": params,
+ "comment": comment
+ })
+ # keybinds_data.pop(base_key)
+
+ return root
+
+if __name__ == "__main__":
+ result = parse_toml_config(args.path)
+ print(json.dumps(result))
\ No newline at end of file
diff --git a/src/share/sleex/scripts/grimblast.sh b/src/share/sleex/scripts/grimblast.sh
deleted file mode 100755
index 8c9c54f9..00000000
--- a/src/share/sleex/scripts/grimblast.sh
+++ /dev/null
@@ -1,277 +0,0 @@
-#!/usr/bin/env bash
-## Grimblast: a helper for screenshots within hyprland
-## Requirements:
-## - `grim`: screenshot utility for wayland
-## - `slurp`: to select an area
-## - `hyprctl`: to read properties of current window (provided by Hyprland)
-## - `hyprpicker`: to freeze the screen when selecting area
-## - `wl-copy`: clipboard utility (provided by wl-clipboard)
-## - `jq`: json utility to parse hyprctl output
-## - `notify-send`: to show notifications (provided by libnotify)
-## Those are needed to be installed, if unsure, run `grimblast check`
-##
-## See `man 1 grimblast` or `grimblast usage` for further details.
-
-## Author: Misterio (https://github.com/misterio77)
-
-## This tool is based on grimshot, with swaymsg commands replaced by their
-## hyprctl equivalents.
-## https://github.com/swaywm/sway/blob/master/contrib/grimshot
-getTargetDirectory() {
- test -f "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" &&
- . "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs"
-
- echo "${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}}"
-}
-
-tmp_editor_directory() {
- echo "/tmp"
-}
-
-#Detect if $GRIMBLAST_EDITOR env exist
-env_editor_confirm() {
- if [ -n "$GRIMBLAST_EDITOR" ]; then
- echo "GRIMBLAST_EDITOR is set. Continuing..."
- else
- echo "GRIMBLAST_EDITOR is not set. Defaulting to gimp"
- GRIMBLAST_EDITOR=gimp
- fi
-}
-
-NOTIFY=no
-CURSOR=
-FREEZE=
-WAIT=no
-SCALE=
-HYPRPICKER_PID=-1
-
-while [ $# -gt 0 ]; do
- key="$1"
-
- case $key in
- -n | --notify)
- NOTIFY=yes
- shift # past argument
- ;;
- -c | --cursor)
- CURSOR=yes
- shift # past argument
- ;;
- -f | --freeze)
- FREEZE=yes
- shift # past argument
- ;;
- -w | --wait)
- shift
- WAIT=$1
- if echo "$WAIT" | grep "[^0-9]" -q; then
- echo "Invalid value for wait '$WAIT'" >&2
- exit 3
- fi
- shift
- ;;
- -s | --scale)
- shift # past argument
- if [ $# -gt 0 ]; then
- SCALE="$1" # assign the next argument to SCALE
- shift # past argument
- else
- echo "Error: Missing argument for --scale option."
- exit 1
- fi
- ;;
- *) # unknown option
- break # done with parsing --flags
- ;;
- esac
-done
-
-ACTION=${1:-usage}
-SUBJECT=${2:-screen}
-FILE=${3:-$(getTargetDirectory)/$(date -Ins).png}
-FILE_EDITOR=${3:-$(tmp_editor_directory)/$(date -Ins).png}
-
-if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "edit" ] && [ "$ACTION" != "copysave" ] && [ "$ACTION" != "check" ]; then
- echo "Usage:"
- echo " grimblast [--notify] [--cursor] [--freeze] [--wait N] [--scale ] (copy|save|copysave|edit) [active|screen|output|area] [FILE|-]"
- echo " grimblast check"
- echo " grimblast usage"
- echo ""
- echo "Commands:"
- echo " copy: Copy the screenshot data into the clipboard."
- echo " save: Save the screenshot to a regular file or '-' to pipe to STDOUT."
- echo " copysave: Combine the previous 2 options."
- echo " edit: Open screenshot in the image editor of your choice (default is gimp). See man page for info."
- echo " check: Verify if required tools are installed and exit."
- echo " usage: Show this message and exit."
- echo ""
- echo "Targets:"
- echo " active: Currently active window."
- echo " screen: All visible outputs."
- echo " output: Currently active output."
- echo " area: Manually select a region or window."
- exit
-fi
-
-notify() {
- notify-send -t 3000 -a grimblast "$@"
-}
-
-notifyOk() {
- [ "$NOTIFY" = "no" ] && return
-
- notify "$@"
-}
-
-notifyError() {
- if [ $NOTIFY = "yes" ]; then
- TITLE=${2:-"Screenshot"}
- MESSAGE=${1:-"Error taking screenshot with grim"}
- notify -u critical "$TITLE" "$MESSAGE"
- else
- echo "$1"
- fi
-}
-
-resetFade() {
- if [[ -n $FADE && -n $FADEOUT ]]; then
- hyprctl keyword animation "$FADE" >/dev/null
- hyprctl keyword animation "$FADEOUT" >/dev/null
- fi
-}
-
-killHyprpicker() {
- if [ ! $HYPRPICKER_PID -eq -1 ]; then
- kill $HYPRPICKER_PID
- fi
-}
-
-die() {
- killHyprpicker
- MSG=${1:-Bye}
- notifyError "Error: $MSG"
- exit 2
-}
-
-check() {
- COMMAND=$1
- if command -v "$COMMAND" >/dev/null 2>&1; then
- RESULT="OK"
- else
- RESULT="NOT FOUND"
- fi
- echo " $COMMAND: $RESULT"
-}
-
-takeScreenshot() {
- FILE=$1
- GEOM=$2
- OUTPUT=$3
- if [ -n "$OUTPUT" ]; then
- grim ${CURSOR:+-c} ${SCALE:+-s "$SCALE"} -o "$OUTPUT" "$FILE" || die "Unable to invoke grim"
- elif [ -z "$GEOM" ]; then
- grim ${CURSOR:+-c} ${SCALE:+-s "$SCALE"} "$FILE" || die "Unable to invoke grim"
- else
- grim ${CURSOR:+-c} ${SCALE:+-s "$SCALE"} -g "$GEOM" "$FILE" || die "Unable to invoke grim"
- resetFade
- fi
-}
-
-wait() {
- if [ "$WAIT" != "no" ]; then
- sleep "$WAIT"
- fi
-}
-
-if [ "$ACTION" = "check" ]; then
- echo "Checking if required tools are installed. If something is missing, install it to your system and make it available in PATH..."
- check grim
- check slurp
- check hyprctl
- check hyprpicker
- check wl-copy
- check jq
- check notify-send
- exit
-elif [ "$SUBJECT" = "active" ]; then
- wait
- FOCUSED=$(hyprctl activewindow -j)
- GEOM=$(echo "$FOCUSED" | jq -r '"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"')
- APP_ID=$(echo "$FOCUSED" | jq -r '.class')
- WHAT="$APP_ID window"
-elif [ "$SUBJECT" = "screen" ]; then
- wait
- GEOM=""
- WHAT="Screen"
-elif [ "$SUBJECT" = "output" ]; then
- wait
- GEOM=""
- OUTPUT=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true)' | jq -r '.name')
- WHAT="$OUTPUT"
-elif [ "$SUBJECT" = "area" ]; then
- if [ "$FREEZE" = "yes" ] && [ "$(command -v "hyprpicker")" ] >/dev/null 2>&1; then
- hyprpicker -r -z &
- sleep 0.2
- HYPRPICKER_PID=$!
- fi
-
- # get fade & fadeOut animation and unset it
- # this removes the black border seen around screenshots
- FADE="$(hyprctl -j animations | jq -jr '.[0][] | select(.name == "fade") | .name, ",", (if .enabled == true then "1" else "0" end), ",", (.speed|floor), ",", .bezier')"
- FADEOUT="$(hyprctl -j animations | jq -jr '.[0][] | select(.name == "fadeOut") | .name, ",", (if .enabled == true then "1" else "0" end), ",", (.speed|floor), ",", .bezier')"
- hyprctl keyword animation 'fade,0,1,default' >/dev/null
- hyprctl keyword animation 'fadeOut,0,1,default' >/dev/null
-
- WORKSPACES="$(hyprctl monitors -j | jq -r 'map(.activeWorkspace.id)')"
- WINDOWS="$(hyprctl clients -j | jq -r --argjson workspaces "$WORKSPACES" 'map(select([.workspace.id] | inside($workspaces)))')"
- # shellcheck disable=2086 # if we don't split, spaces mess up slurp
- GEOM=$(echo "$WINDOWS" | jq -r '.[] | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | slurp $SLURP_ARGS)
-
- # Check if user exited slurp without selecting the area
- if [ -z "$GEOM" ]; then
- killHyprpicker
- resetFade
- exit 1
- fi
- WHAT="Area"
- wait
-elif [ "$SUBJECT" = "window" ]; then
- die "Subject 'window' is now included in 'area'"
-else
- die "Unknown subject to take a screen shot from" "$SUBJECT"
-fi
-
-if [ "$ACTION" = "copy" ]; then
- takeScreenshot - "$GEOM" "$OUTPUT" | wl-copy --type image/png || die "Clipboard error"
- notifyOk "$WHAT copied to buffer"
-elif [ "$ACTION" = "save" ]; then
- if takeScreenshot "$FILE" "$GEOM" "$OUTPUT"; then
- TITLE="Screenshot of $SUBJECT"
- MESSAGE=$(basename "$FILE")
- notifyOk "$TITLE" "$MESSAGE" -i "$FILE"
- echo "$FILE"
- else
- notifyError "Error taking screenshot with grim"
- fi
-elif [ "$ACTION" = "edit" ]; then
- env_editor_confirm
- if takeScreenshot "$FILE_EDITOR" "$GEOM" "$OUTPUT"; then
- TITLE="Screenshot of $SUBJECT"
- MESSAGE="Open screenshot in image editor"
- notifyOk "$TITLE" "$MESSAGE" -i "$FILE_EDITOR"
- $GRIMBLAST_EDITOR "$FILE_EDITOR"
- echo "$FILE_EDITOR"
- else
- notifyError "Error taking screenshot"
- fi
-else
- if [ "$ACTION" = "copysave" ]; then
- takeScreenshot - "$GEOM" "$OUTPUT" | tee "$FILE" | wl-copy --type image/png || die "Clipboard error"
- notifyOk "$WHAT copied to buffer and saved to $FILE" -i "$FILE"
- echo "$FILE"
- else
- notifyError "Error taking screenshot with grim"
- fi
-fi
-
-killHyprpicker
\ No newline at end of file
diff --git a/src/share/sleex/scripts/grimshot.sh b/src/share/sleex/scripts/grimshot.sh
new file mode 100755
index 00000000..cffdb3c4
--- /dev/null
+++ b/src/share/sleex/scripts/grimshot.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+default_extention="png"
+directory="$HOME/Images/Screenshots/Grimshot"
+# TODO:Make automatical check for Images/... or Pictures/... and automatical mkdir
+function copy_only(){
+
+ name="$(date +"%d-%m-%Y_%H:%M").$default_extention"
+ output="/tmp/$name"
+
+ grim $output
+ wl-copy < $output
+
+ notify-send "Grimshot" "Screenshot has been made using:\nCopy only mode"
+ exit
+}
+
+function normalScreenshot() {
+ output="$directory/$name"
+
+ sleep 0.7
+ grim $output
+ wl-copy < $output
+
+ notify-send "Grimshot" "Screenshot $name has been made and copied to clipboard"
+}
+
+if [ "$1" == "--copy_only" ]; then
+ copy_only
+fi
+
+choice=$(printf "$(date +"%d-%m-%Y_%H:%M").$default_extention\nCustom name\nCopy only" | rofi -dmenu -p "Grimshot")
+
+case "$choice" in
+ "$(date +"%d-%m-%Y_%H:%M").$default_extention") name="$(date +"%d-%m-%Y_%H:%M").png";;
+ "Custom name") name=$(rofi -dmenu -p "Screenshot name").$default_extention;;
+ "Copy only") sleep 0.7 && copy_only ;;
+ *) exit
+esac
+
+normalScreenshot
\ No newline at end of file
diff --git a/src/share/sleex/scripts/hyprland/get_keybinds.py b/src/share/sleex/scripts/hyprland/get_keybinds.py
deleted file mode 100755
index 054b982d..00000000
--- a/src/share/sleex/scripts/hyprland/get_keybinds.py
+++ /dev/null
@@ -1,222 +0,0 @@
-#!/usr/bin/env -S\_/bin/sh\_-c\_"source\_\$(eval\_echo\_\$SLEEX_VIRTUAL_ENV)/bin/activate&&exec\_python\_-E\_"\$0"\_"\$@""
-import argparse
-import re
-import os
-from os.path import expandvars as os_expandvars
-from typing import Dict, List
-
-TITLE_REGEX = "#+!"
-HIDE_COMMENT = "[hidden]"
-MOD_SEPARATORS = ['+', ' ']
-COMMENT_BIND_PATTERN = "#/#"
-
-parser = argparse.ArgumentParser(description='Hyprland keybind reader')
-parser.add_argument('--path', type=str, default="/etc/sleex/hyprland/keybinds.conf", help='path to keybind file (sourcing isn\'t supported)')
-args = parser.parse_args()
-content_lines = []
-reading_line = 0
-
-# Little Parser made for hyprland keybindings conf file
-Variables: Dict[str, str] = {}
-
-
-class KeyBinding(dict):
- def __init__(self, mods, key, dispatcher, params, comment) -> None:
- self["mods"] = mods
- self["key"] = key
- self["dispatcher"] = dispatcher
- self["params"] = params
- self["comment"] = comment
-
-class Section(dict):
- def __init__(self, children, keybinds, name) -> None:
- self["children"] = children
- self["keybinds"] = keybinds
- self["name"] = name
-
-
-def read_content(path: str) -> str:
- if (not os.access(os.path.expanduser(os.path.expandvars(path)), os.R_OK)):
- return ("error")
- with open(os.path.expanduser(os.path.expandvars(path)), "r") as file:
- return file.read()
-
-
-def autogenerate_comment(dispatcher: str, params: str = "") -> str:
- match dispatcher:
-
- case "resizewindow":
- return "Resize window"
-
- case "movewindow":
- if(params == ""):
- return "Move window"
- else:
- return "Window: move in {} direction".format({
- "l": "left",
- "r": "right",
- "u": "up",
- "d": "down",
- }.get(params, "null"))
-
- case "pin":
- return "Window: pin (show on all workspaces)"
-
- case "splitratio":
- return "Window split ratio {}".format(params)
-
- case "togglefloating":
- return "Float/unfloat window"
-
- case "resizeactive":
- return "Resize window by {}".format(params)
-
- case "killactive":
- return "Close window"
-
- case "fullscreen":
- return "Toggle {}".format(
- {
- "0": "fullscreen",
- "1": "maximization",
- "2": "fullscreen on Hyprland's side",
- }.get(params, "null")
- )
-
- case "fakefullscreen":
- return "Toggle fake fullscreen"
-
- case "workspace":
- if params == "+1":
- return "Workspace: focus right"
- elif params == "-1":
- return "Workspace: focus left"
- return "Focus workspace {}".format(params)
-
- case "movefocus":
- return "Window: move focus {}".format(
- {
- "l": "left",
- "r": "right",
- "u": "up",
- "d": "down",
- }.get(params, "null")
- )
-
- case "swapwindow":
- return "Window: swap in {} direction".format(
- {
- "l": "left",
- "r": "right",
- "u": "up",
- "d": "down",
- }.get(params, "null")
- )
-
- case "movetoworkspace":
- if params == "+1":
- return "Window: move to right workspace (non-silent)"
- elif params == "-1":
- return "Window: move to left workspace (non-silent)"
- return "Window: move to workspace {} (non-silent)".format(params)
-
- case "movetoworkspacesilent":
- if params == "+1":
- return "Window: move to right workspace"
- elif params == "-1":
- return "Window: move to right workspace"
- return "Window: move to workspace {}".format(params)
-
- case "togglespecialworkspace":
- return "Workspace: toggle special"
-
- case "exec":
- return "Execute: {}".format(params)
-
- case _:
- return ""
-
-def get_keybind_at_line(line_number, line_start = 0):
- global content_lines
- line = content_lines[line_number]
- _, keys = line.split("=", 1)
- keys, *comment = keys.split("#", 1)
-
- mods, key, dispatcher, *params = list(map(str.strip, keys.split(",", 4)))
- params = "".join(map(str.strip, params))
-
- # Remove empty spaces
- comment = list(map(str.strip, comment))
- # Add comment if it exists, else generate it
- if comment:
- comment = comment[0]
- if comment.startswith("[hidden]"):
- return None
- else:
- comment = autogenerate_comment(dispatcher, params)
-
- if mods:
- modstring = mods + MOD_SEPARATORS[0] # Add separator at end to ensure last mod is read
- mods = []
- p = 0
- for index, char in enumerate(modstring):
- if(char in MOD_SEPARATORS):
- if(index - p > 1):
- mods.append(modstring[p:index])
- p = index+1
- else:
- mods = []
-
- return KeyBinding(mods, key, dispatcher, params, comment)
-
-def get_binds_recursive(current_content, scope):
- global content_lines
- global reading_line
- # print("get_binds_recursive({0}, {1}) [@L{2}]".format(current_content, scope, reading_line + 1))
- while reading_line < len(content_lines): # TODO: Adjust condition
- line = content_lines[reading_line]
- heading_search_result = re.search(TITLE_REGEX, line)
- # print("Read line {0}: {1}\tisHeading: {2}".format(reading_line + 1, content_lines[reading_line], "[{0}, {1}, {2}]".format(heading_search_result.start(), heading_search_result.start() == 0, ((heading_search_result != None) and (heading_search_result.start() == 0))) if heading_search_result != None else "No"))
- if ((heading_search_result != None) and (heading_search_result.start() == 0)): # Found title
- # Determine scope
- heading_scope = line.find('!')
- # Lower? Return
- if(heading_scope <= scope):
- reading_line -= 1
- return current_content
-
- section_name = line[(heading_scope+1):].strip()
- # print("[[ Found h{0} at line {1} ]] {2}".format(heading_scope, reading_line+1, content_lines[reading_line]))
- reading_line += 1
- current_content["children"].append(get_binds_recursive(Section([], [], section_name), heading_scope))
-
- elif line.startswith(COMMENT_BIND_PATTERN):
- keybind = get_keybind_at_line(reading_line, line_start=len(COMMENT_BIND_PATTERN))
- if(keybind != None):
- current_content["keybinds"].append(keybind)
-
- elif line == "" or not line.lstrip().startswith("bind"): # Comment, ignore
- pass
-
- else: # Normal keybind
- keybind = get_keybind_at_line(reading_line)
- if(keybind != None):
- current_content["keybinds"].append(keybind)
-
- reading_line += 1
-
- return current_content;
-
-def parse_keys(path: str) -> Dict[str, List[KeyBinding]]:
- global content_lines
- content_lines = read_content(path).splitlines()
- if content_lines[0] == "error":
- return "error"
- return get_binds_recursive(Section([], [], ""), 0)
-
-
-if __name__ == "__main__":
- import json
-
- ParsedKeys = parse_keys(args.path)
- print(json.dumps(ParsedKeys))
diff --git a/src/share/sleex/scripts/hyprland/workspace_action.sh b/src/share/sleex/scripts/hyprland/workspace_action.sh
deleted file mode 100755
index 2552764b..00000000
--- a/src/share/sleex/scripts/hyprland/workspace_action.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/usr/bin/env bash
-hyprctl dispatch "$1" $(((($(hyprctl activeworkspace -j | jq -r .id) - 1) / 10) * 10 + $2))
\ No newline at end of file
diff --git a/src/share/sleex/scripts/record-script.sh b/src/share/sleex/scripts/record-script.sh
index 478c56f7..bb49aeff 100755
--- a/src/share/sleex/scripts/record-script.sh
+++ b/src/share/sleex/scripts/record-script.sh
@@ -7,7 +7,7 @@ getaudiooutput() {
pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2
}
getactivemonitor() {
- hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
+ fht-compositor ipc -j space | jq -r '.monitors[] | select(.active).output'
}
xdgvideo="$(xdg-user-dir VIDEOS)"
diff --git a/src/share/sleex/services/Audio.qml b/src/share/sleex/services/Audio.qml
index eb69dfbc..de1dd0ec 100644
--- a/src/share/sleex/services/Audio.qml
+++ b/src/share/sleex/services/Audio.qml
@@ -2,7 +2,6 @@ import qs.modules.common
import QtQuick
import Quickshell
import Quickshell.Services.Pipewire
-import Quickshell.Hyprland
pragma Singleton
pragma ComponentBehavior: Bound
@@ -49,10 +48,8 @@ Singleton {
}
}
- // Reverted to Hyprland.dispatch, which is the idiomatic method for this shell.
function playSound(relativeSoundPath) {
const fullPath = "/usr/share/sleex/" + relativeSoundPath;
- // Use the absolute path to paplay to be safe.
const command = ["/usr/bin/paplay", `${fullPath}`];
Quickshell.execDetached(command);
}
diff --git a/src/share/sleex/services/Brightness.qml b/src/share/sleex/services/Brightness.qml
index 552b2f88..8a61a125 100644
--- a/src/share/sleex/services/Brightness.qml
+++ b/src/share/sleex/services/Brightness.qml
@@ -8,6 +8,7 @@ import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import QtQuick
+import Sleex.Fhtc
/**
* For managing brightness of monitors. Supports both brightnessctl and ddcutil.
@@ -27,21 +28,21 @@ Singleton {
}
function increaseBrightness(): void {
- const focusedName = Hyprland.focusedMonitor.name;
+ const focusedName = FhtcMonitors.activeMonitorName;
const monitor = monitors.find(m => focusedName === m.screen.name);
if (monitor)
monitor.setBrightness(monitor.brightness + 0.05);
}
function decreaseBrightness(): void {
- const focusedName = Hyprland.focusedMonitor.name;
+ const focusedName = FhtcMonitors.activeMonitorName;
const monitor = monitors.find(m => focusedName === m.screen.name);
if (monitor)
monitor.setBrightness(monitor.brightness - 0.05);
}
function setMonitorBrightness(b): void {
- const focusedName = Hyprland.focusedMonitor.name;
+ const focusedName = FhtcMonitors.activeMonitorName;
const monitor = monitors.find(m => focusedName === m.screen.name);
if (monitor)
monitor.setBrightness(b);
diff --git a/src/share/sleex/services/ConfigLoader.qml b/src/share/sleex/services/ConfigLoader.qml
index 498dcbf0..84df1f8e 100644
--- a/src/share/sleex/services/ConfigLoader.qml
+++ b/src/share/sleex/services/ConfigLoader.qml
@@ -7,7 +7,6 @@ import qs.modules.common.functions
import QtQuick
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Qt.labs.platform
/**
@@ -104,7 +103,6 @@ Singleton {
} else {
root.applyConfig(configFileView.text())
if (!root.preventNextNotification) {
- // Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration reloaded")}" "${root.filePath}"`)
} else {
root.preventNextNotification = false;
}
diff --git a/src/share/sleex/services/HyprlandKeybinds.qml b/src/share/sleex/services/FhtcKeybinds.qml
similarity index 77%
rename from src/share/sleex/services/HyprlandKeybinds.qml
rename to src/share/sleex/services/FhtcKeybinds.qml
index 2dace266..392b056b 100644
--- a/src/share/sleex/services/HyprlandKeybinds.qml
+++ b/src/share/sleex/services/FhtcKeybinds.qml
@@ -7,17 +7,16 @@ import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
-import Quickshell.Hyprland
/**
- * A service that provides access to Hyprland keybinds.
+ * A service that provides access to keybinds.
* Uses the `get_keybinds.py` script to parse comments in config files in a certain format and convert to JSON.
*/
Singleton {
id: root
- property string keybindParserPath: FileUtils.trimFileProtocol(`/usr/share/sleex/scripts/hyprland/get_keybinds.py`)
- property string defaultKeybindConfigPath: FileUtils.trimFileProtocol(`/etc/sleex/hyprland/keybinds.conf`)
- property string userKeybindConfigPath: FileUtils.trimFileProtocol(`${Directories.config}/hypr/custom/keybinds.conf`)
+ property string keybindParserPath: FileUtils.trimFileProtocol(`/usr/share/sleex/scripts/fht/get_keybinds.py`)
+ property string defaultKeybindConfigPath: FileUtils.trimFileProtocol(`/etc/sleex/compositor/keybinds.toml`)
+ property string userKeybindConfigPath: FileUtils.trimFileProtocol(`${Directories.config}/fht/custom/keybinds.toml`)
property var defaultKeybinds: {"children": []}
property var userKeybinds: {"children": []}
property var keybinds: ({
@@ -27,17 +26,6 @@ Singleton {
]
})
- Connections {
- target: Hyprland
-
- function onRawEvent(event) {
- if (event.name == "configreloaded") {
- getDefaultKeybinds.running = true
- getUserKeybinds.running = true
- }
- }
- }
-
Process {
id: getDefaultKeybinds
running: true
diff --git a/src/share/sleex/services/FirstRunExperience.qml b/src/share/sleex/services/FirstRunExperience.qml
index dff06a0c..5b4c18ee 100644
--- a/src/share/sleex/services/FirstRunExperience.qml
+++ b/src/share/sleex/services/FirstRunExperience.qml
@@ -4,7 +4,6 @@ import qs.modules.common.functions
import qs.modules.common
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
Singleton {
id: root
diff --git a/src/share/sleex/services/HyprlandData.qml b/src/share/sleex/services/HyprlandData.qml
deleted file mode 100644
index a9868a45..00000000
--- a/src/share/sleex/services/HyprlandData.qml
+++ /dev/null
@@ -1,85 +0,0 @@
-pragma Singleton
-pragma ComponentBehavior: Bound
-
-import QtQuick
-import Quickshell
-import Quickshell.Io
-import Quickshell.Wayland
-import Quickshell.Hyprland
-
-/**
- * Provides access to some Hyprland data not available in Quickshell.Hyprland.
- */
-Singleton {
- id: root
- property var windowList: []
- property var addresses: []
- property var windowByAddress: ({})
- property var monitors: []
- property var layers: ({})
-
- function updateWindowList() {
- getClients.running = true
- getMonitors.running = true
- }
-
- function updateLayers() {
- getLayers.running = true
- }
-
- Component.onCompleted: {
- updateWindowList()
- updateLayers()
- }
-
- Connections {
- target: Hyprland
-
- function onRawEvent(event) {
- // Filter out redundant old v1 events for the same thing
- if(event.name in [
- "activewindow", "focusedmon", "monitoradded",
- "createworkspace", "destroyworkspace", "moveworkspace",
- "activespecial", "movewindow", "windowtitle"
- ]) return ;
- updateWindowList()
- }
- }
-
- Process {
- id: getClients
- command: ["bash", "-c", "hyprctl clients -j | jq -c"]
- stdout: SplitParser {
- onRead: (data) => {
- root.windowList = JSON.parse(data)
- let tempWinByAddress = {}
- for (var i = 0; i < root.windowList.length; ++i) {
- var win = root.windowList[i]
- tempWinByAddress[win.address] = win
- }
- root.windowByAddress = tempWinByAddress
- root.addresses = root.windowList.map((win) => win.address)
- }
- }
- }
- Process {
- id: getMonitors
- command: ["bash", "-c", "hyprctl monitors -j | jq -c"]
- stdout: SplitParser {
- onRead: (data) => {
- root.monitors = JSON.parse(data)
- }
- }
- }
-
- Process {
- id: getLayers
- command: ["bash", "-c", "hyprctl layers -j | jq -c"]
- stdout: SplitParser {
- onRead: (data) => {
- root.layers = JSON.parse(data)
- }
- }
- }
-}
-
diff --git a/src/share/sleex/services/LatexRenderer.qml b/src/share/sleex/services/LatexRenderer.qml
index ecfbf3bb..12c759d6 100644
--- a/src/share/sleex/services/LatexRenderer.qml
+++ b/src/share/sleex/services/LatexRenderer.qml
@@ -7,7 +7,6 @@ import qs.modules.common
import QtQuick
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Qt.labs.platform
/**
diff --git a/src/share/sleex/services/Monitors.qml b/src/share/sleex/services/Monitors.qml
deleted file mode 100644
index ae24fcd2..00000000
--- a/src/share/sleex/services/Monitors.qml
+++ /dev/null
@@ -1,170 +0,0 @@
-pragma Singleton
-
-import Quickshell
-import Quickshell.Io
-import QtQuick
-
-
-Singleton {
- id: root
-
- readonly property list monitors: []
- readonly property Monitor primary: monitors.find(m => m.primary) ?? null
- property int snapThreshold: 20
-
- reloadableId: "monitors"
-
- function setMonitorPosition(name: string, x: int, y: int): void {
- const monitor = monitors.find(m => m.name === name);
- if (!monitor) return;
-
- setPositionProc.exec([
- "hyprctl", "keyword", "monitor",
- `${name},${monitor.width}x${monitor.height},${x}x${y},${monitor.scale}`
- ]);
- }
-
- function snapMonitors(primaryName: string, secondaryName: string, position: string): void {
- const primary = monitors.find(m => m.name === primaryName);
- const secondary = monitors.find(m => m.name === secondaryName);
- if (!primary || !secondary) return;
-
- let x, y;
- switch (position) {
- case "right":
- x = primary.x + primary.width;
- y = primary.y;
- break;
- case "left":
- x = primary.x - secondary.width;
- y = primary.y;
- break;
- case "top":
- x = primary.x;
- y = primary.y - secondary.height;
- break;
- case "bottom":
- x = primary.x;
- y = primary.y + primary.height;
- break;
- default:
- return;
- }
-
- setMonitorPosition(secondaryName, x, y);
- }
-
- function toggleMonitor(name: string): void {
- const monitor = monitors.find(m => m.name === name);
- if (!monitor) return;
-
- const config = monitor.enabled ?
- `${name},disabled` :
- `${name},${monitor.width}x${monitor.height},${monitor.x}x${monitor.y},${monitor.scale}`;
-
- toggleProc.exec(["hyprctl", "keyword", "monitor", config]);
- }
-
- function refreshMonitors(): void {
- getMonitors.running = true;
- }
-
- // Get monitor information
- Process {
- id: getMonitors
-
- running: true
- command: ["hyprctl", "monitors", "-j"]
- environment: ({
- LANG: "C",
- LC_ALL: "C"
- })
-
- stdout: StdioCollector {
- onStreamFinished: {
- let monitorData;
- try {
- monitorData = JSON.parse(text);
- } catch (e) {
- console.error("Failed to parse monitor data:", e);
- return;
- }
-
- const rMonitors = root.monitors;
-
- // Remove destroyed monitors
- const destroyed = rMonitors.filter(rm =>
- !monitorData.find(m => m.name === rm.name)
- );
- for (const monitor of destroyed) {
- rMonitors.splice(rMonitors.indexOf(monitor), 1).forEach(m => m.destroy());
- }
-
- // Update or create monitors
- for (const monitorInfo of monitorData) {
- const match = rMonitors.find(m => m.name === monitorInfo.name);
- if (match) {
- match.lastIpcObject = monitorInfo;
- } else {
- rMonitors.push(monitorComp.createObject(root, {
- lastIpcObject: monitorInfo
- }));
- }
- }
- }
- }
- }
-
- // Set monitor position
- Process {
- id: setPositionProc
-
- onExited: {
- getMonitors.running = true;
- }
- }
-
- // Toggle monitor
- Process {
- id: toggleProc
-
- onExited: {
- getMonitors.running = true;
- }
- }
-
- // Listen for Hyprland events
- Process {
- running: true
- command: ["socat", "-", "UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock"]
-
- stdout: SplitParser {
- onRead: {
- if (data.startsWith("monitoradded") ||
- data.startsWith("monitorremoved") ||
- data.startsWith("configreloaded")) {
- getMonitors.running = true;
- }
- }
- }
- }
-
- component Monitor: QtObject {
- required property var lastIpcObject
-
- readonly property string name: lastIpcObject.name ?? ""
- readonly property int width: lastIpcObject.width ?? 0
- readonly property int height: lastIpcObject.height ?? 0
- readonly property int x: lastIpcObject.x ?? 0
- readonly property int y: lastIpcObject.y ?? 0
- readonly property real scale: lastIpcObject.scale ?? 1.0
- readonly property bool primary: lastIpcObject.focused ?? false
- readonly property bool enabled: width > 0 && height > 0
- readonly property string resolution: `${width}x${height}`
- }
-
- Component {
- id: monitorComp
- Monitor {}
- }
-}
\ No newline at end of file
diff --git a/src/share/sleex/services/NightLight.qml b/src/share/sleex/services/NightLight.qml
index 5dfa45a2..580adfe3 100644
--- a/src/share/sleex/services/NightLight.qml
+++ b/src/share/sleex/services/NightLight.qml
@@ -4,11 +4,6 @@ import qs.modules.common
import Quickshell
import Quickshell.Io
-/**
- * Simple hyprsunset service with automatic mode.
- * In theory we don't need this because hyprsunset has a config file, but it somehow doesn't work.
- * It should also be possible to control it via hyprctl, but it doesn't work consistently either so we're just killing and launching.
- */
Singleton {
id: root
property bool ready: false
@@ -55,7 +50,7 @@ Singleton {
onColorTemperatureChanged: {
if (root.active)
- Quickshell.execDetached(["hyprctl", "hyprsunset", "temperature", String(colorTemperature)]);
+ Quickshell.execDetached(["gammastep", "-O", String(colorTemperature)]);
}
function reEvaluate() {
@@ -76,12 +71,12 @@ Singleton {
function enable() {
root.active = true;
Quickshell.execDetached(["bash", "-c",
- `pidof hyprsunset || hyprsunset --temperature ${root.colorTemperature}`]);
+ `pidof gammastep || gammastep -O ${root.colorTemperature}`]);
}
function disable() {
root.active = false;
- Quickshell.execDetached(["bash", "-c", "pkill hyprsunset"]);
+ Quickshell.execDetached(["bash", "-c", "pkill gammastep"]);
}
function fetchState() {
@@ -91,7 +86,7 @@ Singleton {
Process {
id: fetchProc
running: true
- command: ["bash", "-c", "hyprctl hyprsunset temperature"]
+ command: ["bash", "-c", "hyprctl hyprsunset temperature"] // TODO: use gammastep
stdout: StdioCollector {
id: stateCollector
onStreamFinished: {
diff --git a/src/share/sleex/services/PersistentStateManager.qml b/src/share/sleex/services/PersistentStateManager.qml
index 5a5addbe..bb126267 100644
--- a/src/share/sleex/services/PersistentStateManager.qml
+++ b/src/share/sleex/services/PersistentStateManager.qml
@@ -6,7 +6,6 @@ import qs.modules.common.functions
import QtQuick
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Qt.labs.platform
/**
diff --git a/src/share/sleex/services/Ydotool.qml b/src/share/sleex/services/Ydotool.qml
index ca9248cd..b7c36453 100644
--- a/src/share/sleex/services/Ydotool.qml
+++ b/src/share/sleex/services/Ydotool.qml
@@ -3,7 +3,6 @@ pragma Singleton
import qs.modules.common
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
Singleton {
id: root
diff --git a/src/share/sleex/settings.qml b/src/share/sleex/settings.qml
index 6098bcc0..af766326 100644
--- a/src/share/sleex/settings.qml
+++ b/src/share/sleex/settings.qml
@@ -10,7 +10,6 @@ import QtQuick.Layouts
import QtQuick.Window
import Quickshell
import Quickshell.Io
-import Quickshell.Hyprland
import Quickshell.Widgets
import qs
import qs.services
diff --git a/src/share/sleex/shell.qml b/src/share/sleex/shell.qml
index 24290ae9..04ae8454 100644
--- a/src/share/sleex/shell.qml
+++ b/src/share/sleex/shell.qml
@@ -18,7 +18,6 @@ import qs.modules.polkit
import qs.modules.screenCorners
import qs.modules.session
import qs.modules.dashboard
-import qs.modules.sidebarLeft
import qs.modules.wallpaperSelector
import qs.modules.background
import qs.modules.lockscreen
@@ -46,7 +45,6 @@ ShellRoot {
property bool enableReloadPopup: true
property bool enableScreenCorners: false
property bool enableSession: true
- property bool enableSidebarLeft: false
property bool enableDashboard: true
property bool enableWallSelector: true
property bool enableBackground: true
@@ -73,7 +71,6 @@ ShellRoot {
LazyLoader { active: true; component: BatteryPopup {} }
LazyLoader { active: enableScreenCorners; component: ScreenCorners {} }
LazyLoader { active: enableSession; component: Session {} }
- LazyLoader { active: enableSidebarLeft; component: SidebarLeft {} }
LazyLoader { active: enableDashboard; component: Dashboard {} }
LazyLoader { active: enableWallSelector; component: WallpaperSelector {} }
LazyLoader { active: enableBackground; component: Background {} }