diff --git a/README-Wayland.org b/README-Wayland.org new file mode 100644 index 0000000..202f032 --- /dev/null +++ b/README-Wayland.org @@ -0,0 +1,92 @@ +#+title: Wayland + +Install [[https://wiki.archlinux.org/title/wayland][wayland]]: ~wayland~, ~xorg-xwayland~ + +* Login Manager + +The login manager is not managed by this configuration but mentioned here since +it interacts with the desktop environment. For ~hyprland~, ~sddm~ is +recommended. + +- ~sddm~: Enable ~sddm.service~ + + A note about themes: The themes must be placed in ~/usr/share/sddm/themes~. + Then in ~/etc/sddm.conf~, enable the relevant theme. By default the breeze + theme comes with ~plasma-workspace~ which is very bloated to install, so + consider some of the alternative themes. + +* Fonts + +- ~otf-font-awesome~: Needed for ~waybar~ to render icons +- Noto Sans/Serif: System font + +* Status Bar and Tray + +- ~waybar~: Status bar, use ~waybar-hyprland-git~ AUR repo to enable clicking! +- ~eww-wayland~: Another status bar, requires ~socat~ to listen for hyprland events. + +* Sway + +This is one choice of a compositor. + +- ~swayfx~: Window manager +- ~python-i3ipc~: Enables inactive window opacity scripts + +** Wallpaper +Use ~multibg-sway~, which assigns a different wallpaper to each workspace and +does not crash. The wallpapers directory ~~/.config/sway/wallpapers~ needs to be setup in this way: +#+begin_src +wallpapers + ├─ eDP-1 + │ ├─ _default.jpg + │ ├─ 1.jpg + │ ├─ 2.png + │ └─ browser.jpg + └─ HDMI-A-1 + ├─ 1.jpg + └─ 3.png +#+end_src +where the output information can be obtained from ~swaymsg -t get_outputs~. + +Supposedly does not work with ~hypr~. + +* Hyprland + +This is another choice of a compositor. + +Note: Setup does not work with NVIDIA GPU without some tweaks. + +- [[https://wiki.archlinux.org/title/Hyprland][hyprland]]: window manager + Until [[https://github.com/elkowar/eww/issues/111][tray support]] has been added to ~eww~ in a stable manner, tray will be handled with waybar. +- ~hyprpaper~: Wallpaper engine + + The command to set a wallpaper: + ~hyprctl hyprpaper wallpaper "$DISPLAY,$FILE"~ +The command for changing keyboard layouts is handled by ~fcitx5~. + +* Utilities + +- [[https://github.com/Horus645/swww][swww]]: Alternate wallpaper engine + + Note that ~~/.config/hypr/wallpapers~ must have jpeg files ~{1..8}.jpg~. A + handy command can be used crop and resize images: + #+begin_src bash +convert $IN \ + -resize 3840x2160^ \ + -gravity center \ + -extent 3840x2160 \ + $OUT + #+end_src +- ~swaync~: Notification server +- ~wofi~: finding programs, drop in replacement for ~rofi~ +- ~grimblast~: Screenshot engine +- ~dolphin~: File explorer +- ~swaylock-effects~: A simple lockscreen +- ~wl-clipboard~: Provides copying +- ~blueman~: Bluetooth connector + +** Configuration + +- ~lxappearance~: Used to configure GTK3 themes +- ~nwg-look~: Configures cursor +- ~wev~: Wayland event interceptor useful for determining keybinds diff --git a/README.org b/README.org index 8a5da2d..9334224 100644 --- a/README.org +++ b/README.org @@ -50,70 +50,6 @@ systemctl start --user mpd - ~neofetch~: Screenfetch - ~macchina~ (aur): Another screenfetch -* Hyprland Desktop Environment - -** Login Manager - -The login manager is not managed by this configuration but mentioned here since -it interacts with the desktop environment. For ~hyprland~, ~sddm~ is -recommended. - -- ~sddm~: Enable ~sddm.service~ - - A note about themes: The themes must be placed in ~/usr/share/sddm/themes~. - Then in ~/etc/sddm.conf~, enable the relevant theme. By default the breeze - theme comes with ~plasma-workspace~ which is very bloated to install, so - consider some of the alternative themes. - -** Fonts - -- ~otf-font-awesome~: Needed for ~waybar~ to render icons -- Noto Sans/Serif: System font - -** Hyprland - -Note: Setup does not work with NVIDIA GPU without some tweaks. - -- [[https://wiki.archlinux.org/title/wayland][wayland]]: display manager: ~wayland~, ~xorg-xwayland~ -- [[https://wiki.archlinux.org/title/Hyprland][hyprland]]: window manager -- ~waybar~: Status bar, use ~waybar-hyprland-git~ AUR repo to enable clicking! -- ~eww-wayland~: Another status bar, requires ~socat~ to listen for hyprland events. - - Until [[https://github.com/elkowar/eww/issues/111][tray support]] has been added to ~eww~ in a stable manner, tray will be handled with waybar. -- ~hyprpaper~: Wallpaper engine - - The command to set a wallpaper: - ~hyprctl hyprpaper wallpaper "$DISPLAY,$FILE"~ -- [[https://github.com/Horus645/swww][swww]]: Alternate wallpaper engine - - Note that ~~/.config/hypr/wallpapers~ must have jpeg files ~{1..8}.jpg~. A - handy command can be used crop and resize images: - #+begin_src bash -convert $IN \ - -resize 3840x2160^ \ - -gravity center \ - -extent 3840x2160 \ - $OUT - #+end_src -- ~swaync~: Notification server - -The command for changing keyboard layouts is handled by ~fcitx5~. - -** Utilities - -- ~wofi~: finding programs, drop in replacement for ~rofi~ -- ~grimblast~: Screenshot engine -- ~dolphin~: File explorer -- ~swaylock-effects~: A simple lockscreen -- ~wl-clipboard~: Provides copying -- ~blueman~: Bluetooth connector - -*** Configuration - -- ~lxappearance~: Used to configure GTK3 themes -- ~nwg-look~: Configures cursor -- ~wev~: Wayland event interceptor useful for determining keybinds - * Dot Files All the configurations which must appear as dot files are not included: diff --git a/eww/bar/eww.yuck b/eww/bar/eww.yuck index cb76b6d..2604de7 100644 --- a/eww/bar/eww.yuck +++ b/eww/bar/eww.yuck @@ -25,47 +25,6 @@ ;(widget_status) )) -(defwidget widget_weather [orientation] - (box - :class "weather" - :orientation orientation - :space-evenly false - :valign "start" - :halign "center" - :spacing 10 - ;(box :class "moon" :orientation "v" moon) ; Not very stable - (box :class "temperature" :orientation orientation - (box :class "icon" "") - "${weather.current_condition[0].temp_C}" - (box :class "feelsLike" "${weather.current_condition[0].FeelsLikeC}") - ) - (box :class "uvIndex" :orientation orientation - (box :class "icon" "") - "${weather.current_condition[0].uvIndex}") - (box :class "humidity" :orientation orientation - (box :class "icon" "") - "${weather.current_condition[0].humidity}" - ) - (box :class "precip" :orientation orientation - (box :class "icon" "") - "${weather.current_condition[0].precipMM}" - ) - (box :class "wind" :orientation orientation - (box :class "icon" "") - "${weather.current_condition[0].windspeedKmph}" - "${weather.current_condition[0].winddir16Point}" - ) - (box :class "cloud" :orientation orientation - (box :class "icon" "") - "${weather.current_condition[0].cloudcover}" - ) - )) - -(defpoll moon :initial "" :interval "180s" - "curl wttr.in/?format=%m") -(defpoll weather :initial "{\"current_condition\": [{\"cloudcover\": \"\"}]}" :interval "180s" - "curl wttr.in/?format=j1") - (defwidget widget_workspaces [] (box :class "workspaces" @@ -83,6 +42,37 @@ "scripts/get-workspaces") +; Weather + +(defwidget widget_weather [orientation] + (box + :class "weather" + :orientation orientation + :space-evenly false + :valign "start" + :halign "center" + :spacing 10 + (button :onclick "scripts/popup weather" "W") + )) + +(defpoll weather_text :initial "" :interval "180s" + "curl --max-time 2 wttr.in") + +(defwindow weather + :geometry (geometry :x "70px" + :y "50%" + :width "270px" + :height "60px") + (box weather_text)) + + +(defwindow calendar + :geometry (geometry :x "70px" + :y "65%" + :width "270px" + :height "60px") + (cal)) + (defwidget widget_clock [] (box :class "clock" diff --git a/eww/bar/scripts/get-workspaces b/eww/bar/scripts/get-workspaces index c5b2d3c..4c2070d 100755 --- a/eww/bar/scripts/get-workspaces +++ b/eww/bar/scripts/get-workspaces @@ -6,81 +6,121 @@ Utility to generate the workspace buttons for eww bar widget. Currently it fetches information from monitor 0. If all monitors have synchronised workspaces this should not be a problem. """ -import os, sys, socket, json +import os, sys, socket, json, subprocess +from typing import Optional WORKSPACE_ICONS = [ - (1, '☱'), - (2, '☲'), - (3, '☳'), - (4, '☴'), - (5, '☵'), - (6, '☶'), - (7, '☷'), - (8, '☰'), + (1, "☱"), + (2, "☲"), + (3, "☳"), + (4, "☴"), + (5, "☵"), + (6, "☶"), + (7, "☷"), + (8, "☰"), ] EXTRA_WORKSPACE_ICONS = [ - (0, '⚌'), - (1, '⚍'), - (2, '⚎'), - (3, '⚏'), + (0, "⚌"), + (1, "⚍"), + (2, "⚎"), + (3, "⚏"), ] -def get_workspace_info(): +Workspaces = dict[int, bool] - workspaces = json.loads(os.popen('hyprctl workspaces -j').read()) - monitors = json.loads(os.popen('hyprctl monitors -j').read()) - if not monitors: - print("No monitor found!", file=sys.stderr) - return [] - - monitor, active = [(m['name'], m['activeWorkspace']['id']) - for m in monitors if m['id'] == 0][0] - workspaces = [(w['id'], w['id'] == active) - for w in workspaces - if w['monitor'] == monitor] - return workspaces - - -def get_widgets(): - # get all workspace keys - workspaces = {k: v for k, v in get_workspace_info()} +def get_widgets(workspaces: Workspaces) -> str: + """ + Create widget sexp from workspace information + """ def get_class(k): key = workspaces.get(k, None) if key is None: - return 'inactive' + return "inactive" elif key: - return 'focused' + return "focused" else: - return 'active' + return "active" buttons = [ f'"id": "{k}", "text": "{icon}", "class": "{get_class(k)}"' for k, icon in WORKSPACE_ICONS ] - buttons = ', '.join(['{' + b + '}' for b in buttons]) - return '[' + buttons + ']' + buttons = ", ".join(["{" + b + "}" for b in buttons]) + return "[" + buttons + "]" -def listen(): +# Compositor specific information + +def hypr_get_workspace_info() -> Workspaces: + workspaces = json.loads(os.popen("hyprctl workspaces -j").read()) + monitors = json.loads(os.popen("hyprctl monitors -j").read()) + if not monitors: + print("No monitor found!", file=sys.stderr) + return [] + + monitor, active = [ + (m["name"], m["activeWorkspace"]["id"]) for m in monitors if m["id"] == 0 + ][0] + workspaces = { + w["id"]: w["id"] == active + for w in workspaces if w["monitor"] == monitor + } + return workspaces + + +def hypr_listen(socket_addr: str): sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - KEY = 'HYPRLAND_INSTANCE_SIGNATURE' - signature = os.environ.get(KEY, None) - if signature is None: - print("Hyprland variable uninitialised.", file=sys.stderr) - return - server_address = f"/tmp/hypr/{signature}/.socket2.sock" try: - sock.connect(server_address) + sock.connect(socket_addr) except: - print(f"Could not connect to {server_address}", file=sys.stderr) + print(f"Could not connect to {socket_addr}", file=sys.stderr) raise # The flush here is very important since python by default buffers! - print(get_widgets(), flush=True) + workspace_info = hypr_get_workspace_info() + print(get_widgets(workspace_info), flush=True) while sock: data = sock.recv(1024) - if b'workspace' in data: - print(get_widgets(), flush=True) + if b"workspace" in data: + workspace_info = hypr_get_workspace_info() + print(get_widgets(workspace_info), flush=True) -listen() +def sway_get_workspace_info() -> Workspaces: + workspaces = json.loads(os.popen("swaymsg --raw -t get_workspaces").read()) + # REVIEW: This has not been tested on a multi-monitor setup. + workspaces = { + int(w["num"]): w["focused"] + for w in workspaces + } + return workspaces + +def sway_listen(): + proc = subprocess.Popen( + ["swaymsg", "-t", "subscribe", "-m", '["workspace"]'], stdout=subprocess.PIPE + ) + workspace_info = sway_get_workspace_info() + print(get_widgets(workspace_info), flush=True) + while line := proc.stdout.readline(): + # Not needed + info = json.loads(line) + workspace_info = sway_get_workspace_info() + print(get_widgets(workspace_info), flush=True) + + +def detect(): + """ + Main entry point, detects the type of compositor used + """ + if signature := os.environ.get("HYPRLAND_INSTANCE_SIGNATURE", None): + socket_addr = f"/tmp/hypr/{signature}/.socket2.sock" + return hypr_listen(socket_addr) + if signature := os.environ.get("SWAYSOCK", None): + return sway_listen() + + print("No compositor found.", file=sys.stderr) + return None + + +if __name__ == "__main__": + detect() diff --git a/eww/bar/scripts/popup b/eww/bar/scripts/popup new file mode 100755 index 0000000..faef20e --- /dev/null +++ b/eww/bar/scripts/popup @@ -0,0 +1,16 @@ +#!/bin/sh + +toggle_popup() { + LOCK="/tmp/eww-$1.lock" + if [[ ! -f "$LOCK" ]]; then + touch "$LOCK" + eww --config ~/.config/eww/bar open $1 + else + eww --config ~/.config/eww/bar close $1 + rm "$LOCK" + fi +} + +if [ "$1" = weather ]; then + toggle_popup weather +fi diff --git a/sway/config b/sway/config new file mode 100644 index 0000000..b10891d --- /dev/null +++ b/sway/config @@ -0,0 +1,44 @@ +# Terminal +set $term alacritty +# Launcher +set $menu wofi --show drun -I -G +set $lock swaylock +set $screenshot_full /usr/share/swayfx/scripts/grimshot copy screen +set $screenshot_area /usr/share/swayfx/scripts/grimshot copy area + +xwayland enable + +scratchpad_minimize disable + +# Visual Effects +corner_radius 20 +smart_corner_radius enable +blur on +blur_xray off +blur_passes 2 +blur_radius 5 +shadows on +shadows_on_csd off +shadow_blur_radius 20 +shadow_color #0000007F +default_dim_inactive 0.0 +dim_inactive_colors.unfocused #000000FF +dim_inactive_colors.urgent #900000FF + +set $border 2 +default_border pixel $border +default_floating_border pixel $border + +for_window [app_id=".*"] gaps inner all set 5 +for_window [app_id=".*"] gaps outer all set 15 +for_window [app_id=".*"] opacity 0.85 +for_window [app_id=__focused__] opacity 0.95 + +# Include keys +include ~/.config/sway/keys.conf + +# Launching programs +include ~/.config/sway/launch.conf + +# Include systemd fix +include /etc/sway/config.d/* diff --git a/sway/keys.conf b/sway/keys.conf new file mode 100644 index 0000000..8199647 --- /dev/null +++ b/sway/keys.conf @@ -0,0 +1,63 @@ +set $mod Mod4 +set $left h +set $down j +set $up k +set $right l + +bindsym $mod+t exec $term +bindsym $mod+q kill +bindsym $mod+space exec $menu +bindsym $mod+semicolon exec $lock +bindsym $mod+Shift+c reload +bindsym Print exec $screenshot_area +bindsym Shift+Print exec $screenshot_full + +# Drag floating windows by holding down $mod and left mouse button. +# Resize them with right mouse button + $mod. +# Despite the name, also works for non-floating windows. +# Change normal to inverse to use left mouse button for resizing and right +# mouse button for dragging. +floating_modifier $mod normal + +bindsym $mod+f fullscreen +bindsym $mod+v floating toggle + +# Move focus +bindsym $mod+$left focus left +bindsym $mod+$down focus down +bindsym $mod+$up focus up +bindsym $mod+$right focus right +# Or use $mod+[up|down|left|right] +bindsym $mod+Left focus left +bindsym $mod+Down focus down +bindsym $mod+Up focus up +bindsym $mod+Right focus right + +# Move the focused window with the same, but add Shift +bindsym $mod+Shift+$left move left +bindsym $mod+Shift+$down move down +bindsym $mod+Shift+$up move up +bindsym $mod+Shift+$right move right +# Ditto, with arrow keys +bindsym $mod+Shift+Left move left +bindsym $mod+Shift+Down move down +bindsym $mod+Shift+Up move up +bindsym $mod+Shift+Right move right + +bindsym $mod+1 workspace number 1 +bindsym $mod+2 workspace number 2 +bindsym $mod+3 workspace number 3 +bindsym $mod+4 workspace number 4 +bindsym $mod+5 workspace number 5 +bindsym $mod+6 workspace number 6 +bindsym $mod+7 workspace number 7 +bindsym $mod+8 workspace number 8 +# Move focused container to workspace +bindsym $mod+Shift+1 move container to workspace number 1; workspace number 1 +bindsym $mod+Shift+2 move container to workspace number 2; workspace number 2 +bindsym $mod+Shift+3 move container to workspace number 3; workspace number 3 +bindsym $mod+Shift+4 move container to workspace number 4; workspace number 4 +bindsym $mod+Shift+5 move container to workspace number 5; workspace number 5 +bindsym $mod+Shift+6 move container to workspace number 6; workspace number 6 +bindsym $mod+Shift+7 move container to workspace number 7; workspace number 7 +bindsym $mod+Shift+8 move container to workspace number 8; workspace number 8 diff --git a/sway/launch.conf b/sway/launch.conf new file mode 100644 index 0000000..ddd1921 --- /dev/null +++ b/sway/launch.conf @@ -0,0 +1,9 @@ +exec /usr/share/swayfx/scripts/inactive-windows-transparency --opacity 0.8 +exec_always eww --config ~/.config/eww/bar open bar +bar { + position top + swaybar_command waybar +} +exec_always multibg-sway ~/.config/sway/wallpapers +exec swaync +exec fcitx5 diff --git a/waybar/config b/waybar/config index 25999b1..bc36509 100644 --- a/waybar/config +++ b/waybar/config @@ -6,7 +6,9 @@ "margin": "0 15 0 5", "modules-left": [ //"wlr/workspaces", - "hyprland/window"], + "hyprland/window", + "sway/window" + ], "modules-center": ["mpd"], "modules-right": [ "tray", @@ -35,7 +37,9 @@ }, "hyprland/window": { "format": " {}", - "separate-outputs": true + }, + "sway/window": { + "format": " {}", }, "keyboard-state": { "numlock": true, @@ -47,14 +51,6 @@ //"unlocked": "" } }, - "hyprland/language": { - "format": "{}", - "format-en": "🇺🇸", - "format-fr": "🇨🇦", - "format-ja": "🇯🇵", - "tooltip-format": "{}", - "tooltip": true - }, "mpd": { "format": "『{artist} - {album} - {title} 』{stateIcon} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}", "format-disconnected": "Disconnected",