summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.Xresources91
l---------.bash_profile1
-rw-r--r--.bashrc57
-rwxr-xr-x.config/dunst/dunstrc457
-rw-r--r--.config/fastfetch/config.jsonc127
-rw-r--r--.config/gtk-3.0/settings.ini4
-rw-r--r--.config/i3/config166
-rw-r--r--.config/i3status/config62
-rwxr-xr-x.config/mpd/mpd.conf54
-rw-r--r--.config/mpv/input.conf23
-rw-r--r--.config/mpv/mpv.conf33
-rw-r--r--.config/mpv/script-opts/crop.conf34
-rw-r--r--.config/mpv/script-opts/encode_webm.conf39
-rw-r--r--.config/mpv/scripts/autoload.lua225
-rw-r--r--.config/mpv/scripts/crop.lua434
-rw-r--r--.config/mpv/scripts/encode.lua315
-rw-r--r--.config/mpv/scripts/exclude.lua10
-rw-r--r--.config/ncmpcpp/bindings543
-rw-r--r--.config/ncmpcpp/config41
-rwxr-xr-x.config/nnn/nnn.bash5
-rwxr-xr-x.config/nsxiv/exec/key-handler11
-rw-r--r--.config/pipewire/pipewire.conf269
-rw-r--r--.config/shell/aliasrc87
-rw-r--r--.config/shell/profile45
-rw-r--r--.config/tmux/tmux.conf91
-rw-r--r--.config/wireplumber/wireplumber.conf.d/51-camera-microphone-input-rename.conf15
-rw-r--r--.config/wireplumber/wireplumber.conf.d/51-soundblaster-output-rename.conf16
-rw-r--r--.config/wireplumber/wireplumber.conf.d/51-starshipmatisse-output-rename.conf18
-rw-r--r--.config/wireplumber/wireplumber.conf.d/52-soundblaster-input-disable.conf15
-rw-r--r--.config/wireplumber/wireplumber.conf.d/52-starshipmatisse-input-disable.conf15
-rw-r--r--.config/x11/xinitrc32
-rw-r--r--.config/yt-dlp/config1
-rw-r--r--.config/zathura/zathurarc29
-rw-r--r--.ratpoisonrc108
-rw-r--r--.vim/vimrc90
l---------.xinitrc1
36 files changed, 3564 insertions, 0 deletions
diff --git a/.Xresources b/.Xresources
new file mode 100644
index 0000000..af21ba2
--- /dev/null
+++ b/.Xresources
@@ -0,0 +1,91 @@
+XCursor.theme: Adwaita
+
+URxvt.preeditType:Root
+! input method
+URxvt.imLocale: ja_JP.utf8
+URxvt.imFont: xft:WenQuanYi Micro Hei Mono:size=12
+URxvt.inputMethod: ibus
+
+URxvt.*font: xft:Iosevka Term Custom:style=Regular:size=11:antialias=True,xft:Joypixels:pixelsize=10
+
+!! true transparency
+!!URxvt.depth: 32
+!!URxvt.background: [90]#000000
+
+!! no transparency
+! light
+! URxvt.background: #eeeeee
+! URxvt.foreground: #444444
+! dark
+URxvt.background: #000000
+URxvt.foreground: #FFFFFF
+
+!! visuals and colors
+/* URxvt.letterSpace: 0 */
+URxvt.letterSpace: 2
+URxvt.scrollBar: False
+URxvt.externalBorder: 0
+URxvt.internalBorder: 0
+URxvt.backgroundBorder: 0
+URxvt.borderLess: False
+!! colors for dracula theme
+!! black
+URxvt.color0: #000000
+URxvt.color8: #343636
+!! red
+URxvt.color1: #e74c7c
+URxvt.color9: #c26f6f
+!! green
+URxvt.color2: #6bb05d
+URxvt.color10: #8dc776
+!! yellow
+URxvt.color3: #e59e67
+URxvt.color11: #e7ac7e
+!! indigo
+URxvt.color4: #5b98a9
+URxvt.color12: #7ab3c3
+!! lavender
+URxvt.color5: #b185db
+URxvt.color13: #bb84e5
+!! cyan
+URxvt.color6: #51a39f
+URxvt.color14: #6db0ad
+!! white
+URxvt.color7: #c4c4c4
+URxvt.color15: #cccccc
+
+!! colors for papercolor-theme
+! !! white and very light gray
+! URxvt.color0: #eeeeee
+! URxvt.color8: #bcbcbc
+! !! red
+! URxvt.color1: #af0000
+! URxvt.color9: #d70000
+! !! dark green and magenta
+! URxvt.color2: #008700
+! URxvt.color10: #d70087
+! !! murky green and purple
+! URxvt.color3: #5f8700
+! URxvt.color11: #8700af
+! !! jeans blue and orange
+! URxvt.color4: #0087af
+! URxvt.color12: #d75f00
+! !! gray and orange
+! URxvt.color5: #878787
+! URxvt.color13: #d75f00
+! !! ocean blue
+! URxvt.color6: #005f87
+! URxvt.color14: #005faf
+! !! dark gray and ocean blue
+! URxvt.color7: #444444
+! URxvt.color15: #005f87
+
+!! perl extensions
+URxvt.perl-ext-common: default,url-select,resize-font,selection-to-clipboard,-confirm-paste
+URxvt.matcher.button: 1
+URxvt.keysym.M-u: perl:url-select:select_next
+URxvt.url-select.launcher: /usr/bin/xdg-open
+URxvt.url-select.underline: true
+
+URxvt.iso14755: False
+URxvt.iso14755_52: False
diff --git a/.bash_profile b/.bash_profile
new file mode 120000
index 0000000..8486fca
--- /dev/null
+++ b/.bash_profile
@@ -0,0 +1 @@
+.config/shell/profile \ No newline at end of file
diff --git a/.bashrc b/.bashrc
new file mode 100644
index 0000000..3b8acc8
--- /dev/null
+++ b/.bashrc
@@ -0,0 +1,57 @@
+umask 0077
+
+# If not running interactively, don't do anything
+case $- in
+ *i*) ;;
+ *) return;;
+esac
+
+stty start undef
+
+export PROMPT_DIRTRIM=2
+
+PS1="\[\033[1;33m\][\[\033[1;35m\]\u\[\033[1;32m\]@\[\033[1;36m\]\h\[\033[1;37m\]:\[\033[1;33m\]\w]\[\033[0m\] "
+
+# append to the history file, don't overwrite it
+shopt -s histappend
+
+# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
+HISTSIZE=1000
+HISTFILESIZE=2000
+
+# check the window size after each command and, if necessary,
+# update the values of LINES and COLUMNS.
+shopt -s checkwinsize
+
+# Enable globbing hidden/dot files (.filename).
+shopt -s dotglob
+
+# make less more friendly for non-text input files, see lesspipe(1)
+#[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
+
+# enable colors
+if [ -f ~/.config/dircolors ]; then
+ eval "`dircolors -b ~/.config/dircolors`"
+fi
+
+
+if [ -f ~/.config/shell/aliasrc ]; then
+ . ~/.config/shell/aliasrc
+fi
+
+if ! shopt -oq posix; then
+ if [ -f /usr/share/bash-completion/bash_completion ]; then
+ . /usr/share/bash-completion/bash_completion
+ elif [ -f /etc/bash_completion ]; then
+ . /etc/bash_completion
+ fi
+fi
+
+# fzf configs
+[ -f ~/.local/src/fzf/fzf.bash ] && . ~/.local/src/fzf/fzf.bash
+export FZF_DEFAULT_OPS="--extended"
+export FZF_DEFAULT_COMMAND="find -type f"
+export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
+
+# NNN
+[ -f ~/.config/nnn/nnn.bash ] && . ~/.config/nnn/nnn.bash
diff --git a/.config/dunst/dunstrc b/.config/dunst/dunstrc
new file mode 100755
index 0000000..ab11296
--- /dev/null
+++ b/.config/dunst/dunstrc
@@ -0,0 +1,457 @@
+# See dunst(5) for all configuration options
+
+[global]
+ ### Display ###
+
+ # Which monitor should the notifications be displayed on.
+ monitor = 0
+
+ # Display notification on focused monitor. Possible modes are:
+ # mouse: follow mouse pointer
+ # keyboard: follow window with keyboard focus
+ # none: don't follow anything
+ #
+ # "keyboard" needs a window manager that exports the
+ # _NET_ACTIVE_WINDOW property.
+ # This should be the case for almost all modern window managers.
+ #
+ # If this option is set to mouse or keyboard, the monitor option
+ # will be ignored.
+ follow = none
+
+ ### Geometry ###
+
+ # dynamic width from 0 to 300
+ # width = (0, 300)
+ # constant width of 300
+ width = (0, 300)
+
+ # The maximum height of a single notification, excluding the frame.
+ height = 300
+
+ # Position the notification in the top right corner
+ origin = top-right
+
+ # Offset from the origin
+ offset = 10x50
+
+ # Scale factor. It is auto-detected if value is 0.
+ scale = 0
+
+ # Maximum number of notification (0 means no limit)
+ notification_limit = 0
+
+ ### Progress bar ###
+
+ # Turn on the progess bar. It appears when a progress hint is passed with
+ # for example dunstify -h int:value:12
+ progress_bar = true
+
+ # Set the progress bar height. This includes the frame, so make sure
+ # it's at least twice as big as the frame width.
+ progress_bar_height = 10
+
+ # Set the frame width of the progress bar
+ progress_bar_frame_width = 1
+
+ # Set the minimum width for the progress bar
+ progress_bar_min_width = 150
+
+ # Set the maximum width for the progress bar
+ progress_bar_max_width = 300
+
+
+ # Show how many messages are currently hidden (because of
+ # notification_limit).
+ indicate_hidden = yes
+
+ # The transparency of the window. Range: [0; 100].
+ # This option will only work if a compositing window manager is
+ # present (e.g. xcompmgr, compiz, etc.). (X11 only)
+ transparency = 0
+
+ # Draw a line of "separator_height" pixel height between two
+ # notifications.
+ # Set to 0 to disable.
+ # If gap_size is greater than 0, this setting will be ignored.
+ separator_height = 2
+
+ # Padding between text and separator.
+ padding = 8
+
+ # Horizontal padding.
+ horizontal_padding = 8
+
+ # Padding between text and icon.
+ text_icon_padding = 0
+
+ # Defines width in pixels of frame around the notification window.
+ # Set to 0 to disable.
+ frame_width = 3
+
+ # Defines color of the frame around the notification window.
+ frame_color = "#aaaaaa"
+
+ # Size of gap to display between notifications - requires a compositor.
+ # If value is greater than 0, separator_height will be ignored and a border
+ # of size frame_width will be drawn around each notification instead.
+ # Click events on gaps do not currently propagate to applications below.
+ gap_size = 0
+
+ # Define a color for the separator.
+ # possible values are:
+ # * auto: dunst tries to find a color fitting to the background;
+ # * foreground: use the same color as the foreground;
+ # * frame: use the same color as the frame;
+ # * anything else will be interpreted as a X color.
+ separator_color = frame
+
+ # Sort messages by urgency.
+ sort = yes
+
+ # Don't remove messages, if the user is idle (no mouse or keyboard input)
+ # for longer than idle_threshold seconds.
+ # Set to 0 to disable.
+ # A client can set the 'transient' hint to bypass this. See the rules
+ # section for how to disable this if necessary
+ # idle_threshold = 120
+
+ ### Text ###
+
+ font = Monospace 10
+
+ # The spacing between lines. If the height is smaller than the
+ # font height, it will get raised to the font height.
+ line_height = 0
+
+ # Possible values are:
+ # full: Allow a small subset of html markup in notifications:
+ # <b>bold</b>
+ # <i>italic</i>
+ # <s>strikethrough</s>
+ # <u>underline</u>
+ #
+ # For a complete reference see
+ # <https://docs.gtk.org/Pango/pango_markup.html>.
+ #
+ # strip: This setting is provided for compatibility with some broken
+ # clients that send markup even though it's not enabled on the
+ # server. Dunst will try to strip the markup but the parsing is
+ # simplistic so using this option outside of matching rules for
+ # specific applications *IS GREATLY DISCOURAGED*.
+ #
+ # no: Disable markup parsing, incoming notifications will be treated as
+ # plain text. Dunst will not advertise that it has the body-markup
+ # capability if this is set as a global setting.
+ #
+ # It's important to note that markup inside the format option will be parsed
+ # regardless of what this is set to.
+ markup = full
+
+ # The format of the message. Possible variables are:
+ # %a appname
+ # %s summary
+ # %b body
+ # %i iconname (including its path)
+ # %I iconname (without its path)
+ # %p progress value if set ([ 0%] to [100%]) or nothing
+ # %n progress value if set without any extra characters
+ # %% Literal %
+ # Markup is allowed
+ format = "<b>%s</b>\n%b"
+
+ # Alignment of message text.
+ # Possible values are "left", "center" and "right".
+ alignment = left
+
+ # Vertical alignment of message text and icon.
+ # Possible values are "top", "center" and "bottom".
+ vertical_alignment = center
+
+ # Show age of message if message is older than show_age_threshold
+ # seconds.
+ # Set to -1 to disable.
+ show_age_threshold = 60
+
+ # Specify where to make an ellipsis in long lines.
+ # Possible values are "start", "middle" and "end".
+ ellipsize = middle
+
+ # Ignore newlines '\n' in notifications.
+ ignore_newline = no
+
+ # Stack together notifications with the same content
+ stack_duplicates = true
+
+ # Hide the count of stacked notifications with the same content
+ hide_duplicate_count = false
+
+ # Display indicators for URLs (U) and actions (A).
+ show_indicators = yes
+
+ ### Icons ###
+
+ # Recursive icon lookup. You can set a single theme, instead of having to
+ # define all lookup paths.
+ enable_recursive_icon_lookup = true
+
+ # Set icon theme (only used for recursive icon lookup)
+ icon_theme = Adwaita
+ # You can also set multiple icon themes, with the leftmost one being used first.
+ # icon_theme = "Adwaita, breeze"
+
+ # Align icons left/right/top/off
+ icon_position = left
+
+ # Scale small icons up to this size, set to 0 to disable. Helpful
+ # for e.g. small files or high-dpi screens. In case of conflict,
+ # max_icon_size takes precedence over this.
+ min_icon_size = 32
+
+ # Scale larger icons down to this size, set to 0 to disable
+ max_icon_size = 128
+
+ # Paths to default icons (only neccesary when not using recursive icon lookup)
+ icon_path = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/
+
+ ### History ###
+
+ # Should a notification popped up from history be sticky or timeout
+ # as if it would normally do.
+ sticky_history = yes
+
+ # Maximum amount of notifications kept in history
+ history_length = 20
+
+ ### Misc/Advanced ###
+
+ # dmenu path.
+ dmenu = /usr/local/bin/dmenu -p dunst:
+
+ # Browser for opening urls in context menu.
+ browser = /usr/bin/firefox
+
+ # Context menu
+ context = ctrl+shift+period
+
+ # Always run rule-defined scripts, even if the notification is suppressed
+ always_run_script = true
+
+ # Define the title of the windows spawned by dunst
+ title = Dunst
+
+ # Define the class of the windows spawned by dunst
+ class = Dunst
+
+ # Define the corner radius of the notification window
+ # in pixel size. If the radius is 0, you have no rounded
+ # corners.
+ # The radius will be automatically lowered if it exceeds half of the
+ # notification height to avoid clipping text and/or icons.
+ corner_radius = 0
+
+ # Ignore the dbus closeNotification message.
+ # Useful to enforce the timeout set by dunst configuration. Without this
+ # parameter, an application may close the notification sent before the
+ # user defined timeout.
+ ignore_dbusclose = false
+
+ ### Wayland ###
+ # These settings are Wayland-specific. They have no effect when using X11
+
+ # Uncomment this if you want to let notications appear under fullscreen
+ # applications (default: overlay)
+ # layer = top
+
+ # Set this to true to use X11 output on Wayland.
+ force_xwayland = false
+
+ ### Legacy
+
+ # Use the Xinerama extension instead of RandR for multi-monitor support.
+ # This setting is provided for compatibility with older nVidia drivers that
+ # do not support RandR and using it on systems that support RandR is highly
+ # discouraged.
+ #
+ # By enabling this setting dunst will not be able to detect when a monitor
+ # is connected or disconnected which might break follow mode if the screen
+ # layout changes.
+ force_xinerama = false
+
+ ### mouse
+
+ # Defines list of actions for each mouse event
+ # Possible values are:
+ # * none: Don't do anything.
+ # * do_action: Invoke the action determined by the action_name rule. If there is no
+ # such action, open the context menu.
+ # * open_url: If the notification has exactly one url, open it. If there are multiple
+ # ones, open the context menu.
+ # * close_current: Close current notification.
+ # * close_all: Close all notifications.
+ # * context: Open context menu for the notification.
+ # * context_all: Open context menu for all notifications.
+ # These values can be strung together for each mouse event, and
+ # will be executed in sequence.
+ mouse_left_click = close_current
+ mouse_middle_click = open_url
+ mouse_right_click = close_all
+
+# Experimental features that may or may not work correctly. Do not expect them
+# to have a consistent behaviour across releases.
+[experimental]
+ # Calculate the dpi to use on a per-monitor basis.
+ # If this setting is enabled the Xft.dpi value will be ignored and instead
+ # dunst will attempt to calculate an appropriate dpi value for each monitor
+ # using the resolution and physical size. This might be useful in setups
+ # where there are multiple screens with very different dpi values.
+ per_monitor_dpi = false
+
+
+[urgency_low]
+ # IMPORTANT: colors have to be defined in quotation marks.
+ # Otherwise the "#" and following would be interpreted as a comment.
+ background = "#222222"
+ foreground = "#888888"
+ timeout = 10
+ # Icon for notifications with low urgency, uncomment to enable
+ #default_icon = /path/to/icon
+
+[urgency_normal]
+ background = "#285577"
+ foreground = "#ffffff"
+ timeout = 10
+ # Icon for notifications with normal urgency, uncomment to enable
+ #default_icon = /path/to/icon
+
+[urgency_critical]
+ background = "#900000"
+ foreground = "#ffffff"
+ frame_color = "#ff0000"
+ timeout = 0
+ # Icon for notifications with critical urgency, uncomment to enable
+ #default_icon = /path/to/icon
+
+# Every section that isn't one of the above is interpreted as a rules to
+# override settings for certain messages.
+#
+# Messages can be matched by
+# appname (discouraged, see desktop_entry)
+# body
+# category
+# desktop_entry
+# icon
+# match_transient
+# msg_urgency
+# stack_tag
+# summary
+#
+# and you can override the
+# background
+# foreground
+# format
+# frame_color
+# fullscreen
+# new_icon
+# set_stack_tag
+# set_transient
+# set_category
+# timeout
+# urgency
+# icon_position
+# skip_display
+# history_ignore
+# action_name
+# word_wrap
+# ellipsize
+# alignment
+# hide_text
+#
+# Shell-like globbing will get expanded.
+#
+# Instead of the appname filter, it's recommended to use the desktop_entry filter.
+# GLib based applications export their desktop-entry name. In comparison to the appname,
+# the desktop-entry won't get localized.
+#
+# SCRIPTING
+# You can specify a script that gets run when the rule matches by
+# setting the "script" option.
+# The script will be called as follows:
+# script appname summary body icon urgency
+# where urgency can be "LOW", "NORMAL" or "CRITICAL".
+#
+# NOTE: It might be helpful to run dunst -print in a terminal in order
+# to find fitting options for rules.
+
+# Disable the transient hint so that idle_threshold cannot be bypassed from the
+# client
+#[transient_disable]
+# match_transient = yes
+# set_transient = no
+#
+# Make the handling of transient notifications more strict by making them not
+# be placed in history.
+#[transient_history_ignore]
+# match_transient = yes
+# history_ignore = yes
+
+# fullscreen values
+# show: show the notifications, regardless if there is a fullscreen window opened
+# delay: displays the new notification, if there is no fullscreen window active
+# If the notification is already drawn, it won't get undrawn.
+# pushback: same as delay, but when switching into fullscreen, the notification will get
+# withdrawn from screen again and will get delayed like a new notification
+#[fullscreen_delay_everything]
+# fullscreen = delay
+#[fullscreen_show_critical]
+# msg_urgency = critical
+# fullscreen = show
+
+#[espeak]
+# summary = "*"
+# script = dunst_espeak.sh
+
+#[script-test]
+# summary = "*script*"
+# script = dunst_test.sh
+
+#[ignore]
+# # This notification will not be displayed
+# summary = "foobar"
+# skip_display = true
+
+#[history-ignore]
+# # This notification will not be saved in history
+# summary = "foobar"
+# history_ignore = yes
+
+#[skip-display]
+# # This notification will not be displayed, but will be included in the history
+# summary = "foobar"
+# skip_display = yes
+
+#[signed_on]
+# appname = Pidgin
+# summary = "*signed on*"
+# urgency = low
+#
+#[signed_off]
+# appname = Pidgin
+# summary = *signed off*
+# urgency = low
+#
+#[says]
+# appname = Pidgin
+# summary = *says*
+# urgency = critical
+#
+#[twitter]
+# appname = Pidgin
+# summary = *twitter.com*
+# urgency = normal
+#
+#[stack-volumes]
+# appname = "some_volume_notifiers"
+# set_stack_tag = "volume"
+#
+# vim: ft=cfg
diff --git a/.config/fastfetch/config.jsonc b/.config/fastfetch/config.jsonc
new file mode 100644
index 0000000..676c183
--- /dev/null
+++ b/.config/fastfetch/config.jsonc
@@ -0,0 +1,127 @@
+// Modified from: https://github.com/fastfetch-cli/fastfetch/pull/1025#issuecomment-2177566138
+{
+ "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
+ // "logo": {
+ // "color": {
+ // "1": "#dc0075",
+ // "2": "#8a8a8a"
+ // },
+ // },
+ "display": {
+ "color": {
+ "separator": "blue"
+ },
+ "separator": " | ",
+ "constants": [
+ ">-----------<+>------------------------------------<"
+ ]
+ },
+ "modules": [
+ "title",
+ {
+ "type": "custom",
+ "format": "{$1}",
+ "outputColor": "separator",
+ },
+ {
+ "type": "os",
+ "key": " OS ",
+ "keyColor": "green"
+ },
+ {
+ "type": "kernel",
+ "key": " Kernel ",
+ "keyColor": "green"
+ },
+ {
+ "type": "uptime",
+ "key": " Uptime ",
+ "keyColor": "green"
+ },
+ {
+ "type": "wm",
+ "key": " WM ",
+ "keyColor": "green",
+ },
+ {
+ "type": "shell",
+ "key": " Shell ",
+ "keyColor": "green"
+ },
+ {
+ "type": "terminal",
+ "key": " Terminal ",
+ "keyColor": "green"
+ },
+ {
+ "type": "packages",
+ "key": " Packages ",
+ "keyColor": "green"
+ },
+ {
+ "type": "custom",
+ "format": "{$1}",
+ "outputColor": "separator"
+ },
+ {
+ "type": "Monitor",
+ "key": " Monitor ",
+ "format": "{1}",
+ "keyColor": "cyan"
+ },
+ {
+ "type": "cpu",
+ "key": " CPU ",
+ "keyColor": "cyan"
+ },
+ {
+ "type": "gpu",
+ "key": " GPU ",
+ "keyColor": "cyan"
+ },
+ {
+ "type": "memory",
+ "key": " RAM ",
+ "keyColor": "cyan"
+ },
+ {
+ "type": "swap",
+ "key": " SWAP ",
+ "keyColor": "cyan"
+ },
+ {
+ "type": "disk",
+ "key": " (root) ",
+ "folders": "/",
+ "keyColor": "cyan"
+ },
+ {
+ "type": "disk",
+ "key": " (home) ",
+ "folders": "/home",
+ "keyColor": "cyan"
+ },
+ {
+ "type": "disk",
+ "key": " (hdd1) ",
+ "folders": "/media/hdd",
+ "keyColor": "cyan"
+ },
+ {
+ "type": "disk",
+ "key": " (hdd2) ",
+ "folders": "/media/seagate",
+ "keyColor": "cyan"
+ },
+ {
+ "type": "custom",
+ "format": "{$1}",
+ "outputColor": "separator"
+ },
+ "break",
+ {
+ "type": "colors",
+ "paddingLeft": 15
+ }
+ ]
+}
diff --git a/.config/gtk-3.0/settings.ini b/.config/gtk-3.0/settings.ini
new file mode 100644
index 0000000..3bb5a50
--- /dev/null
+++ b/.config/gtk-3.0/settings.ini
@@ -0,0 +1,4 @@
+[Settings]
+gtk-key-theme-name = Emacs
+gtk-icon-theme-name = SE98
+gtk-cursor-theme-name = Adwaita
diff --git a/.config/i3/config b/.config/i3/config
new file mode 100644
index 0000000..297d369
--- /dev/null
+++ b/.config/i3/config
@@ -0,0 +1,166 @@
+# i3 config file (v4)
+#
+# Please see https://i3wm.org/docs/userguide.html for a complete reference!
+
+set $mod Mod4
+
+# Font for window titles. Will also be used by the bar unless a different font
+# is used in the bar {} block below.
+font pango:Iosevka Term Custom 10
+
+for_window [class="mpdinfo"] floating enable
+for_window [class="lyrics"] floating enable
+for_window [class="Pcmanfm"] floating enable
+for_window [class="Tk"] floating enable
+for_window [class="MVis"] floating enable
+for_window [title="Friends List"] floating enable
+
+# no_focus [all]
+# focus_on_window_activation none
+
+focus_follows_mouse yes
+# control mouse with keyboard
+bindsym Mod1+Control+g exec --no-startup-id xdotool click --clearmodifiers 1
+bindsym Mod1+Control+r exec --no-startup-id xdotool click --clearmodifiers 3
+bindsym Mod1+Control+t exec --no-startup-id xdotool mousemove_relative -- 0 20
+bindsym Mod1+Control+c exec --no-startup-id xdotool mousemove_relative -- 0 -20
+bindsym Mod1+Control+h exec --no-startup-id xdotool mousemove_relative -- -20 0
+bindsym Mod1+Control+n exec --no-startup-id xdotool mousemove_relative -- 20 0
+bindsym Mod1+Control+z exec --no-startup-id xdotool mousemove 3200 2104
+
+# Use wpctl to adjust volume in Pipewire.
+set $refresh_i3status killall -SIGUSR1 i3status
+bindsym XF86AudioRaiseVolume exec --no-startup-id wpctl set-volume @DEFAULT_SINK@ 5%+ && $refresh_i3status
+bindsym XF86AudioLowerVolume exec --no-startup-id wpctl set-volume @DEFAULT_SINK@ 5%- && $refresh_i3status
+bindsym XF86AudioMute exec --no-startup-id wpctl set-mute @DEFAULT_SINK@ toggle && $refresh_i3status
+bindsym XF86AudioMicMute exec --no-startup-id wpctl set-mute @DEFAULT_SOURCE@ toggle && $refresh_i3status
+bindsym XF86AudioNext exec --no-startup-id mpc next
+bindsym XF86AudioPrev exec --no-startup-id mpc prev
+bindsym XF86AudioPlay exec --no-startup-id mpc toggle
+
+# # Use Mouse+$mod to drag floating windows to their wanted position
+floating_modifier $mod
+
+# # move tiling windows via drag & drop by left-clicking into the title bar,
+# # or left-clicking anywhere into the window while holding the floating modifier.
+tiling_drag modifier titlebar
+
+# start a terminal
+bindsym $mod+Return exec urxvtc
+
+# start dmenu
+bindsym $mod+p exec --no-startup-id dmenu_run -fn "Hack Nerd Font:size=11" -nb "#222222" -nf "#bbbbbb" -sb "#005577" -sf "#eeeeee"
+
+# kill focused window
+bindsym $mod+Shift+C kill
+
+# application launchers
+
+bindsym $mod+Shift+F exec firefox -P default-release
+bindsym $mod+Shift+B exec firefox -P less-strict
+bindsym $mod+Shift+E exec emacsclient -c -a ""
+bindsym $mod+Shift+M exec mpdinfo
+bindsym $mod+Control+b exec bm -y
+bindsym $mod+b exec bm -o
+bindsym $mod+m exec mpdmenu
+bindsym $mod+z exec boomer
+bindsym --release Print exec scrot -u -e 'mv $f ~/pics/'
+bindsym --release $mod+Print exec scrot -s -e 'mv $f ~/pics/'
+
+# change focus
+bindsym $mod+h focus left
+bindsym $mod+j focus down
+bindsym $mod+k focus up
+bindsym $mod+l focus right
+
+# move focused window
+bindsym $mod+Shift+H move left
+bindsym $mod+Shift+J move down
+bindsym $mod+Shift+K move up
+bindsym $mod+Shift+L move right
+
+bindsym $mod+Shift+Control+X exec ~/.local/bin/sw-con
+
+# split in vertical orientation (i3 names these reversed for some reason)
+bindsym $mod+v split h
+
+# split in horizontal orientation
+bindsym $mod+s split v
+
+# enter fullscreen mode for the focused container
+bindsym $mod+f fullscreen toggle
+
+# change container layout (stacked, tabbed, toggle split)
+bindsym $mod+comma layout stacking
+bindsym $mod+period layout toggle split
+bindsym $mod+t layout tabbed
+
+workspace_layout tabbed
+
+# toggle tiling / floating
+bindsym $mod+Shift+space floating toggle
+
+# change focus between tiling / floating windows
+bindsym $mod+space focus mode_toggle
+
+# focus the parent container
+bindsym $mod+a focus parent
+
+# focus the child container
+bindsym $mod+d focus child
+
+# Define names for default workspaces for which we configure key bindings later on.
+# We use variables to avoid repeating the names in multiple places.
+set $ws1 "1"
+set $ws2 "2"
+set $ws3 "3"
+set $ws4 "4"
+
+# switch to workspace
+bindsym $mod+1 workspace number $ws1
+bindsym $mod+2 workspace number $ws2
+bindsym $mod+3 workspace number $ws3
+bindsym $mod+4 workspace number $ws4
+
+# move focused container to workspace
+bindsym $mod+Shift+1 move container to workspace number $ws1
+bindsym $mod+Shift+2 move container to workspace number $ws2
+bindsym $mod+Shift+3 move container to workspace number $ws3
+bindsym $mod+Shift+4 move container to workspace number $ws4
+
+# reload the configuration file
+bindsym $mod+Shift+O reload
+# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
+bindsym $mod+Shift+R restart
+# exit i3 (logs you out of your X session)
+bindsym $mod+Shift+Control+Q exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"
+
+# resize window (you can also use the mouse for that)
+mode "resize" {
+ # These bindings trigger as soon as you enter the resize mode
+
+ # Pressing left will shrink the window’s width.
+ # Pressing right will grow the window’s width.
+ # Pressing up will shrink the window’s height.
+ # Pressing down will grow the window’s height.
+ bindsym h resize shrink width 10 px or 10 ppt
+ bindsym j resize grow height 10 px or 10 ppt
+ bindsym k resize shrink height 10 px or 10 ppt
+ bindsym l resize grow width 10 px or 10 ppt
+
+ # back to normal: Enter or Escape or $mod+r
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+ bindsym $mod+r mode "default"
+}
+
+bindsym $mod+r mode "resize"
+
+# Start i3bar to display a workspace bar (plus the system information i3status
+# finds out, if available)
+bar {
+ font pango:Hack Nerd Font 9
+ position top
+ tray_output primary
+ status_command i3status
+}
diff --git a/.config/i3status/config b/.config/i3status/config
new file mode 100644
index 0000000..8fd3248
--- /dev/null
+++ b/.config/i3status/config
@@ -0,0 +1,62 @@
+# i3status configuration file.
+# see "man i3status" for documentation.
+
+# It is important that this file is edited as UTF-8.
+# The following line should contain a sharp s:
+# ß
+# If the above line is not correctly displayed, fix your editor first!
+
+general {
+ colors = true
+ interval = 2
+}
+
+order += "ethernet br0"
+order += "ethernet tun0"
+order += "ethernet wg0"
+order += "cpu_temperature 0"
+order += "disk /"
+order += "load"
+order += "memory"
+order += "volume master"
+order += "tztime local"
+
+ethernet br0 {
+ format_up = "E: %ip"
+ format_down = "E: down"
+}
+
+ethernet wg0 {
+ format_up = "WG: %ip"
+ format_down = "WG: down"
+}
+
+cpu_temperature 0 {
+ format = "tea: %degrees °C"
+ path = "/sys/class/hwmon/hwmon2/temp1_input"
+}
+
+disk "/" {
+ format = "porn: %used"
+}
+
+load {
+ format = "hot loads: %1min"
+}
+
+memory {
+ memory_used_method = "memavailable"
+ format = "memory: %used/%total"
+ threshold_degraded = "3GB"
+ format_degraded = "MEMORY < %available"
+}
+
+volume master {
+ format = "♪: %volume"
+ format_muted = "♪: muted (%volume)"
+ device = "default"
+}
+
+tztime local {
+ format = "%A, %F %H:%M "
+}
diff --git a/.config/mpd/mpd.conf b/.config/mpd/mpd.conf
new file mode 100755
index 0000000..ee0daa6
--- /dev/null
+++ b/.config/mpd/mpd.conf
@@ -0,0 +1,54 @@
+bind_to_address "0.0.0.0"
+# bind_to_address "/run/mpd/socket"
+
+music_directory "/media/hdd/music"
+playlist_directory "~/.config/mpd/playlists"
+db_file "~/.config/mpd/database"
+log_file "~/.config/mpd/log"
+pid_file "~/.config/mpd/pid"
+state_file "~/.config/mpd/mpdstate"
+
+# default local output
+audio_output {
+ type "pulse"
+ name "Default"
+}
+
+# http streaming
+#audio_output {
+# type "httpd"
+# name "HTTP Stream"
+# encoder "vorbis" # optional, vorbis or lame
+# port "8000"
+# quality "8.0" # do not define if bitrate is defined
+# bitrate "256" # do not define if quality is defined
+# format "44100:16:1"
+# always_on "yes"
+# tags "yes"
+#}
+
+# visualizer output
+audio_output {
+ type "fifo"
+ name "Visualizer feed"
+ path "/tmp/mpd.fifo"
+ format "44100:16:2"
+}
+
+input {
+ enabled "no"
+ plugin "qobuz"
+}
+
+input {
+ enabled "no"
+ plugin "tidal"
+}
+
+decoder {
+ enabled "no"
+ plugin "wildmidi"
+ config_file "/etc/timidity/timidity.cfg"
+}
+
+mixer_type "software"
diff --git a/.config/mpv/input.conf b/.config/mpv/input.conf
new file mode 100644
index 0000000..396711e
--- /dev/null
+++ b/.config/mpv/input.conf
@@ -0,0 +1,23 @@
+# crop.lua
+# ==========
+# start cropping
+c script-message-to crop start-crop hard
+alt+c script-message-to crop start-crop soft
+# delogo mode can be used like so
+l script-message-to crop start-crop delogo
+# remove the crop
+d vf del -1
+
+# or use the ready-made "toggle" binding
+C script-message-to crop toggle-crop hard
+
+# remove the soft zoom
+0 set video-pan-x 0; set video-pan-y 0; set video-zoom 0
+
+# encode.lua
+# ============
+# use default profile (makes vp8 webms)
+E script-message-to encode set-timestamp
+
+# use webm profile
+e script-message-to encode set-timestamp encode_webm
diff --git a/.config/mpv/mpv.conf b/.config/mpv/mpv.conf
new file mode 100644
index 0000000..8196cc7
--- /dev/null
+++ b/.config/mpv/mpv.conf
@@ -0,0 +1,33 @@
+profile=high-quality
+
+vo=gpu-next
+gpu-api=vulkan
+hwdec=vdpau
+scale=ewa_lanczossharp
+cscale=ewa_lanczossharp
+dscale=ewa_lanczossharp
+
+ao=pipewire
+pipewire-buffer=16
+audio-samplerate=192000
+audio-format=float
+
+keep-open=yes
+border=no
+no-border
+scale-antiring=0.7
+
+msg-color=yes
+term-osd-bar=yes
+cursor-autohide=1000
+
+script-opts=ytdl_hook-ytdl_path=yt-dlp
+
+screenshot-directory="~/pics/"
+screenshot-template="%F - [%P]v%#01n"
+
+[extension.webm]
+loop-file=inf
+
+[extension.gif]
+loop-file=inf
diff --git a/.config/mpv/script-opts/crop.conf b/.config/mpv/script-opts/crop.conf
new file mode 100644
index 0000000..9f75cdf
--- /dev/null
+++ b/.config/mpv/script-opts/crop.conf
@@ -0,0 +1,34 @@
+# default mode to be used if not specified in the script-message
+# can be hard, soft or delogo
+# in hard mode, a crop filter is applied to the video
+# in soft mode, video-zoom, video-pan-x and video-pan-y are modified to simulate the cropping
+# in delogo mode, the delogo filter is applied to the selected are
+# hard mode interacts nicely with encode.lua, soft mode does not
+mode=hard
+
+draw_crosshair=yes
+draw_text=yes
+draw_shade=yes
+draw_frame=yes
+frame_border_width=2
+frame_border_color=EEEEEE
+# hexadecimal: 00 is opaque, FF is transparent
+shade_opacity=77
+mouse_support=yes
+
+# movement is defined in pixels in the window
+# which explains the mismatch with the text (in video space)
+coarse_movement=30
+left_coarse=LEFT
+right_coarse=RIGHT
+up_coarse=UP
+down_coarse=DOWN
+fine_movement=1
+left_fine=ALT+LEFT
+right_fine=ALT+RIGHT
+up_fine=ALT+UP
+down_fine=ALT+DOWN
+
+# these two options accept comma separated list of keys
+accept=ENTER,MOUSE_BTN0
+cancel=ESC
diff --git a/.config/mpv/script-opts/encode_webm.conf b/.config/mpv/script-opts/encode_webm.conf
new file mode 100644
index 0000000..f6d661c
--- /dev/null
+++ b/.config/mpv/script-opts/encode_webm.conf
@@ -0,0 +1,39 @@
+# if yes, only encode the currently active tracks
+# for example, mute the player / hide the subtitles if you don't want audio / subs to be part of the extract
+only_active_tracks=no
+
+# whether to preserve some of the applied filters (crop, rotate, flip and mirror) into the extract
+# this is pretty useful in combination with crop.lua
+# note that you cannot copy video streams and apply filters at the same time
+preserve_filters=yes
+
+# apply another filter after the ones from the previous option if any
+# can be used to limit the resolution of the output, for example with
+# append_filter=scale=2*trunc(iw/max(1\,sqrt((iw*ih)/(960*540)))/2):-2
+append_filter=
+
+# additional parameters passed to ffmpeg
+codec=-c:v libvpx-vp9 -crf 1 -b:v 1M -c:a libvorbis
+
+# format of the output filename
+# Does basic interpolation on the following variables: $f, $x, $t, $s, $e, $d, $p, $n which respectively represent
+# input filename, input extension, title, start timestamp, end timestamp, duration, profile name and an incrementing number in case of conflicts
+# if the extension is not among the recognized ones, it will default to mkv
+output_format=$f_$n.webm
+
+# the directory in which to create the extract
+# empty means the same directory as the input file
+# relative paths are relative to mpv's working directory, absolute ones work like you would expect
+output_directory=~/vids/
+
+# if yes, the ffmpeg process will run detached from mpv and we won't know if it succeeded or not
+# if no, we know the result of calling ffmpeg, but we can only encode one extract at a time and mpv will block on exit
+detached=yes
+
+# executable to run when encoding (or its full path if not in PATH)
+# for example, this can be used with a wrapper script that calls ffmpeg and triggers a notification when finished
+# note that the executable gets the ffmpeg arguments as-is, and is expected to call ffmpeg itself
+ffmpeg_command=ffmpeg
+
+# if yes, print the ffmpeg call before executing it
+print=yes
diff --git a/.config/mpv/scripts/autoload.lua b/.config/mpv/scripts/autoload.lua
new file mode 100644
index 0000000..27d9d77
--- /dev/null
+++ b/.config/mpv/scripts/autoload.lua
@@ -0,0 +1,225 @@
+-- This script automatically loads playlist entries before and after the
+-- the currently played file. It does so by scanning the directory a file is
+-- located in when starting playback. It sorts the directory entries
+-- alphabetically, and adds entries before and after the current file to
+-- the internal playlist. (It stops if it would add an already existing
+-- playlist entry at the same position - this makes it "stable".)
+-- Add at most 5000 * 2 files when starting a file (before + after).
+
+--[[
+To configure this script use file autoload.conf in directory script-opts (the "script-opts"
+directory must be in the mpv configuration directory, typically ~/.config/mpv/).
+
+Example configuration would be:
+
+disabled=no
+images=no
+videos=yes
+audio=yes
+ignore_hidden=yes
+
+--]]
+
+MAXENTRIES = 5000
+
+local msg = require 'mp.msg'
+local options = require 'mp.options'
+local utils = require 'mp.utils'
+
+o = {
+ disabled = false,
+ images = true,
+ videos = true,
+ audio = true,
+ ignore_hidden = true
+}
+options.read_options(o)
+
+function Set (t)
+ local set = {}
+ for _, v in pairs(t) do set[v] = true end
+ return set
+end
+
+function SetUnion (a,b)
+ local res = {}
+ for k in pairs(a) do res[k] = true end
+ for k in pairs(b) do res[k] = true end
+ return res
+end
+
+EXTENSIONS_VIDEO = Set {
+ 'mkv', 'avi', 'mp4', 'ogv', 'webm', 'rmvb', 'flv', 'wmv', 'mpeg', 'mpg', 'm4v', '3gp'
+}
+
+EXTENSIONS_AUDIO = Set {
+ 'mp3', 'wav', 'ogm', 'flac', 'm4a', 'wma', 'ogg', 'opus'
+}
+
+EXTENSIONS_IMAGES = Set {
+ 'jpg', 'jpeg', 'png', 'tif', 'tiff', 'gif', 'webp', 'svg', 'bmp'
+}
+
+EXTENSIONS = Set {}
+if o.videos then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_VIDEO) end
+if o.audio then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_AUDIO) end
+if o.images then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_IMAGES) end
+
+function add_files_at(index, files)
+ index = index - 1
+ local oldcount = mp.get_property_number("playlist-count", 1)
+ for i = 1, #files do
+ mp.commandv("loadfile", files[i], "append")
+ mp.commandv("playlist-move", oldcount + i - 1, index + i - 1)
+ end
+end
+
+function get_extension(path)
+ match = string.match(path, "%.([^%.]+)$" )
+ if match == nil then
+ return "nomatch"
+ else
+ return match
+ end
+end
+
+table.filter = function(t, iter)
+ for i = #t, 1, -1 do
+ if not iter(t[i]) then
+ table.remove(t, i)
+ end
+ end
+end
+
+-- splitbynum and alnumcomp from alphanum.lua (C) Andre Bogus
+-- Released under the MIT License
+-- http://www.davekoelle.com/files/alphanum.lua
+
+-- split a string into a table of number and string values
+function splitbynum(s)
+ local result = {}
+ for x, y in (s or ""):gmatch("(%d*)(%D*)") do
+ if x ~= "" then table.insert(result, tonumber(x)) end
+ if y ~= "" then table.insert(result, y) end
+ end
+ return result
+end
+
+function clean_key(k)
+ k = (' '..k..' '):gsub("%s+", " "):sub(2, -2):lower()
+ return splitbynum(k)
+end
+
+-- compare two strings
+function alnumcomp(x, y)
+ local xt, yt = clean_key(x), clean_key(y)
+ for i = 1, math.min(#xt, #yt) do
+ local xe, ye = xt[i], yt[i]
+ if type(xe) == "string" then ye = tostring(ye)
+ elseif type(ye) == "string" then xe = tostring(xe) end
+ if xe ~= ye then return xe < ye end
+ end
+ return #xt < #yt
+end
+
+local autoloaded = nil
+
+function find_and_add_entries()
+ local path = mp.get_property("path", "")
+ local dir, filename = utils.split_path(path)
+ msg.trace(("dir: %s, filename: %s"):format(dir, filename))
+ if o.disabled then
+ msg.verbose("stopping: autoload disabled")
+ return
+ elseif #dir == 0 then
+ msg.verbose("stopping: not a local path")
+ return
+ end
+
+ local pl_count = mp.get_property_number("playlist-count", 1)
+ -- check if this is a manually made playlist
+ if (pl_count > 1 and autoloaded == nil) or
+ (pl_count == 1 and EXTENSIONS[string.lower(get_extension(filename))] == nil) then
+ msg.verbose("stopping: manually made playlist")
+ return
+ else
+ autoloaded = true
+ end
+
+ local pl = mp.get_property_native("playlist", {})
+ local pl_current = mp.get_property_number("playlist-pos-1", 1)
+ msg.trace(("playlist-pos-1: %s, playlist: %s"):format(pl_current,
+ utils.to_string(pl)))
+
+ local files = utils.readdir(dir, "files")
+ if files == nil then
+ msg.verbose("no other files in directory")
+ return
+ end
+ table.filter(files, function (v, k)
+ -- The current file could be a hidden file, ignoring it doesn't load other
+ -- files from the current directory.
+ if (o.ignore_hidden and not (v == filename) and string.match(v, "^%.")) then
+ return false
+ end
+ local ext = get_extension(v)
+ if ext == nil then
+ return false
+ end
+ return EXTENSIONS[string.lower(ext)]
+ end)
+ table.sort(files, alnumcomp)
+
+ if dir == "." then
+ dir = ""
+ end
+
+ -- Find the current pl entry (dir+"/"+filename) in the sorted dir list
+ local current
+ for i = 1, #files do
+ if files[i] == filename then
+ current = i
+ break
+ end
+ end
+ if current == nil then
+ return
+ end
+ msg.trace("current file position in files: "..current)
+
+ local append = {[-1] = {}, [1] = {}}
+ for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1
+ for i = 1, MAXENTRIES do
+ local file = files[current + i * direction]
+ local pl_e = pl[pl_current + i * direction]
+ if file == nil or file[1] == "." then
+ break
+ end
+
+ local filepath = dir .. file
+ if pl_e then
+ -- If there's a playlist entry, and it's the same file, stop.
+ msg.trace(pl_e.filename.." == "..filepath.." ?")
+ if pl_e.filename == filepath then
+ break
+ end
+ end
+
+ if direction == -1 then
+ if pl_current == 1 then -- never add additional entries in the middle
+ msg.info("Prepending " .. file)
+ table.insert(append[-1], 1, filepath)
+ end
+ else
+ msg.info("Adding " .. file)
+ table.insert(append[1], filepath)
+ end
+ end
+ end
+
+ add_files_at(pl_current + 1, append[1])
+ add_files_at(pl_current, append[-1])
+end
+
+mp.register_event("start-file", find_and_add_entries)
+
diff --git a/.config/mpv/scripts/crop.lua b/.config/mpv/scripts/crop.lua
new file mode 100644
index 0000000..adbfaa3
--- /dev/null
+++ b/.config/mpv/scripts/crop.lua
@@ -0,0 +1,434 @@
+local opts = {
+ mode = "hard", -- can be "hard" or "soft". If hard, apply a crop filter, if soft zoom + pan. Or a bonus "delogo" mode
+ draw_shade = true,
+ shade_opacity = "77",
+ draw_frame = false,
+ frame_border_width = 2,
+ frame_border_color = "EEEEEE",
+ draw_crosshair = true,
+ draw_text = true,
+ mouse_support = true,
+ coarse_movement = 30,
+ left_coarse = "LEFT",
+ right_coarse = "RIGHT",
+ up_coarse = "UP",
+ down_coarse = "DOWN",
+ fine_movement = 1,
+ left_fine = "ALT+LEFT",
+ right_fine = "ALT+RIGHT",
+ up_fine = "ALT+UP",
+ down_fine = "ALT+DOWN",
+ accept = "ENTER,MOUSE_BTN0",
+ cancel = "ESC",
+}
+(require 'mp.options').read_options(opts)
+
+function split(input)
+ local ret = {}
+ for str in string.gmatch(input, "([^,]+)") do
+ ret[#ret + 1] = str
+ end
+ return ret
+end
+local msg = require 'mp.msg'
+
+opts.accept = split(opts.accept)
+opts.cancel = split(opts.cancel)
+function mode_ok(mode)
+ return mode == "soft" or mode == "hard" or mode == "delogo"
+end
+if not mode_ok(opts.mode) then
+ msg.error("Invalid mode value: " .. opts.mode)
+ return
+end
+
+local assdraw = require 'mp.assdraw'
+local active = false
+local active_mode = "" -- same possible values as opts.mode
+local rect_centered = false
+local rect_keepaspect = false
+local needs_drawing = false
+local crop_first_corner = nil -- in normalized video space
+local crop_cursor = {
+ x = 0,
+ y = 0
+}
+
+function redraw()
+ needs_drawing = true
+end
+
+function rect_from_two_points(p1, p2, centered, ratio)
+ local c1 = {p1.x, p1.y}
+ local c2 = {p2.x, p2.y}
+ if ratio then
+ -- adjust position of p2, such
+ if math.abs(c2[1] - c1[1]) < ratio * math.abs(c2[2] - c1[2]) then
+ local is_left = c2[1] < c1[1] and -1 or 1
+ c2[1] = c1[1] + is_left * math.abs(c2[2] - c1[2]) * ratio
+ else
+ local is_up = c2[2] < c1[2] and -1 or 1
+ c2[2] = c1[2] + is_up * math.abs(c2[1] - c1[1]) / ratio
+ end
+ end
+ if centered then
+ -- p1 is center => convert it into corner
+ c1[1] = c1[1] - (c2[1] - c1[1])
+ c1[2] = c1[2] - (c2[2] - c1[2])
+ end
+ -- sort corners
+ if c1[1] > c2[1] then c1[1], c2[1] = c2[1], c1[1] end
+ if c1[2] > c2[2] then c1[2], c2[2] = c2[2], c1[2] end
+ return { x = c1[1], y = c1[2] }, { x = c2[1], y = c2[2] }
+end
+
+function clamp(low, value, high)
+ if value <= low then
+ return low
+ elseif value >= high then
+ return high
+ else
+ return value
+ end
+end
+
+function clamp_point(top_left, point, bottom_right)
+ return {
+ x = clamp(top_left.x, point.x, bottom_right.x),
+ y = clamp(top_left.y, point.y, bottom_right.y)
+ }
+end
+
+function screen_to_video_norm(point, dim)
+ return {
+ x = (point.x - dim.ml) / (dim.w - dim.ml - dim.mr),
+ y = (point.y - dim.mt) / (dim.h - dim.mt - dim.mb)
+ }
+end
+
+function video_norm_to_screen(point, dim)
+ return {
+ x = math.floor(point.x * (dim.w - dim.ml - dim.mr) + dim.ml + 0.5),
+ y = math.floor(point.y * (dim.h - dim.mt - dim.mb) + dim.mt + 0.5)
+ }
+end
+
+function draw_shade(ass, unshaded, window)
+ ass:new_event()
+ ass:pos(0, 0)
+ ass:append("{\\an7}")
+ ass:append("{\\bord0}")
+ ass:append("{\\shad0}")
+ ass:append("{\\c&H000000&}")
+ ass:append("{\\1a&H" .. opts.shade_opacity .. "}")
+ ass:append("{\\2a&HFF}")
+ ass:append("{\\3a&HFF}")
+ ass:append("{\\4a&HFF}")
+ local c1, c2 = unshaded.top_left, unshaded.bottom_right
+ local v = window
+ -- c1.x c2.x
+ -- +-----+------------+
+ -- | | ur |
+ -- c1.y| ul +-------+----+
+ -- | | | |
+ -- c2.y+-----+-------+ lr |
+ -- | ll | |
+ -- +-------------+----+
+ ass:draw_start()
+ ass:rect_cw(v.top_left.x, v.top_left.y, c1.x, c2.y) -- ul
+ ass:rect_cw(c1.x, v.top_left.y, v.bottom_right.x, c1.y) -- ur
+ ass:rect_cw(v.top_left.x, c2.y, c2.x, v.bottom_right.y) -- ll
+ ass:rect_cw(c2.x, c1.y, v.bottom_right.x, v.bottom_right.y) -- lr
+ ass:draw_stop()
+ -- also possible to draw a rect over the whole video
+ -- and \iclip it in the middle, but seemingy slower
+end
+
+function draw_frame(ass, frame)
+ ass:new_event()
+ ass:pos(0, 0)
+ ass:append("{\\an7}")
+ ass:append("{\\bord0}")
+ ass:append("{\\shad0}")
+ ass:append("{\\c&H" .. opts.frame_border_color .. "&}")
+ ass:append("{\\1a&H00&}")
+ ass:append("{\\2a&HFF&}")
+ ass:append("{\\3a&HFF&}")
+ ass:append("{\\4a&HFF&}")
+ local c1, c2 = frame.top_left, frame.bottom_right
+ local b = opts.frame_border_width
+ ass:draw_start()
+ ass:rect_cw(c1.x, c1.y - b, c2.x + b, c1.y)
+ ass:rect_cw(c2.x, c1.y, c2.x + b, c2.y + b)
+ ass:rect_cw(c1.x - b, c2.y, c2.x, c2.y + b)
+ ass:rect_cw(c1.x - b, c1.y - b, c1.x, c2.y)
+ ass:draw_stop()
+end
+
+function draw_crosshair(ass, center, window_size)
+ ass:new_event()
+ ass:pos(0, 0)
+ ass:append("{\\an7}")
+ ass:append("{\\bord0}")
+ ass:append("{\\shad0}")
+ ass:append("{\\c&HBBBBBB&}")
+ ass:append("{\\1a&H00&}")
+ ass:append("{\\2a&HFF&}")
+ ass:append("{\\3a&HFF&}")
+ ass:append("{\\4a&HFF&}")
+ ass:draw_start()
+ ass:rect_cw(center.x - 0.5, 0, center.x + 0.5, window_size.h)
+ ass:rect_cw(0, center.y - 0.5, window_size.w, center.y + 0.5)
+ ass:draw_stop()
+end
+
+function draw_position_text(ass, text, position, window_size, offset)
+ ass:new_event()
+ local align = 1
+ local ofx = 1
+ local ofy = -1
+ if position.x > window_size.w / 2 then
+ align = align + 2
+ ofx = -1
+ end
+ if position.y < window_size.h / 2 then
+ align = align + 6
+ ofy = 1
+ end
+ ass:append("{\\an"..align.."}")
+ ass:append("{\\fs26}")
+ ass:append("{\\bord1.5}")
+ ass:pos(ofx*offset + position.x, ofy*offset + position.y)
+ ass:append(text)
+end
+
+function draw_crop_zone()
+ if needs_drawing then
+ local dim = mp.get_property_native("osd-dimensions")
+ if not dim then
+ cancel_crop()
+ return
+ end
+
+ local cursor = {
+ x = crop_cursor.x,
+ y = crop_cursor.y,
+ }
+ local ass = assdraw.ass_new()
+
+ if crop_first_corner and (opts.draw_shade or opts.draw_frame) then
+ local frame = {}
+ frame.top_left, frame.bottom_right = rect_from_two_points(
+ video_norm_to_screen(crop_first_corner, dim),
+ cursor,
+ rect_centered,
+ rect_keepaspect and dim.w/dim.h)
+ -- don't draw shade over non-visible video parts
+ if opts.draw_shade then
+ local window = {
+ top_left = { x = 0, y = 0 },
+ bottom_right = { x = dim.w, y = dim.h },
+ }
+ draw_shade(ass, frame, window)
+ end
+ if opts.draw_frame then
+ draw_frame(ass, frame)
+ end
+ end
+
+
+ if opts.draw_crosshair then
+ draw_crosshair(ass, cursor, { w = dim.w, h = dim.h })
+ end
+
+ if opts.draw_text then
+ local vop = mp.get_property_native("video-out-params")
+ if vop then
+ local cursor_norm = screen_to_video_norm(cursor, dim)
+ local text = string.format("%d, %d", cursor_norm.x * vop.w, cursor_norm.y * vop.h)
+ if crop_first_corner then
+ text = string.format("%s (%dx%d)", text,
+ math.abs((cursor_norm.x - crop_first_corner.x) * vop.w ),
+ math.abs((cursor_norm.y - crop_first_corner.y) * vop.h )
+ )
+ end
+ draw_position_text(ass, text, cursor, { w = dim.w, h = dim.h }, 6)
+ end
+ end
+
+ mp.set_osd_ass(dim.w, dim.h, ass.text)
+ needs_drawing = false
+ end
+end
+
+function crop_video(x1, y1, x2, y2)
+ if active_mode == "soft" then
+ local w = x2 - x1
+ local h = y2 - y1
+ local dim = mp.get_property_native("osd-dimensions")
+ if not dim then return end
+
+ local zoom = mp.get_property_number("video-zoom")
+ local newZoom1 = math.log(dim.h * (2 ^ zoom) / (dim.h - dim.mt - dim.mb) / h) / math.log(2)
+ local newZoom2 = math.log(dim.w * (2 ^ zoom) / (dim.w - dim.ml - dim.mr) / w) / math.log(2)
+ mp.set_property("video-zoom", math.min(newZoom1, newZoom2))
+ mp.set_property("video-pan-x", 0.5 - (x1 + w / 2))
+ mp.set_property("video-pan-y", 0.5 - (y1 + h / 2))
+ elseif active_mode == "hard" or active_mode == "delogo" then
+ x1 = clamp(0, x1, 1)
+ y1 = clamp(0, y1, 1)
+ x2 = clamp(0, x2, 1)
+ y2 = clamp(0, y2, 1)
+ local vop = mp.get_property_native("video-out-params")
+ local vf_table = mp.get_property_native("vf")
+ local x = math.floor(x1 * vop.w + 0.5)
+ local y = math.floor(y1 * vop.h + 0.5)
+ local w = math.floor((x2 - x1) * vop.w + 0.5)
+ local h = math.floor((y2 - y1) * vop.h + 0.5)
+ if active_mode == "delogo" then
+ -- delogo is a little special and needs some padding to function
+ w = math.min(vop.w - 1, w)
+ h = math.min(vop.h - 1, h)
+ x = math.max(1, x)
+ y = math.max(1, y)
+ if x + w == vop.w then w = w - 1 end
+ if y + h == vop.h then h = h - 1 end
+ end
+ vf_table[#vf_table + 1] = {
+ name=(active_mode == "hard") and "crop" or "delogo",
+ params= { x = tostring(x), y = tostring(y), w = tostring(w), h = tostring(h) }
+ }
+ mp.set_property_native("vf", vf_table)
+ end
+end
+
+function update_crop_zone_state()
+ local dim = mp.get_property_native("osd-dimensions")
+ if not dim then
+ cancel_crop()
+ return
+ end
+ local corner = crop_cursor
+ if crop_first_corner == nil then
+ crop_first_corner = screen_to_video_norm(crop_cursor, dim)
+ redraw()
+ else
+ local c1, c2 = rect_from_two_points(
+ video_norm_to_screen(crop_first_corner, dim),
+ crop_cursor,
+ rect_centered,
+ rect_keepaspect and dim.w/dim.h)
+ local c1norm = screen_to_video_norm(c1, dim)
+ local c2norm = screen_to_video_norm(c2, dim)
+ crop_video(c1norm.x, c1norm.y, c2norm.x, c2norm.y)
+ cancel_crop()
+ end
+end
+
+local bindings = {}
+local bindings_repeat = {}
+
+function cancel_crop()
+ crop_first_corner = nil
+ for key, _ in pairs(bindings) do
+ mp.remove_key_binding("crop-"..key)
+ end
+ for key, _ in pairs(bindings_repeat) do
+ mp.remove_key_binding("crop-"..key)
+ end
+ mp.unobserve_property(redraw)
+ mp.unregister_idle(draw_crop_zone)
+ mp.set_osd_ass(1280, 720, '')
+ active = false
+end
+
+function start_crop(mode)
+ if active then return end
+ if not mp.get_property_native("osd-dimensions") then return end
+ if mode and not mode_ok(mode) then
+ msg.error("Invalid mode value: " .. mode)
+ return
+ end
+ local mode_maybe = mode or opts.mode
+ if mode_maybe ~= 'soft' then
+ local hwdec = mp.get_property("hwdec-current")
+ if hwdec and hwdec ~= "no" and not string.find(hwdec, "-copy$") then
+ msg.error("Cannot crop with hardware decoding active (see manual)")
+ return
+ end
+ end
+ active = true
+ active_mode = mode_maybe
+
+ if opts.mouse_support then
+ crop_cursor.x, crop_cursor.y = mp.get_mouse_pos()
+ end
+ redraw()
+ for key, func in pairs(bindings) do
+ mp.add_forced_key_binding(key, "crop-"..key, func)
+ end
+ for key, func in pairs(bindings_repeat) do
+ mp.add_forced_key_binding(key, "crop-"..key, func, { repeatable = true })
+ end
+ mp.register_idle(draw_crop_zone)
+ mp.observe_property("osd-dimensions", nil, redraw)
+end
+
+function toggle_crop(mode)
+ if mode and not mode_ok(mode) then
+ msg.error("Invalid mode value: " .. mode)
+ end
+ local toggle_mode = mode or opts.mode
+ if toggle_mode == "soft" then return end -- can't toggle soft mode
+
+ local remove_filter = function()
+ local to_remove = (toggle_mode == "hard") and "crop" or "delogo"
+ local vf_table = mp.get_property_native("vf")
+ if #vf_table > 0 then
+ for i = #vf_table, 1, -1 do
+ if vf_table[i].name == to_remove then
+ for j = i, #vf_table-1 do
+ vf_table[j] = vf_table[j+1]
+ end
+ vf_table[#vf_table] = nil
+ mp.set_property_native("vf", vf_table)
+ return true
+ end
+ end
+ end
+ return false
+ end
+ if not remove_filter() then
+ start_crop(mode)
+ end
+end
+
+-- bindings
+if opts.mouse_support then
+ bindings["MOUSE_MOVE"] = function() crop_cursor.x, crop_cursor.y = mp.get_mouse_pos(); redraw() end
+end
+for _, key in ipairs(opts.accept) do
+ bindings[key] = update_crop_zone_state
+end
+for _, key in ipairs(opts.cancel) do
+ bindings[key] = cancel_crop
+end
+function movement_func(move_x, move_y)
+ return function()
+ crop_cursor.x = crop_cursor.x + move_x
+ crop_cursor.y = crop_cursor.y + move_y
+ redraw()
+ end
+end
+bindings_repeat[opts.left_coarse] = movement_func(-opts.coarse_movement, 0)
+bindings_repeat[opts.right_coarse] = movement_func(opts.coarse_movement, 0)
+bindings_repeat[opts.up_coarse] = movement_func(0, -opts.coarse_movement)
+bindings_repeat[opts.down_coarse] = movement_func(0, opts.coarse_movement)
+bindings_repeat[opts.left_fine] = movement_func(-opts.fine_movement, 0)
+bindings_repeat[opts.right_fine] = movement_func(opts.fine_movement, 0)
+bindings_repeat[opts.up_fine] = movement_func(0, -opts.fine_movement)
+bindings_repeat[opts.down_fine] = movement_func(0, opts.fine_movement)
+
+
+mp.add_key_binding(nil, "start-crop", start_crop)
+mp.add_key_binding(nil, "toggle-crop", toggle_crop)
diff --git a/.config/mpv/scripts/encode.lua b/.config/mpv/scripts/encode.lua
new file mode 100644
index 0000000..df518d8
--- /dev/null
+++ b/.config/mpv/scripts/encode.lua
@@ -0,0 +1,315 @@
+local utils = require "mp.utils"
+local msg = require "mp.msg"
+local options = require "mp.options"
+
+local ON_WINDOWS = (package.config:sub(1,1) ~= "/")
+
+local start_timestamp = nil
+local profile_start = ""
+
+-- implementation detail of the osd message
+local timer = nil
+local timer_duration = 2
+
+function append_table(lhs, rhs)
+ for i = 1,#rhs do
+ lhs[#lhs+1] = rhs[i]
+ end
+ return lhs
+end
+
+function file_exists(name)
+ local f = io.open(name, "r")
+ if f ~= nil then
+ io.close(f)
+ return true
+ else
+ return false
+ end
+end
+
+function get_extension(path)
+ local candidate = string.match(path, "%.([^.]+)$")
+ if candidate then
+ for _, ext in ipairs({ "mkv", "webm", "mp4", "avi" }) do
+ if candidate == ext then
+ return candidate
+ end
+ end
+ end
+ return "mkv"
+end
+
+function get_output_string(dir, format, input, extension, title, from, to, profile)
+ local res = utils.readdir(dir)
+ if not res then
+ return nil
+ end
+ local files = {}
+ for _, f in ipairs(res) do
+ files[f] = true
+ end
+ local output = format
+ output = string.gsub(output, "$f", function() return input end)
+ output = string.gsub(output, "$t", function() return title end)
+ output = string.gsub(output, "$s", function() return seconds_to_time_string(from, true) end)
+ output = string.gsub(output, "$e", function() return seconds_to_time_string(to, true) end)
+ output = string.gsub(output, "$d", function() return seconds_to_time_string(to-from, true) end)
+ output = string.gsub(output, "$x", function() return extension end)
+ output = string.gsub(output, "$p", function() return profile end)
+ if ON_WINDOWS then
+ output = string.gsub(output, "[/\\|<>?:\"*]", "_")
+ end
+ if not string.find(output, "$n") then
+ return files[output] and nil or output
+ end
+ local i = 1
+ while true do
+ local potential_name = string.gsub(output, "$n", tostring(i))
+ if not files[potential_name] then
+ return potential_name
+ end
+ i = i + 1
+ end
+end
+
+function get_video_filters()
+ local filters = {}
+ for _, vf in ipairs(mp.get_property_native("vf")) do
+ local name = vf["name"]
+ name = string.gsub(name, '^lavfi%-', '')
+ local filter
+ if name == "crop" then
+ local p = vf["params"]
+ filter = string.format("crop=%d:%d:%d:%d", p.w, p.h, p.x, p.y)
+ elseif name == "mirror" then
+ filter = "hflip"
+ elseif name == "flip" then
+ filter = "vflip"
+ elseif name == "rotate" then
+ local rotation = vf["params"]["angle"]
+ -- rotate is NOT the filter we want here
+ if rotation == "90" then
+ filter = "transpose=clock"
+ elseif rotation == "180" then
+ filter = "transpose=clock,transpose=clock"
+ elseif rotation == "270" then
+ filter = "transpose=cclock"
+ end
+ end
+ filters[#filters + 1] = filter
+ end
+ return filters
+end
+
+function get_input_info(default_path, only_active)
+ local accepted = {
+ video = true,
+ audio = not mp.get_property_bool("mute"),
+ sub = mp.get_property_bool("sub-visibility")
+ }
+ local ret = {}
+ for _, track in ipairs(mp.get_property_native("track-list")) do
+ local track_path = track["external-filename"] or default_path
+ if not only_active or (track["selected"] and accepted[track["type"]]) then
+ local tracks = ret[track_path]
+ if not tracks then
+ ret[track_path] = { track["ff-index"] }
+ else
+ tracks[#tracks + 1] = track["ff-index"]
+ end
+ end
+ end
+ return ret
+end
+
+function seconds_to_time_string(seconds, full)
+ local ret = string.format("%02d:%02d.%03d"
+ , math.floor(seconds / 60) % 60
+ , math.floor(seconds) % 60
+ , seconds * 1000 % 1000
+ )
+ if full or seconds > 3600 then
+ ret = string.format("%d:%s", math.floor(seconds / 3600), ret)
+ end
+ return ret
+end
+
+function start_encoding(from, to, settings)
+ local args = {
+ settings.ffmpeg_command,
+ "-loglevel", "panic", "-hide_banner",
+ }
+ local append_args = function(table) args = append_table(args, table) end
+
+ local path = mp.get_property("path")
+ local is_stream = not file_exists(path)
+ if is_stream then
+ path = mp.get_property("stream-path")
+ end
+
+ local track_args = {}
+ local start = seconds_to_time_string(from, false)
+ local input_index = 0
+ for input_path, tracks in pairs(get_input_info(path, settings.only_active_tracks)) do
+ append_args({
+ "-ss", start,
+ "-i", input_path,
+ })
+ if settings.only_active_tracks then
+ for _, track_index in ipairs(tracks) do
+ track_args = append_table(track_args, { "-map", string.format("%d:%d", input_index, track_index)})
+ end
+ else
+ track_args = append_table(track_args, { "-map", tostring(input_index)})
+ end
+ input_index = input_index + 1
+ end
+
+ append_args({"-to", tostring(to-from)})
+ append_args(track_args)
+
+ -- apply some of the video filters currently in the chain
+ local filters = {}
+ if settings.preserve_filters then
+ filters = get_video_filters()
+ end
+ if settings.append_filter ~= "" then
+ filters[#filters + 1] = settings.append_filter
+ end
+ if #filters > 0 then
+ append_args({ "-filter:v", table.concat(filters, ",") })
+ end
+
+ -- split the user-passed settings on whitespace
+ for token in string.gmatch(settings.codec, "[^%s]+") do
+ args[#args + 1] = token
+ end
+
+ -- path of the output
+ local output_directory = settings.output_directory
+ if output_directory == "" then
+ if is_stream then
+ output_directory = "."
+ else
+ output_directory, _ = utils.split_path(path)
+ end
+ else
+ output_directory = string.gsub(output_directory, "^~", os.getenv("HOME") or "~")
+ end
+ local input_name = mp.get_property("filename/no-ext") or "encode"
+ local title = mp.get_property("media-title")
+ local extension = get_extension(path)
+ local output_name = get_output_string(output_directory, settings.output_format, input_name, extension, title, from, to, settings.profile)
+ if not output_name then
+ mp.osd_message("Invalid path " .. output_directory)
+ return
+ end
+ args[#args + 1] = utils.join_path(output_directory, output_name)
+
+ if settings.print then
+ local o = ""
+ -- fuck this is ugly
+ for i = 1, #args do
+ local fmt = ""
+ if i == 1 then
+ fmt = "%s%s"
+ elseif i >= 2 and i <= 4 then
+ fmt = "%s"
+ elseif args[i-1] == "-i" or i == #args or args[i-1] == "-filter:v" then
+ fmt = "%s '%s'"
+ else
+ fmt = "%s %s"
+ end
+ o = string.format(fmt, o, args[i])
+ end
+ print(o)
+ end
+ if settings.detached then
+ utils.subprocess_detached({ args = args })
+ else
+ local res = utils.subprocess({ args = args, max_size = 0, cancellable = false })
+ if res.status == 0 then
+ mp.osd_message("Finished encoding succesfully")
+ else
+ mp.osd_message("Failed to encode, check the log")
+ end
+ end
+end
+
+function clear_timestamp()
+ timer:kill()
+ start_timestamp = nil
+ profile_start = ""
+ mp.remove_key_binding("encode-ESC")
+ mp.remove_key_binding("encode-ENTER")
+ mp.osd_message("", 0)
+end
+
+function set_timestamp(profile)
+ if not mp.get_property("path") then
+ mp.osd_message("No file currently playing")
+ return
+ end
+ if not mp.get_property_bool("seekable") then
+ mp.osd_message("Cannot encode non-seekable media")
+ return
+ end
+
+ if not start_timestamp or profile ~= profile_start then
+ profile_start = profile
+ start_timestamp = mp.get_property_number("time-pos")
+ local msg = function()
+ mp.osd_message(
+ string.format("encode [%s]: waiting for end timestamp", profile or "default"),
+ timer_duration
+ )
+ end
+ msg()
+ timer = mp.add_periodic_timer(timer_duration, msg)
+ mp.add_forced_key_binding("ESC", "encode-ESC", clear_timestamp)
+ mp.add_forced_key_binding("ENTER", "encode-ENTER", function() set_timestamp(profile) end)
+ else
+ local from = start_timestamp
+ local to = mp.get_property_number("time-pos")
+ if to <= from then
+ mp.osd_message("Second timestamp cannot be before the first", timer_duration)
+ timer:kill()
+ timer:resume()
+ return
+ end
+ clear_timestamp()
+ mp.osd_message(string.format("Encoding from %s to %s"
+ , seconds_to_time_string(from, false)
+ , seconds_to_time_string(to, false)
+ ), timer_duration)
+ -- include the current frame into the extract
+ local fps = mp.get_property_number("container-fps") or 30
+ to = to + 1 / fps / 2
+ local settings = {
+ detached = true,
+ container = "",
+ only_active_tracks = false,
+ preserve_filters = true,
+ append_filter = "",
+ codec = "-an -sn -c:v libvpx -crf 10 -b:v 1000k",
+ output_format = "$f_$n.webm",
+ output_directory = "",
+ ffmpeg_command = "ffmpeg",
+ print = true,
+ }
+ if profile then
+ options.read_options(settings, profile)
+ if settings.container ~= "" then
+ msg.warn("The 'container' setting is deprecated, use 'output_format' now")
+ settings.output_format = settings.output_format .. "." .. settings.container
+ end
+ settings.profile = profile
+ else
+ settings.profile = "default"
+ end
+ start_encoding(from, to, settings)
+ end
+end
+
+mp.add_key_binding(nil, "set-timestamp", set_timestamp)
diff --git a/.config/mpv/scripts/exclude.lua b/.config/mpv/scripts/exclude.lua
new file mode 100644
index 0000000..7a64028
--- /dev/null
+++ b/.config/mpv/scripts/exclude.lua
@@ -0,0 +1,10 @@
+mp.observe_property('playlist-count', 'number', function ()
+ local playlist = mp.get_property_native('playlist')
+ for i = #playlist, 1, -1 do
+ for _, extension in pairs({'cue', 'txt', 'jpg', 'jpeg', 'png', 'log', 'description', 'en.vtt', 'info.json', 'webp'}) do
+ if playlist[i].filename:match('%.' .. extension .. '$') then
+ mp.commandv('playlist-remove', i-1)
+ end
+ end
+ end
+end)
diff --git a/.config/ncmpcpp/bindings b/.config/ncmpcpp/bindings
new file mode 100644
index 0000000..6962dea
--- /dev/null
+++ b/.config/ncmpcpp/bindings
@@ -0,0 +1,543 @@
+##############################################################
+## This is the example bindings file. Copy it to ##
+## ~/.ncmpcpp/bindings or $XDG_CONFIG_HOME/ncmpcpp/bindings ##
+## and set up your preferences ##
+##############################################################
+##
+##### General rules #####
+##
+## 1) Because each action has runtime checks whether it's
+## ok to run it, a few actions can be bound to one key.
+## Actions will be bound in order given in configuration
+## file. When a key is pressed, first action in order
+## will test itself whether it's possible to run it. If
+## test succeeds, action is executed and other actions
+## bound to this key are ignored. If it doesn't, next
+## action in order tests itself etc.
+##
+## 2) It's possible to bind more that one action at once
+## to a key. It can be done using the following syntax:
+##
+## def_key "key"
+## action1
+## action2
+## ...
+##
+## This creates a chain of actions. When such chain is
+## executed, each action in chain is run until the end of
+## chain is reached or one of its actions fails to execute
+## due to its requirements not being met. If multiple actions
+## and/or chains are bound to the same key, they will be
+## consecutively run until one of them gets fully executed.
+##
+## 3) When ncmpcpp starts, bindings configuration file is
+## parsed and then ncmpcpp provides "missing pieces"
+## of default keybindings. If you want to disable some
+## bindings, there is a special action called 'dummy'
+## for that purpose. Eg. if you want to disable ability
+## to crop playlists, you need to put the following
+## into configuration file:
+##
+## def_key "C"
+## dummy
+##
+## After that ncmpcpp will not bind any default action
+## to this key.
+##
+## 4) To let you write simple macros, the following special
+## actions are provided:
+##
+## - push_character "character" - pushes given special
+## character into input queue, so it will be immediately
+## picked by ncmpcpp upon next call to readKey function.
+## Accepted values: mouse, up, down, page_up, page_down,
+## home, end, space, enter, insert, delete, left, right,
+## tab, ctrl-a, ctrl-b, ..., ctrl-z, ctrl-[, ctrl-\\,
+## ctrl-], ctrl-^, ctrl-_, f1, f2, ..., f12, backspace.
+## In addition, most of these names can be prefixed with
+## alt-/ctrl-/shift- to be recognized with the appropriate
+## modifier key(s).
+##
+## - push_characters "string" - pushes given string into
+## input queue.
+##
+## - require_runnable "action" - checks whether given action
+## is runnable and fails if it isn't. This is especially
+## useful when mixed with previous two functions. Consider
+## the following macro definition:
+##
+## def_key "key"
+## push_characters "custom_filter"
+## apply_filter
+##
+## If apply_filter can't be currently run, we end up with
+## sequence of characters in input queue which will be
+## treated just as we typed them. This may lead to unexpected
+## results (in this case 'c' will most likely clear current
+## playlist, 'u' will trigger database update, 's' will stop
+## playback etc.). To prevent such thing from happening, we
+## need to change above definition to this one:
+##
+## def_key "key"
+## require_runnable "apply_filter"
+## push_characters "custom_filter"
+## apply_filter
+##
+## Here, first we test whether apply_filter can be actually run
+## before we stuff characters into input queue, so if condition
+## is not met, whole chain is aborted and we're fine.
+##
+## - require_screen "screen" - checks whether given screen is
+## currently active. accepted values: browser, clock, help,
+## media_library, outputs, playlist, playlist_editor,
+## search_engine, tag_editor, visualizer, last_fm, lyrics,
+## selected_items_adder, server_info, song_info,
+## sort_playlist_dialog, tiny_tag_editor.
+##
+## - run_external_command "command" - runs given command using
+## system() function.
+##
+## 5) In addition to binding to a key, you can also bind actions
+## or chains of actions to a command. If it comes to commands,
+## syntax is very similar to defining keys. Here goes example
+## definition of a command:
+##
+## def_command "quit" [deferred]
+## stop
+## quit
+##
+## If you execute the above command (which can be done by
+## invoking action execute_command, typing 'quit' and pressing
+## enter), ncmpcpp will stop the player and then quit. Note the
+## presence of word 'deferred' enclosed in square brackets. It
+## tells ncmpcpp to wait for confirmation (ie. pressing enter)
+## after you typed quit. Instead of 'deferred', 'immediate'
+## could be used. Then ncmpcpp will not wait for confirmation
+## (enter) and will execute the command the moment it sees it.
+##
+## Note: while command chains are executed, internal environment
+## update (which includes current window refresh and mpd status
+## update) is not performed for performance reasons. However, it
+## may be desirable to do so in some situration. Therefore it's
+## possible to invoke by hand by performing 'update enviroment'
+## action.
+##
+## Note: There is a difference between:
+##
+## def_key "key"
+## action1
+##
+## def_key "key"
+## action2
+##
+## and
+##
+## def_key "key"
+## action1
+## action2
+##
+## First one binds two single actions to the same key whilst
+## second one defines a chain of actions. The behavior of
+## these two is different and is described in (1) and (2).
+##
+## Note: Function def_key accepts non-ascii characters.
+##
+##### List of unbound actions #####
+##
+## The following actions are not bound to any key/command:
+##
+## - set_volume
+##
+#
+#def_key "mouse"
+# mouse_event
+#
+def_key "k"
+ scroll_up
+
+#def_key "shift-up"
+# select_item
+# scroll_up
+#
+def_key "j"
+ scroll_down
+
+#def_key "shift-down"
+# select_item
+# scroll_down
+#
+#def_key "["
+# scroll_up_album
+#
+#def_key "]"
+# scroll_down_album
+#
+#def_key "{"
+# scroll_up_artist
+#
+#def_key "}"
+# scroll_down_artist
+#
+def_key "u"
+ page_up
+
+def_key "d"
+ page_down
+
+#def_key "home"
+# move_home
+#
+#def_key "end"
+# move_end
+#
+#def_key "insert"
+# select_item
+#
+#def_key "enter"
+# enter_directory
+#
+#def_key "enter"
+# toggle_output
+#
+#def_key "enter"
+# run_action
+#
+#def_key "enter"
+# play_item
+#
+#def_key "space"
+# add_item_to_playlist
+#
+#def_key "space"
+# toggle_lyrics_update_on_song_change
+#
+#def_key "space"
+# toggle_visualization_type
+#
+#def_key "delete"
+# delete_playlist_items
+#
+#def_key "delete"
+# delete_browser_items
+#
+#def_key "delete"
+# delete_stored_playlist
+#
+#def_key "right"
+# next_column
+#
+#def_key "right"
+# slave_screen
+#
+#def_key "right"
+# volume_up
+#
+#def_key "+"
+# volume_up
+#
+#def_key "left"
+# previous_column
+#
+#def_key "left"
+# master_screen
+#
+#def_key "left"
+# volume_down
+#
+#def_key "-"
+# volume_down
+#
+#def_key ":"
+# execute_command
+#
+#def_key "tab"
+# next_screen
+#
+#def_key "shift-tab"
+# previous_screen
+#
+#def_key "f1"
+# show_help
+#
+#def_key "1"
+# show_playlist
+#
+#def_key "2"
+# show_browser
+#
+#def_key "2"
+# change_browse_mode
+#
+#def_key "3"
+# show_search_engine
+#
+#def_key "3"
+# reset_search_engine
+#
+#def_key "4"
+# show_media_library
+#
+#def_key "4"
+# toggle_media_library_columns_mode
+#
+#def_key "5"
+# show_playlist_editor
+#
+#def_key "6"
+# show_tag_editor
+#
+#def_key "7"
+# show_outputs
+#
+#def_key "8"
+# show_visualizer
+#
+#def_key "="
+# show_clock
+#
+#def_key "@"
+# show_server_info
+#
+#def_key "s"
+# stop
+#
+#def_key "p"
+# pause
+#
+#def_key ">"
+# next
+#
+#def_key "<"
+# previous
+#
+#def_key "ctrl-h"
+# jump_to_parent_directory
+#
+#def_key "ctrl-h"
+# replay_song
+#
+#def_key "backspace"
+# jump_to_parent_directory
+#
+#def_key "backspace"
+# replay_song
+#
+#def_key "f"
+# seek_forward
+#
+#def_key "b"
+# seek_backward
+#
+#def_key "r"
+# toggle_repeat
+#
+#def_key "z"
+# toggle_random
+#
+#def_key "y"
+# save_tag_changes
+#
+#def_key "y"
+# start_searching
+#
+#def_key "y"
+# toggle_single
+#
+#def_key "R"
+# toggle_consume
+#
+#def_key "Y"
+# toggle_replay_gain_mode
+#
+#def_key "T"
+# toggle_add_mode
+#
+#def_key "|"
+# toggle_mouse
+#
+#def_key "#"
+# toggle_bitrate_visibility
+#
+#def_key "Z"
+# shuffle
+#
+#def_key "x"
+# toggle_crossfade
+#
+#def_key "X"
+# set_crossfade
+#
+def_key "U"
+ update_database
+
+#def_key "ctrl-s"
+# sort_playlist
+#
+#def_key "ctrl-s"
+# toggle_browser_sort_mode
+#
+#def_key "ctrl-s"
+# toggle_media_library_sort_mode
+#
+#def_key "ctrl-r"
+# reverse_playlist
+#
+#def_key "ctrl-f"
+# apply_filter
+#
+#def_key "ctrl-_"
+# select_found_items
+#
+#def_key "/"
+# find
+#
+#def_key "/"
+# find_item_forward
+#
+#def_key "?"
+# find
+#
+#def_key "?"
+# find_item_backward
+#
+#def_key "."
+# next_found_item
+#
+#def_key ","
+# previous_found_item
+#
+#def_key "w"
+# toggle_find_mode
+#
+#def_key "e"
+# edit_song
+#
+#def_key "e"
+# edit_library_tag
+#
+#def_key "e"
+# edit_library_album
+#
+#def_key "e"
+# edit_directory_name
+#
+#def_key "e"
+# edit_playlist_name
+#
+#def_key "e"
+# edit_lyrics
+#
+#def_key "i"
+# show_song_info
+#
+#def_key "I"
+# show_artist_info
+#
+#def_key "g"
+# jump_to_position_in_song
+#
+#def_key "l"
+# show_lyrics
+#
+#def_key "ctrl-v"
+# select_range
+#
+#def_key "v"
+# reverse_selection
+#
+#def_key "V"
+# remove_selection
+#
+#def_key "B"
+# select_album
+#
+#def_key "a"
+# add_selected_items
+#
+#def_key "c"
+# clear_playlist
+#
+#def_key "c"
+# clear_main_playlist
+#
+#def_key "C"
+# crop_playlist
+#
+#def_key "C"
+# crop_main_playlist
+#
+#def_key "m"
+# move_sort_order_up
+#
+#def_key "m"
+# move_selected_items_up
+#
+#def_key "n"
+# move_sort_order_down
+#
+#def_key "n"
+# move_selected_items_down
+#
+#def_key "M"
+# move_selected_items_to
+#
+#def_key "A"
+# add
+#
+#def_key "S"
+# save_playlist
+#
+#def_key "o"
+# jump_to_playing_song
+#
+#def_key "G"
+# jump_to_browser
+#
+#def_key "G"
+# jump_to_playlist_editor
+#
+#def_key "~"
+# jump_to_media_library
+#
+#def_key "E"
+# jump_to_tag_editor
+#
+#def_key "U"
+# toggle_playing_song_centering
+#
+#def_key "P"
+# toggle_display_mode
+#
+#def_key "\\"
+# toggle_interface
+#
+#def_key "!"
+# toggle_separators_between_albums
+#
+#def_key "L"
+# toggle_lyrics_fetcher
+#
+#def_key "F"
+# fetch_lyrics_in_background
+#
+#def_key "alt-l"
+# toggle_fetching_lyrics_in_background
+#
+#def_key "ctrl-l"
+# toggle_screen_lock
+#
+#def_key "`"
+# toggle_library_tag_type
+#
+#def_key "`"
+# refetch_lyrics
+#
+#def_key "`"
+# add_random_items
+#
+#def_key "ctrl-p"
+# set_selected_items_priority
+#
+#def_key "q"
+# quit
+#
diff --git a/.config/ncmpcpp/config b/.config/ncmpcpp/config
new file mode 100644
index 0000000..69ff9d3
--- /dev/null
+++ b/.config/ncmpcpp/config
@@ -0,0 +1,41 @@
+# Files
+ncmpcpp_directory = ~/.config/ncmpcpp
+lyrics_directory = ~/.config/mpd/lyrics
+
+mpd_host = localhost
+mpd_port = 6600
+mpd_music_dir = /media/hdd/music
+mpd_connection_timeout = 5
+
+# Playlist
+playlist_disable_highlight_delay = 0
+playlist_display_mode = classic
+playlist_show_remaining_time = yes
+
+browser_display_mode = columns
+autocenter_mode = yes
+follow_now_playing_lyrics = yes
+lyrics_fetchers = tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, genius, internet
+
+# UI and colors
+external_editor = $EDITOR
+colors_enabled = yes
+current_item_prefix = $(blue)$r
+current_item_suffix = $/r$(end)
+current_item_inactive_column_prefix = $(cyan)$r
+current_item_inactive_column_suffix = $/r$(end)
+header_window_color = cyan
+main_window_color = white
+active_window_border = blue
+song_columns_list_format = (5)[blue]{l} (35)[green]{t|f:Title} (30)[magenta]{aE} (30)[yellow]{bE}
+song_list_format = {$3%n | $9}{$7%a - $9}{$5%t$9}|{$8%f$9}$R{$6 | %b$9}{$3 | %l$9}
+volume_color = red
+progressbar_color = cyan
+progressbar_elapsed_color = white
+statusbar_color = white
+user_interface = classic
+selected_item_prefix = *
+now_playing_prefix = "> "
+centered_cursor = yes
+display_bitrate = yes
+enable_window_title = yes
diff --git a/.config/nnn/nnn.bash b/.config/nnn/nnn.bash
new file mode 100755
index 0000000..2bc60d2
--- /dev/null
+++ b/.config/nnn/nnn.bash
@@ -0,0 +1,5 @@
+# NNN
+
+export NNN_PLUG='t:preview-tabbed'
+
+export NNN_FIFO="/tmp/nnn.fifo"
diff --git a/.config/nsxiv/exec/key-handler b/.config/nsxiv/exec/key-handler
new file mode 100755
index 0000000..14dda4c
--- /dev/null
+++ b/.config/nsxiv/exec/key-handler
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+while read -r file
+do
+ case "$1" in
+ "C-d") rm "$file" ;;
+ "C-c") cat "$file" | xclip -sel c -t image/png ;;
+ "C-w") xwallpaper --maximize "$file" ;;
+ "C-r") mv "$file" "$(dmenu </dev/null -p 'Rename to: ').${file##*.}"
+ esac
+done
diff --git a/.config/pipewire/pipewire.conf b/.config/pipewire/pipewire.conf
new file mode 100644
index 0000000..173b665
--- /dev/null
+++ b/.config/pipewire/pipewire.conf
@@ -0,0 +1,269 @@
+# Daemon config file for PipeWire version "0.3.63" #
+#
+# Copy and edit this file in /etc/pipewire for system-wide changes
+# or in ~/.config/pipewire for local changes.
+#
+# It is also possible to place a file with an updated section in
+# /etc/pipewire/pipewire.conf.d/ for system-wide changes or in
+# ~/.config/pipewire/pipewire.conf.d/ for local changes.
+#
+
+context.properties = {
+ ## Configure properties in the system.
+ #library.name.system = support/libspa-support
+ #context.data-loop.library.name.system = support/libspa-support
+ #support.dbus = true
+ #link.max-buffers = 64
+ link.max-buffers = 16 # version < 3 clients can't handle more
+ #mem.warn-mlock = false
+ #mem.allow-mlock = true
+ #mem.mlock-all = false
+ #clock.power-of-two-quantum = true
+ #log.level = 2
+ #cpu.zero.denormals = false
+
+ core.daemon = true # listening for socket connections
+ core.name = pipewire-0 # core name and socket name
+
+ ## Properties for the DSP configuration.
+ default.clock.rate = 44100
+ default.clock.allowed-rates = [ 44100 ]
+ default.clock.quantum = 192
+ default.clock.min-quantum = 512
+ #default.clock.max-quantum = 2048
+ #default.clock.quantum-limit = 8192
+ #default.video.width = 640
+ #default.video.height = 480
+ #default.video.rate.num = 25
+ #default.video.rate.denom = 1
+ #
+ #settings.check-quantum = false
+ #settings.check-rate = false
+ #
+ # These overrides are only applied when running in a vm.
+ #vm.overrides = {
+ # default.clock.min-quantum = 1024
+ #}
+}
+
+context.spa-libs = {
+ #<factory-name regex> = <library-name>
+ #
+ # Used to find spa factory names. It maps an spa factory name
+ # regular expression to a library name that should contain
+ # that factory.
+ #
+ audio.convert.* = audioconvert/libspa-audioconvert
+ avb.* = avb/libspa-avb
+ api.alsa.* = alsa/libspa-alsa
+ api.v4l2.* = v4l2/libspa-v4l2
+ api.libcamera.* = libcamera/libspa-libcamera
+ #api.bluez5.* = bluez5/libspa-bluez5
+ api.vulkan.* = vulkan/libspa-vulkan
+ #api.jack.* = jack/libspa-jack
+ support.* = support/libspa-support
+ #videotestsrc = videotestsrc/libspa-videotestsrc
+ #audiotestsrc = audiotestsrc/libspa-audiotestsrc
+}
+
+context.modules = [
+ #{ name = <module-name>
+ # [ args = { <key> = <value> ... } ]
+ # [ flags = [ [ ifexists ] [ nofail ] ]
+ #}
+ #
+ # Loads a module with the given parameters.
+ # If ifexists is given, the module is ignored when it is not found.
+ # If nofail is given, module initialization failures are ignored.
+ #
+
+ # Uses realtime scheduling to boost the audio thread priorities. This uses
+ # RTKit if the user doesn't have permission to use regular realtime
+ # scheduling.
+ { name = libpipewire-module-rt
+ args = {
+ nice.level = -11
+ #rt.prio = 88
+ #rt.time.soft = -1
+ #rt.time.hard = -1
+ }
+ flags = [ ifexists nofail ]
+ }
+
+ # The native communication protocol.
+ { name = libpipewire-module-protocol-native }
+
+ # The profile module. Allows application to access profiler
+ # and performance data. It provides an interface that is used
+ # by pw-top and pw-profiler.
+ { name = libpipewire-module-profiler }
+
+ # Allows applications to create metadata objects. It creates
+ # a factory for Metadata objects.
+ { name = libpipewire-module-metadata }
+
+ # Creates a factory for making devices that run in the
+ # context of the PipeWire server.
+ { name = libpipewire-module-spa-device-factory }
+
+ # Creates a factory for making nodes that run in the
+ # context of the PipeWire server.
+ { name = libpipewire-module-spa-node-factory }
+
+ # Allows creating nodes that run in the context of the
+ # client. Is used by all clients that want to provide
+ # data to PipeWire.
+ { name = libpipewire-module-client-node }
+
+ # Allows creating devices that run in the context of the
+ # client. Is used by the session manager.
+ { name = libpipewire-module-client-device }
+
+ # The portal module monitors the PID of the portal process
+ # and tags connections with the same PID as portal
+ # connections.
+ { name = libpipewire-module-portal
+ flags = [ ifexists nofail ]
+ }
+
+ # The access module can perform access checks and block
+ # new clients.
+ { name = libpipewire-module-access
+ args = {
+ # access.allowed to list an array of paths of allowed
+ # apps.
+ #access.allowed = [
+ # /usr/bin/pipewire-media-session
+ #]
+
+ # An array of rejected paths.
+ #access.rejected = [ ]
+
+ # An array of paths with restricted access.
+ #access.restricted = [ ]
+
+ # Anything not in the above lists gets assigned the
+ # access.force permission.
+ #access.force = flatpak
+ }
+ }
+
+ # Makes a factory for wrapping nodes in an adapter with a
+ # converter and resampler.
+ { name = libpipewire-module-adapter }
+
+ # Makes a factory for creating links between ports.
+ { name = libpipewire-module-link-factory }
+
+ # Provides factories to make session manager objects.
+ { name = libpipewire-module-session-manager }
+
+ # Use libcanberra to play X11 Bell
+ { name = libpipewire-module-x11-bell
+ args = {
+ #sink.name = ""
+ #sample.name = "bell-window-system"
+ #x11.display = null
+ #x11.xauthority = null
+ }
+ flags = [ ifexists nofail ]
+ }
+]
+
+context.objects = [
+ #{ factory = <factory-name>
+ # [ args = { <key> = <value> ... } ]
+ # [ flags = [ [ nofail ] ]
+ #}
+ #
+ # Creates an object from a PipeWire factory with the given parameters.
+ # If nofail is given, errors are ignored (and no object is created).
+ #
+ #{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } }
+ #{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags = [ nofail ] }
+ #{ factory = spa-device-factory args = { factory.name = api.alsa.enum.udev } }
+ #{ factory = spa-node-factory args = { factory.name = api.alsa.seq.bridge node.name = Internal-MIDI-Bridge } }
+ #{ factory = adapter args = { factory.name = audiotestsrc node.name = my-test } }
+ #{ factory = spa-node-factory args = { factory.name = api.vulkan.compute.source node.name = my-compute-source } }
+
+ # A default dummy driver. This handles nodes marked with the "node.always-driver"
+ # property when no other driver is currently active. JACK clients need this.
+ { factory = spa-node-factory
+ args = {
+ factory.name = support.node.driver
+ node.name = Dummy-Driver
+ node.group = pipewire.dummy
+ priority.driver = 20000
+ }
+ }
+ { factory = spa-node-factory
+ args = {
+ factory.name = support.node.driver
+ node.name = Freewheel-Driver
+ priority.driver = 19000
+ node.group = pipewire.freewheel
+ node.freewheel = true
+ }
+ }
+ # This creates a new Source node. It will have input ports
+ # that you can link, to provide audio for this source.
+ #{ factory = adapter
+ # args = {
+ # factory.name = support.null-audio-sink
+ # node.name = "my-mic"
+ # node.description = "Microphone"
+ # media.class = "Audio/Source/Virtual"
+ # audio.position = "FL,FR"
+ # }
+ #}
+
+ # This creates a single PCM source device for the given
+ # alsa device path hw:0. You can change source to sink
+ # to make a sink in the same way.
+ # { factory = adapter
+ # args = {
+ # factory.name = api.alsa.pcm.source
+ # node.name = "alsa-source"
+ # node.description = "PCM Source"
+ # media.class = "Audio/Source"
+ # api.alsa.path = "hw:Creative,0"
+ # api.alsa.period-size = 1024
+ # api.alsa.headroom = 0
+ # api.alsa.disable-mmap = false
+ # api.alsa.disable-batch = false
+ # audio.format = "S16LE"
+ # audio.rate = 48000
+ # audio.channels = 2
+ # audio.position = "FL,FR"
+ # }
+ # }
+
+ # { factory = adapter
+ # args = {
+ # factory.name = api.alsa.pcm.sink
+ # node.name = "alsa-dmix-internal"
+ # node.description = "PCM Internal"
+ # media.class = "Audio/Sink"
+ # api.alsa.path = "dmix:Creative,0"
+ # }
+ # }
+]
+
+context.exec = [
+ #{ path = <program-name> [ args = "<arguments>" ] }
+ #
+ # Execute the given program with arguments.
+ #
+ # You can optionally start the session manager here,
+ # but it is better to start it as a systemd service.
+ # Run the session manager with -h for options.
+ #
+ { path = "/usr/bin/wireplumber" args = "" }
+ #
+ # You can optionally start the pulseaudio-server here as well
+ # but it is better to start it as a systemd service.
+ # It can be interesting to start another daemon here that listens
+ # on another address with the -a option (eg. -a tcp:4713).
+ #
+ { path = "/usr/bin/pipewire" args = "-c pipewire-pulse.conf" }
+]
diff --git a/.config/shell/aliasrc b/.config/shell/aliasrc
new file mode 100644
index 0000000..a6d785a
--- /dev/null
+++ b/.config/shell/aliasrc
@@ -0,0 +1,87 @@
+## MISCELLANEOUS
+
+# various shortcuts
+alias reboot='sudo reboot'
+alias poweroff='sudo shutdown -hP now'
+alias refresh='. ~/.bashrc'
+alias jpwine='LANG=ja_JP.UTF-8 WINEDEBUG=-all wine'
+alias emd='/usr/bin/emacs --daemon &'
+alias emc='/usr/bin/emacsclient -c -a ""'
+alias z='zathura'
+alias nnn='nnn -Hde'
+alias cl='clear;fastfetch'
+alias ea='$EDITOR ~/.config/shell/aliasrc'
+alias cam='mpv --profile=low-latency --untimed /dev/video0'
+alias df='df -h'
+alias tma='tmux a -t'
+alias tmn='tmux new-session -A -s'
+alias wgu='sudo wg-quick up /etc/wireguard/wg0.conf'
+alias wgd='sudo wg-quick down /etc/wireguard/wg0.conf'
+alias zzz='sudo zzz'
+alias sc='echo -ne "\e[1 q"'
+alias ska="ps -u $USER | awk 'NR > 1 { print $1 }' | xargs -t kill"
+
+alias alert='pw-play /usr/share/sounds/freedesktop/stereo/complete.oga >/dev/null'
+
+# compiler
+alias clang='clang -march=native -O3 -flto=thin'
+
+# rename files in cwd in their current order to 4-digit numbers
+alias ofn='/bin/ls | cat -n | while read n f; do perl-rename "s/${f%.*}/$(printf "%04d" "$n")/" "$f"; done'
+
+# restart pipewire after suspend if it stops working
+alias pw-restart='pkill pipewire && sleep 3s && setsid pipewire &>/dev/null'
+
+# set preferred keyboard options
+alias setkeys='xset r rate 250 30 && setxkbmap -layout us,us -variant dvorak, -option "ctrl:nocaps,compose:rctrl"'
+
+# pad numbers in filenames with zeros
+alias padz='perl-rename "s/\d+/sprintf(\"%02d\",$&)/e"'
+
+# cp mv and rm always verbose
+alias cp='cp -v'
+alias mv='mv -v'
+alias rm='rm -v'
+
+# colorize grep output
+alias grep='grep --color=auto -i'
+alias zgrep='zgrep --color=auto -i'
+alias egrep='egrep --color=auto -i'
+
+# ls shortcuts
+alias ls='ls --color=always --group-directories-first'
+alias ll='ls -lh'
+alias la='ls -A'
+alias lla='ll -A'
+alias lc='ls | wc -l'
+
+# control audio
+alias headset='wpctl set-default "$(wpctl status | grep Headphones | cut -b11-12)"'
+alias speakers='wpctl set-default "$(wpctl status | grep Speakers | cut -b11-12)"'
+alias getvol='wpctl get-volume @DEFAULT_SINK@'
+alias setvol='wpctl set-volume @DEFAULT_SINK@'
+
+# shortcut for dotfiles repo
+alias dfiles='/usr/bin/git --git-dir=$HOME/.dotfiles --work-tree=$HOME'
+alias dcomm='dfiles commit -m'
+alias dpush='dfiles push'
+
+# package management
+alias qu='equery u'
+alias qd='equery d'
+alias qg='equery g'
+alias qf='equery f'
+alias qb='equery b'
+alias qy='equery y'
+
+alias eli='eix -c --installed'
+alias els='eix -c --selected'
+
+alias esync='sudo emaint sync'
+alias pemup='emerge -pvuND @world'
+alias emup='sudo emerge -vuND @world'
+alias pemin='emerge -pv'
+alias emin='sudo emerge -v'
+alias pemrm='emerge -pvc'
+alias emrm='sudo emerge -vc'
+alias emsr='emerge -s'
diff --git a/.config/shell/profile b/.config/shell/profile
new file mode 100644
index 0000000..b670bb0
--- /dev/null
+++ b/.config/shell/profile
@@ -0,0 +1,45 @@
+RUBY_VERSION=$(ruby -e 'puts RbConfig::CONFIG["ruby_version"]')
+export GEM_HOME="$HOME/.local/share/gem/ruby/$RUBY_VERSION"
+export GEM_PATH="$HOME/.local/share/gem/ruby/$RUBY_VERSION"
+
+export _JAVA_AWT_WM_NONREPARENTING=1
+export EDITOR="vim"
+export SUDO_EDITOR="nano"
+export TERMINAL="urxvt"
+export BROWSER="firefox"
+export LESS="-F -X $LESS"
+
+export XDG_CONFIG_HOME="$HOME/.config"
+export XDG_DATA_HOME="$HOME/.local/share"
+export XDG_CACHE_HOME="$HOME/.cache"
+
+export XINITRC="$XDG_CONFIG_HOME/x11/xinitrc"
+
+export MAIL=~/.mutt/Maildir
+export NO_AT_BRIDGE=1
+
+export GTK_IM_MODULE='ibus'
+export QT_IM_MODULE='ibus'
+export XMODIFIERS='@im=ibus'
+
+export VDPAU_DRIVER=radeonsi
+
+export EIX_LIMIT=0
+export EIX_LIMIT_COMPACT=0
+
+export LC_ALL=en_US.utf8
+
+export UV_CACHE_DIR="$HOME/.local/share/uv/cache"
+export SQLITE_HISTORY="$HOME/.cache/.sqlite_history"
+export PYTHON_HISTORY="$HOME/.cache/.python_history"
+
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib64"
+export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/lib64/pkgconfig"
+export INFOPATH="$INFOPATH:/usr/local/share/info:$HOME/.local/share/info"
+export MANPATH="$MANPATH:/usr/local/man"
+export GOPATH="$HOME/.local/share/go"
+export PATH="$PATH:$HOME/.local/bin:$HOME/.local/src/fzf/bin:$HOME/.npm/bin:/sbin:/usr/sbin:$GOPATH/bin:$HOME/.cargo/bin"
+
+[ -f $HOME/.bashrc ] && . $HOME/.bashrc
+
+[ -f /etc/motd.tcl ] && /etc/motd.tcl
diff --git a/.config/tmux/tmux.conf b/.config/tmux/tmux.conf
new file mode 100644
index 0000000..fe41598
--- /dev/null
+++ b/.config/tmux/tmux.conf
@@ -0,0 +1,91 @@
+set -g default-command "${SHELL}"
+
+# Status-bar settings
+set -g status-right "%A, %F - %H:%M"
+set -g window-status-current-style "underscore"
+set -g message-command-style 'fg=#000000,bg=#FFFF00'
+set -g message-style 'fg=#000000, bg=#FFFF00'
+# dark
+set -g status-bg '#333333'
+set -g status-fg '#FFFFFF'
+# light
+# set -g status-bg '#bbbbbb'
+# set -g status-fg '#000000'
+set -g set-titles on
+set -g set-titles-string "#T"
+set -g automatic-rename off
+
+# Enable RGB colour if running in xterm(1)
+# set-option -sa terminal-overrides ",xterm*:Tc"
+
+# Change the default $TERM to tmux-256color
+# set -g default-terminal "tmux-256color"
+
+# Set history-limit
+set -g history-limit 30000
+
+# enable emacs mode-keys
+set -g mode-keys emacs
+
+# various window option
+set -g base-index 1
+set -g pane-base-index 1
+set-window-option -g pane-base-index 1
+set-option -g renumber-windows on
+setw -g aggressive-resize on
+
+## KEYBINDS
+
+# Change the prefix key to C-a
+set -g prefix C-Space
+unbind C-b
+bind C-Space send-prefix
+
+unbind C-l
+
+bind C-s split-window -v -c "#{pane_current_path}"
+bind C-v split-window -h -c "#{pane_current_path}"
+bind C-w killp
+bind C-q killw
+bind C-p previous-window
+bind C-n next-window
+bind h select-pane -L
+bind j select-pane -D
+bind k select-pane -U
+bind l select-pane -R
+bind -n M-n swap-window -t +1\; select-window -t +1
+bind -n M-p swap-window -t -1\; select-window -t -1
+bind -n M-h resize-pane -L 5
+bind -n M-j resize-pane -D 5
+bind -n M-k resize-pane -U 5
+bind -n M-l resize-pane -R 5
+
+# Smart pane switching with awareness of Vim splits.
+# See: https://github.com/christoomey/vim-tmux-navigator
+# is_vim="ps -o state= -o comm= -t '#{pane_tty}' \
+# | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|l?n?vim?x?)(diff)?$'"
+# bind-key -n 'C-h' if-shell "$is_vim" 'send-keys C-h' 'select-pane -L'
+# bind-key -n 'C-j' if-shell "$is_vim" 'send-keys C-j' 'select-pane -D'
+# bind-key -n 'C-k' if-shell "$is_vim" 'send-keys C-k' 'select-pane -U'
+# bind-key -n 'C-l' if-shell "$is_vim" 'send-keys C-l' 'select-pane -R'
+# tmux_version='$(tmux -V | sed -En "s/^tmux ([0-9]+(.[0-9]+)?).*/\1/p")'
+# if-shell -b '[ "$(echo "$tmux_version < 3.0" | bc)" = 1 ]' \
+# "bind-key -n 'C-\\' if-shell \"$is_vim\" 'send-keys C-\\' 'select-pane -l'"
+# if-shell -b '[ "$(echo "$tmux_version >= 3.0" | bc)" = 1 ]' \
+# "bind-key -n 'C-\\' if-shell \"$is_vim\" 'send-keys C-\\\\' 'select-pane -l'"
+
+# bind-key -T copy-mode-vi 'C-H' select-pane -L
+# bind-key -T copy-mode-vi 'C-J' select-pane -D
+# bind-key -T copy-mode-vi 'C-K' select-pane -U
+# bind-key -T copy-mode-vi 'C-L' select-pane -R
+# bind-key -T copy-mode-vi 'C-\' select-pane -l
+
+bind C-l send-keys 'C-l'
+bind C-k send-keys 'C-k'
+
+# Turn the mouse off
+set -g mouse off
+
+# Keys to toggle monitoring activity in a window and the synchronize-panes option
+bind m set monitor-activity
+bind y set synchronize-panes\; display 'synchronize-panes #{?synchronize-panes,on,off}'
diff --git a/.config/wireplumber/wireplumber.conf.d/51-camera-microphone-input-rename.conf b/.config/wireplumber/wireplumber.conf.d/51-camera-microphone-input-rename.conf
new file mode 100644
index 0000000..8656458
--- /dev/null
+++ b/.config/wireplumber/wireplumber.conf.d/51-camera-microphone-input-rename.conf
@@ -0,0 +1,15 @@
+monitor.alsa.rules = [
+ {
+ matches = [
+ {
+ node.name = "alsa_input.usb-SunplusIT_Inc_FHD_Camera_Microphone_01.00.00-02.analog-stereo"
+ }
+ ]
+ actions = {
+ update-props = {
+ node.description = "Camera Microphone",
+ node.nick = "Camera Microphone"
+ }
+ }
+ }
+]
diff --git a/.config/wireplumber/wireplumber.conf.d/51-soundblaster-output-rename.conf b/.config/wireplumber/wireplumber.conf.d/51-soundblaster-output-rename.conf
new file mode 100644
index 0000000..75dd728
--- /dev/null
+++ b/.config/wireplumber/wireplumber.conf.d/51-soundblaster-output-rename.conf
@@ -0,0 +1,16 @@
+monitor.alsa.rules = [
+ {
+ matches = [
+ {
+ node.name = "alsa_output.pci-0000_29_00.0.analog-stereo"
+ }
+ ]
+ actions = {
+ update-props = {
+ node.description = "Headphones",
+ node.nick = "Headphones"
+ }
+ }
+ }
+]
+
diff --git a/.config/wireplumber/wireplumber.conf.d/51-starshipmatisse-output-rename.conf b/.config/wireplumber/wireplumber.conf.d/51-starshipmatisse-output-rename.conf
new file mode 100644
index 0000000..a6c6697
--- /dev/null
+++ b/.config/wireplumber/wireplumber.conf.d/51-starshipmatisse-output-rename.conf
@@ -0,0 +1,18 @@
+monitor.alsa.rules = [
+ {
+ matches = [
+ {
+ node.name = "alsa_output.pci-0000_31_00.4.iec958-stereo"
+ }
+ ]
+
+ actions = {
+ update-props = {
+ node.description = "Speakers",
+ node.nick = "Speakers"
+ }
+ }
+ }
+]
+
+
diff --git a/.config/wireplumber/wireplumber.conf.d/52-soundblaster-input-disable.conf b/.config/wireplumber/wireplumber.conf.d/52-soundblaster-input-disable.conf
new file mode 100644
index 0000000..b36545c
--- /dev/null
+++ b/.config/wireplumber/wireplumber.conf.d/52-soundblaster-input-disable.conf
@@ -0,0 +1,15 @@
+monitor.alsa.rules = [
+ {
+ matches = [
+ {
+ node.name = "alsa_input.pci-0000_29_00.0.analog-stereo"
+ }
+ ]
+ actions = {
+ update-props = {
+ node.disabled = true
+ }
+ }
+ }
+]
+
diff --git a/.config/wireplumber/wireplumber.conf.d/52-starshipmatisse-input-disable.conf b/.config/wireplumber/wireplumber.conf.d/52-starshipmatisse-input-disable.conf
new file mode 100644
index 0000000..86cdd32
--- /dev/null
+++ b/.config/wireplumber/wireplumber.conf.d/52-starshipmatisse-input-disable.conf
@@ -0,0 +1,15 @@
+monitor.alsa.rules = [
+ {
+ matches = [
+ {
+ node.name = "alsa_input.pci-0000_31_00.4.analog-stereo"
+ }
+ ]
+ actions = {
+ update-props = {
+ node.disabled = true
+ }
+ }
+ }
+]
+
diff --git a/.config/x11/xinitrc b/.config/x11/xinitrc
new file mode 100644
index 0000000..e734be9
--- /dev/null
+++ b/.config/x11/xinitrc
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+if command -v dbus-launch > /dev/null && test -z "${DBUS_SESSION_BUS_ADDRESS}"; then
+ eval $(dbus-launch --sh-syntax --exit-with-session)
+fi
+
+eval "$(gpg-agent --daemon --allow-preset-passphrase)"
+eval "$(ssh-agent)"
+
+#xrandr --addmode HDMI-A-0 1280x1024
+# xrandr --output HDMI-A-0 --mode 1280x960 --primary
+# xrandr --output DisplayPort-1 --mode 1920x1080 --left-of HDMI-A-0
+xrandr --output DisplayPort-1 --mode 1920x1080 --primary
+xrandr --output HDMI-A-0 --mode 1280x960 --left-of DisplayPort-1
+
+xset r rate 250 30
+xset s off -dpms
+setxkbmap -option "ctrl:nocaps,compose:rctrl"
+xrdb -merge ~/.Xresources
+
+/usr/bin/dunst &
+/usr/bin/ibus-daemon -d -r -x
+/usr/bin/otd-daemon &
+/usr/bin/urxvtd -q -o -f &
+/usr/bin/emacs --daemon &
+/usr/bin/pipewire &
+/usr/bin/mpd &
+$HOME/.local/bin/set-wallpaper &
+/usr/libexec/polkit-gnome-authentication-agent-1 &
+#$HOME/.cargo/bin/mpd-discord-rpc &
+
+exec i3
diff --git a/.config/yt-dlp/config b/.config/yt-dlp/config
new file mode 100644
index 0000000..aeb3bda
--- /dev/null
+++ b/.config/yt-dlp/config
@@ -0,0 +1 @@
+--verbose -ciw -f bestvideo[ext=mp4][vcodec!*=av01]+bestaudio[ext=m4a]/best[ext=mp4]/best --merge-output-format mkv
diff --git a/.config/zathura/zathurarc b/.config/zathura/zathurarc
new file mode 100644
index 0000000..96c2ac1
--- /dev/null
+++ b/.config/zathura/zathurarc
@@ -0,0 +1,29 @@
+set statusbar-h-padding 0
+set statusbar-v-padding 0
+set page-padding 1
+set selection-clipboard clipboard
+set database "sqlite"
+
+map u scroll half-up
+map d scroll half-down
+map n scroll full-down
+map p scroll full-up
+map [fullscreen] u scroll half-up
+map [fullscreen] d scroll half-down
+map [fullscreen] n scroll full-down
+map [fullscreen] p scroll full-up
+map D toggle_page_mode
+map r reload
+map R rotate
+map i recolor
+map ^p print
+map g goto top
+
+#stop at page boundries
+set scroll-page-aware true
+set scroll-full-overlap 0.01
+set scroll-step 100
+
+unmap f
+map f toggle_fullscreen
+map [fullscreen] f toggle_fullscreen
diff --git a/.ratpoisonrc b/.ratpoisonrc
new file mode 100644
index 0000000..300f7c6
--- /dev/null
+++ b/.ratpoisonrc
@@ -0,0 +1,108 @@
+set rudeness 15
+
+# set info style
+set winname class
+set wingravity center
+set transgravity center
+set border 0
+set bgcolor #000000
+set fgcolor #c4c4c4
+
+# Set the escape key to the ctrl-z
+escape C-z
+
+# ALIASES
+
+# store frames
+alias store-fs1 exec ratpoison -c "setenv fs1 `ratpoison -c 'fdump'`"
+alias restore-fs1 exec ratpoison -c "frestore `ratpoison -c 'getenv fs1'`"
+alias store-fs2 exec ratpoison -c "setenv fs2 `ratpoison -c 'fdump'`"
+alias restore-fs2 exec ratpoison -c "frestore `ratpoison -c 'getenv fs2'`"
+alias store-fs3 exec ratpoison -c "setenv fs3 `ratpoison -c 'fdump'`"
+alias restore-fs3 exec ratpoison -c "frestore `ratpoison -c 'getenv fs3'`"
+alias store-fs4 exec ratpoison -c "setenv fs4 `ratpoison -c 'fdump'`"
+alias restore-fs4 exec ratpoison -c "frestore `ratpoison -c 'getenv fs4'`"
+alias store-fs5 exec ratpoison -c "setenv fs5 `ratpoison -c 'fdump'`"
+alias restore-fs5 exec ratpoison -c "frestore `ratpoison -c 'getenv fs5'`"
+
+## KEYBINDS
+
+# unbind defaults
+unbind b
+unbind C-b
+unbind a
+unbind C-a
+unbind p
+unbind C-p
+unbind n
+unbind C-n
+unbind f
+unbind C-f
+unbind m
+unbind C-m
+unbind l
+unbind C-l
+unbind c
+unbind C-c
+unbind exclam
+unbind C-exclam
+unbind Return
+unbind C-Return
+unbind N
+unbind P
+
+
+# switch between windows and monitors
+definekey top s-n next
+definekey top s-p prev
+definekey top s-Left nextscreen
+definekey top s-Right nextscreen
+
+# manage frames and windows
+definekey top s-h focusleft
+definekey top s-j focusdown
+definekey top s-k focusup
+definekey top s-l focusright
+definekey top s-H exchangeleft
+definekey top s-J exchangedown
+definekey top s-K exchangeup
+definekey top s-L exchangeright
+definekey top s-C delete
+
+bind M-exclam store-fs1
+bind M-1 restore-fs1
+bind M-at store-fs2
+bind M-2 restore-fs2
+bind M-numbersign store-fs3
+bind M-3 restore-fs3
+bind M-dollar store-fs4
+bind M-4 restore-fs4
+bind M-percent store-fs5
+bind M-5 restore-fs5
+
+# volume and media control
+definekey top XF86AudioRaiseVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
+definekey top XF86AudioLowerVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
+definekey top XF86AudioMute exec wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
+definekey top XF86AudioPrev exec mpc prev && exec ratpoison -c "echo $(mpc status)"
+definekey top XF86AudioNext exec mpc next && exec ratpoison -c "echo $(mpc status)"
+definekey top XF86AudioPlay exec mpc toggle && exec ratpoison -c "echo $(mpc status)"
+
+# Information
+bind C-v exec ratpoison -c "echo $(wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk '{ print $3,$1,$2*100 }')%"
+bind C-a exec ratpoison -c "echo `date +'%A, %F - %H:%M'` `cal | tail -n +2 | sed -e 's/^Su/\n\n Su/' -e 's/.*/ & /' -e \"s/\ $(date +%e) /\<$(date +%e)\>/\"`"
+bind C-d exec ratpoison -c "echo $(date +'%A, %F - %H:%M')"
+bind C-t exec ratpoison -c "echo $(sensors | sed -n /Tctl/p | sed 's/Tctl: *+/Temp: /')"
+
+# application keybinds
+definekey top s-Return exec urxvtc
+definekey top s-z exec boomer
+definekey top s-Print exec scrot -s -e 'mv $f ~/pics/'
+definekey top Print exec scrot -u -e 'mv $f ~/pics/'
+definekey top s-b exec bm -o
+definekey top s-B exec bm -y
+bind C-f exec firefox -P default-release
+bind C-F exec firefox -P less-strict
+bind C-e exec emacsclient -c -a ""
+bind C-m exec mpdmenu
+bind C-p exec dmenu_run -m 0 -fn "xft:Hack Nerd Font:size=11" -nb "#222222" -nf "#bbbbbb" -sb "#005577" -sf "#eeeeee"
diff --git a/.vim/vimrc b/.vim/vimrc
new file mode 100644
index 0000000..6e6e218
--- /dev/null
+++ b/.vim/vimrc
@@ -0,0 +1,90 @@
+syntax enable
+filetype plugin on
+
+set nocompatible
+
+set path+=**
+set wildmenu
+
+set modeline
+set modelines=5
+set nobackup
+set nowritebackup
+set updatetime=300
+set hidden
+set noswapfile
+set laststatus=2
+set shortmess+=c
+set clipboard=unnamedplus
+
+let g:markdown_fenced_languages = ['javascript', 'js=javascript', 'json=javascript']
+
+let $RTP=split(&runtimepath, ',')[0]
+let $RC="$HOME/.vim/vimrc"
+
+call plug#begin()
+
+Plug 'dracula/vim', { 'as': 'dracula' }
+Plug 'NLKNguyen/papercolor-theme'
+Plug 'tpope/vim-commentary'
+Plug 'tpope/vim-surround'
+Plug 'preservim/nerdtree'
+
+call plug#end()
+
+" set background=light
+" colorscheme PaperColor
+" colorscheme catppuccin-latte
+
+set background=dark
+colorscheme dracula
+
+highlight Normal ctermbg=black
+set autoindent
+set smartindent
+set expandtab
+set tabstop=4
+set shiftwidth=4
+
+set number
+set relativenumber
+set showcmd
+set cmdheight=1
+set completeopt=menuone,noinsert,noselect
+set splitright
+set splitbelow
+set ignorecase
+set smartcase
+set showmatch
+set signcolumn=yes
+set incsearch
+set diffopt+=vertical
+
+" Use ^] to jump to tag under cursor
+" Use g^] for ambiguous tags
+" Use ^t to jump back up the tag stack
+command! MakeTags !ctags -R *
+
+let mapleader="\<space>"
+
+nmap <silent> <c-h> <c-w>h
+nmap <silent> <c-j> <c-w>j
+nmap <silent> <c-k> <c-w>k
+nmap <silent> <c-l> <c-w>l
+
+nmap <leader>Q :bufdo bdelete<CR>
+nnoremap <C-c> :bp\|bd #<CR>
+
+nmap <leader>ve :edit $MYVIMRC<CR>
+nmap <leader>vr :source $MYVIMRC <bar> :doautocmd BufRead<CR>
+
+nnoremap <leader>d :cd %:p:h<CR>:pwd<CR>
+
+nnoremap <leader><C-n> :NERDTreeToggle<CR>
+nnoremap <leader>n :NERDTreeFocus<CR>
+
+map gf :edit <cfile><CR>
+
+nmap <leader>x :!xdg-open %<CR><CR>
+
+nmap <leader>s :!echo -ne "\e[2 q"<CR><CR>
diff --git a/.xinitrc b/.xinitrc
new file mode 120000
index 0000000..518bb5d
--- /dev/null
+++ b/.xinitrc
@@ -0,0 +1 @@
+.config/x11/xinitrc \ No newline at end of file