Compare commits
31
Commits
8d8373aedd
..
test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9c9bf546b | ||
|
|
f0106699b9 | ||
|
|
5b8821f5a2 | ||
|
|
8f5a286b79 | ||
|
|
8f7e191a84 | ||
|
|
74ebd86694 | ||
|
|
a30fb7394e | ||
|
|
032378ca59 | ||
|
|
f728bb1392 | ||
|
|
b43b71b8d2 | ||
|
|
d8e090d656 | ||
|
|
7952915c47 | ||
|
|
9608642c84 | ||
|
|
2581c8a411 | ||
|
|
c0d27247fd | ||
|
|
3e5a1f97f8 | ||
|
|
1a18f1e261 | ||
|
|
a98fa33b63 | ||
|
|
f91c71461b | ||
|
|
1e46c33b7b | ||
|
|
41a55741f4 | ||
|
|
4ff24084cb | ||
|
|
c2ae3d05be | ||
|
|
8d31459e9f | ||
|
|
54f98286a6 | ||
|
|
98cdb8808e | ||
|
|
de89d6fb52 | ||
|
|
85f449b02e | ||
|
|
76f179e657 | ||
|
|
e9bab91e5f | ||
|
|
31811894b3 |
@@ -1,2 +1,70 @@
|
|||||||
# dotfiles-hypr
|
# Hyprland Dotfiles
|
||||||
|
|
||||||
|
Кастомное окружение рабочего стола для **Hyprland**, сфокусированное на производительности и глубокой интеграции с системными событиями через **Eww**. Обновление интерфейса происходит мгновенно по сигналу от системы, а не по тяжелым таймерам.
|
||||||
|
|
||||||
|
## Стек
|
||||||
|
|
||||||
|
| Компонент | Технология | Описание |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| **Window Manager** | Hyprland | Wayland-композитор с динамическими воркспейсами. |
|
||||||
|
| **Widget system** | Eww | Отрисовка баров и виджетов на базе GTK. |
|
||||||
|
| **State Management** | Python / Socat | Скрипты-демоны для обработки IPC-событий. |
|
||||||
|
| **Notification** | Dunst | Легковесный демон уведомлений. |
|
||||||
|
| **Application Launcher** | Rofi | Переписанный под Wayland лаунчер с кастомной темой. |
|
||||||
|
|
||||||
|
## Архитектурные особенности
|
||||||
|
|
||||||
|
### 1. Workspace Management
|
||||||
|
В отличие от стандартных модулей, здесь используется `UnifiedWorkspaceManager` на Python.
|
||||||
|
* **Механизм:** Скрипт подключается к Unix-сокету Hyprland (`.socket2.sock`) и слушает поток событий (`openwindow`, `closewindow`, `activewindow`).
|
||||||
|
* **Оптимизация:** Обновление json-состояния происходит только при изменении структуры окон, что сводит нагрузку на cpu к минимуму.
|
||||||
|
* **Визуализация:** Поддержка динамических иконок приложений и индикация состояния `urgent` (через css-анимацию `pulse-lavender`).
|
||||||
|
|
||||||
|
<video src="assets/eww_left.mkv" width="100%" autoplay loop muted></video>
|
||||||
|
|
||||||
|
### 2. Системный мониторинг и управление
|
||||||
|
Комплекс виджетов в правой части панели разделен на независимые модули:
|
||||||
|
|
||||||
|
* **Audio Subsytem:** Использует `pactl subscribe`. Виджет мгновенно отражает изменения громкости, сделанные горячими клавишами, без опроса системы в цикле.
|
||||||
|
* **Hardware Stats:** Мониторинг cpu (avg), ram и nvidia gpu temp. Реализована логика визуального предупреждения: при достижении критических температур (75°C+) или нагрузки (90%+), виджет переходит в состояние `.alert`, меняя визуальный стиль.
|
||||||
|
* **Keyboard LED Monitor:** Реализован через прямую прослушку `/dev/input/` с использованием библиотеки `evdev`. Позволяет отображать состояние Caps/Num Lock даже если они переключаются внешними скриптами.
|
||||||
|
|
||||||
|
<video src="assets/eww_right.mkv" width="100%" autoplay loop muted></video>
|
||||||
|
|
||||||
|
## Dunst & Rofi Integration
|
||||||
|
|
||||||
|
Уведомления и лаунчер полностью синхронизированы по цветам и радиусам скругления с основным баром, создавая единое визуальное пространство.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Превью Dunst и Rofi</summary>
|
||||||
|
|
||||||
|
#### Dunst (Notifications)
|
||||||
|
<video src="assets/dunst.mkv" width="80%" autoplay loop muted></video>
|
||||||
|
|
||||||
|
#### Rofi (App Launcher)
|
||||||
|
<video src="assets/rofi.mkv" width="80%" autoplay loop muted></video>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Зависимости
|
||||||
|
|
||||||
|
`eww` доступен только в `AUR`:
|
||||||
|
|
||||||
|
```Bash
|
||||||
|
yay -S eww
|
||||||
|
```
|
||||||
|
|
||||||
|
`dunst`, `rofi`, `hyprland`:
|
||||||
|
```Bash
|
||||||
|
pacman -S dunst rofi hyprland
|
||||||
|
```
|
||||||
|
|
||||||
|
Зависимости для `eww`:
|
||||||
|
```Bash
|
||||||
|
pacman -S xorg-xrand jq xdg-desktop-portal-gtk hyprpolkitagent hyprlock hypridle python-dbus python-evdev python-gobject libnotify wl-clipboard cliphist socat
|
||||||
|
```
|
||||||
|
|
||||||
|
Для корректной работы `python-evdev` добавляет своего пользователя в группу `input`:
|
||||||
|
```Bash
|
||||||
|
usermoad -aG input $USER
|
||||||
|
```
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5
-5
@@ -1,14 +1,14 @@
|
|||||||
[global]
|
[global]
|
||||||
font = Inter, FiraCode Nerd Font 11
|
font = Inter, FiraCode Nerd Font 11
|
||||||
format = "<b>%s</b>\n%b" // заголовок жирным, переход на строку, тело
|
format = "<b>%s</b>\n%b"
|
||||||
alignment = left
|
alignment = left
|
||||||
vertical_alignment = center
|
vertical_alignment = center
|
||||||
word_wrap = yes // перенос длинных строк
|
word_wrap = yes
|
||||||
width = (0, 350)
|
width = (0, 350)
|
||||||
height = (0, 110)
|
height = (0, 110)
|
||||||
offset = (35, 65)
|
offset = (35, 65)
|
||||||
origin = top-right
|
origin = top-right
|
||||||
follow = keyboard // на мониторе, где фокус клавиатуры
|
follow = keyboard
|
||||||
corner_radius = 10
|
corner_radius = 10
|
||||||
padding = 15
|
padding = 15
|
||||||
horizontal_padding = 15
|
horizontal_padding = 15
|
||||||
@@ -24,8 +24,8 @@
|
|||||||
progress_bar_max_width = 320
|
progress_bar_max_width = 320
|
||||||
icon_position = mouse_left_click
|
icon_position = mouse_left_click
|
||||||
max_icon_size = 64
|
max_icon_size = 64
|
||||||
mouse_left_click = do_action, close_current // лкм: действие и закрыть
|
mouse_left_click = do_action, close_current
|
||||||
mouse_right_click = close_all // пкм: закрыть все
|
mouse_right_click = close_all
|
||||||
|
|
||||||
[urgency_low]
|
[urgency_low]
|
||||||
background = "#363a4fe6"
|
background = "#363a4fe6"
|
||||||
|
|||||||
+29
-6
@@ -115,6 +115,7 @@ separator {
|
|||||||
// воркспейсы
|
// воркспейсы
|
||||||
.workspaces-rail {
|
.workspaces-rail {
|
||||||
@extend .capsule;
|
@extend .capsule;
|
||||||
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
transition: all $t-norm $ease-oil;
|
transition: all $t-norm $ease-oil;
|
||||||
}
|
}
|
||||||
@@ -138,11 +139,10 @@ separator {
|
|||||||
// раскладка
|
// раскладка
|
||||||
.kb-pill {
|
.kb-pill {
|
||||||
@extend .capsule;
|
@extend .capsule;
|
||||||
|
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
|
||||||
.kb-label {
|
.kb-label { transition: color $t-norm ease-in-out; }
|
||||||
transition: color $t-norm ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.caps-on {
|
&.caps-on {
|
||||||
border-color: $yellow;
|
border-color: $yellow;
|
||||||
@@ -165,10 +165,16 @@ separator {
|
|||||||
// статистика
|
// статистика
|
||||||
.stats-pill {
|
.stats-pill {
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
|
|
||||||
|
.slide-wrapper { transition: all $t-fast ease-in-out; }
|
||||||
|
|
||||||
&.alert {
|
&.alert {
|
||||||
border-color: $red;
|
border-color: $red;
|
||||||
border-width: 0.75px;
|
border-width: 0.75px;
|
||||||
.slide-wrapper { border-color: $red; }
|
.slide-wrapper {
|
||||||
|
border-color: $red;
|
||||||
|
transition: all $t-slow ease-in-out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,13 +187,28 @@ separator {
|
|||||||
.volume-pill {
|
.volume-pill {
|
||||||
@extend .capsule;
|
@extend .capsule;
|
||||||
|
|
||||||
|
.icon, .stat-label { transition: color $t-norm ease-in-out; }
|
||||||
.icon { margin-right: 14px; }
|
.icon { margin-right: 14px; }
|
||||||
|
.slide-wrapper { transition: all $t-fast ease-in-out; }
|
||||||
|
|
||||||
&.muted {
|
&.muted {
|
||||||
border-color: #444;
|
border-color: #444;
|
||||||
.icon, .stat-label { color: $muted-gray; }
|
.icon, .stat-label { color: $muted-gray; }
|
||||||
scale slider { background-color: $muted-gray; }
|
|
||||||
scale trough highlight { background-color: #444; }
|
scale slider {
|
||||||
|
background-color: $muted-gray;
|
||||||
|
transition: background-color $t-norm ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
scale trough highlight {
|
||||||
|
background-color: #444;
|
||||||
|
transition: background-color $t-norm ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-wrapper {
|
||||||
|
border-color: #444;
|
||||||
|
transition: all $t-slow ease-in-out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,8 +217,10 @@ separator {
|
|||||||
// трей
|
// трей
|
||||||
.tray-capsule {
|
.tray-capsule {
|
||||||
@extend .capsule;
|
@extend .capsule;
|
||||||
|
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
|
||||||
image { margin: 0 2px; opacity: 0.8; }
|
image { margin: 0 2px; opacity: 0.8; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-13
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
;; слушатели
|
;; слушатели
|
||||||
(deflisten ws_data :initial "{}" "python3 $HOME/.config/eww/scripts/workspaces.py")
|
(deflisten ws_data :initial "{}" "python3 $HOME/.config/eww/scripts/workspaces.py")
|
||||||
(deflisten volume :initial "0" "python3 $HOME/.config/eww/scripts/volume.py")
|
(deflisten volume :initial '{"volume": 0, "muted": false}' "python3 -u $HOME/.config/eww/scripts/volume.py")
|
||||||
(deflisten layout :initial "EN" "socat -u UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | stdbuf -oL awk -F'>>' '/activelayout/ {split(\$2, a, \",\"); print toupper(substr(a[2], 1, 2))}'")
|
(deflisten layout :initial "EN" "socat -u UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | stdbuf -oL awk -F'>>' '/activelayout/ {split(\$2, a, \",\"); print toupper(substr(a[2], 1, 2))}'")
|
||||||
(deflisten tray_count :initial "0" "python3 $HOME/.config/eww/scripts/tray_monitor.py")
|
(deflisten tray_count :initial "0" "python3 $HOME/.config/eww/scripts/tray_monitor.py")
|
||||||
(deflisten kb_leds :initial '{"caps":"0","num":"0"}' "python3 $HOME/.config/eww/scripts/kb_leds.py")
|
(deflisten kb_leds :initial '{"caps":"0","num":"0"}' "python3 $HOME/.config/eww/scripts/kb_leds.py")
|
||||||
@@ -21,8 +21,9 @@
|
|||||||
;; пуллы
|
;; пуллы
|
||||||
(defpoll timestr :interval "1s" "date '+%H:%M'")
|
(defpoll timestr :interval "1s" "date '+%H:%M'")
|
||||||
(defpoll datestr :interval "1m" "date '+%A, %B %d'")
|
(defpoll datestr :interval "1m" "date '+%A, %B %d'")
|
||||||
(defpoll temp :interval "30s" :initial "0" "awk '{print int($1/1000)}' /sys/class/hwmon/hwmon2/temp1_input")
|
(defpoll temp :interval "5s" :initial "0" "cat /sys/class/hwmon/$(grep -l 'coretemp' /sys/class/hwmon/hwmon*/name | cut -d/ -f5)/temp1_input | awk '{print int($1/1000)}'")
|
||||||
(defpoll gpu_temp :interval "30s" :initial "0" "nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits")
|
(defpoll gpu_temp :interval "30s" :initial "0" "nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits")
|
||||||
|
(defpoll disk_free :interval "1m" "python3 $HOME/.config/eww/scripts/disk.py")
|
||||||
|
|
||||||
;; ==============
|
;; ==============
|
||||||
;; воркспейсы
|
;; воркспейсы
|
||||||
@@ -72,15 +73,21 @@
|
|||||||
|
|
||||||
;; звук
|
;; звук
|
||||||
(defwidget volumewidget [reveal_state reveal_var]
|
(defwidget volumewidget [reveal_state reveal_var]
|
||||||
(eventbox :cursor "pointer" :onclick "eww update ${reveal_var}=${!reveal_state}"
|
(eventbox :cursor "pointer"
|
||||||
(box :class "capsule volume-pill ${reveal_state ? 'active' : ''} ${volume == 0 || volume == '' ? 'muted' : ''}"
|
:onclick "eww update ${reveal_var}=${!reveal_state}"
|
||||||
|
:onrightclick "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
|
||||||
|
:onscroll "echo {} | sed -e 's/up/5%+/g' -e 's/down/5%-/g' | xargs wpctl set-volume -l 1.35 @DEFAULT_AUDIO_SINK@"
|
||||||
|
(box :class "capsule volume-pill ${reveal_state ? 'active' : ''} ${volume.muted || volume.volume == 0 || volume.volume == '0' ? 'muted' : ''}"
|
||||||
:space-evenly false
|
:space-evenly false
|
||||||
(label :class "icon" :text {volume == 0 || volume == "" ? "" : (volume < 50 ? "" : "")})
|
(label :class "icon"
|
||||||
(label :class "stat-label" :text "${volume}%" :width 38)
|
:text {volume.muted || volume.volume == 0 || volume.volume == "0" ? "" : (volume.volume < 50 ? "" : "")})
|
||||||
(revealer :reveal reveal_state :transition "slideleft"
|
(label :class "stat-label"
|
||||||
|
:text "${volume.volume}%" :width 38)
|
||||||
|
(revealer :reveal reveal_state
|
||||||
|
:transition "slideleft"
|
||||||
(box :class "slide-wrapper"
|
(box :class "slide-wrapper"
|
||||||
(scale :class "vol-bar" :min 0 :max 101 :value {volume ?: 0}
|
(scale :class "vol-bar" :min 0 :max 101 :value {volume.volume}
|
||||||
:onchange "pactl set-sink-volume @DEFAULT_SINK@ {}%"))))))
|
:onchange "wpctl set-volume @DEFAULT_AUDIO_SINK@ {}%"))))))
|
||||||
|
|
||||||
;; часы
|
;; часы
|
||||||
(defwidget timewidget [reveal_state reveal_var]
|
(defwidget timewidget [reveal_state reveal_var]
|
||||||
@@ -103,7 +110,7 @@
|
|||||||
(group :icon "" :value "${round(EWW_RAM.used_mem_perc, 0)}%" :alert {EWW_RAM.used_mem_perc > 90})
|
(group :icon "" :value "${round(EWW_RAM.used_mem_perc, 0)}%" :alert {EWW_RAM.used_mem_perc > 90})
|
||||||
(revealer :reveal reveal_state :transition "slideleft"
|
(revealer :reveal reveal_state :transition "slideleft"
|
||||||
(box :space-evenly false :class "slide-wrapper"
|
(box :space-evenly false :class "slide-wrapper"
|
||||||
(group :icon "" :value "${round(EWW_DISK['/'].free / 1073741824, 1)}GB" :alert {EWW_DISK['/'].free / 1073741824 < 10})
|
(group :icon "" :value "${round(disk_free, 1)}GB" :alert {disk_free < 10})
|
||||||
(group :icon "" :value "${temp}°" :alert {(temp ?: 0) > 75})
|
(group :icon "" :value "${temp}°" :alert {(temp ?: 0) > 75})
|
||||||
(group :icon "" :value "${gpu_temp}°" :alert {(gpu_temp ?: 0) > 75}))))))
|
(group :icon "" :value "${gpu_temp}°" :alert {(gpu_temp ?: 0) > 75}))))))
|
||||||
|
|
||||||
@@ -131,12 +138,13 @@
|
|||||||
(statswidget :reveal_state s_rev :reveal_var s_var)
|
(statswidget :reveal_state s_rev :reveal_var s_var)
|
||||||
(volumewidget :reveal_state v_rev :reveal_var v_var))))
|
(volumewidget :reveal_state v_rev :reveal_var v_var))))
|
||||||
|
|
||||||
;; дополнительный бар (без трея)
|
;; дополнительный бар
|
||||||
(defwidget side_bar [mon_key t_rev t_var s_rev s_var v_rev v_var]
|
(defwidget side_bar [mon_key t_rev t_var s_rev s_var v_rev v_var]
|
||||||
(centerbox :class "bar-inner"
|
(centerbox :class "bar-inner"
|
||||||
(box :halign "start" (workspaces :ws_data {ws_data[mon_key]}))
|
(box :halign "start" (workspaces :ws_data {ws_data[mon_key]}))
|
||||||
(box :halign "center" (timewidget :reveal_state t_rev :reveal_var t_var))
|
(box :halign "center" (timewidget :reveal_state t_rev :reveal_var t_var))
|
||||||
(box :halign "end" :space-evenly false
|
(box :halign "end" :space-evenly false
|
||||||
|
(tray_dynamic)
|
||||||
(kb_widget)
|
(kb_widget)
|
||||||
(statswidget :reveal_state s_rev :reveal_var s_var)
|
(statswidget :reveal_state s_rev :reveal_var s_var)
|
||||||
(volumewidget :reveal_state v_rev :reveal_var v_var))))
|
(volumewidget :reveal_state v_rev :reveal_var v_var))))
|
||||||
@@ -164,8 +172,8 @@
|
|||||||
;; обои
|
;; обои
|
||||||
(defwindow wallpaper_0 :monitor 0 :stacking "bg" :focusable false
|
(defwindow wallpaper_0 :monitor 0 :stacking "bg" :focusable false
|
||||||
:geometry (geometry :width "100%" :height "100%")
|
:geometry (geometry :width "100%" :height "100%")
|
||||||
(box :style "background-image: url('/home/pyrschtjag/Pictures/wallhaven-x1lr3l.jpg'); background-size: cover;"))
|
(box :style "background-image: url('/home/pyrschtjag/Pictures/wallhaven-0wl9jp.jpg'); background-size: cover;"))
|
||||||
|
|
||||||
(defwindow wallpaper_1 :monitor 1 :stacking "bg" :focusable false
|
(defwindow wallpaper_1 :monitor 1 :stacking "bg" :focusable false
|
||||||
:geometry (geometry :width "100%" :height "100%")
|
:geometry (geometry :width "100%" :height "100%")
|
||||||
(box :style "background-image: url('/home/pyrschtjag/Pictures/wallhaven-7jj86e.jpg'); background-size: cover;"))
|
(box :style "background-image: url('/home/pyrschtjag/Pictures/wallhaven-1jygr3.jpg'); background-size: cover;"))
|
||||||
|
|||||||
Executable
+22
@@ -0,0 +1,22 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
class DiskMonitor:
|
||||||
|
def __init__(self, path="/"):
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def get_free_gb(self):
|
||||||
|
try:
|
||||||
|
st = os.statvfs(self.path)
|
||||||
|
free_bytes = st.f_bavail * st.f_frsize
|
||||||
|
return round(free_bytes / (1024**3), 1)
|
||||||
|
except Exception:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
def push_update(self):
|
||||||
|
sys.stdout.write(f"{self.get_free_gb()}\n")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
DiskMonitor().push_update()
|
||||||
+23
-28
@@ -1,42 +1,37 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import os
|
import json
|
||||||
import signal
|
import re
|
||||||
|
|
||||||
class VolumeMonitor:
|
class VolumeMonitor:
|
||||||
def __init__(self):
|
def get_vol_info(self):
|
||||||
# отдаём текущую громкость при старте
|
|
||||||
self.push_update()
|
|
||||||
|
|
||||||
def get_vol(self):
|
|
||||||
# достаём цифры из pactl
|
|
||||||
try:
|
try:
|
||||||
raw = subprocess.check_output(["pactl", "get-sink-volume", "@DEFAULT_SINK@"], stderr=subprocess.DEVNULL).decode()
|
# Получаем громкость + состояние
|
||||||
if "/" in raw:
|
raw = subprocess.check_output(["wpctl", "get-volume", "@DEFAULT_AUDIO_SINK@"]).decode().strip()
|
||||||
# забираем канал и чистим от процентов
|
|
||||||
return raw.split("/")[1].strip().replace("%", "")
|
# Получаем громкость
|
||||||
except:
|
match = re.search(r"(\d\.\d+)", raw)
|
||||||
return "0"
|
vol_val = float(match.group(1)) if match else 0.0
|
||||||
return "0"
|
percent = int(vol_val * 100)
|
||||||
|
|
||||||
|
# Проверяем наличие метки muted
|
||||||
|
is_muted = "[MUTED]" in raw
|
||||||
|
|
||||||
|
return json.dumps({"volume": percent, "muted": is_muted})
|
||||||
|
except Exception:
|
||||||
|
return json.dumps({"volume": 0, "muted": False})
|
||||||
|
|
||||||
def push_update(self):
|
def push_update(self):
|
||||||
# отдаём данные в stdout для eww
|
sys.stdout.write(f"{self.get_vol_info()}\n")
|
||||||
vol = self.get_vol()
|
|
||||||
sys.stdout.write(f"{vol}\n")
|
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
# подписываемся на события аудио-сервера
|
self.push_update()
|
||||||
proc = subprocess.Popen(["pactl", "subscribe"], stdout=subprocess.PIPE, text=True)
|
proc = subprocess.Popen(["pw-mon"], stdout=subprocess.PIPE, text=True, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
try:
|
for line in proc.stdout:
|
||||||
for line in proc.stdout:
|
if "changed" in line:
|
||||||
# если изменился основной выход
|
self.push_update()
|
||||||
if "sink" in line:
|
|
||||||
self.push_update()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
proc.terminate()
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
VolumeMonitor().run()
|
VolumeMonitor().run()
|
||||||
+106
-110
@@ -1,10 +1,9 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import subprocess
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import socket
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
|
|
||||||
class UnifiedWorkspaceManager:
|
class UnifiedWorkspaceManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -16,11 +15,8 @@ class UnifiedWorkspaceManager:
|
|||||||
self.monitors_cache = {}
|
self.monitors_cache = {}
|
||||||
|
|
||||||
self.load_icon_configuration()
|
self.load_icon_configuration()
|
||||||
self.clients_refresh()
|
|
||||||
self.monitors_refresh()
|
|
||||||
|
|
||||||
def load_icon_configuration(self):
|
def load_icon_configuration(self):
|
||||||
# тянем иконки из конфига
|
|
||||||
config_path = os.path.join(self.script_directory, "workspaces_map.json")
|
config_path = os.path.join(self.script_directory, "workspaces_map.json")
|
||||||
try:
|
try:
|
||||||
with open(config_path, 'r') as file:
|
with open(config_path, 'r') as file:
|
||||||
@@ -28,53 +24,59 @@ class UnifiedWorkspaceManager:
|
|||||||
except:
|
except:
|
||||||
self.icon_map = {"default": "", "no-icon": ""}
|
self.icon_map = {"default": "", "no-icon": ""}
|
||||||
|
|
||||||
def monitors_refresh(self):
|
async def monitors_refresh(self):
|
||||||
# обновляем инфу по мониторам
|
|
||||||
try:
|
try:
|
||||||
raw = subprocess.check_output(["hyprctl", "-j", "monitors"], stderr=subprocess.DEVNULL)
|
proc = await asyncio.create_subprocess_exec(
|
||||||
monitors = json.loads(raw)
|
"hyprctl", "-j", "monitors",
|
||||||
self.monitors_cache = {
|
stdout=asyncio.subprocess.PIPE,
|
||||||
m['name']: {
|
stderr=asyncio.subprocess.DEVNULL
|
||||||
"id": m['id'],
|
)
|
||||||
"active_workspace_id": m['activeWorkspace']['id'],
|
stdout, _ = await proc.communicate()
|
||||||
"focused": m.get('focused', False)
|
if proc.returncode == 0:
|
||||||
} for m in monitors
|
monitors = json.loads(stdout.decode())
|
||||||
}
|
self.monitors_cache = {
|
||||||
|
m['name']: {
|
||||||
|
"id": m['id'],
|
||||||
|
"active_workspace_id": m['activeWorkspace']['id'],
|
||||||
|
"focused": m.get('focused', False)
|
||||||
|
} for m in monitors
|
||||||
|
}
|
||||||
except:
|
except:
|
||||||
self.monitors_cache = {}
|
self.monitors_cache = {}
|
||||||
|
|
||||||
def clients_refresh(self):
|
async def clients_refresh(self):
|
||||||
# полный сброс и пересборка кэша окон
|
|
||||||
try:
|
try:
|
||||||
raw = subprocess.check_output(["hyprctl", "-j", "clients"], stderr=subprocess.DEVNULL)
|
proc = await asyncio.create_subprocess_exec(
|
||||||
clients = json.loads(raw)
|
"hyprctl", "-j", "clients",
|
||||||
self.clients_cache = {
|
stdout=asyncio.subprocess.PIPE,
|
||||||
c['address'].strip().replace('0x', ''): {
|
stderr=asyncio.subprocess.DEVNULL
|
||||||
"class": c['class'],
|
)
|
||||||
"workspace_id": c['workspace']['id'],
|
stdout, _ = await proc.communicate()
|
||||||
"monitor_id": c['monitor']
|
if proc.returncode == 0:
|
||||||
} for c in clients
|
clients = json.loads(stdout.decode())
|
||||||
}
|
self.clients_cache = {
|
||||||
|
c['address'].strip().replace('0x', ''): {
|
||||||
|
"class": c['class'],
|
||||||
|
"workspace_id": c['workspace']['id'],
|
||||||
|
"monitor_id": c['monitor']
|
||||||
|
} for c in clients
|
||||||
|
}
|
||||||
except:
|
except:
|
||||||
self.clients_cache = {}
|
self.clients_cache = {}
|
||||||
|
|
||||||
def get_current_state_json(self):
|
def get_current_state_json(self):
|
||||||
# собираем финальный json
|
|
||||||
try:
|
try:
|
||||||
output = {}
|
output = {}
|
||||||
|
|
||||||
for mon_name, mon_data in self.monitors_cache.items():
|
for mon_name, mon_data in self.monitors_cache.items():
|
||||||
m_id = mon_data["id"]
|
m_id = mon_data["id"]
|
||||||
active_id = mon_data["active_workspace_id"]
|
active_id = mon_data["active_workspace_id"]
|
||||||
focused = mon_data["focused"]
|
focused = mon_data["focused"]
|
||||||
|
|
||||||
# готовим 10 слотов
|
|
||||||
workspaces = {
|
workspaces = {
|
||||||
str(i): {"exists": False, "active": False, "focused": False, "urgent": False, "icons": []}
|
str(i): {"exists": False, "active": False, "focused": False, "urgent": False, "icons": []}
|
||||||
for i in range(1, 11)
|
for i in range(1, 11)
|
||||||
}
|
}
|
||||||
|
|
||||||
# раскидываем окна по воркспейсам
|
|
||||||
for addr, client in self.clients_cache.items():
|
for addr, client in self.clients_cache.items():
|
||||||
if client['monitor_id'] == m_id:
|
if client['monitor_id'] == m_id:
|
||||||
ws_id = str(client['workspace_id'])
|
ws_id = str(client['workspace_id'])
|
||||||
@@ -88,12 +90,10 @@ class UnifiedWorkspaceManager:
|
|||||||
if icon not in slot["icons"]:
|
if icon not in slot["icons"]:
|
||||||
slot["icons"].append(icon)
|
slot["icons"].append(icon)
|
||||||
|
|
||||||
# помечаем текущий активный воркспейс
|
|
||||||
curr_ws = str(active_id)
|
curr_ws = str(active_id)
|
||||||
if curr_ws in workspaces:
|
if curr_ws in workspaces:
|
||||||
workspaces[curr_ws].update({"active": True, "focused": focused, "exists": True})
|
workspaces[curr_ws].update({"active": True, "focused": focused, "exists": True})
|
||||||
|
|
||||||
# рисуем no-icon
|
|
||||||
for ws in workspaces.values():
|
for ws in workspaces.values():
|
||||||
if ws["exists"] and not ws["icons"]:
|
if ws["exists"] and not ws["icons"]:
|
||||||
ws["icons"] = [self.icon_map.get("no-icon", "")]
|
ws["icons"] = [self.icon_map.get("no-icon", "")]
|
||||||
@@ -105,109 +105,105 @@ class UnifiedWorkspaceManager:
|
|||||||
return "{}"
|
return "{}"
|
||||||
|
|
||||||
def push_update(self):
|
def push_update(self):
|
||||||
# отдаём json в stdout при изменении
|
|
||||||
current = self.get_current_state_json()
|
current = self.get_current_state_json()
|
||||||
if current != self.last_sent_json:
|
if current != self.last_sent_json:
|
||||||
self.last_sent_json = current
|
self.last_sent_json = current
|
||||||
sys.stdout.write(current + '\n')
|
sys.stdout.write(current + '\n')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
def update_client(self, window_address):
|
async def update_client(self, window_address):
|
||||||
# обновляем окно
|
|
||||||
try:
|
try:
|
||||||
clean_addr = window_address.replace('0x', '')
|
clean_addr = window_address.replace('0x', '')
|
||||||
search_addr = f"0x{clean_addr}"
|
search_addr = f"0x{clean_addr}"
|
||||||
|
|
||||||
raw = subprocess.check_output(["hyprctl", "-j", "clients"], stderr=subprocess.DEVNULL)
|
proc = await asyncio.create_subprocess_exec(
|
||||||
clients = json.loads(raw)
|
"hyprctl", "-j", "clients",
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.DEVNULL
|
||||||
|
)
|
||||||
|
stdout, _ = await proc.communicate()
|
||||||
|
|
||||||
target = next((c for c in clients if c['address'] == search_addr), None)
|
if proc.returncode == 0:
|
||||||
|
clients = json.loads(stdout.decode())
|
||||||
|
target = next((c for c in clients if c['address'] == search_addr), None)
|
||||||
|
|
||||||
if target:
|
if target:
|
||||||
self.clients_cache[clean_addr] = {
|
self.clients_cache[clean_addr] = {
|
||||||
"class": target.get('class', 'unknown'),
|
"class": target.get('class', 'unknown'),
|
||||||
"workspace_id": target['workspace']['id'],
|
"workspace_id": target['workspace']['id'],
|
||||||
"monitor_id": target['monitor']
|
"monitor_id": target['monitor']
|
||||||
}
|
}
|
||||||
else:
|
return
|
||||||
self.clients_cache.pop(clean_addr, None)
|
|
||||||
|
|
||||||
|
self.clients_cache.pop(clean_addr, None)
|
||||||
except:
|
except:
|
||||||
# если что-то отвалилось, то просто пересобираем всё
|
await self.clients_refresh()
|
||||||
self.clients_refresh()
|
|
||||||
|
|
||||||
def run(self):
|
async def run(self):
|
||||||
|
await self.clients_refresh()
|
||||||
|
await self.monitors_refresh()
|
||||||
self.push_update()
|
self.push_update()
|
||||||
|
|
||||||
def event_socket_listener():
|
signature = os.environ.get('HYPRLAND_INSTANCE_SIGNATURE')
|
||||||
# слушаем сокет хайпера
|
socket_path = f"/run/user/{os.getuid()}/hypr/{signature}/.socket2.sock"
|
||||||
signature = os.environ.get('HYPRLAND_INSTANCE_SIGNATURE')
|
|
||||||
socket_path = f"/run/user/{os.getuid()}/hypr/{signature}/.socket2.sock"
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
|
reader, writer = await asyncio.open_unix_connection(socket_path)
|
||||||
sock.connect(socket_path)
|
|
||||||
buf = ""
|
|
||||||
while True:
|
|
||||||
data = sock.recv(4096).decode('utf-8', errors='ignore')
|
|
||||||
if not data: break
|
|
||||||
buf += data
|
|
||||||
|
|
||||||
if '\n' in buf:
|
while True:
|
||||||
lines = buf.split('\n')
|
line_bytes = await reader.readline()
|
||||||
buf = lines.pop()
|
if not line_bytes:
|
||||||
|
break
|
||||||
|
|
||||||
trigger = False
|
line = line_bytes.decode('utf-8', errors='ignore').strip()
|
||||||
for line in lines:
|
if ">>" not in line:
|
||||||
if ">>" not in line: continue
|
continue
|
||||||
event, args = line.split('>>')
|
|
||||||
|
|
||||||
# ивенты с окнами
|
event, args = line.split('>>')
|
||||||
if event in ["openwindow", "movewindow"]:
|
trigger = False
|
||||||
addr = args.split(',')[0].replace('0x', '')
|
|
||||||
self.update_client(addr)
|
|
||||||
trigger = True
|
|
||||||
|
|
||||||
elif event == "closewindow":
|
if event in ["openwindow", "movewindow"]:
|
||||||
addr = args.replace('0x', '')
|
addr = args.split(',')[0].replace('0x', '')
|
||||||
self.clients_cache.pop(addr, None)
|
await self.update_client(addr)
|
||||||
self.urgent_window_addresses.discard(addr)
|
trigger = True
|
||||||
trigger = True
|
|
||||||
|
|
||||||
# ивенты с мониторами
|
elif event == "closewindow":
|
||||||
elif event in ["workspace", "workspacev2", "monitoradded", "monitorremoved"]:
|
addr = args.replace('0x', '')
|
||||||
self.monitors_refresh()
|
self.clients_cache.pop(addr, None)
|
||||||
trigger = True
|
self.urgent_window_addresses.discard(addr)
|
||||||
|
trigger = True
|
||||||
|
|
||||||
elif event in ["focusedmon", "focusedmonv2"]:
|
elif event in ["workspace", "workspacev2", "monitoradded", "monitorremoved"]:
|
||||||
mon_name = args.split(',')[0]
|
await self.monitors_refresh()
|
||||||
for name in self.monitors_cache:
|
trigger = True
|
||||||
self.monitors_cache[name]["focused"] = (name == mon_name)
|
|
||||||
trigger = True
|
|
||||||
|
|
||||||
# алерты
|
elif event in ["focusedmon", "focusedmonv2"]:
|
||||||
elif event == "urgent":
|
mon_name = args.split(',')[0]
|
||||||
self.urgent_window_addresses.add(args.replace('0x', ''))
|
for name in self.monitors_cache:
|
||||||
trigger = True
|
self.monitors_cache[name]["focused"] = (name == mon_name)
|
||||||
|
trigger = True
|
||||||
|
|
||||||
elif event in ["activewindow", "activewindowv2"]:
|
elif event == "urgent":
|
||||||
addr = args.split(',')[0].replace('0x', '')
|
self.urgent_window_addresses.add(args.replace('0x', ''))
|
||||||
if addr in self.urgent_window_addresses:
|
trigger = True
|
||||||
self.urgent_window_addresses.remove(addr)
|
|
||||||
trigger = True
|
|
||||||
|
|
||||||
if trigger:
|
elif event in ["activewindow", "activewindowv2"]:
|
||||||
self.push_update()
|
addr = args.split(',')[0].replace('0x', '')
|
||||||
except:
|
if addr in self.urgent_window_addresses:
|
||||||
time.sleep(2) # реконнект, если хайпер отвалился
|
self.urgent_window_addresses.remove(addr)
|
||||||
|
trigger = True
|
||||||
|
|
||||||
# слушаем поток в фоне
|
if trigger:
|
||||||
threading.Thread(target=event_socket_listener, daemon=True).start()
|
self.push_update()
|
||||||
|
|
||||||
try:
|
except (asyncio.CancelledError, KeyboardInterrupt):
|
||||||
while True: time.sleep(100)
|
break
|
||||||
except KeyboardInterrupt: sys.exit(0)
|
except Exception as e:
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
UnifiedWorkspaceManager().run()
|
try:
|
||||||
|
asyncio.run(UnifiedWorkspaceManager().run())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(0)
|
||||||
@@ -4,9 +4,9 @@
|
|||||||
"firefox": "",
|
"firefox": "",
|
||||||
"code-oss": "",
|
"code-oss": "",
|
||||||
"org.telegram.desktop": "",
|
"org.telegram.desktop": "",
|
||||||
"kitty": "",
|
"foot": "",
|
||||||
"steam": "",
|
"steam": "",
|
||||||
"spotify": "",
|
"Spotify": "",
|
||||||
"discord": "",
|
"discord": "",
|
||||||
"no-icon": "",
|
"no-icon": "",
|
||||||
"default": ""
|
"default": ""
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
animations {
|
||||||
|
enabled = yes
|
||||||
|
|
||||||
|
# NAME, X0, Y0, X1, Y1
|
||||||
|
bezier = easeOutQuint, 0.23, 1, 0.32, 1
|
||||||
|
bezier = easeInOutCubic, 0.65, 0.05, 0.36, 1
|
||||||
|
bezier = linear, 0, 0, 1, 1
|
||||||
|
bezier = almostLinear, 0.5, 0.5, 0.75, 1
|
||||||
|
bezier = quick, 0.15, 0, 0.1, 1
|
||||||
|
|
||||||
|
# NAME, ONOFF, SPEED, CURVE, [STYLE]
|
||||||
|
animation = global, 1, 10, default
|
||||||
|
animation = border, 1, 5.39, easeOutQuint
|
||||||
|
animation = windows, 1, 4.79, easeOutQuint
|
||||||
|
animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
|
||||||
|
animation = windowsOut, 1, 1.49, linear, popin 87%
|
||||||
|
animation = fadeIn, 1, 1.73, almostLinear
|
||||||
|
animation = fadeOut, 1, 1.46, almostLinear
|
||||||
|
animation = fade, 1, 3.03, quick
|
||||||
|
animation = layers, 1, 3.81, easeOutQuint
|
||||||
|
animation = layersIn, 1, 4, easeOutQuint, fade
|
||||||
|
animation = layersOut, 1, 1.5, linear, fade
|
||||||
|
animation = fadeLayersIn, 1, 1.79, almostLinear
|
||||||
|
animation = fadeLayersOut, 1, 1.39, almostLinear
|
||||||
|
animation = workspaces, 1, 1.94, almostLinear, fade
|
||||||
|
animation = workspacesIn, 1, 1.21, almostLinear, fade
|
||||||
|
animation = workspacesOut, 1, 1.94, almostLinear, fade
|
||||||
|
animation = zoomFactor, 1, 7, quick
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
# basic
|
||||||
|
bind = $mainMod, return, exec, $terminal
|
||||||
|
bind = $mainMod SHIFT, Q, killactive,
|
||||||
|
bind = $mainMod SHIFT, X, exec, command -v hyprshutdown >/dev/null 2>&1 && hyprshutdown || hyprctl dispatch exit
|
||||||
|
bind = $mainMod, E, exec, $fileManager
|
||||||
|
bind = $mainMod, M, togglefloating,
|
||||||
|
bind = $mainMod, F, fullscreen, 0
|
||||||
|
bind = $mainMod, D, exec, $menu
|
||||||
|
bind = $mainMod, P, pseudo,
|
||||||
|
bind = $mainMod, J, layoutmsg, togglesplit
|
||||||
|
bind = $mainMod, V, exec, rofi -modi clipboard:~/.config/rofi/cliphist-rofi-img -show clipboard -show-icons -theme ~/.config/rofi/clipboard.rasi
|
||||||
|
bind = , Print, exec, AREA=$(slurp) && grim -g "$AREA" - | wl-copy && notify-send -u low "Screenshot" "Captured: $AREA"
|
||||||
|
bind = SUPER, Print, exec, ACTIVE_CLASS=$(hyprctl activewindow -j | jq -r '.class') && hyprctl -j activewindow | jq -r '"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | grim -g - - | wl-copy && notify-send -u low "Screenshot" "Captured: $ACTIVE_CLASS"
|
||||||
|
bind = CTRL, Print, exec, MONITOR=$(hyprctl monitors -j | jq -r '.[] | select(.focused) | .name') && grim -o "$MONITOR" - | wl-copy && notify-send -u low "Screenshot" "Captured: $MONITOR"
|
||||||
|
|
||||||
|
# movefocus
|
||||||
|
bind = $mainMod, left, movefocus, l
|
||||||
|
bind = $mainMod, right, movefocus, r
|
||||||
|
bind = $mainMod, up, movefocus, u
|
||||||
|
bind = $mainMod, down, movefocus, d
|
||||||
|
|
||||||
|
# movewindow
|
||||||
|
bind = $mainMod SHIFT, left, movewindow, l
|
||||||
|
bind = $mainMod SHIFT, right, movewindow, r
|
||||||
|
bind = $mainMod SHIFT, up, movewindow, u
|
||||||
|
bind = $mainMod SHIFT, down, movewindow, d
|
||||||
|
|
||||||
|
# workspace
|
||||||
|
bind = $mainMod, 1, workspace, 1
|
||||||
|
bind = $mainMod, 2, workspace, 2
|
||||||
|
bind = $mainMod, 3, workspace, 3
|
||||||
|
bind = $mainMod, 4, workspace, 4
|
||||||
|
bind = $mainMod, 5, workspace, 5
|
||||||
|
bind = $mainMod, 6, workspace, 6
|
||||||
|
bind = $mainMod, 7, workspace, 7
|
||||||
|
bind = $mainMod, 8, workspace, 8
|
||||||
|
bind = $mainMod, 9, workspace, 9
|
||||||
|
bind = $mainMod, 0, workspace, 10
|
||||||
|
|
||||||
|
# movetoworkspace
|
||||||
|
bind = $mainMod SHIFT, 1, movetoworkspace, 1
|
||||||
|
bind = $mainMod SHIFT, 2, movetoworkspace, 2
|
||||||
|
bind = $mainMod SHIFT, 3, movetoworkspace, 3
|
||||||
|
bind = $mainMod SHIFT, 4, movetoworkspace, 4
|
||||||
|
bind = $mainMod SHIFT, 5, movetoworkspace, 5
|
||||||
|
bind = $mainMod SHIFT, 6, movetoworkspace, 6
|
||||||
|
bind = $mainMod SHIFT, 7, movetoworkspace, 7
|
||||||
|
bind = $mainMod SHIFT, 8, movetoworkspace, 8
|
||||||
|
bind = $mainMod SHIFT, 9, movetoworkspace, 9
|
||||||
|
bind = $mainMod SHIFT, 0, movetoworkspace, 10
|
||||||
|
|
||||||
|
# scratchpad
|
||||||
|
bind = $mainMod, S, togglespecialworkspace, scratchpad
|
||||||
|
bind = $mainMod SHIFT, S, movetoworkspace, special:scratchpad
|
||||||
|
bind = $mainMod CTRL, S, movetoworkspace, e+0
|
||||||
|
|
||||||
|
# movewindow / resizewindow
|
||||||
|
bindm = $mainMod, mouse:272, movewindow
|
||||||
|
bindm = $mainMod, mouse:273, resizewindow
|
||||||
|
|
||||||
|
# multimedia
|
||||||
|
bindel = $mainMod, F7, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
||||||
|
bindel = $mainMod, F8, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
||||||
|
bindel = $mainMod, F8, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
|
||||||
|
bindel = $mainMod, F9, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
|
||||||
|
|
||||||
|
# playerctl
|
||||||
|
bindl = $mainMod, F10, exec, playerctl previous
|
||||||
|
bindl = $mainMod, F11, exec, playerctl play-pause
|
||||||
|
bindl = $mainMod, F12, exec, playerctl next
|
||||||
|
|
||||||
|
# resize mode
|
||||||
|
bind = $mainMod, R, exec, hyprctl keyword general:col.active_border "rgb(e3a1d2)" ; hyprctl keyword general:border_size 5
|
||||||
|
bind = $mainMod, R, submap, resize
|
||||||
|
|
||||||
|
submap = resize
|
||||||
|
binde = , right, resizeactive, 20 0
|
||||||
|
binde = , left, resizeactive, -20 0
|
||||||
|
binde = , up, resizeactive, 0 -20
|
||||||
|
binde = , down, resizeactive, 0 20
|
||||||
|
bind = , escape, exec, hyprctl keyword general:col.active_border "rgb(a8afe1)" ; hyprctl keyword general:border_size 1
|
||||||
|
bind = , escape, submap, reset
|
||||||
|
bind = , return, exec, hyprctl keyword general:col.active_border "rgb(a8afe1)" ; hyprctl keyword general:border_size 1
|
||||||
|
bind = , return, submap, reset
|
||||||
|
submap = reset
|
||||||
|
|
||||||
|
bind = $mainMod, L, exec, loginctl lock-session
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
decoration {
|
||||||
|
rounding = 12
|
||||||
|
rounding_power = 2
|
||||||
|
active_opacity = 1.0
|
||||||
|
inactive_opacity = 0.8
|
||||||
|
|
||||||
|
shadow {
|
||||||
|
enabled = true
|
||||||
|
range = 4
|
||||||
|
render_power = 3
|
||||||
|
color = rgba(00000066)
|
||||||
|
}
|
||||||
|
|
||||||
|
blur {
|
||||||
|
enabled = true
|
||||||
|
size = 3
|
||||||
|
passes = 2
|
||||||
|
vibrancy = 0.1696
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
dwindle {
|
||||||
|
pseudotile = true
|
||||||
|
preserve_split = true
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
env = XCURSOR_THEME,Bibata-Modern-Classic
|
||||||
|
env = XCURSOR_SIZE,24
|
||||||
|
env = LIBVA_DRIVER_NAME,nvidia
|
||||||
|
env = __GLX_VENDOR_LIBRARY_NAME,nvidia
|
||||||
|
env = NVD_BACKEND,direct
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
exec-once = xrandr --output HDMI-A-1 --primary
|
||||||
|
exec-once = wl-paste --type text --watch cliphist store
|
||||||
|
exec-once = wl-paste --type image --watch cliphist store
|
||||||
|
exec-once = systemctl --user start hyprpolkitagent
|
||||||
|
exec-once = gsettings set org.gnome.desktop.interface gtk-theme "Adwaita-dark"
|
||||||
|
exec-once = gsettings set org.gnome.desktop.interface color-scheme "prefer-dark"
|
||||||
|
exec-once = eww daemon && sleep 1 && eww open-many bar_0 bar_1 wallpaper_0 wallpaper_1
|
||||||
|
exec-once = hypridle --grace 15
|
||||||
|
exec-once = python3 /home/pyrschtjag/Documents/.ntfy-notify/ntfy-notify.py
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
general {
|
||||||
|
gaps_in = 4
|
||||||
|
gaps_out = 42,6,6,6
|
||||||
|
border_size = 1
|
||||||
|
col.active_border = rgb(a8afe1)
|
||||||
|
col.inactive_border = rgba(363a4fcc)
|
||||||
|
resize_on_border = false
|
||||||
|
allow_tearing = false
|
||||||
|
layout = dwindle
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
input {
|
||||||
|
kb_layout = us,ru
|
||||||
|
kb_variant =
|
||||||
|
kb_model =
|
||||||
|
kb_options = grp:win_space_toggle
|
||||||
|
kb_rules =
|
||||||
|
follow_mouse = 1
|
||||||
|
sensitivity = 0
|
||||||
|
force_no_accel = true
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
master {
|
||||||
|
new_status = master
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
misc {
|
||||||
|
force_default_wallpaper = -1
|
||||||
|
disable_hyprland_logo = true
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
monitor = HDMI-A-1, 1920x1080@60, 0x0, 1
|
||||||
|
monitor = DVI-I-1, 1600x900@60, 1920x120, 1
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
$mainMod = SUPER
|
||||||
|
$terminal = foot
|
||||||
|
//$fileManager = kitty mc
|
||||||
|
$menu = rofi -show drun
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
windowrule {
|
||||||
|
name = suppress-maximize-events
|
||||||
|
match:class = .*
|
||||||
|
suppress_event = maximize
|
||||||
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
name = fix-xwayland-drags
|
||||||
|
match:class = ^$
|
||||||
|
match:title = ^$
|
||||||
|
match:xwayland = true
|
||||||
|
match:float = true
|
||||||
|
match:fullscreen = false
|
||||||
|
match:pin = false
|
||||||
|
no_focus = true
|
||||||
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
name = move-hyprland-run
|
||||||
|
match:class = hyprland-run
|
||||||
|
move = 20 monitor_h-120
|
||||||
|
float = yes
|
||||||
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
name = scratchpad
|
||||||
|
match:workspace = name:special:scratchpad
|
||||||
|
border_color = rgb(a8afe1) rgba(3dd8ffb9)
|
||||||
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
name = telegram-media-viewer
|
||||||
|
match:class = ^(org.telegram.desktop|telegramdesktop)$
|
||||||
|
match:title = ^(Media viewer)$
|
||||||
|
fullscreen = on
|
||||||
|
fullscreen_state = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
name = firefox-youtube
|
||||||
|
match:class = ^(firefox)$
|
||||||
|
match:title = .*(YouTube — Mozilla Firefox)$
|
||||||
|
opacity = 1.0 override 1.0 override 1.0 override
|
||||||
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
name = firefox-picture-in-picture
|
||||||
|
match:class = ^(firefox)$
|
||||||
|
match:title = ^(Picture-in-Picture)$
|
||||||
|
opacity = 1.0 override 1.0 override 1.0 override
|
||||||
|
float = true
|
||||||
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
name = steam-app
|
||||||
|
match:class = ^(steam_app_)(.*)
|
||||||
|
fullscreen = on
|
||||||
|
fullscreen_state = 2
|
||||||
|
monitor = HDMI-A-1
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
workspace = 1, monitor:HDMI-A-1
|
||||||
|
workspace = 2, monitor:HDMI-A-1
|
||||||
|
workspace = 3, monitor:HDMI-A-1
|
||||||
|
workspace = 4, monitor:HDMI-A-1
|
||||||
|
workspace = 5, monitor:HDMI-A-1
|
||||||
|
workspace = 6, monitor:HDMI-A-1
|
||||||
|
workspace = 7, monitor:HDMI-A-1
|
||||||
|
workspace = 8, monitor:DVI-I-1
|
||||||
|
workspace = 9, monitor:DVI-I-1
|
||||||
|
workspace = 10, monitor:DVI-I-1
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
general {
|
||||||
|
lock_cmd = pidof hyprlock || hyprlock
|
||||||
|
before_sleep_cmd = loginctl lock-session
|
||||||
|
after_sleep_cmd = hyprctl dispatch dpms on
|
||||||
|
ignore_dbus_inhibit = false
|
||||||
|
ignore_wayland_inhibit = false
|
||||||
|
ignore_systemd_inhibit = false
|
||||||
|
}
|
||||||
|
|
||||||
|
listener {
|
||||||
|
timeout = 300
|
||||||
|
on-timeout = loginctl lock-session
|
||||||
|
}
|
||||||
|
|
||||||
|
listener {
|
||||||
|
timeout = 360
|
||||||
|
on-timeout = hyprctl dispatch dpms off
|
||||||
|
on-resume = hyprctl dispatch dpms on
|
||||||
|
}
|
||||||
|
|
||||||
|
listener {
|
||||||
|
timeout = 600
|
||||||
|
on-timeout = systemctl suspend
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
source = ~/.config/hypr/config/monitors.conf
|
||||||
|
source = ~/.config/hypr/config/workspaces.conf
|
||||||
|
source = ~/.config/hypr/config/programs.conf
|
||||||
|
source = ~/.config/hypr/config/exec.conf
|
||||||
|
source = ~/.config/hypr/config/env.conf
|
||||||
|
source = ~/.config/hypr/config/general.conf
|
||||||
|
source = ~/.config/hypr/config/decoration.conf
|
||||||
|
source = ~/.config/hypr/config/animations.conf
|
||||||
|
//source = ~/.config/hypr/config/dwindle.conf
|
||||||
|
source = ~/.config/hypr/config/master.conf
|
||||||
|
source = ~/.config/hypr/config/misc.conf
|
||||||
|
source = ~/.config/hypr/config/input.conf
|
||||||
|
source = ~/.config/hypr/config/bind.conf
|
||||||
|
source = ~/.config/hypr/config/windowrule.conf
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
general {
|
||||||
|
hide_cursor = true
|
||||||
|
ignore_empty_input = true
|
||||||
|
}
|
||||||
|
|
||||||
|
background {
|
||||||
|
monitor =
|
||||||
|
path = screenshot
|
||||||
|
blur_passes = 2
|
||||||
|
blur_size = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
input-field {
|
||||||
|
monitor =
|
||||||
|
size = 250, 60
|
||||||
|
outline_thickness = 2
|
||||||
|
dots_size = 0.2
|
||||||
|
dots_spacing = 0.2
|
||||||
|
dots_center = true
|
||||||
|
outer_color = rgba(168, 175, 225, 0.5)
|
||||||
|
inner_color = rgba(30, 30, 46, 0.5)
|
||||||
|
font_color = rgb(202, 211, 245)
|
||||||
|
fade_on_empty = true
|
||||||
|
placeholder_text = <i>Password...</i>
|
||||||
|
hide_input = false
|
||||||
|
position = 0, -154
|
||||||
|
halign = center
|
||||||
|
valign = center
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
monitor =
|
||||||
|
text = $TIME
|
||||||
|
color = rgba(202, 211, 245, 0.8)
|
||||||
|
font_size = 94
|
||||||
|
font_family = Noto Sans Bold
|
||||||
|
position = 0, 0
|
||||||
|
halign = center
|
||||||
|
valign = center
|
||||||
|
}
|
||||||
Executable
+20
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
MONITOR_NAME="DVI-I-1"
|
||||||
|
WIDTH=1920
|
||||||
|
HEIGHT=120
|
||||||
|
WIDHT_FIXED=$((WIDTH+10))
|
||||||
|
|
||||||
|
hyprctl keyword monitor "$MONITOR_NAME, 1600x900@60, ${WIDHT_FIXED}x${HEIGHT}, 1" && eww reload
|
||||||
|
notify-send -t 1500 "$MONITOR_NAME: Game Mode" "Monitor moved to ${WIDHT_FIXED}x${HEIGHT}"
|
||||||
|
|
||||||
|
if [[ "$1" == *"="* ]]; then
|
||||||
|
VAR=$1
|
||||||
|
shift
|
||||||
|
env "$VAR" "$@"
|
||||||
|
else
|
||||||
|
"$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyprctl -- keyword monitor "$MONITOR_NAME, 1600x900@60, ${WIDTH}x${HEIGHT}, 1" && eww reload
|
||||||
|
notify-send -t 1500 "$MONITOR_NAME: Desktop Mode" "Monitor returned to ${WIDTH}x${HEIGHT}"
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
@import "config.rasi"
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
modi: "clipboard:~/.config/rofi/cliphist-rofi-img";
|
||||||
|
show-icons: true;
|
||||||
|
display-clipboard: " Clipboard";
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
orientation: horizontal;
|
||||||
|
spacing: 15px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
children: [ element-text, element-icon ];
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
size: 42px;
|
||||||
|
expand: false;
|
||||||
|
horizontal-align: 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
vertical-align: 0.5;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0.0;
|
||||||
|
}
|
||||||
Executable
+22
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
tmp_dir="/tmp/cliphist"
|
||||||
|
rm -rf "$tmp_dir"
|
||||||
|
|
||||||
|
if [[ -n "$1" ]]; then
|
||||||
|
cliphist decode <<<"$1" | wl-copy
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$tmp_dir"
|
||||||
|
|
||||||
|
read -r -d '' prog <<EOF
|
||||||
|
/^[0-9]+\s<meta http-equiv=/ { next }
|
||||||
|
match(\$0, /^([0-9]+)\s(\[\[\s)?binary.*(jpg|jpeg|png|bmp)/, grp) {
|
||||||
|
system("echo " grp[1] "\\\\\t | cliphist decode >$tmp_dir/"grp[1]"."grp[3])
|
||||||
|
print \$0"\0icon\x1f$tmp_dir/"grp[1]"."grp[3]
|
||||||
|
next
|
||||||
|
}
|
||||||
|
1
|
||||||
|
EOF
|
||||||
|
cliphist list | gawk "$prog"
|
||||||
Reference in New Issue
Block a user