sway: add sway and waybar
This commit is contained in:
@@ -2,8 +2,13 @@ PATH=$HOME/.dots/scripts:$HOME/.cargo/bin:$HOME/.ghcup/bin:$HOME/.local/bin:$HOM
|
|||||||
EDITOR=hx
|
EDITOR=hx
|
||||||
VISUAL=hx
|
VISUAL=hx
|
||||||
|
|
||||||
|
SSH_AUTH_SOCK=/run/user/1000/keyring/ssh
|
||||||
|
|
||||||
# XXX: render shadow on wayland
|
# XXX: render shadow on wayland
|
||||||
KITTY_DISABLE_WAYLAND=1
|
# KITTY_DISABLE_WAYLAND=1
|
||||||
|
# KITTY_ENABLE_WAYLAND=1
|
||||||
|
|
||||||
|
_JAVA_AWT_WM_NONREPARENTING=1
|
||||||
|
|
||||||
# support jp input in gnome
|
# support jp input in gnome
|
||||||
GTK_IM_MODULE=ibus
|
GTK_IM_MODULE=ibus
|
||||||
|
|||||||
146
files/sway/.config/sway/config
Normal file
146
files/sway/.config/sway/config
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
### Variables
|
||||||
|
set $mod Mod4
|
||||||
|
floating_modifier $mod normal
|
||||||
|
|
||||||
|
### Output configuration (swaymsg -t get_output)
|
||||||
|
output * bg ~/cloud/images/wallpaper/wallpaper.png fill
|
||||||
|
# output eDP-1 scale 2
|
||||||
|
|
||||||
|
# disable laptop when closed
|
||||||
|
set $laptop eDP-1
|
||||||
|
bindswitch --reload --locked lid:on output $laptop disable
|
||||||
|
bindswitch --reload --locked lid:off output $laptop enable
|
||||||
|
|
||||||
|
workspace_layout tabbed
|
||||||
|
smart_borders on
|
||||||
|
# default_border none
|
||||||
|
|
||||||
|
exec swayidle -w \
|
||||||
|
timeout 300 'swaylock -i ~/cloud/images/wallpaper/lock.png -s center -u' \
|
||||||
|
timeout 600 'systemctl suspend' \
|
||||||
|
before-sleep 'swaylock -i ~/cloud/images/wallpaper/lock.png -s center -u'
|
||||||
|
|
||||||
|
### Input configuration
|
||||||
|
|
||||||
|
input "type:keyboard" {
|
||||||
|
xkb_layout eu
|
||||||
|
xkb_options caps:swapescape
|
||||||
|
repeat_delay 200
|
||||||
|
repeat_rate 100
|
||||||
|
}
|
||||||
|
|
||||||
|
bindgesture swipe:right workspace prev
|
||||||
|
bindgesture swipe:left workspace next
|
||||||
|
|
||||||
|
### Key bindings
|
||||||
|
|
||||||
|
# System
|
||||||
|
bindsym --locked XF86MonBrightnessDown exec brightnessctl set 5%-
|
||||||
|
bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+
|
||||||
|
|
||||||
|
bindsym --locked XF86AudioMute exec wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
||||||
|
bindsym --locked XF86AudioLowerVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- -l 1.2
|
||||||
|
bindsym --locked XF86AudioRaiseVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+ -l 1.2
|
||||||
|
|
||||||
|
bindsym $mod+Control+BackSpace exec swaylock -i ~/cloud/images/wallpaper/lock.png -s center -u
|
||||||
|
bindsym $mod+Control+Shift+BackSpace exec systemctl suspend
|
||||||
|
|
||||||
|
bindsym $mod+Shift+s exec ~/.config/sway/screenshot -s region
|
||||||
|
|
||||||
|
bindsym $mod+Shift+c reload
|
||||||
|
bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit'
|
||||||
|
bindsym $mod+Shift+q kill
|
||||||
|
|
||||||
|
# Run applications
|
||||||
|
bindsym $mod+Return exec kitty
|
||||||
|
bindsym $mod+d exec wofi --show drun -I -i -a
|
||||||
|
bindsym $mod+Shift+d exec wofi --show run -i -a
|
||||||
|
bindsym $mod+e exec nautilus
|
||||||
|
|
||||||
|
# Moving around
|
||||||
|
set $left h
|
||||||
|
set $down j
|
||||||
|
set $up k
|
||||||
|
set $right l
|
||||||
|
# Move your focus around
|
||||||
|
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
|
||||||
|
|
||||||
|
# Workspaces
|
||||||
|
# Switch to workspace
|
||||||
|
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
|
||||||
|
bindsym $mod+9 workspace number 9
|
||||||
|
bindsym $mod+0 workspace number 10
|
||||||
|
# Move focused container to workspace
|
||||||
|
bindsym $mod+Shift+1 move container to workspace number 1
|
||||||
|
bindsym $mod+Shift+2 move container to workspace number 2
|
||||||
|
bindsym $mod+Shift+3 move container to workspace number 3
|
||||||
|
bindsym $mod+Shift+4 move container to workspace number 4
|
||||||
|
bindsym $mod+Shift+5 move container to workspace number 5
|
||||||
|
bindsym $mod+Shift+6 move container to workspace number 6
|
||||||
|
bindsym $mod+Shift+7 move container to workspace number 7
|
||||||
|
bindsym $mod+Shift+8 move container to workspace number 8
|
||||||
|
bindsym $mod+Shift+9 move container to workspace number 9
|
||||||
|
bindsym $mod+Shift+0 move container to workspace number 10
|
||||||
|
|
||||||
|
# Layout stuff
|
||||||
|
bindsym $mod+f fullscreen
|
||||||
|
bindsym $mod+Shift+space floating toggle
|
||||||
|
bindsym $mod+space focus mode_toggle
|
||||||
|
bindsym $mod+a focus parent
|
||||||
|
|
||||||
|
# Scratchpad
|
||||||
|
bindsym $mod+Shift+minus move scratchpad
|
||||||
|
bindsym $mod+minus scratchpad show
|
||||||
|
|
||||||
|
# Theming
|
||||||
|
font pango: JetBrainsMono Nerd Font Mono 10
|
||||||
|
bar {
|
||||||
|
position top
|
||||||
|
mode dock
|
||||||
|
output *
|
||||||
|
|
||||||
|
# status_command while ~/.config/sway/status.sh; do sleep 1; done
|
||||||
|
swaybar_command waybar
|
||||||
|
|
||||||
|
colors {
|
||||||
|
statusline #5c6a72
|
||||||
|
background #fdf6e3
|
||||||
|
inactive_workspace #fdf6e3 #fdf6e3 #5c6a72
|
||||||
|
focused_workspace #8da101 #8da101 #fdf6e3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.focused #A7C080 #A7C080 #2D353B #A7C080 #A7C080
|
||||||
|
client.focused_inactive #3D484D #2D353B #859289 #3D484D #3D484D
|
||||||
|
client.unfocused #3D484D #2D353B #859289 #3D484D #3D484D
|
||||||
|
client.urgent #E67E80 #E67E80 #2D353B #E67E80 #E67E80
|
||||||
|
client.placeholder #2D353B #2D353B #859289 #2D353B #2D353B
|
||||||
|
client.background #2D353B
|
||||||
|
|
||||||
|
include /etc/sway/config.d/*
|
||||||
|
|
||||||
|
# Autostart
|
||||||
|
exec --no-startup-id netbird-ui
|
||||||
|
exec --no-startup-id nm-applet
|
||||||
|
exec --no-startup-id keepassxc
|
||||||
|
exec --no-startup-id nextcloud
|
||||||
|
|
||||||
|
# SSH
|
||||||
|
exec eval $(gnome-keyring-daemon --start)
|
||||||
|
exec export SSH_AUTH_SOCK
|
||||||
923
files/sway/.config/sway/screenshot
Executable file
923
files/sway/.config/sway/screenshot
Executable file
@@ -0,0 +1,923 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import abc
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
from datetime import datetime
|
||||||
|
from functools import partial
|
||||||
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Dict,
|
||||||
|
Iterable,
|
||||||
|
Iterator,
|
||||||
|
List,
|
||||||
|
NamedTuple,
|
||||||
|
Union,
|
||||||
|
Optional,
|
||||||
|
Set,
|
||||||
|
Tuple,
|
||||||
|
Type,
|
||||||
|
)
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
try:
|
||||||
|
import tomllib
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
import tomli as tomllib
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationError(NamedTuple):
|
||||||
|
path: str
|
||||||
|
error: str
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_VALIDATOR = Callable[[str, Any], Iterator[ValidationError]]
|
||||||
|
|
||||||
|
|
||||||
|
def config_dict_validator(
|
||||||
|
field_validators: Dict[str, CONFIG_VALIDATOR]
|
||||||
|
) -> CONFIG_VALIDATOR:
|
||||||
|
def validate(path: str, value: Any) -> Iterator[ValidationError]:
|
||||||
|
if not isinstance(value, Dict):
|
||||||
|
yield ValidationError(path, "Should be a dictionary.")
|
||||||
|
return
|
||||||
|
|
||||||
|
unknown_fields = set(value).difference(field_validators)
|
||||||
|
for unknown_field in unknown_fields:
|
||||||
|
yield ValidationError(f"{path}.{unknown_field}", "Field does not exist.")
|
||||||
|
|
||||||
|
for field_name, field_value in value.items():
|
||||||
|
if field_name not in field_validators:
|
||||||
|
continue
|
||||||
|
|
||||||
|
yield from field_validators[field_name](f"{path}.{field_name}", field_value)
|
||||||
|
|
||||||
|
return validate
|
||||||
|
|
||||||
|
|
||||||
|
def config_type_validator(*types: Type) -> CONFIG_VALIDATOR:
|
||||||
|
def validate(path: str, value: Any) -> Iterator[ValidationError]:
|
||||||
|
if not isinstance(value, types):
|
||||||
|
yield ValidationError(
|
||||||
|
path, f"Type {type(value)} is not one of {', '.join(types)}."
|
||||||
|
)
|
||||||
|
|
||||||
|
return validate
|
||||||
|
|
||||||
|
|
||||||
|
def config_enum_validator(values: Set[Any]) -> CONFIG_VALIDATOR:
|
||||||
|
def validate(path: str, value: Any) -> Iterator[ValidationError]:
|
||||||
|
if value not in values:
|
||||||
|
yield ValidationError(path, f"Value '{value}' not one of {values}.")
|
||||||
|
|
||||||
|
return validate
|
||||||
|
|
||||||
|
def config_list_validator(*types: Type) -> CONFIG_VALIDATOR:
|
||||||
|
def validate(path: str, value: Any) -> Iterator[ValidationError]:
|
||||||
|
if not isinstance(value, list):
|
||||||
|
yield ValidationError(path, f"Expected a list")
|
||||||
|
return
|
||||||
|
|
||||||
|
for i, element in enumerate(value):
|
||||||
|
if not isinstance(element, types):
|
||||||
|
yield ValidationError(
|
||||||
|
f"{path}[{i}]", f"Type {type(value)} is not one of {', '.join(types)}."
|
||||||
|
)
|
||||||
|
|
||||||
|
return validate
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigError(Exception):
|
||||||
|
def __init__(self, errors: List[ValidationError]) -> None:
|
||||||
|
super().__init__(
|
||||||
|
"Config errors: \n"
|
||||||
|
+ "\n".join(f" {error.path}: {error.error}" for error in errors)
|
||||||
|
)
|
||||||
|
self.errors = errors
|
||||||
|
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
CONFIG_DEFAULT = {
|
||||||
|
"screenshot": {
|
||||||
|
"prompt": "📷> ",
|
||||||
|
"file_name": "screenshots/screenshot_%Y-%m-%dT%H:%M:%S.png",
|
||||||
|
},
|
||||||
|
"screencast": {
|
||||||
|
"prompt": "📹> ",
|
||||||
|
"pid_file": "${XDG_RUNTIME_DIR}/sway-interactive-screenshot.${WAYLAND_DISPLAY}.video.pid",
|
||||||
|
"file_name": "screencast_%Y-%m-%dT%H:%M:%S.mkv",
|
||||||
|
"audio": "ask",
|
||||||
|
},
|
||||||
|
"notification_actions": {
|
||||||
|
"dragon": {
|
||||||
|
"command": "dragon-drop",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ask" : {
|
||||||
|
"command" : "rofi",
|
||||||
|
"args": [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_validate = staticmethod(
|
||||||
|
config_dict_validator(
|
||||||
|
{
|
||||||
|
"screenshot": config_dict_validator(
|
||||||
|
{
|
||||||
|
"prompt": config_type_validator(str),
|
||||||
|
"save_dir": config_type_validator(str),
|
||||||
|
"file_name": config_type_validator(str),
|
||||||
|
"type": config_enum_validator({"png", "jpeg", "ppm"}),
|
||||||
|
"jpeg_quality": config_type_validator(int),
|
||||||
|
"png_level": config_type_validator(int),
|
||||||
|
"cursor": config_type_validator(bool),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"screencast": config_dict_validator(
|
||||||
|
{
|
||||||
|
"prompt": config_type_validator(str),
|
||||||
|
"save_dir": config_type_validator(str),
|
||||||
|
"pid_file": config_type_validator(str),
|
||||||
|
"file_name": config_type_validator(str),
|
||||||
|
"audio": config_enum_validator({"yes", "no", "ask"}),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"notification_actions": config_dict_validator(
|
||||||
|
{
|
||||||
|
"dragon": config_dict_validator(
|
||||||
|
{
|
||||||
|
"command": config_type_validator(str),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"ask" : config_dict_validator(
|
||||||
|
{
|
||||||
|
"command" : config_type_validator(str),
|
||||||
|
"args" : config_list_validator(str),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
config: Dict[str, Any]
|
||||||
|
|
||||||
|
def __init__(self, filepath: Union[Path, str, None]) -> None:
|
||||||
|
self._load(filepath)
|
||||||
|
errors = list(self._validate("", self.config))
|
||||||
|
if errors:
|
||||||
|
raise ConfigError(errors)
|
||||||
|
|
||||||
|
def get(self, *path: str) -> Any:
|
||||||
|
value: Any = self.config
|
||||||
|
for item in path:
|
||||||
|
if item not in value:
|
||||||
|
default: Any = self.CONFIG_DEFAULT
|
||||||
|
for item in path:
|
||||||
|
default = default.get(item)
|
||||||
|
if default is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return default
|
||||||
|
|
||||||
|
value = value.get(item)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def _load(self, filepath: Union[Path, str, None]) -> None:
|
||||||
|
if filepath is None:
|
||||||
|
xdg_config_home = os.getenv("XDG_CONFIG_HOME", "~/.config")
|
||||||
|
filepath = Path(xdg_config_home) / "sway-interactive-screenshot/config.toml"
|
||||||
|
|
||||||
|
filepath = Path(filepath).expanduser()
|
||||||
|
|
||||||
|
if filepath.exists():
|
||||||
|
with open(filepath, "rb") as f:
|
||||||
|
self.config = tomllib.load(f)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.config = {}
|
||||||
|
|
||||||
|
|
||||||
|
class CanceledError(Exception):
|
||||||
|
def __init__(self, msg: Optional[str] = None) -> None:
|
||||||
|
super().__init__(msg)
|
||||||
|
self.msg = msg
|
||||||
|
|
||||||
|
|
||||||
|
class Area(NamedTuple):
|
||||||
|
output: Optional[str] = None
|
||||||
|
geometry: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class Window(NamedTuple):
|
||||||
|
name: str
|
||||||
|
x: int
|
||||||
|
y: int
|
||||||
|
width: int
|
||||||
|
height: int
|
||||||
|
focused: bool
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"window: {self.name}"
|
||||||
|
|
||||||
|
def get_geometry_str(self) -> str:
|
||||||
|
return f"{self.x},{self.y} {self.width}x{self.height}"
|
||||||
|
|
||||||
|
def get_area(self) -> Area:
|
||||||
|
return Area(geometry=self.get_geometry_str())
|
||||||
|
|
||||||
|
|
||||||
|
class AllOutputs:
|
||||||
|
selection_name = "all-outputs"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return "all outputs"
|
||||||
|
|
||||||
|
def get_area(self) -> Area:
|
||||||
|
return Area()
|
||||||
|
|
||||||
|
|
||||||
|
class Region:
|
||||||
|
selection_name = "region"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return "region"
|
||||||
|
|
||||||
|
def get_area(self) -> Area:
|
||||||
|
geometry = ask_geometry()
|
||||||
|
if geometry is None:
|
||||||
|
raise CanceledError("No region selected.")
|
||||||
|
|
||||||
|
return Area(geometry=geometry)
|
||||||
|
|
||||||
|
|
||||||
|
class FocusedWindow:
|
||||||
|
selection_name = "focused-window"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return "focused window"
|
||||||
|
|
||||||
|
def get_area(self) -> Area:
|
||||||
|
window = next(
|
||||||
|
(window for window in get_windows() if window.focused),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if window is None:
|
||||||
|
raise CanceledError("Could not find any focused window.")
|
||||||
|
|
||||||
|
return window.get_area()
|
||||||
|
|
||||||
|
|
||||||
|
class SelectWindow:
|
||||||
|
selection_name = "select-window"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return "select window"
|
||||||
|
|
||||||
|
def get_area(self) -> Area:
|
||||||
|
windows = list(get_windows())
|
||||||
|
if not windows:
|
||||||
|
raise CanceledError("No visible window found.")
|
||||||
|
|
||||||
|
geometry = ask_geometry(window.get_geometry_str() for window in windows)
|
||||||
|
if geometry is None:
|
||||||
|
raise CanceledError("No window selected.")
|
||||||
|
|
||||||
|
return Area(geometry=geometry)
|
||||||
|
|
||||||
|
|
||||||
|
class FocusedOutput:
|
||||||
|
selection_name = "focused-output"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return "focused output"
|
||||||
|
|
||||||
|
def get_area(self) -> Area:
|
||||||
|
output = next(
|
||||||
|
(output for output in get_outputs() if output.focused),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if output is None:
|
||||||
|
raise CanceledError("Could not find any focused output.")
|
||||||
|
|
||||||
|
return output.get_area()
|
||||||
|
|
||||||
|
|
||||||
|
class Output(NamedTuple):
|
||||||
|
name: str
|
||||||
|
model: Optional[str]
|
||||||
|
focused: bool
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
model = f" ({self.model})" if self.model else ""
|
||||||
|
focused = " (focused)" if self.focused else ""
|
||||||
|
return f"output: {self.name}{model}{focused}"
|
||||||
|
|
||||||
|
def get_area(self) -> Area:
|
||||||
|
return Area(output=self.name)
|
||||||
|
|
||||||
|
|
||||||
|
class SelectOutput:
|
||||||
|
selection_name = "select-output"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return "select output"
|
||||||
|
|
||||||
|
def get_area(self) -> Area:
|
||||||
|
output = ask_output()
|
||||||
|
if output is None:
|
||||||
|
raise CanceledError("No output selected.")
|
||||||
|
|
||||||
|
return Area(output=output)
|
||||||
|
|
||||||
|
|
||||||
|
def get_windows() -> Iterator[Window]:
|
||||||
|
def walk(node: Dict[str, Any]) -> Iterator[Window]:
|
||||||
|
sub_nodes = node.get("floating_nodes")
|
||||||
|
if sub_nodes:
|
||||||
|
for sub_node in sub_nodes:
|
||||||
|
yield from walk(sub_node)
|
||||||
|
sub_nodes = node.get("nodes")
|
||||||
|
if sub_nodes:
|
||||||
|
for sub_node in sub_nodes:
|
||||||
|
yield from walk(sub_node)
|
||||||
|
elif node.get("visible") and node.get("pid"):
|
||||||
|
rect = node["rect"]
|
||||||
|
yield Window(
|
||||||
|
name=node["name"],
|
||||||
|
x=rect["x"],
|
||||||
|
y=rect["y"],
|
||||||
|
width=rect["width"],
|
||||||
|
height=rect["height"],
|
||||||
|
focused=node["focused"],
|
||||||
|
)
|
||||||
|
|
||||||
|
process = subprocess.run(
|
||||||
|
["swaymsg", "-t", "get_tree"],
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
tree = json.loads(process.stdout.decode())
|
||||||
|
return walk(tree)
|
||||||
|
|
||||||
|
|
||||||
|
def get_outputs() -> Iterator[Output]:
|
||||||
|
process = subprocess.run(
|
||||||
|
["swaymsg", "-t", "get_outputs"],
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
for output in json.loads(process.stdout.decode()):
|
||||||
|
if output["active"]:
|
||||||
|
yield Output(
|
||||||
|
name=output["name"],
|
||||||
|
model=output["model"],
|
||||||
|
focused=output["focused"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def ask_rofi(
|
||||||
|
choices: List[Any],
|
||||||
|
*,
|
||||||
|
prompt: Optional[str] = None,
|
||||||
|
lines: Optional[int] = None,
|
||||||
|
index: bool = False,
|
||||||
|
additional_args: Optional[List[str]] = None
|
||||||
|
) -> Union[int, str, None]:
|
||||||
|
args = ["rofi", "-dmenu"]
|
||||||
|
|
||||||
|
if index:
|
||||||
|
args.extend(("-format","i"))
|
||||||
|
|
||||||
|
if prompt is not None:
|
||||||
|
args.extend(("-p", prompt))
|
||||||
|
|
||||||
|
if lines is not None:
|
||||||
|
args.extend(("-l", str(lines)))
|
||||||
|
|
||||||
|
if additional_args:
|
||||||
|
args.extend(additional_args)
|
||||||
|
|
||||||
|
process = subprocess.run(
|
||||||
|
args,
|
||||||
|
input=b"\n".join(str(choice).encode() for choice in choices),
|
||||||
|
capture_output=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
if process.returncode != 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
stdout = process.stdout.decode()
|
||||||
|
|
||||||
|
breakpoint()
|
||||||
|
|
||||||
|
return int(stdout) if index else stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def ask_fuzzel(
|
||||||
|
choices: List[Any],
|
||||||
|
*,
|
||||||
|
prompt: Optional[str] = None,
|
||||||
|
lines: Optional[int] = None,
|
||||||
|
index: bool = False,
|
||||||
|
additional_args: Optional[List[str]] = None
|
||||||
|
) -> Union[int, str, None]:
|
||||||
|
args = ["fuzzel", "--dmenu"]
|
||||||
|
|
||||||
|
if index:
|
||||||
|
args.append("--index")
|
||||||
|
|
||||||
|
if prompt is not None:
|
||||||
|
args.extend(("--prompt", prompt))
|
||||||
|
|
||||||
|
if lines is not None:
|
||||||
|
args.extend(("--lines", str(lines)))
|
||||||
|
|
||||||
|
if additional_args:
|
||||||
|
args.extend(additional_args)
|
||||||
|
|
||||||
|
process = subprocess.run(
|
||||||
|
args,
|
||||||
|
input=b"\n".join(str(choice).encode() for choice in choices),
|
||||||
|
capture_output=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
if process.returncode != 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
stdout = process.stdout.decode()
|
||||||
|
|
||||||
|
return int(stdout) if index else stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
ASK_FNS = {
|
||||||
|
"rofi" : ask_rofi,
|
||||||
|
"fuzzel" : ask_fuzzel
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ask = ask_fuzzel
|
||||||
|
|
||||||
|
def notify(
|
||||||
|
title: str,
|
||||||
|
summary: Optional[str] = None,
|
||||||
|
*,
|
||||||
|
icon: Optional[str] = None,
|
||||||
|
actions: Optional[List[Tuple[str, str]]] = None,
|
||||||
|
) -> Optional[str]:
|
||||||
|
args = ["notify-send"]
|
||||||
|
|
||||||
|
if icon is not None:
|
||||||
|
args.extend(("--icon", icon))
|
||||||
|
|
||||||
|
for action_name, action_desc in actions or ():
|
||||||
|
args.extend(("-A", f"{action_name}={action_desc}"))
|
||||||
|
|
||||||
|
args.extend(("--", title))
|
||||||
|
if summary:
|
||||||
|
args.append(summary)
|
||||||
|
|
||||||
|
process = subprocess.run(args, capture_output=True)
|
||||||
|
if process.returncode != 0:
|
||||||
|
print("Error while sending notification.", file=sys.stderr)
|
||||||
|
print(process.stderr.decode(), file=sys.stderr)
|
||||||
|
|
||||||
|
return process.stdout.decode().strip() or None
|
||||||
|
|
||||||
|
|
||||||
|
def take_screenshot(
|
||||||
|
*,
|
||||||
|
filepath: str,
|
||||||
|
geometry: Optional[str] = None,
|
||||||
|
output: Optional[str] = None,
|
||||||
|
cursor: Optional[bool] = None,
|
||||||
|
type_: Optional[str] = None,
|
||||||
|
jpeg_quality: Optional[int] = None,
|
||||||
|
png_level: Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
|
args = ["grim"]
|
||||||
|
|
||||||
|
if geometry is not None:
|
||||||
|
args.extend(("-g", geometry))
|
||||||
|
|
||||||
|
if output is not None:
|
||||||
|
args.extend(("-o", output))
|
||||||
|
|
||||||
|
if cursor:
|
||||||
|
args.append("-c")
|
||||||
|
|
||||||
|
if type_ is not None:
|
||||||
|
args.extend(("-t", type_))
|
||||||
|
|
||||||
|
if png_level is not None and (type == "png" or filepath.endswith(".png")):
|
||||||
|
args.extend(("-l", str(png_level)))
|
||||||
|
|
||||||
|
if jpeg_quality is not None and (
|
||||||
|
type == "jpeg" or filepath.endswith(".jpg") or filepath.endswith(".jpeg")
|
||||||
|
):
|
||||||
|
args.extend(("-q", str(jpeg_quality)))
|
||||||
|
|
||||||
|
args.append(filepath)
|
||||||
|
subprocess.run(args, check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def take_screencast(
|
||||||
|
*,
|
||||||
|
filepath: str,
|
||||||
|
geometry: Optional[str] = None,
|
||||||
|
output: Optional[str] = None,
|
||||||
|
audio: Union[bool, str, None] = None,
|
||||||
|
pid_file: Optional[Path] = None,
|
||||||
|
) -> None:
|
||||||
|
args = ["wf-recorder", "-f", filepath]
|
||||||
|
|
||||||
|
if geometry is not None:
|
||||||
|
args.extend(("-g", geometry))
|
||||||
|
|
||||||
|
if output is not None:
|
||||||
|
args.extend(("-o", output))
|
||||||
|
|
||||||
|
if audio is not None:
|
||||||
|
if audio is True:
|
||||||
|
args.append("-a")
|
||||||
|
else:
|
||||||
|
args.extend(("-a", str(audio)))
|
||||||
|
|
||||||
|
with subprocess.Popen(args) as process:
|
||||||
|
try:
|
||||||
|
if pid_file:
|
||||||
|
pid_file.write_text(str(process.pid))
|
||||||
|
process.wait()
|
||||||
|
if process.returncode != 0:
|
||||||
|
raise Exception(
|
||||||
|
f"wf-recorder exited with status code {process.returncode}"
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
if pid_file:
|
||||||
|
pid_file.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def ask_geometry(geometries: Optional[Iterable[str]] = None) -> Optional[str]:
|
||||||
|
if geometries is not None:
|
||||||
|
geomerties_input = b"\n".join(geometry.encode() for geometry in geometries)
|
||||||
|
else:
|
||||||
|
geomerties_input = None
|
||||||
|
|
||||||
|
process = subprocess.run(
|
||||||
|
["slurp"],
|
||||||
|
capture_output=True,
|
||||||
|
input=geomerties_input,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if process.returncode != 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return process.stdout.decode().strip()
|
||||||
|
|
||||||
|
|
||||||
|
def ask_output() -> Optional[str]:
|
||||||
|
process = subprocess.run(
|
||||||
|
["slurp", "-o", "-f", "%o"], capture_output=True, check=False
|
||||||
|
)
|
||||||
|
if process.returncode != 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return process.stdout.decode().strip()
|
||||||
|
|
||||||
|
|
||||||
|
def edit_capture(filepath: str) -> None:
|
||||||
|
subprocess.run(["swappy", "-f", filepath, "-o", filepath], check=False)
|
||||||
|
|
||||||
|
|
||||||
|
def copy_file_to_clipboard(filepath: str) -> None:
|
||||||
|
with open(filepath, "rb") as file:
|
||||||
|
with subprocess.Popen("wl-copy", stdin=file) as process:
|
||||||
|
process.wait()
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationAction(abc.ABC):
|
||||||
|
name: str
|
||||||
|
description: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@abc.abstractmethod
|
||||||
|
def run(cls, *, filepath: Path, config_getter: Callable[[str], Any]) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class EditNotificationAction(NotificationAction):
|
||||||
|
name = "default"
|
||||||
|
description = "Edit"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, *, filepath: Path, config_getter: Callable[[str], Any]) -> None:
|
||||||
|
filepath_str = str(filepath.expanduser())
|
||||||
|
edit_capture(filepath_str)
|
||||||
|
copy_file_to_clipboard(filepath_str)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteNotificationAction(NotificationAction):
|
||||||
|
name = "delete"
|
||||||
|
description = "Delete"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, *, filepath: Path, config_getter: Callable[[str], Any]) -> None:
|
||||||
|
filepath.expanduser().unlink()
|
||||||
|
notify("Deleted")
|
||||||
|
|
||||||
|
|
||||||
|
class DragonNotificationAction(NotificationAction):
|
||||||
|
name = "dragon"
|
||||||
|
description = "Drag and drop"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, *, filepath: Path, config_getter: Callable[[str], Any]) -> None:
|
||||||
|
subprocess.run([config_getter("command"), filepath.expanduser()], check=False)
|
||||||
|
|
||||||
|
|
||||||
|
class OpenNotificationAction(NotificationAction):
|
||||||
|
name = "open"
|
||||||
|
description = "Open"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, *, filepath: Path, config_getter: Callable[[str], Any]) -> None:
|
||||||
|
subprocess.run(["xdg-open", filepath.expanduser()], check=False)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_arguments():
|
||||||
|
argparser = argparse.ArgumentParser(
|
||||||
|
prog="sway-interactive-screenshot",
|
||||||
|
description="Interactively take screenshots with Sway.",
|
||||||
|
)
|
||||||
|
selections = (
|
||||||
|
Region,
|
||||||
|
FocusedOutput,
|
||||||
|
AllOutputs,
|
||||||
|
SelectOutput,
|
||||||
|
FocusedWindow,
|
||||||
|
SelectWindow,
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
"-s",
|
||||||
|
"--selection",
|
||||||
|
choices=[selection.selection_name for selection in selections],
|
||||||
|
help="Selection mode.",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
"--save-dir",
|
||||||
|
help="Directory where screenshots are saved.",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
"-o",
|
||||||
|
"--output",
|
||||||
|
help="Output file name. If set, --save-dir is ignored.",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
"--video",
|
||||||
|
help="Make a screen video recording.",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
argparser.add_argument(
|
||||||
|
"-c",
|
||||||
|
"--config",
|
||||||
|
help="Config file.",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = argparser.parse_args()
|
||||||
|
if args.selection is not None:
|
||||||
|
args.selection = next(
|
||||||
|
(
|
||||||
|
selection
|
||||||
|
for selection in selections
|
||||||
|
if selection.selection_name == args.selection
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
class CaptureMode(abc.ABC):
|
||||||
|
def __init__(self, config_getter: Callable[[str], Any]) -> None:
|
||||||
|
self.config_getter = config_getter
|
||||||
|
|
||||||
|
def get_prompt(self) -> str:
|
||||||
|
return self.config_getter("prompt")
|
||||||
|
|
||||||
|
def get_filename(self) -> str:
|
||||||
|
return datetime.now().strftime(self.config_getter("file_name"))
|
||||||
|
|
||||||
|
def get_save_dir(self) -> str:
|
||||||
|
return self.config_getter("save_dir")
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_display_name(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def early_exit(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def capture(self, *, area: Area, filepath: str) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_notification_actions(self) -> Iterable[Type[NotificationAction]]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_notification_icon(self, filepath: str) -> Optional[str]:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class Screenshot(CaptureMode):
|
||||||
|
def get_display_name(self) -> str:
|
||||||
|
return "Screenshot"
|
||||||
|
|
||||||
|
def capture(self, *, area: Area, filepath: str) -> None:
|
||||||
|
take_screenshot(
|
||||||
|
filepath=filepath,
|
||||||
|
geometry=area.geometry,
|
||||||
|
output=area.output,
|
||||||
|
cursor=self.config_getter("cursor"),
|
||||||
|
type_=self.config_getter("type"),
|
||||||
|
jpeg_quality=self.config_getter("jpeg_quality"),
|
||||||
|
png_level=self.config_getter("png_level"),
|
||||||
|
)
|
||||||
|
copy_file_to_clipboard(filepath)
|
||||||
|
|
||||||
|
def get_notification_actions(self) -> Iterable[Type[NotificationAction]]:
|
||||||
|
return (
|
||||||
|
EditNotificationAction,
|
||||||
|
DeleteNotificationAction,
|
||||||
|
DragonNotificationAction,
|
||||||
|
OpenNotificationAction,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_notification_icon(self, filepath: str) -> Optional[str]:
|
||||||
|
return filepath
|
||||||
|
|
||||||
|
|
||||||
|
class Screencast(CaptureMode):
|
||||||
|
def get_display_name(self) -> str:
|
||||||
|
return "Screencast"
|
||||||
|
|
||||||
|
def get_pid_filepath(self) -> Path:
|
||||||
|
uid = os.getuid()
|
||||||
|
xdg_runtime_dir = os.getenv("XDG_RUNTIME_DIR", f"/run/user/{uid}")
|
||||||
|
wayland_display = os.getenv("WAYLAND_DISPLAY", "wayland-1")
|
||||||
|
|
||||||
|
return Path(
|
||||||
|
self.config_getter("pid_file")
|
||||||
|
.replace("${XDG_RUNTIME_DIR}", xdg_runtime_dir)
|
||||||
|
.replace("${WAYLAND_DISPLAY}", wayland_display)
|
||||||
|
.replace("${UID}", str(uid))
|
||||||
|
)
|
||||||
|
|
||||||
|
def early_exit(self) -> bool:
|
||||||
|
pid_filepath = self.get_pid_filepath()
|
||||||
|
if pid_filepath.exists():
|
||||||
|
pid = int(pid_filepath.read_text())
|
||||||
|
print(
|
||||||
|
f"Sending kill signal to {pid} to stop video recording.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
os.kill(pid, signal.SIGINT)
|
||||||
|
notify(self.get_display_name(), "Stopping recording.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def capture(self, *, area: Area, filepath: str) -> None:
|
||||||
|
audio_raw_input = self.config_getter("audio")
|
||||||
|
if audio_raw_input == "ask":
|
||||||
|
audio_raw_input = ask(["yes", "no"], prompt=self.get_prompt() + "Audio? ")
|
||||||
|
if audio_raw_input is None:
|
||||||
|
raise CanceledError("No audio preference selected.")
|
||||||
|
|
||||||
|
start_recording = ask(
|
||||||
|
["yes", "cancel"], prompt=self.get_prompt() + "Start recording? "
|
||||||
|
)
|
||||||
|
if start_recording != "yes":
|
||||||
|
raise CanceledError()
|
||||||
|
|
||||||
|
take_screencast(
|
||||||
|
filepath=filepath,
|
||||||
|
geometry=area.geometry,
|
||||||
|
output=area.output,
|
||||||
|
audio=audio_raw_input == "yes",
|
||||||
|
pid_file=self.get_pid_filepath(),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_notification_actions(self) -> Iterable[Type[NotificationAction]]:
|
||||||
|
return (
|
||||||
|
DeleteNotificationAction,
|
||||||
|
DragonNotificationAction,
|
||||||
|
OpenNotificationAction,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
mode: Optional[CaptureMode] = None
|
||||||
|
try:
|
||||||
|
args = parse_arguments()
|
||||||
|
config = Config(args.config)
|
||||||
|
mode: CaptureMode = (
|
||||||
|
Screenshot(partial(config.get, "screenshot"))
|
||||||
|
if not args.video
|
||||||
|
else Screencast(partial(config.get, "screencast"))
|
||||||
|
)
|
||||||
|
if mode.early_exit():
|
||||||
|
return
|
||||||
|
|
||||||
|
global ask
|
||||||
|
ask = partial(
|
||||||
|
ASK_FNS[config.get("ask","command")],
|
||||||
|
additional_args=config.get("ask","args")
|
||||||
|
)
|
||||||
|
|
||||||
|
save_dir = args.save_dir
|
||||||
|
if save_dir is None:
|
||||||
|
save_dir = mode.get_save_dir()
|
||||||
|
if save_dir is None:
|
||||||
|
save_dir = os.getenv("SWAY_INTERACTIVE_SCREENSHOT_SAVEDIR", "~")
|
||||||
|
|
||||||
|
save_dir = Path(save_dir)
|
||||||
|
save_dir.expanduser().mkdir(parents=True, exist_ok=True)
|
||||||
|
filepath = Path(args.output) if args.output else None
|
||||||
|
|
||||||
|
if args.selection is None:
|
||||||
|
choices = [
|
||||||
|
Region(),
|
||||||
|
FocusedOutput(),
|
||||||
|
*((AllOutputs(),) if isinstance(mode, Screenshot) else ()),
|
||||||
|
SelectOutput(),
|
||||||
|
*get_outputs(),
|
||||||
|
FocusedWindow(),
|
||||||
|
SelectWindow(),
|
||||||
|
*get_windows(),
|
||||||
|
]
|
||||||
|
|
||||||
|
choice_idx = ask(choices, prompt=mode.get_prompt(), index=True)
|
||||||
|
if choice_idx is None:
|
||||||
|
return
|
||||||
|
if choice_idx == -1:
|
||||||
|
raise CanceledError("No option selected.")
|
||||||
|
|
||||||
|
choice = choices[choice_idx]
|
||||||
|
else:
|
||||||
|
choice = args.selection()
|
||||||
|
|
||||||
|
area = choice.get_area()
|
||||||
|
if not filepath:
|
||||||
|
filepath = save_dir / mode.get_filename()
|
||||||
|
|
||||||
|
mode.capture(filepath=str(filepath.expanduser()), area=area)
|
||||||
|
|
||||||
|
action_name = notify(
|
||||||
|
mode.get_display_name(),
|
||||||
|
summary=f"File saved as <i>{filepath}</i>.",
|
||||||
|
icon=mode.get_notification_icon(filepath.expanduser()),
|
||||||
|
actions=[
|
||||||
|
(action.name, action.description)
|
||||||
|
for action in mode.get_notification_actions()
|
||||||
|
],
|
||||||
|
)
|
||||||
|
action = next(
|
||||||
|
(
|
||||||
|
action
|
||||||
|
for action in mode.get_notification_actions()
|
||||||
|
if action.name == action_name
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
if action is not None:
|
||||||
|
action.run(filepath=filepath,
|
||||||
|
config_getter=partial(config.get,
|
||||||
|
"notification_actions",
|
||||||
|
action.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as err: # pylint: disable=broad-except
|
||||||
|
display_name = (
|
||||||
|
mode.get_display_name()
|
||||||
|
if mode is not None
|
||||||
|
else "sway-interactive-screenshot"
|
||||||
|
)
|
||||||
|
if isinstance(err, CanceledError):
|
||||||
|
notify(f"{display_name} canceled", summary=err.msg)
|
||||||
|
else:
|
||||||
|
notify(f"{display_name} error", str(err))
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
64
files/sway/.config/sway/status.sh
Executable file
64
files/sway/.config/sway/status.sh
Executable file
@@ -0,0 +1,64 @@
|
|||||||
|
# Change this according to your device
|
||||||
|
################
|
||||||
|
# Variables
|
||||||
|
################
|
||||||
|
|
||||||
|
# Keyboard input name
|
||||||
|
# keyboard_input_name="1:2:AT_Raw_Set_2_keyboard"
|
||||||
|
|
||||||
|
# Date and time
|
||||||
|
date_and_week=$(date "+%a %d.%b (KW%-V)")
|
||||||
|
current_time=$(date "+%H:%M")
|
||||||
|
|
||||||
|
#############
|
||||||
|
# Commands
|
||||||
|
#############
|
||||||
|
|
||||||
|
# Battery or charger
|
||||||
|
battery_charge=$(upower --show-info $(upower --enumerate | grep 'BAT') | egrep "percentage" | awk '{print $2}')
|
||||||
|
battery_status=$(upower --show-info $(upower --enumerate | grep 'BAT') | egrep "state" | awk '{print $2}')
|
||||||
|
|
||||||
|
# Audio and multimedia
|
||||||
|
audio_volume=$(pamixer --sink `pactl list sinks short | grep RUNNING | awk '{print $1}'` --get-volume)
|
||||||
|
audio_is_muted=$(pamixer --sink `pactl list sinks short | grep RUNNING | awk '{print $1}'` --get-mute)
|
||||||
|
loadavg_5min=$(cat /proc/loadavg | awk -F ' ' '{print $2}')
|
||||||
|
|
||||||
|
# Brightness
|
||||||
|
brightness=$(printf %.0f $(light))
|
||||||
|
|
||||||
|
# Removed weather because we are requesting it too many times to have a proper
|
||||||
|
# refresh on the bar
|
||||||
|
#weather=$(curl -Ss 'https://wttr.in/Pontevedra?0&T&Q&format=1')
|
||||||
|
|
||||||
|
if [ $battery_status = "discharging" ];
|
||||||
|
then
|
||||||
|
battery_pluggedin='⚠'
|
||||||
|
else
|
||||||
|
battery_pluggedin='⚡'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ $network ]
|
||||||
|
then
|
||||||
|
network_active="⛔"
|
||||||
|
else
|
||||||
|
network_active="⇆"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $player_status = "Playing" ]
|
||||||
|
then
|
||||||
|
song_status='▶'
|
||||||
|
elif [ $player_status = "Paused" ]
|
||||||
|
then
|
||||||
|
song_status='⏸'
|
||||||
|
else
|
||||||
|
song_status='⏹'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $audio_is_muted = "true" ]
|
||||||
|
then
|
||||||
|
audio_active='🔇'
|
||||||
|
else
|
||||||
|
audio_active='🔊'
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "🏋 $loadavg_5min | $audio_active $audio_volume% | ☀️ $brightness | $battery_pluggedin $battery_charge | $date_and_week 🕘 $current_time"
|
||||||
184
files/waybar/.config/waybar/config
Executable file
184
files/waybar/.config/waybar/config
Executable file
@@ -0,0 +1,184 @@
|
|||||||
|
// =============================================================================
|
||||||
|
//
|
||||||
|
// Waybar configuration
|
||||||
|
//
|
||||||
|
// Configuration reference: https://github.com/Alexays/Waybar/wiki/Configuration
|
||||||
|
//
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
{
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Global configuration
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
"layer": "top",
|
||||||
|
|
||||||
|
"position": "top",
|
||||||
|
|
||||||
|
// If height property would be not present, it'd be calculated dynamically
|
||||||
|
"height": 30,
|
||||||
|
|
||||||
|
"modules-left": [
|
||||||
|
"sway/workspaces",
|
||||||
|
// "sway/mode",
|
||||||
|
"sway/window"
|
||||||
|
],
|
||||||
|
"modules-center": [
|
||||||
|
],
|
||||||
|
"modules-right": [
|
||||||
|
// "network",
|
||||||
|
"pulseaudio",
|
||||||
|
// "memory",
|
||||||
|
// "cpu",
|
||||||
|
// "temperature",
|
||||||
|
"backlight",
|
||||||
|
// "custom/keyboard-layout",
|
||||||
|
"battery",
|
||||||
|
"clock#date",
|
||||||
|
"clock#time",
|
||||||
|
"tray"
|
||||||
|
],
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Modules
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
"battery": {
|
||||||
|
"interval": 10,
|
||||||
|
"states": {
|
||||||
|
"warning": 30,
|
||||||
|
"critical": 15
|
||||||
|
},
|
||||||
|
// Connected to AC
|
||||||
|
"format": " {icon} {capacity}%", // Icon: bolt
|
||||||
|
// Not connected to AC
|
||||||
|
// "format-discharging": "{icon} {capacity}%",
|
||||||
|
"format-discharging": "{capacity}%",
|
||||||
|
"format-icons": [
|
||||||
|
"", // Icon: battery-full
|
||||||
|
"", // Icon: battery-three-quarters
|
||||||
|
"", // Icon: battery-half
|
||||||
|
"", // Icon: battery-quarter
|
||||||
|
"" // Icon: battery-empty
|
||||||
|
],
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"backlight": {
|
||||||
|
"device": "intel_backlight",
|
||||||
|
// "format": "{icon} {percent}%",
|
||||||
|
"format": "{percent}%",
|
||||||
|
"format-icons": ["", ""]
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"clock#time": {
|
||||||
|
"interval": 1,
|
||||||
|
"format": "{:%H:%M}",
|
||||||
|
"tooltip": false,
|
||||||
|
"on-click": "gnome-clocks"
|
||||||
|
},
|
||||||
|
|
||||||
|
"clock#date": {
|
||||||
|
"interval": 10,
|
||||||
|
// "format": " {:%e %b %Y}", // Icon: calendar-alt
|
||||||
|
"format": "{:%e %b %Y}", // Icon: calendar-alt
|
||||||
|
"tooltip-format": "{:%e %B %Y}",
|
||||||
|
"on-click": "gnome-calendar"
|
||||||
|
},
|
||||||
|
|
||||||
|
"cpu": {
|
||||||
|
"interval": 5,
|
||||||
|
"format": " {usage}% ({load})", // Icon: microchip
|
||||||
|
"states": {
|
||||||
|
"warning": 70,
|
||||||
|
"critical": 90
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom/keyboard-layout": {
|
||||||
|
"exec": "swaymsg -t get_inputs | grep -m1 'xkb_active_layout_name' | cut -d '\"' -f4",
|
||||||
|
// Interval set only as a fallback, as the value is updated by signal
|
||||||
|
"interval": 30,
|
||||||
|
"format": " {}", // Icon: keyboard
|
||||||
|
// Signal sent by Sway key binding (~/.config/sway/key-bindings)
|
||||||
|
"signal": 1, // SIGHUP
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"memory": {
|
||||||
|
"interval": 5,
|
||||||
|
"format": " {}%", // Icon: memory
|
||||||
|
"states": {
|
||||||
|
"warning": 70,
|
||||||
|
"critical": 90
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"network": {
|
||||||
|
"interval": 5,
|
||||||
|
"format-wifi": " {essid} ({signalStrength}%)", // Icon: wifi
|
||||||
|
"format-ethernet": " {ifname}: {ipaddr}/{cidr}", // Icon: ethernet
|
||||||
|
"format-disconnected": "⚠ Disconnected",
|
||||||
|
"tooltip-format": "{ifname}: {ipaddr}"
|
||||||
|
},
|
||||||
|
|
||||||
|
"sway/mode": {
|
||||||
|
"format": "<span style=\"italic\"> {}</span>", // Icon: expand-arrows-alt
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"sway/window": {
|
||||||
|
"format": "{}",
|
||||||
|
"max-length": 120
|
||||||
|
},
|
||||||
|
|
||||||
|
"sway/workspaces": {
|
||||||
|
"all-outputs": false,
|
||||||
|
"disable-scroll": true,
|
||||||
|
"format": "{icon} {name}",
|
||||||
|
"format-icons": {
|
||||||
|
"urgent": "",
|
||||||
|
"focused": "",
|
||||||
|
"default": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"pulseaudio": {
|
||||||
|
//"scroll-step": 1,
|
||||||
|
"format": "{volume}%",
|
||||||
|
"format-bluetooth": "{volume}%",
|
||||||
|
"format-muted": "Mute",
|
||||||
|
"format-icons": {
|
||||||
|
"headphones": "",
|
||||||
|
"handsfree": "",
|
||||||
|
"headset": "",
|
||||||
|
"phone": "",
|
||||||
|
"portable": "",
|
||||||
|
"car": "",
|
||||||
|
"default": ["", ""]
|
||||||
|
},
|
||||||
|
"on-click": "pavucontrol"
|
||||||
|
},
|
||||||
|
|
||||||
|
"temperature": {
|
||||||
|
"critical-threshold": 80,
|
||||||
|
"interval": 5,
|
||||||
|
"format": "{icon} {temperatureC}°C",
|
||||||
|
"format-icons": [
|
||||||
|
"", // Icon: temperature-empty
|
||||||
|
"", // Icon: temperature-quarter
|
||||||
|
"", // Icon: temperature-half
|
||||||
|
"", // Icon: temperature-three-quarters
|
||||||
|
"" // Icon: temperature-full
|
||||||
|
],
|
||||||
|
"tooltip": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"tray": {
|
||||||
|
"icon-size": 15,
|
||||||
|
"spacing": 10
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
197
files/waybar/.config/waybar/style.css
Executable file
197
files/waybar/.config/waybar/style.css
Executable file
@@ -0,0 +1,197 @@
|
|||||||
|
/* =============================================================================
|
||||||
|
*
|
||||||
|
* Waybar configuration
|
||||||
|
*
|
||||||
|
* Configuration reference: https://github.com/Alexays/Waybar/wiki/Configuration
|
||||||
|
*
|
||||||
|
* =========================================================================== */
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* Keyframes
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
@keyframes blink-warning {
|
||||||
|
70% {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
color: white;
|
||||||
|
background-color: orange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink-critical {
|
||||||
|
70% {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
color: white;
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* Base styles
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Reset all styles */
|
||||||
|
* {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
min-height: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The whole bar */
|
||||||
|
#waybar {
|
||||||
|
background: #2D353B;
|
||||||
|
color: white;
|
||||||
|
/* font-family: Cantarell, Noto Sans, sans-serif; */
|
||||||
|
font-family: JetBrainsMono Nerd Font Mono;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Each module */
|
||||||
|
#battery,
|
||||||
|
#backlight,
|
||||||
|
#clock,
|
||||||
|
#cpu,
|
||||||
|
#custom-keyboard-layout,
|
||||||
|
#memory,
|
||||||
|
#mode,
|
||||||
|
#network,
|
||||||
|
#pulseaudio,
|
||||||
|
#temperature,
|
||||||
|
#tray {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* Module styles
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#battery {
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-direction: alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.warning {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.critical {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.warning.discharging {
|
||||||
|
animation-name: blink-warning;
|
||||||
|
animation-duration: 3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#battery.critical.discharging {
|
||||||
|
animation-name: blink-critical;
|
||||||
|
animation-duration: 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clock {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cpu {
|
||||||
|
/* No styles */
|
||||||
|
}
|
||||||
|
|
||||||
|
#cpu.warning {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cpu.critical {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#memory {
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-direction: alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
#memory.warning {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
#memory.critical {
|
||||||
|
color: red;
|
||||||
|
animation-name: blink-critical;
|
||||||
|
animation-duration: 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mode {
|
||||||
|
background: #64727D;
|
||||||
|
border-top: 2px solid white;
|
||||||
|
/* To compensate for the top border and still have vertical centering */
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#network {
|
||||||
|
/* No styles */
|
||||||
|
}
|
||||||
|
|
||||||
|
#network.disconnected {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pulseaudio {
|
||||||
|
/* No styles */
|
||||||
|
}
|
||||||
|
|
||||||
|
#pulseaudio.muted {
|
||||||
|
/* No styles */
|
||||||
|
}
|
||||||
|
|
||||||
|
#custom-spotify {
|
||||||
|
color: rgb(102, 220, 105);
|
||||||
|
}
|
||||||
|
|
||||||
|
#temperature {
|
||||||
|
/* No styles */
|
||||||
|
}
|
||||||
|
|
||||||
|
#temperature.critical {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tray {
|
||||||
|
/* No styles */
|
||||||
|
}
|
||||||
|
|
||||||
|
#window {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button {
|
||||||
|
border-top: 2px solid transparent;
|
||||||
|
/* To compensate for the top border and still have vertical centering */
|
||||||
|
padding-bottom: 2px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
color: #888888;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button.focused {
|
||||||
|
border-color: #A7C080;
|
||||||
|
/* color: white; */
|
||||||
|
color: #A7C080;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button.urgent {
|
||||||
|
border-color: #c9545d;
|
||||||
|
color: #c9545d;
|
||||||
|
}
|
||||||
40
files/wofi/.config/wofi/style.css
Normal file
40
files/wofi/.config/wofi/style.css
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
window {
|
||||||
|
margin: 0px;
|
||||||
|
border: 2px solid #7A8478;
|
||||||
|
background-color: #1E2326;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input {
|
||||||
|
margin: 5px;
|
||||||
|
border: none;
|
||||||
|
color: #D3C6AA;
|
||||||
|
background-color: #272E33;
|
||||||
|
}
|
||||||
|
|
||||||
|
#inner-box {
|
||||||
|
margin: 5px;
|
||||||
|
border: none;
|
||||||
|
background-color: #1E2326;
|
||||||
|
}
|
||||||
|
|
||||||
|
#outer-box {
|
||||||
|
margin: 5px;
|
||||||
|
border: none;
|
||||||
|
background-color: #1E2326;
|
||||||
|
}
|
||||||
|
|
||||||
|
#scroll {
|
||||||
|
margin: 0px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#text {
|
||||||
|
margin: 5px;
|
||||||
|
border: none;
|
||||||
|
color: #D3C6AA;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry:selected {
|
||||||
|
background-color: #272E33;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user