From 722eb377f7cf18a7e83a77e62bb4a5e4c40d4c55 Mon Sep 17 00:00:00 2001 From: Akemi Izuko Date: Sat, 23 Dec 2023 20:14:15 -0700 Subject: [PATCH] New: i3 configs --- bin/screenshot_wayland.py | 331 +++++++++++++++++++------------ i3/brightness_lock.sh | 58 ++++++ i3/config | 405 ++++++++++++++++++++++++++++++++++++++ install.sh | 1 + sway/window_dimensions.py | 50 +++-- 5 files changed, 705 insertions(+), 140 deletions(-) create mode 100755 i3/brightness_lock.sh create mode 100644 i3/config diff --git a/bin/screenshot_wayland.py b/bin/screenshot_wayland.py index ca8cdd8..8171d7f 100755 --- a/bin/screenshot_wayland.py +++ b/bin/screenshot_wayland.py @@ -4,7 +4,8 @@ # Additional dependencies: # - Python's pillow library # - pip install --user pillow-avif-plugin # Until PIL merges avif support -# - grim +# - grim on wayland or scrot on x11 +# - slurp on wayland slop on x11 # - swappy import argparse, os, sys, time, re, shutil, tarfile, tempfile, pillow_avif from subprocess import run, Popen, PIPE, DEVNULL @@ -15,20 +16,46 @@ from typing import * # Global constants RELATIVE_DIR = Path("Pictures/screenshots_wayland") # Default save directory -DIR = Path.home()/RELATIVE_DIR +DIR = Path.home() / RELATIVE_DIR DIMENSIONS_REGEX = "([0-9]+),([0-9]+) ([0-9]+)x([0-9]+)" +SLOP_REGEX = "([0-9]+)x([0-9]+)\+([0-9]+)\+([0-9]+)" EXTENSION_REGEX = "\.([A-z0-9]+)$" ORIGINAL_REGEX = "([0-9]+(_[0-9]{0,3})?)\.png" EDIT_REGEX = "([0-9]+(_[0-9]{0,3})?)_edit_([0-9]+)" + EXTENSION_REGEX -DS_BG_LIGHT = 'white' -DS_SHADOW_LIGHT = 'black' -DS_BG_DARK = '#d3869b' -DS_SHADOW_DARK = 'black' +DS_BG_LIGHT = "white" +DS_SHADOW_LIGHT = "black" +DS_BG_DARK = "#d3869b" +DS_SHADOW_DARK = "black" DEFAULT_EDIT_QUALITY = 50 -DEFAULT_EDIT_EXTENSION = 'avif' +DEFAULT_EDIT_EXTENSION = "avif" + + +class ScreenshotDimensions: + def __init__(self, x, y, w, h): + self.x = int(x) + self.y = int(y) + self.w = int(w) + self.h = int(h) + + @classmethod + def from_string(self, s: str): + s = s.strip() + if m := re.fullmatch(DIMENSIONS_REGEX, s): + return self(m[1], m[2], m[3], m[4]) + elif m := re.fullmatch(SLOP_REGEX, s): + return self(m[3], m[4], m[1], m[2]) + + raise Exception(f"`{s}` does not match pattern {DIMENSIONS_REGEX}") + + def as_grim(self) -> str: + return f"{self.x},{self.y} {self.w}x{self.h}" + + def as_scrot(self) -> str: + return f"{self.x},{self.y},{self.w},{self.h}" + # Returns a path to an unused screenshot in DIR def get_sceenshot_path() -> Path: @@ -40,6 +67,7 @@ def get_sceenshot_path() -> Path: else: return p + # Returns a free path to the last screenshot in DIR # (original_path, new_edit_path) # @@ -59,18 +87,38 @@ def get_edit_path(ext: str) -> (Path, Path): return DIR / originals[-1], DIR / f"{latest}_edit_{new_index}.{ext}" + # Copies the image to the wayland clipboard def copy_to_clipboard(pic: Path): - with open(pic, 'r') as img: - run(["wl-copy"], stdin=img, timeout=4) + # TODO: xcompatability + try: + if "wayland" in os.environ["XDG_SESSION_TYPE"]: + with open(pic, "r") as img: + run(["wl-copy"], stdin=img, timeout=4) + return + except KeyError: + pass + + run( + ["xclip", "-selection", "clipboard", "-t", "image/png", "-i", str(pic)], + timeout=4, + ) + # Interactively gets user to select an area of the screen -# String matches regex /[0-9]+,[0-9]+ [0-9]+x[0-9]+/ -# Example: 287,526 474x369 -def select_area() -> str: - slurp = run(["slurp"], text=True, stdout=PIPE) - slurp.check_returncode() - return slurp.stdout.strip() +def select_area() -> ScreenshotDimensions: + try: + if "wayland" in os.environ["XDG_SESSION_TYPE"]: + slurp = run(["slurp"], text=True, stdout=PIPE) + slurp.check_returncode() + return ScreenshotDimensions.from_string(slurp.stdout) + except KeyError: + pass + + slop = run(["slop"], text=True, stdout=PIPE) + slop.check_returncode() + return ScreenshotDimensions.from_string(slop.stdout) + # Adds drop-shadow to an image. If `img` and `save` are the same path, the image # is overwritten @@ -79,26 +127,39 @@ def add_drop_shadow(img: Path, save: Path, back_color: str, shadow_color: str): mode = img.mode border_width = max(img.size) // 20 # 5% of larger dim - width = img.size[0] + border_width*2 - height = img.size[1] + border_width*2 + width = img.size[0] + border_width * 2 + height = img.size[1] + border_width * 2 shadow = Image.new(mode, img.size, color=shadow_color) canvas = Image.new(mode, (width, height), color=back_color) - canvas.paste(shadow, box=(border_width, int(border_width*1.16))) + canvas.paste(shadow, box=(border_width, int(border_width * 1.16))) canvas = canvas.filter(GaussianBlur(border_width // 4)) canvas.paste(img, box=(border_width, border_width)) canvas.save(save, optimize=True) -# Uses grim for wayland to take a screenshot. Default to full screen without -# dims. exact_dims must match a slurp regex -def take_screenshot(path, exact_dims=None): + +# Uses grim for wayland, scropt for x to take a screenshot. Default to full +# screen without dims. exact_dims must match a slurp regex +def take_screenshot(path, exact_dims: ScreenshotDimensions = None): + try: + if "wayland" in os.environ["XDG_SESSION_TYPE"]: + if exact_dims is not None: + run(["grim", "-g", exact_dims.as_grim(), path]).check_returncode() + else: + run(["grim", path]).check_returncode() + return + except KeyError: + pass + + # x11 version if exact_dims is not None: - run(["grim", "-g", exact_dims, path]).check_returncode() + run(["scrot", "-a", exact_dims.as_scrot(), "-F", path]).check_returncode() else: - run(["grim", path]).check_returncode() + run(["scrot", "-F", path]).check_returncode() + # Takes a screenshot as specified by args def take_subcommand(args): @@ -120,6 +181,7 @@ def take_subcommand(args): if args.file is not None: shutil.copyfile(save_path, args.file) + # Applies an edit to the latest screenshot def edit_subcommand(args): og_path, edit_path = get_edit_path(args.extension) @@ -148,6 +210,7 @@ def edit_subcommand(args): if args.file is not None: shutil.copyfile(edit_path, args.file) + # Saves `DIR` into a tar file def archive_subcommand(args): # Get the list of images to backup @@ -161,16 +224,17 @@ def archive_subcommand(args): # Write compressed pictures to tmpdir with progress bar TMP_DIR = tempfile.mkdtemp() - ext = f'.{args.extension}' + ext = f".{args.extension}" count = len(pics) sys.stdout.write( - f"Compressing {count} images into {ext} @ quality {args.quality}\n") - sys.stdout.write(f'Progress: 0/{count}') + f"Compressing {count} images into {ext} @ quality {args.quality}\n" + ) + sys.stdout.write(f"Progress: 0/{count}") for i, pic in enumerate(pics): - og = DIR/pic - out = TMP_DIR/pic.with_suffix(ext) + og = DIR / pic + out = TMP_DIR / pic.with_suffix(ext) with Image.open(og) as img: img.save(out, quality=args.quality, method=6, optimize=True) @@ -178,20 +242,21 @@ def archive_subcommand(args): stat = os.stat(og) os.utime(out, times=(stat.st_atime, stat.st_mtime)) - sys.stdout.write(f'\rProgress: {i+1}/{count}' + ' ' * 40) + sys.stdout.write(f"\rProgress: {i+1}/{count}" + " " * 40) - sys.stdout.write('\nDone!\n') + sys.stdout.write("\nDone!\n") - pics = [TMP_DIR/pic.with_suffix(ext) for pic in pics] + pics = [TMP_DIR / pic.with_suffix(ext) for pic in pics] # Pack into tar file with tarfile.open(args.tar_path, "w:gz") as tar: for pic in pics: tar.add(pic, arcname=pic.name) + # Provides a drawing editor for the latest image def markup_subcommand(args): - og_path, edit_path = get_edit_path('png') + og_path, edit_path = get_edit_path("png") if args.show_latest: print(og_path) @@ -205,6 +270,7 @@ def markup_subcommand(args): if args.file is not None: shutil.copyfile(edit_path, args.file) + # =================================================================== # Parse args # =================================================================== @@ -214,20 +280,19 @@ def parser_dir(s: str): else: raise NotADirectoryError(f"`{s}` is not a directory") -def parse_dimensions(s: str) -> str: - s = s.strip() - if re.fullmatch(DIMENSIONS_REGEX, s): - return s - raise Exception(f"`{s}` does not match pattern {DIMENSIONS_REGEX}") +def parse_dimensions(s: str) -> ScreenshotDimensions: + return ScreenshotDimensions.from_string(s) + def parse_percent(s: str) -> int: s = s.strip() try: - return int(re.fullmatch('([0-9]+)%?', s)[1]) + return int(re.fullmatch("([0-9]+)%?", s)[1]) except IndexError: raise Exception(f"{s} is not a valid percent. Does not match /[0-9]+%?/") + def parse_size(s: str) -> (int, int): s = s.strip() try: @@ -236,6 +301,7 @@ def parse_size(s: str) -> (int, int): except: raise Exception(f"{s} does not match size regex /[0-9]+[x ][0-9]+/") + def parse_tar(s: str) -> Path: s = s.strip() @@ -244,151 +310,172 @@ def parse_tar(s: str) -> Path: else: raise Exception(f"{s} is not a path to a .{{tar,tgz,tar.gz}} file") + parser = argparse.ArgumentParser( - prog="Screenshot Wayland v1.0.0", - description='Take a screenshot on Sway' -); + prog="Screenshot Wayland v1.0.0", description="Take a screenshot on Sway" +) parser.add_argument( - '-s', '--screenshot-dir', + "-s", + "--screenshot-dir", type=parser_dir, metavar="", help=f"Save and edit directory. Default: ~/{RELATIVE_DIR}", -); +) # Subcommands ==== -subcommands = parser.add_subparsers(dest='subcommand', required=True); +subcommands = parser.add_subparsers(dest="subcommand", required=True) # Take ==== -take_subcmd = subcommands.add_parser( - "take", help="Takes a screenshot"); +take_subcmd = subcommands.add_parser("take", help="Takes a screenshot") -take_common = argparse.ArgumentParser(add_help=False); +take_common = argparse.ArgumentParser(add_help=False) take_common.add_argument( - '-c', '--clipboard', - action='store_true', - help='Save the screenshot to your clipboard', -); + "-c", + "--clipboard", + action="store_true", + help="Save the screenshot to your clipboard", +) take_common.add_argument( - "file", nargs='?', type=Path, - help="Save the screenshot to this file name" -); + "file", nargs="?", type=Path, help="Save the screenshot to this file name" +) # Different possible screenshot regions -region = take_subcmd.add_subparsers(dest='region', required=True); +region = take_subcmd.add_subparsers(dest="region", required=True) full = region.add_parser( - 'full', parents=[take_common], - help='Take a screenshot of the entire screen' -); + "full", parents=[take_common], help="Take a screenshot of the entire screen" +) exact = region.add_parser( - 'exact', parents=[take_common], - help="Exact dimensions of screenshot: 'x,y width,height'" -); + "exact", + parents=[take_common], + help="Exact dimensions of screenshot: 'x,y width,height'", +) select = region.add_parser( - 'select', parents=[take_common], - help="Use `slurp` to select a region with your mouse" -); + "select", + parents=[take_common], + help="Use `slurp` or `slop` to select a region with your mouse", +) exact.add_argument( - 'dimensions', + "dimensions", type=parse_dimensions, metavar="'N,N NxN'", - help="Exact dimensions of screenshot: 'x,y width,height'" -); + help="Exact dimensions of screenshot: 'x,y widthxheight'", +) # Edit ==== edit_subcmd = subcommands.add_parser( - "edit", help="Apply an edit to the latest screenshot"); + "edit", help="Apply an edit to the latest screenshot" +) edit_subcmd.add_argument( - '-r', '--rescale', + "-r", + "--rescale", type=parse_percent, metavar="", - help='Rescale the latest screenshot to of the original', -); + help="Rescale the latest screenshot to of the original", +) edit_subcmd.add_argument( - '-s', '--size', + "-s", + "--size", type=parse_size, metavar="", - help='Set the dimensions of the latest screenshot', -); + help="Set the dimensions of the latest screenshot", +) edit_subcmd.add_argument( - '-c', '--clipboard', - action='store_true', - help='Save the edited screenshot to your clipboard', -); + "-c", + "--clipboard", + action="store_true", + help="Save the edited screenshot to your clipboard", +) edit_subcmd.add_argument( - '-d', '--drop-shadow', - action='store', - choices=['light', 'dark'], - help='Apply a drop shadow with a light/dark background', -); + "-d", + "--drop-shadow", + action="store", + choices=["light", "dark"], + help="Apply a drop shadow with a light/dark background", +) edit_subcmd.add_argument( - '-e', '--extension', metavar="", + "-e", + "--extension", + metavar="", type=str, default=DEFAULT_EDIT_EXTENSION, - help='Change image extension and image type saved', -); + help="Change image extension and image type saved", +) edit_subcmd.add_argument( - '-q', '--quality', + "-q", + "--quality", type=parse_percent, default=DEFAULT_EDIT_QUALITY, - metavar='', - help='Set quality of new image. [0, 100], higher means bigger file', -); + metavar="", + help="Set quality of new image. [0, 100], higher means bigger file", +) edit_subcmd.add_argument( - '--overwrite', - action='store_true', - help='Overwrite original image with the edited image', -); + "--overwrite", + action="store_true", + help="Overwrite original image with the edited image", +) edit_subcmd.add_argument( - "file", nargs='?', type=Path, + "file", + nargs="?", + type=Path, help="Save the edited screenshot to this file name", -); +) # Archive ==== archive_subcmd = subcommands.add_parser( - "archive", help="Backup the screenshots directory to a tar file"); + "archive", help="Backup the screenshots directory to a tar file" +) archive_subcmd.add_argument( - '-e', '--extension', + "-e", + "--extension", type=str, default=DEFAULT_EDIT_EXTENSION, metavar="", - help='Change image extension and image type backed up', -); + help="Change image extension and image type backed up", +) archive_subcmd.add_argument( - '-q', '--quality', + "-q", + "--quality", type=parse_percent, default=DEFAULT_EDIT_QUALITY, - metavar='', - help='Set quality of backed up images. [0, 100], higher means bigger file', -); + metavar="", + help="Set quality of backed up images. [0, 100], higher means bigger file", +) archive_subcmd.add_argument( - 'which', metavar='', - choices=['all', 'unedited'], - help='One of {all,unedited}. Backup only unedited images or all of them', -); + "which", + metavar="", + choices=["all", "unedited"], + help="One of {all,unedited}. Backup only unedited images or all of them", +) archive_subcmd.add_argument( - "tar_path", metavar='', + "tar_path", + metavar="", type=parse_tar, help="Destination tar file. Should be .{tar,tgz,tar.gz}", -); +) # Markup ==== markup_subcmd = subcommands.add_parser( - "markup", help="Markup the latest screenshot in swappy"); + "markup", help="Markup the latest screenshot in swappy" +) markup_subcmd.add_argument( - '-c', '--clipboard', - action='store_true', - help='Save the screenshot to your clipboard', -); + "-c", + "--clipboard", + action="store_true", + help="Save the screenshot to your clipboard", +) markup_subcmd.add_argument( - '-s', '--show-latest', - action='store_true', - help='Show the path to the latest image and exit', -); + "-s", + "--show-latest", + action="store_true", + help="Show the path to the latest image and exit", +) markup_subcmd.add_argument( - "file", nargs='?', type=Path, + "file", + nargs="?", + type=Path, help="Save the edited screenshot to this file name", -); +) -args = parser.parse_args(); +args = parser.parse_args() # Second layer of parser checks if args.screenshot_dir is not None: diff --git a/i3/brightness_lock.sh b/i3/brightness_lock.sh new file mode 100755 index 0000000..8865042 --- /dev/null +++ b/i3/brightness_lock.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# Locks the screens and lowers-brightness. Restores brightness on unlock + +# Defaults for levels when restored +declare light_restore=3 +declare ddc_restore=3 + +# Levels when dimmed +declare -ri light_dim=1 +declare -ri ddc_dim=1 + +get_brightness() { + local lvl + + if command -v light &>/dev/null; then + lvl="$(light -G)" + + if [[ $lvl =~ ^[0-9]+(\.[0-9]+)?$ ]]; then + light_restore="$lvl" + fi + fi + + if command -v ddcutil &>/dev/null; then + lvl="$(ddcutil getvcp 10 | awk '{ + gsub(" ",""); # Remove spaces + split($0, a, "="); + split(a[2], a, ","); print a[1] + }')" + + if [[ $lvl =~ ^[0-9]+$ ]]; then + ddc_restore="$lvl" + fi + fi +} + +set_brightness() { + local -r light_lvl="$1" + local -r ddc_lvl="$2" + + if command -v light &>/dev/null; then + light -S "$light_lvl" + fi + + if command -v ddcutil &>/dev/null; then + ddcutil setvcp 10 "$ddc_lvl" + fi +} + +i3lock -i ~/.config/swaylock/default_wallpaper.png & + +get_brightness +set_brightness $light_dim $ddc_dim + +while pgrep i3lock &>/dev/null; do + sleep 1 +done + +set_brightness $light_restore $ddc_restore diff --git a/i3/config b/i3/config new file mode 100644 index 0000000..01e75ad --- /dev/null +++ b/i3/config @@ -0,0 +1,405 @@ +# This file has been auto-generated by i3-config-wizard(1). +# It will not be overwritten, so edit it as you like. +# +# Should you change your keyboard layout some time, delete +# this file and re-run i3-config-wizard(1). +# + +# i3 config file (v4) +# +# Please see https://i3wm.org/docs/userguide.html for a complete reference! + +set $mod1 Mod4 +set $mod2 Mod4+Shift + +# Font for window titles. Will also be used by the bar unless a different font +# is used in the bar {} block below. +#font Meslo LG M Regular Nerd Font Complete:monospace 11 +font pango:monospace 11 + +# This font is widely installed, provides lots of unicode glyphs, right-to-left +# text rendering and scalability on retina/hidpi displays (thanks to pango). +#font pango:DejaVu Sans Mono 8 + +# Pane movement keys - vim +set $pane_left h +set $pane_down j +set $pane_up k +set $pane_right l + +# Symmetric keys - mpv +set $left_outer q +set $left_inner w +set $right_inner e +set $right_outer r + +# Space movement keys - gaming +set $space_up w +set $space_left a +set $space_down s +set $space_right d + +# Resize keys - vim windows +set $size_up comma +set $size_down period + +set $term alacritty +set $menu rlaunch + +# Sound effects and additional features +set $volume_command ~/.config/sway/pulse_audio_volume.sh +set $volume_change_sound ~/.config/sway/volume_change_sound.mp3 +set $screenshot_sound ffplay -nodisp -autoexit -v error ~/.config/sway/screenshot_sound.mp3 + +# Duplicate screenshot path, for quick uploads +set $screenshot_tmp /dev/shm/screenshot_shm.png +set $screenshot_script ~/.configs_pointer/bin/screenshot_wayland.py + +#╔─────────────────────────────────────────────────────────────────────────────╗ +#│ Pαηεs αηd cδηταiηεrs | +#╚─────────────────────────────────────────────────────────────────────────────╝ + # Move focus +bindsym $mod1+$pane_left focus left +bindsym $mod1+$pane_down focus down +bindsym $mod1+$pane_up focus up +bindsym $mod1+$pane_right focus right + + # Move focused window +bindsym $mod2+$pane_left move left +bindsym $mod2+$pane_down move down +bindsym $mod2+$pane_up move up +bindsym $mod2+$pane_right move right + + # Layout modifiers +bindsym $mod2+f floating toggle +bindsym $mod2+s layout stacking +bindsym $mod2+t layout tabbed +bindsym $mod2+r layout toggle + + # Binary split container +bindsym $mod1+b split toggle +#bindsym $mod1+v split none # Does not exist in i3 +bindsym $mod1+m fullscreen + + # Switch container layout Stacking|Tabbed|Rotate-bsp +bindsym $mod1+s layout stacking +bindsym $mod1+t layout tabbed +bindsym $mod1+r layout toggle split + + # Traverse window tree +bindsym $mod1+u focus parent +bindsym $mod1+d focus child + + # Alt-tab cycles windows, like on proprietary systems +bindsym $mod1+tab exec ~/.config/sway/cycle_windows.py next +bindsym $mod2+tab exec ~/.config/sway/cycle_windows.py previous + + # Swap focus between the tiling area and the floating area +#bindsym $mod+space focus mode_toggle + + # Important rust jerk +bindsym $mod1+ctrl+r exec wtype "asExpectedOfRust" +#bindsym $mod1+ctrl+s reload + + # Automatic upload image to discord +bindsym $mod1+ctrl+u exec sudo -u emiliko ~/.configs_pointer/bin/auto_image_upload_discord.sh + + # Paste by typing +bindsym Mod4+Ctrl+v exec ~/.configs_pointer/bin/wpastetype.py + +# rEsize containers ==== +bindsym $mod1+e mode "resize" + +mode "resize" { + # Horizontal, shift for fine adjust + bindsym $size_up resize grow width 16px + bindsym $size_down resize shrink width 16px + bindsym $size_up+Shift resize grow width 4px + bindsym $size_down+Shift resize shrink width 4px + + # Vertical, shift for fine adjust + bindsym $mod1+$size_down resize shrink height 16px + bindsym $mod1+$size_up resize grow height 16px + bindsym $mod1+$size_down+Shift resize shrink height 4px + bindsym $mod1+$size_up+Shift resize grow height 4px + + # Escaping + bindsym $mod1+f mode "workspace_tuning" + + bindsym Shift+end mode "default" + bindsym Ctrl+k mode "default" + bindsym Ctrl+bracketleft mode "default" + bindsym Escape mode "default" +} + +# Gaps ==== +bindsym $mod2+bracketright gaps inner current minus 3 +bindsym $mod2+bracketleft gaps inner current plus 3 +#bindsym $mod2+equal gaps toggle + # Standard outer gapping for 27" screen +bindsym $mod2+c gaps vertical current set 100, \ + gaps horizontal current set 200 + # Same as above, shifted left slightly +bindsym $mod2+g gaps vertical current set 100, \ + gaps left current set 150, \ + gaps right current set 250 + +bindsym $mod1+g mode "gapping" +mode "gapping" { + # General resize + bindsym $mod2+bracketright gaps inner current minus 3 + bindsym $mod2+bracketleft gaps inner current plus 3 + + # Horizontal + bindsym q gaps left current minus 2 + bindsym w gaps left current plus 2 + bindsym e gaps right current plus 2 + bindsym r gaps right current minus 2 + + # Vertical + bindsym $mod1+q gaps top current minus 2 + bindsym $mod1+w gaps top current plus 2 + bindsym $mod1+e gaps bottom current plus 2 + bindsym $mod1+r gaps bottom current minus 2 + + # Escaping + bindsym Shift+end mode "default" + bindsym Ctrl+k mode "default" + bindsym Ctrl+bracketleft mode "default" + bindsym Escape mode "default" +} + +#╔─────────────────────────────────────────────────────────────────────────────╗ +#│ Wδrksραcεs | +#╚─────────────────────────────────────────────────────────────────────────────╝ +bindsym $mod1+bracketleft workspace prev +bindsym $mod1+bracketright workspace next + +bindsym $mod2+a workspace prev +bindsym $mod2+d workspace next + +bindsym $mod1+1 workspace number 1 +bindsym $mod1+2 workspace number 2 +bindsym $mod1+3 workspace number 3 +bindsym $mod1+4 workspace number 4 +bindsym $mod1+5 workspace number 5 +bindsym $mod1+6 workspace number 6 +bindsym $mod1+7 workspace number 7 +bindsym $mod1+8 workspace number 8 +bindsym $mod1+9 workspace number 9 + +# Advanced workspace tuning ==== +bindsym $mod1+f mode "workspace_tuning" + +mode "workspace_tuning" { + + # Move focused container to workspace + bindsym 1 move container to workspace number 1 + bindsym 2 move container to workspace number 2 + bindsym 3 move container to workspace number 3 + bindsym 4 move container to workspace number 4 + bindsym 5 move container to workspace number 5 + bindsym 6 move container to workspace number 6 + bindsym 7 move container to workspace number 7 + bindsym 8 move container to workspace number 8 + bindsym 9 move container to workspace number 9 + + # Swap containers + bindsym $mod1+$pane_left mark --add "_swap", focus left, swap container with mark "_swap", focus left, unmark "_swap" + bindsym $mod1+$pane_down mark --add "_swap", focus down, swap container with mark "_swap", focus down, unmark "_swap" + bindsym $mod1+$pane_up mark --add "_swap", focus up, swap container with mark "_swap", focus up, unmark "_swap" + bindsym $mod1+$pane_right mark --add "_swap", focus right, swap container with mark "_swap", focus right, unmark "_swap" + + # Stack, Tab, Rotate + bindsym s layout stacking + bindsym t layout tabbed + bindsym r layout toggle split + + bindsym f fullscreen + bindsym Shift+f fullscreen + + # Escaping + bindsym $mod1+e mode "resize" + + bindsym Shift+end mode "default" + bindsym Ctrl+k mode "default" + bindsym Ctrl+bracketleft mode "default" + bindsym Escape mode "default" +} + +#╔─────────────────────────────────────────────────────────────────────────────╗ +#│ Iηρμτ αηd δμτρμτ αdjμsτmεητs | +#╚─────────────────────────────────────────────────────────────────────────────╝ +# Volume controls +bindsym XF86AudioRaiseVolume exec $volume_command 4 $volume_change_sound +bindsym XF86AudioLowerVolume exec $volume_command -4 $volume_change_sound +bindsym shift+XF86AudioRaiseVolume exec $volume_command 1 $volume_change_sound +bindsym shift+XF86AudioLowerVolume exec $volume_command -1 $volume_change_sound +bindsym XF86AudioMute exec ~/.config/sway/toggle_mute.sh + +# External brightness control + # For laptops +bindsym XF86MonBrightnessUp exec light -A 1 +bindsym XF86MonBrightnessDown exec light -U 1 + # For external monitors +bindsym XF86AudioNext exec ddcutil setvcp 10 + 4 +bindsym XF86AudioPrev exec ddcutil setvcp 10 - 4 +bindsym F10 exec ddcutil setvcp 10 + 1 +bindsym F8 exec ddcutil setvcp 10 - 1 +bindsym XF86Back exec ddcutil setvcp 12 + 2 +bindsym Menu exec ddcutil setvcp 12 - 2 + +#╔─────────────────────────────────────────────────────────────────────────────╗ +#│ Hδτkεy dαεmδη | +#╚─────────────────────────────────────────────────────────────────────────────╝ + # Terminal +bindsym $mod2+Return exec $term + # Chromium +bindsym $mod2+n exec chromium + # Firefox +bindsym $mod2+p exec MOZ_ENABLE_WAYLAND=1 firefox --private-window + # App launcher, like spotlight +bindsym $mod1+space exec $menu + # Reload sway config +bindsym $mod1+ctrl+s reload + # Close window +bindsym $mod2+q kill + # Dragging windows +#floating_modifier $mod1 normal +floating_modifier $mod1 + # Screenlock +bindsym $mod2+i exec ~/.config/i3/brightness_lock.sh + # Scratchpad +bindsym $mod1+Shift+minus move scratchpad +bindsym $mod1+minus scratchpad show + # Warpd - Keyboard-driven mouse +bindsym Mod4+Control+j exec warpd --hint +bindsym Mod4+Control+n exec warpd --normal + # Xremap +exec sudo ~/.configs_pointer/bin/switch_keyboard.sh pc + # IME Module +bindsym $mod1+i exec ~/.config/sway/toggle_fcitx.sh + # Mako notifications +exec mako + # Exit sway (hit 3 times repeatedly to force exit) +bindsym $mod2+Escape exec ~/.config/sway/sway_exit.sh + +exec --no-startup-id feh --bg-fill ~/.configs_pointer/sway/default_wallpaper.png + +client.focused #F4A66E #F4A66E #000000 #F4A66E #F4A66E + +# Screenshots ==== +bindsym $mod2+6 mode "screenshots" +bindsym $mod2+5 mode "screenshots"; exec $screenshot_script take select -c $screenshot_tmp && $screenshot_sound +bindsym $mod2+4 mode "screenshots"; exec $screenshot_script take exact "$(~/.config/sway/window_dimensions.py)" && \ + $screenshot_script edit -c -q 100 -d "$(colo.sh -t)" -e png --overwrite $screenshot_tmp && $screenshot_sound + +mode "screenshots" { + # Screenshooting in processing "steps" + # 1. Get screenshot + bindsym a exec $screenshot_script take select -c $screenshot_tmp && $screenshot_sound + bindsym f exec $screenshot_script take full -c $screenshot_tmp && $screenshot_sound + bindsym m mode "default"; exec $screenshot_script markup -c $screenshot_tmp && $screenshot_sound + bindsym g mode "default"; exec gimp $($screenshot_script markup --show-latest) + + # 2. Downsize the screenshot, since 4k is too big + bindsym 1 exec $screenshot_script edit -c -e png -q '40%' -r '50%' $screenshot_tmp && $screenshot_sound + bindsym 2 exec $screenshot_script edit -c -e png -q '40%' -r '60%' $screenshot_tmp && $screenshot_sound + bindsym 3 exec $screenshot_script edit -c -e png -q '40%' -r '70%' $screenshot_tmp && $screenshot_sound + bindsym 4 exec $screenshot_script edit -c -e png -q '40%' -r '80%' $screenshot_tmp && $screenshot_sound + bindsym 5 exec $screenshot_script edit -c -e png -q '80%' -r '50%' $screenshot_tmp && $screenshot_sound + bindsym 6 exec $screenshot_script edit -c -e png -q '80%' -r '60%' $screenshot_tmp && $screenshot_sound + bindsym 7 exec $screenshot_script edit -c -e png -q '80%' -r '70%' $screenshot_tmp && $screenshot_sound + bindsym 8 exec $screenshot_script edit -c -e png -q '80%' -r '80%' $screenshot_tmp && $screenshot_sound + bindsym 9 exec $screenshot_script edit -c -e png -q '80%' -r '90%' $screenshot_tmp && $screenshot_sound + bindsym 0 exec $screenshot_script edit -c -e png -q '80%' -r '100%' $screenshot_tmp && $screenshot_sound + + # OR compress image + bindsym space mode "shrink_screenshots" + + # 3. Copy the smaller image back to clipboard and return to default + bindsym return mode "default" + bindsym Shift+end mode "default" + bindsym Ctrl+k mode "default" + bindsym Ctrl+bracketleft mode "default" + bindsym Escape mode "default" +} + +mode "shrink_screenshots" { + # 2. Compress the screenshot, hard + bindsym 1 exec $screenshot_script edit -c -e webp -q '40%' -r '50%' $screenshot_tmp && $screenshot_sound + bindsym 2 exec $screenshot_script edit -c -e webp -q '40%' -r '60%' $screenshot_tmp && $screenshot_sound + bindsym 3 exec $screenshot_script edit -c -e webp -q '40%' -r '70%' $screenshot_tmp && $screenshot_sound + bindsym 4 exec $screenshot_script edit -c -e webp -q '40%' -r '80%' $screenshot_tmp && $screenshot_sound + bindsym 5 exec $screenshot_script edit -c -e webp -q '80%' -r '50%' $screenshot_tmp && $screenshot_sound + bindsym 6 exec $screenshot_script edit -c -e webp -q '80%' -r '60%' $screenshot_tmp && $screenshot_sound + bindsym 7 exec $screenshot_script edit -c -e webp -q '80%' -r '70%' $screenshot_tmp && $screenshot_sound + bindsym 8 exec $screenshot_script edit -c -e webp -q '80%' -r '80%' $screenshot_tmp && $screenshot_sound + bindsym 9 exec $screenshot_script edit -c -e webp -q '80%' -r '90%' $screenshot_tmp && $screenshot_sound + bindsym 0 exec $screenshot_script edit -c -e webp -q '80%' -r '100%' $screenshot_tmp && $screenshot_sound + + bindsym space mode "screenshots" + + # 3. Back out + bindsym return mode "default" + bindsym Shift+end mode "default" + bindsym Ctrl+k mode "default" + bindsym Ctrl+bracketleft mode "default" + bindsym Escape mode "default" +} + +# Global mpv ==== +bindsym $mod2+m mode "mpv_global" + +mode "mpv_global" { + bindsym j exec ~/.config/sway/mpv_keys.sh 'j' + bindsym k exec ~/.config/sway/mpv_keys.sh 'k' + bindsym l exec ~/.config/sway/mpv_keys.sh 'l' + bindsym Left exec ~/.config/sway/mpv_keys.sh 'LEFT' + bindsym Right exec ~/.config/sway/mpv_keys.sh 'RIGHT' + bindsym space exec ~/.config/sway/mpv_keys.sh 'SPACE' + bindsym bracketleft exec ~/.config/sway/mpv_keys.sh '[' + bindsym bracketright exec ~/.config/sway/mpv_keys.sh ']' + bindsym minus exec ~/.config/sway/mpv_keys.sh '-' + bindsym equal exec ~/.config/sway/mpv_keys.sh '=' + bindsym m exec ~/.config/sway/mpv_keys.sh 'm' + bindsym A exec ~/.config/sway/mpv_keys.sh 'A' + + bindsym return mode "default" + bindsym Ctrl+k mode "default" + bindsym Shift+End mode "default" + bindsym Escape mode "default" +} + +#╔─────────────────────────────────────────────────────────────────────────────╗ +#│ Sτylεs | +#╚─────────────────────────────────────────────────────────────────────────────╝ +bar { + #status_command i3status + i3bar_command i3bar --transparency + position bottom + + binding_mode_indicator yes + + # Vertical horizontal + #gaps 0 10 + + mode dock + + status_command while ~/.config/sway/swaybar_status.sh; do sleep 60; done + + colors { + # Foreground color + statusline #e7d6ad + background #22222200 + + # border background text + focused_workspace #F4B36Eff #F4B36Eff #000000 + inactive_workspace #00000000 #444444 #e7d6ad + urgent_workspace #fadb2f #fadb2f #000000 + + binding_mode #8ec07c #8ec07c #000000 + binding_mode #8ec07c #d3869b #000000 + binding_mode #fadb2f #fadb2f #000000 + } +} diff --git a/install.sh b/install.sh index 2577720..9120c8f 100755 --- a/install.sh +++ b/install.sh @@ -35,6 +35,7 @@ declare -r UNIX_CONFIGS=(\ ) \ LINUX_ONLY=(\ # Swayland + i3 ~/.config/i3 sway ~/.config/sway swaylock ~/.config/swaylock xremap ~/.config/xremap diff --git a/sway/window_dimensions.py b/sway/window_dimensions.py index aca3ee4..bfc8800 100755 --- a/sway/window_dimensions.py +++ b/sway/window_dimensions.py @@ -6,33 +6,37 @@ # The printed dimensions will match the slup regex below: # /[0-9]+,[0-9]+ [0-9]+x[0-9]+/ import json +import os from subprocess import run, PIPE from typing import Optional + def get_sway_tree() -> dict: - swaymsg = run(["swaymsg", "-t", "get_tree"], stdout=PIPE) + swaymsg = run([MSG_COMMAND, "-t", "get_tree"], stdout=PIPE) swaymsg.check_returncode() return json.loads(swaymsg.stdout) + def format_window_coords(window: dict, rect: dict) -> str: if sum(window.values()) == 0: # Multiple windows selected - x = rect['x'] - y = rect['y'] - w = rect['width'] - h = rect['height'] - elif window['y'] + window['height'] == rect['height']: # Account for border - x = rect['x'] + window['x'] - y = rect['y'] - w = window['width'] - h = window['height'] - window['y'] + x = rect["x"] + y = rect["y"] + w = rect["width"] + h = rect["height"] + elif window["y"] + window["height"] == rect["height"]: # Account for border + x = rect["x"] + window["x"] + y = rect["y"] + w = window["width"] + h = window["height"] - window["y"] else: - x = rect['x'] + window['x'] - y = rect['y'] + window['y'] - w = window['width'] - h = window['height'] + x = rect["x"] + window["x"] + y = rect["y"] + window["y"] + w = window["width"] + h = window["height"] return f"{x},{y} {w}x{h}" + # Return dict indexing path to the first entry with "key" matching "val" # For example, it may return # ['nodes', 1, 'nodes', 1, 'nodes', 0, 'nodes', 0] @@ -57,17 +61,27 @@ def trace_json_path(js, find_key, find_val) -> Optional[list]: return None + def focused_sway_area(): tree = get_sway_tree() - trace = trace_json_path(tree, 'focused', True) + trace = trace_json_path(tree, "focused", True) if trace is None: - print('No focused window was found') + print("No focused window was found") exit(1) for i in trace: tree = tree[i] - return format_window_coords(tree['window_rect'], tree['rect']) + return format_window_coords(tree["window_rect"], tree["rect"]) + + +if __name__ == "__main__": + try: + if "wayland" in os.environ["XDG_SESSION_TYPE"]: + MSG_COMMAND = "swaymsg" + else: + MSG_COMMAND = "i3-msg" + except KeyError: + MSG_COMMAND = "i3-msg" -if __name__ == '__main__': print(focused_sway_area())