Compare commits
12 Commits
main
...
75-organiz
Author | SHA1 | Date |
---|---|---|
Marty Oehme | 1059f8eb9f | |
Marty Oehme | 69511541ec | |
Marty Oehme | 52b054a2d2 | |
Marty Oehme | 0e6e61c470 | |
Marty Oehme | 7242ffce62 | |
Marty Oehme | 2f7a484b16 | |
Marty Oehme | b40b6d8c74 | |
Marty Oehme | c9f0ca46e3 | |
Marty Oehme | bb900a090f | |
Marty Oehme | 902ca7dc02 | |
Marty Oehme | 8e3dae10fe | |
Marty Oehme | 3aed80e072 |
Binary file not shown.
Before Width: | Height: | Size: 194 KiB |
Binary file not shown.
Before Width: | Height: | Size: 946 KiB |
Binary file not shown.
Before Width: | Height: | Size: 217 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.2 KiB |
|
@ -1,100 +0,0 @@
|
|||
[helpers]
|
||||
|
||||
# BASE: A base system. Sets up a nice xdg (zsh) shell environment, utility scripts and
|
||||
# a development environment based on git and nvim.
|
||||
|
||||
[base]
|
||||
depends = ["shell", "git", "nvim", "scripts", "ssh", "terminal", "bootstrap"]
|
||||
|
||||
[bootstrap.files]
|
||||
"bootstrap/dotlink.sh" = "~/.config/sh/alias.d/dotlink.sh"
|
||||
|
||||
[shell.files]
|
||||
"sh/README.md" = { target = "~/NOWHERE", type = "symbolic", if = "false" }
|
||||
sh = "~"
|
||||
|
||||
[git.files]
|
||||
"git/README.md" = { target = "~/NOWHERE", type = "symbolic", if = "false" }
|
||||
git = "~"
|
||||
|
||||
[nvim.files]
|
||||
"nvim/.config/nvim/spell/de.utf-8.add.spl" = { target = "~/.config/nvim/spell/de.utf-8.add.spl", type = "symbolic" }
|
||||
"nvim/.config/nvim/spell/en.utf-8.add.spl" = { target = "~/.config/nvim/spell/en.utf-8.add.spl", type = "symbolic" }
|
||||
nvim = "~"
|
||||
|
||||
[scripts.files]
|
||||
"scripts/README.md" = { target = "~/NOWHERE", type = "symbolic", if = "false" }
|
||||
scripts = "~"
|
||||
|
||||
[ssh.files]
|
||||
ssh = "~"
|
||||
|
||||
[terminal.files]
|
||||
"terminal/.config/vifm" = "~/.config/vifm"
|
||||
"terminal/.config/vifm/vifmrc" = { target = "~/.config/vifm/vifmrc", type = "symbolic" }
|
||||
terminal = "~"
|
||||
|
||||
# LINUX: A linux machine, with systemd enabled, auto-mounting set up and a nice productivity suite.
|
||||
|
||||
[linux]
|
||||
depends = ["base", "disks", "pass", "office", "services", "social", "writing"]
|
||||
|
||||
[disks.files]
|
||||
"disks/README.md" = { target = "~/NOWHERE", type = "symbolic", if = "false" }
|
||||
disks = "~"
|
||||
|
||||
[pass.files]
|
||||
"pass/README.md" = { target = "~/NOWHERE", type = "symbolic", if = "false" }
|
||||
"pass/.local/share/pass-pick/assets/rofi-menu.gif" = { target = "~/NOWHERE", type = "symbolic", if = "false" }
|
||||
pass = "~"
|
||||
|
||||
[office.files]
|
||||
"office/README.md" = { target = "~/NOWHERE", type = "symbolic", if = "false" }
|
||||
"office/.config/glow/email.json" = { target = "~/.config/glow/email.json", type = "symbolic" }
|
||||
"office/.config/isync/mbsyncrc" = { target = "~/.config/isync/mbsyncrc", type = "template" }
|
||||
"office/.config/msmtp/config" = { target = "~/.config/msmtp/config", type = "template" }
|
||||
"office/.config/neomutt/account" = { target = "~/.config/neomutt/account", type = "template" }
|
||||
"office/.config/neomutt/profile.gmail" = { target = "~/.config/neomutt/profile.gmail", type = "template" }
|
||||
"office/.config/neomutt/profile.private" = { target = "~/.config/neomutt/profile.private", type = "template" }
|
||||
office = "~"
|
||||
|
||||
[services.files]
|
||||
"services/README.md" = { target = "~/NOWHERE", type = "symbolic", if = "false" }
|
||||
services = "~"
|
||||
|
||||
[social.files]
|
||||
social = "~"
|
||||
|
||||
[writing.files]
|
||||
"writing/README.md" = { target = "~/NOWHERE", type = "symbolic", if = "false" }
|
||||
"writing/.config/papis/papistui.yaml" = { target = "~/.config/papis/papistui.yaml", type = "symbolic" }
|
||||
"writing/.config/sioyek/prefs_user.config" = { target = "~/.config/sioyek/prefs_user.config", type = "template", prepend = "# TEMPLATED BY DOTTER\n" }
|
||||
writing = "~"
|
||||
|
||||
# WORKSTATION: A desktop machine, with wayland environment and display attached.
|
||||
|
||||
[workstation]
|
||||
depends = ["linux", "desktop", "multimedia", "qutebrowser"]
|
||||
|
||||
[desktop.files]
|
||||
"desktop/.config/flavours/templates" = { target = "~/.config/flavours/templates", type = "symbolic" }
|
||||
"desktop/.config/waybar/config" = { target = "~/.config/waybar/config", type = "symbolic" }
|
||||
"desktop/.config/mako/config" = { target = "~/.config/mako/config", type = "template", prepend = "# TEMPLATED BY DOTTER\n" }
|
||||
"desktop/README.md" = { target = "~/NOWHERE", type = "symbolic", if = "false" }
|
||||
desktop = "~"
|
||||
|
||||
[multimedia.files]
|
||||
"multimedia/README.md" = { target = "~/NOWHERE", type = "symbolic", if = "false" }
|
||||
"multimedia/.config/mpv/scripts" = { target = "~/.config/mpv/scripts", type = "symbolic" }
|
||||
"multimedia/.config/ncmpcpp/config" = { target = "~/.config/ncmpcpp/config", type = "symbolic" }
|
||||
"multimedia/.config/mpv/fonts/uosc_icons.otf" = { target = "~/.config/mpv/fonts/uosc_icons.otf", type = "symbolic" }
|
||||
"multimedia/.config/mpv/fonts/uosc_textures.ttf" = { target = "~/.config/mpv/fonts/uosc_textures.ttf", type = "symbolic" }
|
||||
multimedia = "~"
|
||||
|
||||
[qutebrowser.files]
|
||||
"qutebrowser/config" = "~/.config/qutebrowser"
|
||||
"qutebrowser/scripts" = "~/.local/bin"
|
||||
"qutebrowser/data" = "~/.local/share/qutebrowser"
|
||||
|
||||
[system.files]
|
||||
"bootstrap/system-packages" = { target = "/", type = "symbolic", owner = "root" }
|
|
@ -1,13 +0,0 @@
|
|||
includes = []
|
||||
packages = ["workstation"]
|
||||
|
||||
[files]
|
||||
|
||||
[variables]
|
||||
|
||||
multimedia_beets_musicbrainz_user = ""
|
||||
multimedia_beets_musicbrainz_pass = ""
|
||||
multimedia_mopidy_subidy_url = ""
|
||||
multimedia_mopidy_subidy_user = ""
|
||||
multimedia_mopidy_subidy_pass = ""
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
packages = ["system", "workstation"]
|
||||
|
||||
[files]
|
||||
|
||||
[variables]
|
|
@ -1,27 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
COMMIT_MSG_FILE="$1"
|
||||
COMMIT_SOURCE="$2"
|
||||
|
||||
BOOTSTRAPDIR="bootstrap"
|
||||
pkg_committed="$(cat "$(git rev-parse --show-toplevel)"/$BOOTSTRAPDIR/packages*.tsv | grep -v -e '^Name Description Source Target' | cut -f1 | sort)"
|
||||
pkg_onsystem=$(pacman -Qqett | sort)
|
||||
|
||||
# get files only in repo, and only on machine
|
||||
only_committed=$(comm -23 <(echo "$pkg_committed") <(echo "$pkg_onsystem"))
|
||||
only_onsystem=$(comm -13 <(echo "$pkg_committed") <(echo "$pkg_onsystem"))
|
||||
|
||||
# if we have no changes, do nothing
|
||||
if [ -n "$only_onsystem" ] || [ -n "$only_committed" ]; then
|
||||
text=$(printf "\-- PACKAGE CHANGES --\nPackages on machine but not committed:\n%s\n\nPackages committed but not on machine:\n%s\n" "$only_onsystem" "$only_committed" | sed 's/^/# /gm')
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# prepend package changes to message
|
||||
case $COMMIT_SOURCE in
|
||||
"" | message, | template,)
|
||||
msg=$(echo "$text" | cat - "$COMMIT_MSG_FILE")
|
||||
printf "%s" "$msg" >"$COMMIT_MSG_FILE"
|
||||
;;
|
||||
esac
|
|
@ -1,7 +1,3 @@
|
|||
# don't move the cache into repo
|
||||
/.dotter/cache/
|
||||
/.dotter/cache.toml
|
||||
|
||||
# no idea why gopass adds this image to config path
|
||||
gopass-logo-small.png
|
||||
#
|
||||
|
@ -10,40 +6,3 @@ colorscheme.vim
|
|||
colorscheme
|
||||
colorscheme.py
|
||||
colorscheme.rasi
|
||||
|
||||
.assets/README.md
|
||||
|
||||
# do not add massive gutenberg thesaurus to repo
|
||||
nvim/.config/nvim/thesaurus
|
||||
# if we have a pre-compiled plugin list don't add it
|
||||
nvim/.config/nvim/plugin/packer_compiled.lua
|
||||
|
||||
# mpv sponsorblock api
|
||||
sponsorblock.db
|
||||
sponsorblock.db.tmp
|
||||
sponsorblock.txt
|
||||
|
||||
# ignore any just-in-time settings that took place in qutebrowser
|
||||
/qutebrowser/.config/qutebrowser/autoconfig.yml
|
||||
/qutebrowser/.config/qutebrowser/bookmarks
|
||||
/qutebrowser/.config/qutebrowser/qsettings
|
||||
/qutebrowser/.config/qutebrowser/quickmarks
|
||||
/qutebrowser/.config/qutebrowser/stylesheets
|
||||
# ignore the generated readability file for webpages
|
||||
readability.html
|
||||
# ignore the adblock file generated by qutebrowser
|
||||
blocked-hosts
|
||||
|
||||
# ignore vifm & ueberzug utility files
|
||||
vifm-help.txt
|
||||
vifmimgpdfpage
|
||||
vifmimgpdffile
|
||||
vifminfo
|
||||
vifminfo.json
|
||||
|
||||
# styler configs
|
||||
colorscheme.yml
|
||||
|
||||
# taskwarrior
|
||||
office/.config/task/task-sync.rc
|
||||
office/.config/task/contexts
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
# This file is a template, and might need editing before it works on your project.
|
||||
# see https://docs.gitlab.com/ce/ci/yaml/README.html for all available options
|
||||
|
||||
# you can delete this line if you're not using Docker
|
||||
image: fnichol/check-shell:latest
|
||||
|
||||
analyze:
|
||||
stage: test
|
||||
before_script:
|
||||
- shellcheck --version
|
||||
script:
|
||||
- echo "--------- CHECKING POSIX SHELLSCRIPTS -------------"
|
||||
- find . -type f -name '*.sh' | xargs shellcheck -Calways
|
||||
- echo "--------- CHECKING ZSH SHELLSCRIPTS -------------"
|
||||
- find . -type f -name '*.zsh' | xargs shellcheck -Calways -s bash -e SC2034
|
||||
|
||||
lint:
|
||||
stage: test
|
||||
before_script:
|
||||
- shfmt -version
|
||||
script:
|
||||
- echo "--------- CHECKING POSIX SHELLSCRIPTS -------------"
|
||||
- find . -type f -name '*.sh' | xargs shfmt -d -i 2
|
||||
- echo "--------- CHECKING ZSH SHELLSCRIPTS -------------"
|
||||
- find . -type f -name '*.zsh' | xargs shfmt -d -i 2
|
||||
|
||||
test:
|
||||
stage: test
|
||||
image: alpine
|
||||
before_script:
|
||||
- apk add git bash
|
||||
- git clone https://github.com/bats-core/bats-core.git /bats
|
||||
- /bats/bin/bats --version
|
||||
script:
|
||||
- /bats/bin/bats -r .
|
|
@ -1,6 +0,0 @@
|
|||
[submodule "pass/.local/share/pass-pick"]
|
||||
path = pass/.local/share/pass-pick
|
||||
url = https://git.martyoeh.me/Marty/pass-pick.git
|
||||
[submodule "scripts/.local/share/uoeia"]
|
||||
path = scripts/.local/share/uoeia
|
||||
url = https://git.martyoeh.me/Marty/uoeia.git
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Marty Oehme
|
||||
Copyright (c) 2019 Marty Oehme
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
87
README.md
87
README.md
|
@ -1,78 +1,57 @@
|
|||
# `~/🌹`
|
||||
|
||||
Note that the below screenshots still show the X configuration from [v0.1](https://gitlab.com/marty-oehme/dotfiles/-/tags/v0.1) which is *very* old by now.
|
||||
The current dotfiles are geared toward wayland for which the setup looks similar but not identical to the previews below.
|
||||
# dotfiles Read-Me and Roadmap
|
||||
|
||||
## What's in these dotfiles
|
||||
|
||||
* [x] wayland setup using `riverwm` with quick access to many overlays and picking tools for styles, downloads, browsing history, passwords and more
|
||||
* [x] vim configuration for simple programming tasks (especially python/bash/lua) and prose (markdown/quarto/latex)
|
||||
* [x] vim configuration for simple programming tasks (especially go/typescript/python/bash) and prose
|
||||
* [x] academic workflow tools, to allow quick citation, pdf compilation, and preview
|
||||
* [x] simple, efficient waybar with package update notification and mpris integration
|
||||
* [x] system-wide color management (terminals, vim, qutebrowser, polybar, xresources) through [`flavours`](https://github.com/Misterio77/flavours) application using [base16](http://chriskempson.com/projects/base16/) themes
|
||||
* [x] quick theme switching by activating `flavours` and fuzzy-searching themes with hot-key (default `<Super>=<Shift>+S`)
|
||||
* [x] quick directory jumping using `z`, with `fzf` integration
|
||||
* [x] `fzf`-like integrations for bibtex citation, vim buffer management, most recently used switching, shell command history, and more
|
||||
* [x] password management with `pass` and picking it with automatic typing into any window
|
||||
* [x] simple, efficient polybar with package update notification, and spotify (mpris) integration
|
||||
* [x] tmux session management through `tm` and `tl` tools
|
||||
* [x] tmux fuzzy-searching of terminal sessions to switch to with hot-key (`<C-A><C-j>`)
|
||||
* [x] system-wide color management (terminals, vim, qutebrowser, polybar, xresources) through `styler` command using [base16](http://chriskempson.com/projects/base16/) themes
|
||||
* [x] quick theme switching by activating `styler` and fuzzy-searching themes with hot-key (`<Super>+F8`)
|
||||
* [x] many vim color-schemes with quick light/dark switching (`F8`) and individual theme switch (`<Space>+F8`)
|
||||
* [x] quick directory jumping using z, with fzf integration
|
||||
* [x] fzf integrations for bibtex citation, vim buffer management, most recently used switching, shell command history, and more
|
||||
|
||||
[![Styler recoloring demo](https://gitlab.com/marty-oehme/dotfiles/-/wikis/uploads/bde87deda694590a2e08e21552e11309/styler.webp)](https://gitlab.com/marty-oehme/dotfiles/-/wikis/uploads/90894e53eff378db4d7f9f49e7a69fab/styler.mp4)
|
||||
![Overview](_assets/gaps.png)
|
||||
|
||||
## Quick-Start
|
||||
|
||||
The dotfiles use `dotter` to link themselves in the home directory. You can clone this repository anywhere (though I have mine in `~/.dotfiles` as it seemed most logical for me).
|
||||
|
||||
I would recommend doing an initial `git clone --recursive` for this repository, since it contains git [submodules](https://nering.dev/2016/git-submodules-vs-subtrees/), which will then automatically get pulled in as well.
|
||||
Of course, you can do it non-recursively and then just pull those modules selectively which you actually want.
|
||||
The dotfiles use `GNU stow` to link themselves in the home directory. You can clone this repository anywhere (though I have mine in `~/.dotfiles` as it seemed most logical for me).
|
||||
|
||||
Once in the repository directory, when you then run `./install.sh` it will install many of the packages I use (though they are probably slightly out-of-date) and link the dotfiles into the home directory.
|
||||
I would mostly recommend this on fresh machines or a test machine first - it *will* link my personal dotfiles and, if you allow it, *will* install quite a few packages.
|
||||
By default it will ask your consent for some steps -- use `./install.sh -f` to force yes to everything.
|
||||
|
||||
The dotfile installation procedure is based on `dotter`, it will generally *not overwrite* anything already in the home directory, but of course be observant when doing ptentially destructive operations.
|
||||
|
||||
> **NOTE**
|
||||
> The same non-destructive installation procedure does *not* apply to the package installation and system setting file linking, where it can potentially overwrite or remove existing files.
|
||||
|
||||
After all files are linked and you open a new shell session, the `dotlink` alias will allow you to re-link all dotfiles from anywhere on the system.[^1]
|
||||
|
||||
[^1]: This alias only works when the dotfiles are cloned into `~/.dotfiles`, mirroring my setup.
|
||||
This is due to a hard-coded cd into this directory.
|
||||
If your dotfiles lie in another directory and you want to use the dotlink alias, simply change the corresponding line in `bootstrap/.config/sh/alias.d/dotlink.sh`
|
||||
Since it is based on `stow`, it will not overwrite anything already in the home directory.
|
||||
If you do not want to install any packages but only link the dotfiles run `./_bootstrap/autostow.sh -s`, once again from the main repository directory.
|
||||
|
||||
Both automatic installation paths are presumably somewhat brittle. In any case, I would suggest to manually look through the files for things you want instead of copying and activating everything.
|
||||
Dotfiles are too personal to be standardized like that.
|
||||
They're pets, not cattle.
|
||||
Enjoy!
|
||||
|
||||
## Main Modules
|
||||
## Main Applications
|
||||
|
||||
![Overview - an older image of the dotfile desktop with gaps, showing git logs, styler logs, duckduckgo in a browser, and a vifm view of the dotfiles themselves](https://gitlab.com/marty-oehme/dotfiles/-/wikis/uploads/aaf0319d575dc192ea0f4bd6eaf83c08/gaps.png)
|
||||
|
||||
* [`wayland`](https://github.com/wayland-project/wayland) - Containing basics for fully functional tiling wayland setup:
|
||||
* [`river`](https://github.com/riverwm/river) - Tiling window manager for wayland
|
||||
* [`waybar`](https://github.com/Alexays/Waybar) - Easily customizable statusbar for wayland
|
||||
* [`bemenu`](https://github.com/Cloudef/bemenu) - Extended dmenu replacement for wayland, X11 and ncurses
|
||||
* [`fontconfig`] - System-wide font replacements and styling settings
|
||||
* [`wezterm`](https://wezfurlong.org/wezterm/) - Terminal emulator (fast, understandable and lua configurable)
|
||||
* [`tmux`](https://github.com/tmux/tmux/) - terminal multiplexer (slowly migrating away in favor of wezterm)
|
||||
* [`alacritty`](https://github.com/jwilm/alacritty) - Terminal emulator (GPU accelerated and customizable)
|
||||
* [`gopass`](https://github.com/gopasspw/gopass) - Password management suite, building on (and largely compatible with)
|
||||
`pass` for unix
|
||||
* [`i3`](https://i3wm.org/) - Tiling window manager
|
||||
* [`nvim`](https://neovim.io/) - Neovim configuration
|
||||
* [`vifm`](https://github.com/vifm/vifm) - vim-like file-manager
|
||||
* [`pandoc`](https://pandoc.org) - Pandoc plaintext transformation options (mostly latex templates)
|
||||
* [`picom`](https://github.com/yshui/picom) - X11 compositor (maintained fork from compton)
|
||||
* [`polybar`](https://github.com/polybar/polybar) - Easy to customize statusbar
|
||||
* [`qutebrowser`](https://github.com/qutebrowser/qutebrowser) - vim-key enabled web browser
|
||||
* [`pass`](pass/README.md) - Password management suite
|
||||
* [`bibtex`] - LateX/BibteX/pandoc plaintext writing & reference suite
|
||||
* [`git`](git/README.md) - distributed version control system.
|
||||
* [`office`](office/README.md) - office/productivity software for writing e-mail and setting appointments
|
||||
* [`rofi`](https://github.com/davatorium/rofi) - Application launcher, dmenu replacement
|
||||
* [`sxhkd`](https://github.com/baskerville/sxhkd) - X11 hotkey manager
|
||||
* [`tmux`](https://github.com/tmux/tmux/) - terminal multiplexer
|
||||
* [`vifm`](https://github.com/vifm/vifm) - vim-like file-manager
|
||||
|
||||
## Notes
|
||||
|
||||
* Generally, most configuration for applications attempts to follow the XDG specifications, keeping configuration in .config directory and supplementary files in .local/share directory. Over time, I am moving more applications to this standard: it keeps the home directory clean, and the separation of configuration, binaries, and data relatively clear.
|
||||
* The `zsh` directory contains all setup for the z-shell, my daily work environment. It should not be required for working with any other module but will add additional functionality to many (such as command auto-completion and so on). `sh` sets some base functionality for any shell you may wish to work in. It is, for now, the only module that is required for some other modules to work.[^shreq]
|
||||
* `rofi` contains additional scripts and a simple theming framework for rofi and should probably be reorganized to put the correct files into the correct directories (per xdg) at some point.
|
||||
* Whereas `sh` module scripts are requirements for other scripts, `.local/bin` in the `scripts` module contains most executable user scripts. Most of these have been migrated to other corresponding modules (e.g. if a script exclusively targets git functionality, it will live there), some useful --- or left-over --- stand-alone scripts remain however.
|
||||
* `.local/share/pandoc` contains configuration for academic latex writing (pandoc, really) and is of interest if you want to use this functionality.
|
||||
* `.xinitrc` is used for x initialization and program startup. At some point, some of the consistently running applications may be moved to systemd/runit as supervised services.
|
||||
* Generally, top-level directories starting with a . are only meaningful for the *repository* not for the functionality of the machine that these dotfiles are deployed on. That means `.gitlab-ci.yml`, `.assets/`, `.gitignore` and similar files and directories will not show up in the final deployment in any home directory. Perhaps they should be called dotdot-files since they're the dotfiles for my dotfiles. 🙂 (Also, '[dotfiles](https://en.wikipedia.org/wiki/Semantic_satiation)'.)
|
||||
* `.config/shell` contains all the general zsh/bash/sh configuration and environment variables usually contained in `.zshrc`, `.zprofile` and similar. It is divided in login shell config (loginrc.d), general shell config (rc.d) and zsh specific (zsh.d). Over time this should be migrated to specific `stow` 'units', but for now here is where it is.
|
||||
* `.config/rofi` contains additional scripts and a simple theming framework for rofi and should probably be migrated into the correct directories at some point.
|
||||
* `.local/bin` in `scripts` `stow` unit contains most executable user scripts.
|
||||
* `.local/share/pandoc` contains configuration for academic latex (pandoc, really) writing and is of interest if you want to use this functionality.
|
||||
* `.xinitrc` is used for x initialization and program startup.
|
||||
* `.gitlab-ci.yml` is only used for simple CI code linting and static analysis on gitlab, can be deleted on individual deployments.
|
||||
|
||||
[^shreq]: I may remove this requirement in the future to make modules more self-contained. However, relying on some base utility scripts makes it easier to avoid duplicating such functionality for each individual script in other modules.
|
||||
|
||||
![Gapless - the same image as above, only displayed without gaps](https://gitlab.com/marty-oehme/dotfiles/-/wikis/uploads/21791f77da013cdac64f11eff61584e3/gapless.png)
|
||||
![Gapless](_assets/gapless.png)
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
## Unfinished ideas
|
||||
|
||||
* Read out tmux session / window / panel names (or, for panels/windows, the names of programs running) and pass them to rofi for it to show them and allow 'tabbing' to them a-la Alt-Tab windows
|
||||
|
||||
## Implementables
|
||||
|
||||
### styler
|
||||
|
||||
* [x] styler should be able to present a list of all installed packages/processors/themes
|
||||
* [ ] styler should have an additional display for each application that a theme is available
|
||||
* [x] use same style for processors as for packages
|
||||
* [x] enable downloading
|
||||
* [x] from github / link > user inputs author/repo and it automatically downloads package/processor (decided by name)
|
||||
* [x] so, `styler download chriskempson/base16-vim` will automatically clone into package directory
|
||||
* [x] `styler download marty-oehme/yabam16-vim` will automatically clone into processor directory
|
||||
* [x] `styler download someone/any-string-here` should also automatically download into processor dir, but print a warning
|
||||
* [ ] enable suggestion of missing packages when downloading processors
|
||||
* [ ] some processors contain `readonly dependency=("author/package")` format to make sure they run against the right package
|
||||
* [ ] when this is the case and the package does not exist, warn the user or propose to automatically download the package
|
||||
* [ ] enable suggestion of processors/packages when running empty download
|
||||
* [ ] `styler download` should suggest some processors to download (i.e. applications), and either suggest packages or make use of the previous suggestion and have them automatically suggested when installing the processor
|
||||
* [x] enable switch for theme switching/permanent theming through styler
|
||||
* [x] styler should, by default, only *switch* applications to the new theme but not make the theme default
|
||||
* [x] when invoked with saving switch, the processors should make sure that the theme will be the default theme for the next run of the application
|
||||
* [x] perhaps styler should invoke processors with additional "switch" "save" first variable being passed, which they will use to differentiate
|
||||
* [ ] enable shell completion for themes
|
||||
* [ ] read configuration from configrc file (containing processors/packages to download, applications to set?)
|
||||
* [ ] Bug: duplicate processors mess it up
|
||||
* [ ] Find out which *applications* will be styled (perhaps grep comparison of processors/packages and if both have an application name, display this)
|
||||
* [ ] Feature: styler processor calling could be extended for a post-hook, which would be run after setting the new themes. So, it could work like `processor variables "switch"`, `processor variables "set"`, `processor variables "post"`, to have these hooks. Hooks could even be extended to switch_pre, switch, switch_post, set_pre, set, set_post; though perhaps YAGNI.
|
||||
* [ ] Feature: add `get` option, where you can input an app/processor and it displays its current theme; or do `get` without argument to display it for each app/processor?
|
||||
* [ ] a better way to set a processor and package which should target an app
|
||||
* [ ] packages do not know about styler (and shouldn't), processors *do*, processors should carry the information of which packages they can be used for, and should recommend package installation on download
|
||||
* [ ] the process workflow/lifecycle:
|
||||
* [ ] styler gathers all processors (with some algorithm deciding which takes precedence for applications)
|
||||
* [ ] the processors carry dependencies on packages, styler ensures these are met
|
||||
* [ ] styler sources the processes and calls, in order
|
||||
* [ ] set_pre -- called before any process sets themes
|
||||
* [ ] set -- called for each processor in turn
|
||||
* [ ] set_post -- called after each processor sets themes
|
||||
* [ ] theme_pre -- called before each processor switches themes
|
||||
* [ ] theme -- called for each processor in turn
|
||||
* [ ] theme_post-- called after each processor switches themes
|
||||
|
||||
### processors
|
||||
|
||||
* [x] shell should set shell colors even when invoked through rofi
|
||||
* [x] shell should set it for *all* terminals, not just the one it ran it (through pgrep?)
|
||||
* [ ] shell should be able to save the colorscheme permanently
|
||||
* [x] qutebrowser should be able to save the colorscheme permanently
|
||||
* [x] vim should actually save the colorscheme, not just the name of it?
|
Binary file not shown.
After Width: | Height: | Size: 452 KiB |
Binary file not shown.
After Width: | Height: | Size: 502 KiB |
|
@ -0,0 +1,129 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# autostow.sh
|
||||
#
|
||||
# Stow automatic symlinking script
|
||||
#
|
||||
# Invokes stow for every folder within this repository, and that's it.
|
||||
|
||||
ignore="${AUTOSTOW_IGNORED_DIRS}"
|
||||
|
||||
main() {
|
||||
case "$1" in
|
||||
-s | --stow)
|
||||
have_stow
|
||||
stow_dirs
|
||||
exit 0
|
||||
;;
|
||||
-d | --delete)
|
||||
have_stow
|
||||
unstow_dirs
|
||||
exit 0
|
||||
;;
|
||||
-n | --dry-run)
|
||||
have_stow
|
||||
dryrun
|
||||
exit 0
|
||||
;;
|
||||
-v | --version)
|
||||
printf "System bootstrap script.\n\n©Marty Oehme\n\nVersion: 0.2.1\n"
|
||||
;;
|
||||
-h | --help | *)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
}
|
||||
|
||||
stow_dirs() {
|
||||
printf "Creating dotfile symlinks .................................................\n"
|
||||
for d in */; do
|
||||
|
||||
if is_ignored "$d"; then
|
||||
printf "ignoring %s\n" "$d"
|
||||
continue
|
||||
fi
|
||||
|
||||
printf "stowing %s\n" "$d"
|
||||
stow -S -t ~ "$d"
|
||||
done
|
||||
printf "Done creating symlinks ....................................................\n"
|
||||
}
|
||||
|
||||
unstow_dirs() {
|
||||
printf "Removing dotfile symlinks .................................................\n"
|
||||
for d in */; do
|
||||
|
||||
if is_ignored "$d"; then
|
||||
printf "ignoring %s\n" "$d"
|
||||
continue
|
||||
fi
|
||||
|
||||
printf "unstowing %s\n" "$d"
|
||||
stow -D -t ~ "$d"
|
||||
done
|
||||
printf "Done removing symlinks ....................................................\n"
|
||||
}
|
||||
|
||||
dryrun() {
|
||||
printf "Printing processed directories ............................................\n"
|
||||
for d in */; do
|
||||
|
||||
if is_ignored "$d"; then
|
||||
printf "ignoring %s\n" "$d"
|
||||
continue
|
||||
fi
|
||||
|
||||
printf "processing %s\n" "$d"
|
||||
done
|
||||
printf "Done printing directories .................................................\n"
|
||||
}
|
||||
|
||||
is_ignored() {
|
||||
IFS=":"
|
||||
for ign in $ignore; do
|
||||
# it is either passed in through our environment variable
|
||||
if [ "$ign" = "$1" ] || [ "$ign/" = "$1" ]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
# or it starts with a _ which is ignored by default (that's the regex).
|
||||
# (using herestring to avoid cat>grep)
|
||||
if grep -q -e '^_[[:alnum:]]\{1,\}' <<<"$1"; then return 0; fi
|
||||
return 1
|
||||
}
|
||||
|
||||
have_stow() {
|
||||
if ! type stow >/dev/null 2>&1; then
|
||||
printf "GNU stow needs to be installed for this script to function. Please install stow through your package manager.\n"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
usage() {
|
||||
printf "%s\n" \
|
||||
"" \
|
||||
" autostow.sh - Automatically stow your dotfiles." \
|
||||
" Uses GNU stow to set up automatic links to any dotfiles in subdirectories of this directory." \
|
||||
"" \
|
||||
" Usage: stow.sh -dhsn" \
|
||||
"" \
|
||||
" Options:" \
|
||||
"" \
|
||||
" -h | --help Print out this help." \
|
||||
"" \
|
||||
" -s | --stow Install dotfiles, by symlinking any directory found next to stow.sh using GNU stow." \
|
||||
"" \
|
||||
" -d | --delete Remove dotfiles, by unlinking any directory found next to stow.sh using GNU stow." \
|
||||
"" \
|
||||
" -n | --dry-run Do not invoke any operation but print out directories affected, simulating a dry-run." \
|
||||
"" \
|
||||
" Note, by default any directory starting with an underscore _directoryname will be ignored." \
|
||||
" Additional folders to ignore can be set through the environment variable AUTOSTOW_IGNORED_DIRS " \
|
||||
" using a colon-separated string: AUTOSTOW_IGNORED_DIRS=\"directories:to:ignore\" " \
|
||||
"" \
|
||||
""
|
||||
}
|
||||
|
||||
main "$@"
|
|
@ -0,0 +1,92 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Simple app bootstrapping script
|
||||
|
||||
#=== main function ============================================================
|
||||
# NAME: main
|
||||
# DESCRIPTION: Display usage information for this script.
|
||||
# PARAMETERS: see usage function
|
||||
#==============================================================================
|
||||
packages="${BOOTSTRAP_PACKAGES:-packages.csv}"
|
||||
|
||||
main() {
|
||||
local cmd=""
|
||||
local ret=0
|
||||
|
||||
case "$1" in
|
||||
-v | --version)
|
||||
printf "Package bootstrap script.\n\n©Marty Oehme\n\nVersion: 0.3\n"
|
||||
;;
|
||||
-h | --help)
|
||||
printf "Usage: install [-f|--force][-v|--version][-h|--help]\n\n-f Do not ask for any confirmations but force update and installation.\n"
|
||||
;;
|
||||
-f | --force)
|
||||
install true
|
||||
;;
|
||||
*)
|
||||
install false
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
|
||||
$cmd "$@"
|
||||
ret=$((ret + $?))
|
||||
exit $ret
|
||||
}
|
||||
|
||||
install_yay() {
|
||||
# check for existing yay installation
|
||||
if type yay >/dev/null 2>&1; then
|
||||
echo "Existing yay installation found ..........................................."
|
||||
return
|
||||
fi
|
||||
|
||||
# use tmp dir to make yay
|
||||
target=$(mktemp -d)
|
||||
git clone https://aur.archlinux.org/yay.git "$target"
|
||||
cd "$target" || exit
|
||||
makepkg -si
|
||||
}
|
||||
|
||||
update_repos() {
|
||||
unattended="$1"
|
||||
if "$unattended"; then
|
||||
yay -Sqyy --noconfirm
|
||||
else
|
||||
yay -Syy
|
||||
fi
|
||||
}
|
||||
|
||||
install_packages() {
|
||||
unattended="$1"
|
||||
if "$unattended"; then
|
||||
yay -Squ --noconfirm --needed - <"$packages"
|
||||
else
|
||||
yay -Su --needed - <"$packages"
|
||||
fi
|
||||
}
|
||||
|
||||
check_consent() {
|
||||
echo "This will take a while and install many packages. Proceed [y/N]?"
|
||||
read -r yes
|
||||
if [[ "$yes" != y* ]]; then
|
||||
echo "Exiting."
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
unattended=$1
|
||||
echo "Beginning package bootstrap ..............................................."
|
||||
if ! "$unattended"; then
|
||||
check_consent
|
||||
fi
|
||||
echo "Installing yay ............................................................"
|
||||
install_yay
|
||||
echo "Installing apps ..........................................................."
|
||||
update_repos "$unattended"
|
||||
install_packages "$unattended"
|
||||
echo "Done ......................................................................"
|
||||
}
|
||||
|
||||
main "$@"
|
|
@ -0,0 +1,122 @@
|
|||
alacritty
|
||||
atool
|
||||
anki
|
||||
bash
|
||||
bibtool
|
||||
bison
|
||||
bzip2
|
||||
dhcpcd
|
||||
dialog
|
||||
docker
|
||||
dunst
|
||||
exa
|
||||
fasd
|
||||
feh
|
||||
flashfocus-git
|
||||
fzf
|
||||
gawk
|
||||
git
|
||||
git-lfs
|
||||
glow
|
||||
gnome-keyring
|
||||
gopass
|
||||
grep
|
||||
gzip
|
||||
haveged
|
||||
htop
|
||||
hugo
|
||||
i3-gaps
|
||||
i3lock
|
||||
jabref-latest
|
||||
jpdftweak
|
||||
keybase-bin
|
||||
less
|
||||
libnotify
|
||||
libtool
|
||||
littler
|
||||
logrotate
|
||||
lynx
|
||||
mosh
|
||||
mpv
|
||||
nano
|
||||
neovim
|
||||
nerd-fonts-fira-code
|
||||
nerd-fonts-iosevka
|
||||
nextcloud-client
|
||||
ntfs-3g
|
||||
ntp
|
||||
openssh
|
||||
os-prober
|
||||
pacman
|
||||
pandoc
|
||||
pandoc-citeproc
|
||||
picom
|
||||
playerctl
|
||||
polybar
|
||||
powertop
|
||||
pulseaudio
|
||||
pulseaudio-alsa
|
||||
pulsemixer
|
||||
python-pip
|
||||
python-pybtex
|
||||
python-pynvim
|
||||
qutebrowser
|
||||
r
|
||||
redshift
|
||||
ripgrep
|
||||
rng-tools
|
||||
rofi
|
||||
rofi-calc
|
||||
rofi-dmenu
|
||||
rofi-greenclip
|
||||
rofimoji
|
||||
rtv
|
||||
scrot
|
||||
sed
|
||||
sshfs
|
||||
stow
|
||||
surfraw
|
||||
sxhkd
|
||||
tar
|
||||
texinfo
|
||||
texlive-bibtexextra
|
||||
texlive-core
|
||||
texlive-fontsextra
|
||||
texlive-formatsextra
|
||||
texlive-games
|
||||
texlive-humanities
|
||||
texlive-latexextra
|
||||
texlive-music
|
||||
texlive-pictures
|
||||
texlive-pstricks
|
||||
texlive-publishers
|
||||
texlive-science
|
||||
tmux
|
||||
tomb
|
||||
topgrade
|
||||
ttf-comic-neue
|
||||
ttf-heuristica
|
||||
ttf-signika
|
||||
unclutter
|
||||
unrar
|
||||
unzip
|
||||
vagrant
|
||||
vifm
|
||||
virtualbox
|
||||
wget
|
||||
which
|
||||
xcape
|
||||
xclip
|
||||
xdg-user-dirs
|
||||
xdotool
|
||||
xorg-server
|
||||
xorg-xev
|
||||
xorg-xinit
|
||||
xorg-xinput
|
||||
xorg-xrandr
|
||||
yarn
|
||||
yay
|
||||
youtube-dl
|
||||
zathura
|
||||
zathura-pdf-mupdf
|
||||
zsh
|
|
|
@ -0,0 +1,660 @@
|
|||
# Configuration for Alacritty, the GPU enhanced terminal emulator.
|
||||
|
||||
# Any items in the `env` entry below will be added as
|
||||
# environment variables. Some entries may override variables
|
||||
# set by alacritty itself.
|
||||
#env:
|
||||
# TERM variable
|
||||
#
|
||||
# This value is used to set the `$TERM` environment variable for
|
||||
# each instance of Alacritty. If it is not present, alacritty will
|
||||
# check the local terminfo database and use `alacritty` if it is
|
||||
# available, otherwise `xterm-256color` is used.
|
||||
#TERM: xterm-256color
|
||||
|
||||
window:
|
||||
# Window dimensions (changes require restart)
|
||||
#
|
||||
# Specified in number of columns/lines, not pixels.
|
||||
# If both are `0`, this setting is ignored.
|
||||
dimensions:
|
||||
columns: 0
|
||||
lines: 0
|
||||
|
||||
# Window position (changes require restart)
|
||||
#
|
||||
# Specified in number of pixels.
|
||||
# If the position is not set, the window manager will handle the placement.
|
||||
#position:
|
||||
# x: 0
|
||||
# y: 0
|
||||
|
||||
# Window padding (changes require restart)
|
||||
#
|
||||
# Blank space added around the window in pixels. This padding is scaled
|
||||
# by DPI and the specified value is always added at both opposing sides.
|
||||
padding:
|
||||
x: 0
|
||||
y: 0
|
||||
|
||||
# Spread additional padding evenly around the terminal content.
|
||||
dynamic_padding: false
|
||||
|
||||
# Window decorations
|
||||
#
|
||||
# Values for `decorations`:
|
||||
# - full: Borders and title bar
|
||||
# - none: Neither borders nor title bar
|
||||
#
|
||||
# Values for `decorations` (macOS only):
|
||||
# - transparent: Title bar, transparent background and title bar buttons
|
||||
# - buttonless: Title bar, transparent background, but no title bar buttons
|
||||
decorations: full
|
||||
|
||||
# Startup Mode (changes require restart)
|
||||
#
|
||||
# Values for `startup_mode`:
|
||||
# - Windowed
|
||||
# - Maximized
|
||||
# - Fullscreen
|
||||
#
|
||||
# Values for `startup_mode` (macOS only):
|
||||
# - SimpleFullscreen
|
||||
startup_mode: Windowed
|
||||
|
||||
# Window title
|
||||
#title: Alacritty
|
||||
|
||||
# Window class (Linux only):
|
||||
#class: Alacritty
|
||||
|
||||
scrolling:
|
||||
# Maximum number of lines in the scrollback buffer.
|
||||
# Specifying '0' will disable scrolling.
|
||||
history: 10000
|
||||
|
||||
# Number of lines the viewport will move for every line scrolled when
|
||||
# scrollback is enabled (history > 0).
|
||||
multiplier: 3
|
||||
|
||||
# Faux Scrolling
|
||||
#
|
||||
# The `faux_multiplier` setting controls the number of lines the terminal
|
||||
# should scroll when the alternate screen buffer is active. This is used
|
||||
# to allow mouse scrolling for applications like `man`.
|
||||
#
|
||||
# Specifying `0` will disable faux scrolling.
|
||||
# faux_multiplier: 3
|
||||
|
||||
# Scroll to the bottom when new text is written to the terminal.
|
||||
auto_scroll: false
|
||||
|
||||
# Spaces per Tab (changes require restart)
|
||||
#
|
||||
# This setting defines the width of a tab in cells.
|
||||
#
|
||||
# Some applications, like Emacs, rely on knowing about the width of a tab.
|
||||
# To prevent unexpected behavior in these applications, it's also required to
|
||||
# change the `it` value in terminfo when altering this setting.
|
||||
tabspaces: 8
|
||||
|
||||
# Font configuration (changes require restart)
|
||||
font:
|
||||
# Normal (roman) font face
|
||||
normal:
|
||||
# Font family
|
||||
#
|
||||
# Default:
|
||||
# - (macOS) Menlo
|
||||
# - (Linux) monospace
|
||||
# - (Windows) Consolas
|
||||
family: monospace
|
||||
|
||||
# The `style` can be specified to pick a specific face.
|
||||
#style: Regular
|
||||
|
||||
# Bold font face
|
||||
bold:
|
||||
# Font family
|
||||
#
|
||||
# If the bold family is not specified, it will fall back to the
|
||||
# value specified for the normal font.
|
||||
family: monospace
|
||||
|
||||
# The `style` can be specified to pick a specific face.
|
||||
style: Bold
|
||||
|
||||
# Italic font face
|
||||
italic:
|
||||
# Font family
|
||||
#
|
||||
# If the italic family is not specified, it will fall back to the
|
||||
# value specified for the normal font.
|
||||
family: monospace
|
||||
|
||||
# The `style` can be specified to pick a specific face.
|
||||
style: Italic
|
||||
|
||||
# Point size
|
||||
size: 10.5
|
||||
|
||||
# Offset is the extra space around each character. `offset.y` can be thought of
|
||||
# as modifying the line spacing, and `offset.x` as modifying the letter spacing.
|
||||
offset:
|
||||
x: 0
|
||||
y: 0
|
||||
|
||||
# Glyph offset determines the locations of the glyphs within their cells with
|
||||
# the default being at the bottom. Increasing `x` moves the glyph to the right,
|
||||
# increasing `y` moves the glyph upwards.
|
||||
glyph_offset:
|
||||
x: 0
|
||||
y: 0
|
||||
|
||||
# Thin stroke font rendering (macOS only)
|
||||
#
|
||||
# Thin strokes are suitable for retina displays, but for non-retina screens
|
||||
# it is recommended to set `use_thin_strokes` to `false`
|
||||
#
|
||||
# macOS >= 10.14.x:
|
||||
#
|
||||
# If the font quality on non-retina display looks bad then set
|
||||
# `use_thin_strokes` to `true` and enable font smoothing by running the
|
||||
# following command:
|
||||
# `defaults write -g CGFontRenderingFontSmoothingDisabled -bool NO`
|
||||
#
|
||||
# This is a global setting and will require a log out or restart to take
|
||||
# effect.
|
||||
use_thin_strokes: true
|
||||
|
||||
# If `true`, bold text is drawn using the bright color variants.
|
||||
draw_bold_text_with_bright_colors: false
|
||||
|
||||
# Colors (Gruvbox light)
|
||||
gruvboxlight: &gruvbox-light
|
||||
# Default colors
|
||||
primary:
|
||||
# hard contrast: background = '0xf9f5d7'
|
||||
background: '0xfbf1c7'
|
||||
# soft contrast: background = '0xf2e5bc'
|
||||
foreground: '0x3c3836'
|
||||
|
||||
# Normal colors
|
||||
normal:
|
||||
black: '0xfbf1c7'
|
||||
red: '0xcc241d'
|
||||
green: '0x98971a'
|
||||
yellow: '0xd79921'
|
||||
blue: '0x458588'
|
||||
magenta: '0xb16286'
|
||||
cyan: '0x689d6a'
|
||||
white: '0x7c6f64'
|
||||
|
||||
# Bright colors
|
||||
bright:
|
||||
black: '0x928374'
|
||||
red: '0x9d0006'
|
||||
green: '0x79740e'
|
||||
yellow: '0xb57614'
|
||||
blue: '0x076678'
|
||||
magenta: '0x8f3f71'
|
||||
cyan: '0x427b58'
|
||||
white: '0x3c3836'
|
||||
|
||||
# Colors (Gruvbox dark)
|
||||
gruvboxdark: &gruvbox-dark
|
||||
# Default colors
|
||||
primary:
|
||||
# hard contrast: background = '0x1d2021'
|
||||
background: '0x282828'
|
||||
# soft contrast: background = '0x32302f'
|
||||
foreground: '0xebdbb2'
|
||||
|
||||
# Normal colors
|
||||
normal:
|
||||
black: '0x282828'
|
||||
red: '0xcc241d'
|
||||
green: '0x98971a'
|
||||
yellow: '0xd79921'
|
||||
blue: '0x458588'
|
||||
magenta: '0xb16286'
|
||||
cyan: '0x689d6a'
|
||||
white: '0xa89984'
|
||||
|
||||
# Bright colors
|
||||
bright:
|
||||
black: '0x928374'
|
||||
red: '0xfb4934'
|
||||
green: '0xb8bb26'
|
||||
yellow: '0xfabd2f'
|
||||
blue: '0x83a598'
|
||||
magenta: '0xd3869b'
|
||||
cyan: '0x8ec07c'
|
||||
white: '0xebdbb2'
|
||||
|
||||
# Colors (One Dark)
|
||||
onedark: &one-dark
|
||||
# Default colors
|
||||
primary:
|
||||
background: '0x1e2127'
|
||||
foreground: '0xabb2bf'
|
||||
|
||||
# Normal colors
|
||||
normal:
|
||||
black: '0x1e2127'
|
||||
red: '0xe06c75'
|
||||
green: '0x98c379'
|
||||
yellow: '0xd19a66'
|
||||
blue: '0x61afef'
|
||||
magenta: '0xc678dd'
|
||||
cyan: '0x56b6c2'
|
||||
white: '0xabb2bf'
|
||||
|
||||
# Bright colors
|
||||
bright:
|
||||
black: '0x5c6370'
|
||||
red: '0xe06c75'
|
||||
green: '0x98c379'
|
||||
yellow: '0xd19a66'
|
||||
blue: '0x61afef'
|
||||
magenta: '0xc678dd'
|
||||
cyan: '0x56b6c2'
|
||||
white: '0xffffff'
|
||||
|
||||
# Colors (PaperColor Light -- works as One Light)
|
||||
onelight: &one-light
|
||||
# Default colors
|
||||
primary:
|
||||
background: '0xEEEEEE'
|
||||
foreground: '0x4D4D4C'
|
||||
|
||||
# Normal colors
|
||||
normal:
|
||||
black: '0xEDEDED'
|
||||
red: '0xD7005F'
|
||||
green: '0x718C00'
|
||||
yellow: '0xD75F00'
|
||||
blue: '0x4271AE'
|
||||
magenta: '0x8959A8'
|
||||
cyan: '0x3E999F'
|
||||
white: '0x4D4D4C'
|
||||
|
||||
# Bright colors
|
||||
bright:
|
||||
black: '0x969694'
|
||||
red: '0xD7005F'
|
||||
green: '0x718C00'
|
||||
yellow: '0xD75F00'
|
||||
blue: '0x4271AE'
|
||||
magenta: '0x8959A8'
|
||||
cyan: '0x3E999F'
|
||||
white: '0xF5F5F5'
|
||||
|
||||
colors: *gruvbox-dark
|
||||
|
||||
# Visual Bell
|
||||
#
|
||||
# Any time the BEL code is received, Alacritty "rings" the visual bell. Once
|
||||
# rung, the terminal background will be set to white and transition back to the
|
||||
# default background color. You can control the rate of this transition by
|
||||
# setting the `duration` property (represented in milliseconds). You can also
|
||||
# configure the transition function by setting the `animation` property.
|
||||
#
|
||||
# Values for `animation`:
|
||||
# - Ease
|
||||
# - EaseOut
|
||||
# - EaseOutSine
|
||||
# - EaseOutQuad
|
||||
# - EaseOutCubic
|
||||
# - EaseOutQuart
|
||||
# - EaseOutQuint
|
||||
# - EaseOutExpo
|
||||
# - EaseOutCirc
|
||||
# - Linear
|
||||
#
|
||||
# Specifying a `duration` of `0` will disable the visual bell.
|
||||
visual_bell:
|
||||
animation: EaseOutCubic
|
||||
duration: 0
|
||||
color: '0x222220'
|
||||
|
||||
# Background opacity
|
||||
#
|
||||
# Window opacity as a floating point number from `0.0` to `1.0`.
|
||||
# The value `0.0` is completely transparent and `1.0` is opaque.
|
||||
background_opacity: 1.0
|
||||
|
||||
# Mouse bindings
|
||||
#
|
||||
# Available fields:
|
||||
# - mouse
|
||||
# - action
|
||||
# - mods (optional)
|
||||
#
|
||||
# Values for `mouse`:
|
||||
# - Middle
|
||||
# - Left
|
||||
# - Right
|
||||
# - Numeric identifier such as `5`
|
||||
#
|
||||
# All available `mods` and `action` values are documented in the key binding
|
||||
# section.
|
||||
mouse_bindings:
|
||||
- { mouse: Middle, action: PasteSelection }
|
||||
|
||||
mouse:
|
||||
# Click settings
|
||||
#
|
||||
# The `double_click` and `triple_click` settings control the time
|
||||
# alacritty should wait for accepting multiple clicks as one double
|
||||
# or triple click.
|
||||
double_click: { threshold: 300 }
|
||||
triple_click: { threshold: 300 }
|
||||
|
||||
# If this is `true`, the cursor is temporarily hidden when typing.
|
||||
hide_when_typing: false
|
||||
|
||||
url:
|
||||
# URL launcher
|
||||
#
|
||||
# This program is executed when clicking on a text which is recognized as a URL.
|
||||
# The URL is always added to the command as the last parameter.
|
||||
#
|
||||
# When set to `None`, URL launching will be disabled completely.
|
||||
#
|
||||
# Default:
|
||||
# - (macOS) open
|
||||
# - (Linux) xdg-open
|
||||
# - (Windows) explorer
|
||||
#launcher: xdg-open
|
||||
|
||||
# URL modifiers
|
||||
#
|
||||
# These are the modifiers that need to be held down for opening URLs when clicking
|
||||
# on them. The available modifiers are documented in the key binding section.
|
||||
modifiers: None
|
||||
|
||||
selection:
|
||||
semantic_escape_chars: ",│`|:\"' ()[]{}<>"
|
||||
|
||||
# When set to `true`, selected text will be copied to the primary clipboard.
|
||||
save_to_clipboard: true
|
||||
|
||||
# Allow terminal applications to change Alacritty's window title.
|
||||
dynamic_title: true
|
||||
|
||||
cursor:
|
||||
# Cursor style
|
||||
#
|
||||
# Values for `style`:
|
||||
# - ▇ Block
|
||||
# - _ Underline
|
||||
# - | Beam
|
||||
style: Beam
|
||||
|
||||
# If this is `true`, the cursor will be rendered as a hollow box when the
|
||||
# window is not focused.
|
||||
unfocused_hollow: true
|
||||
|
||||
# Live config reload (changes require restart)
|
||||
live_config_reload: true
|
||||
|
||||
# Shell
|
||||
#
|
||||
# You can set `shell.program` to the path of your favorite shell, e.g. `/bin/fish`.
|
||||
# Entries in `shell.args` are passed unmodified as arguments to the shell.
|
||||
#
|
||||
# Default:
|
||||
# - (Linux/macOS) /bin/bash --login
|
||||
# - (Windows) powershell
|
||||
#shell:
|
||||
# program: /bin/bash
|
||||
# args:
|
||||
# - --login
|
||||
|
||||
# Windows 10 ConPTY backend (Windows only)
|
||||
#
|
||||
# This will enable better color support and may resolve other issues,
|
||||
# however this API and its implementation is still young and so is
|
||||
# disabled by default, as stability may not be as good as the winpty
|
||||
# backend.
|
||||
#
|
||||
# Alacritty will fall back to the WinPTY automatically if the ConPTY
|
||||
# backend cannot be initialized.
|
||||
enable_experimental_conpty_backend: false
|
||||
|
||||
# Send ESC (\x1b) before characters when alt is pressed.
|
||||
alt_send_esc: true
|
||||
|
||||
debug:
|
||||
# Display the time it takes to redraw each frame.
|
||||
render_timer: false
|
||||
|
||||
# Keep the log file after quitting Alacritty.
|
||||
persistent_logging: false
|
||||
|
||||
# Log level
|
||||
#
|
||||
# Values for `log_level`:
|
||||
# - None
|
||||
# - Error
|
||||
# - Warn
|
||||
# - Info
|
||||
# - Debug
|
||||
# - Trace
|
||||
log_level: Warn
|
||||
|
||||
# Print all received window events.
|
||||
print_events: false
|
||||
|
||||
# Record all characters and escape sequences as test data.
|
||||
ref_test: false
|
||||
|
||||
# Key bindings
|
||||
#
|
||||
# Key bindings are specified as a list of objects. Each binding will specify a
|
||||
# key and modifiers required to trigger it, terminal modes where the binding is
|
||||
# applicable, and what should be done when the key binding fires. It can either
|
||||
# send a byte sequence to the running application (`chars`), execute a
|
||||
# predefined action (`action`) or fork and execute a specified command plus
|
||||
# arguments (`command`).
|
||||
#
|
||||
# Bindings are always filled by default, but will be replaced when a new binding
|
||||
# with the same triggers is defined. To unset a default binding, it can be
|
||||
# mapped to the `None` action.
|
||||
#
|
||||
# Example:
|
||||
# `- { key: V, mods: Control|Shift, action: Paste }`
|
||||
#
|
||||
# Available fields:
|
||||
# - key
|
||||
# - mods (optional)
|
||||
# - chars | action | command (exactly one required)
|
||||
# - mode (optional)
|
||||
#
|
||||
# Values for `key`:
|
||||
# - `A` -> `Z`
|
||||
# - `F1` -> `F12`
|
||||
# - `Key1` -> `Key0`
|
||||
#
|
||||
# A full list with available key codes can be found here:
|
||||
# https://docs.rs/glutin/*/glutin/enum.VirtualKeyCode.html#variants
|
||||
#
|
||||
# Instead of using the name of the keys, the `key` field also supports using
|
||||
# the scancode of the desired key. Scancodes have to be specified as a
|
||||
# decimal number.
|
||||
# This command will allow you to display the hex scancodes for certain keys:
|
||||
# `showkey --scancodes`
|
||||
#
|
||||
# Values for `mods`:
|
||||
# - Command
|
||||
# - Control
|
||||
# - Super
|
||||
# - Shift
|
||||
# - Alt
|
||||
#
|
||||
# Multiple `mods` can be combined using `|` like this: `mods: Control|Shift`.
|
||||
# Whitespace and capitalization is relevant and must match the example.
|
||||
#
|
||||
# Values for `chars`:
|
||||
# The `chars` field writes the specified string to the terminal. This makes
|
||||
# it possible to pass escape sequences.
|
||||
# To find escape codes for bindings like `PageUp` ("\x1b[5~"), you can run
|
||||
# the command `showkey -a` outside of tmux.
|
||||
# Note that applications use terminfo to map escape sequences back to
|
||||
# keys. It is therefore required to update the terminfo when
|
||||
# changing an escape sequence.
|
||||
#
|
||||
# Values for `action`:
|
||||
# - Paste
|
||||
# - PasteSelection
|
||||
# - Copy
|
||||
# - IncreaseFontSize
|
||||
# - DecreaseFontSize
|
||||
# - ResetFontSize
|
||||
# - ScrollPageUp
|
||||
# - ScrollPageDown
|
||||
# - ScrollToTop
|
||||
# - ScrollToBottom
|
||||
# - ClearHistory
|
||||
# - Hide
|
||||
# - Quit
|
||||
# - ClearLogNotice
|
||||
# - SpawnNewInstance
|
||||
# - None
|
||||
#
|
||||
# Values for `command`:
|
||||
# The `command` field must be a map containing a `program` string and
|
||||
# an `args` array of command line parameter strings.
|
||||
#
|
||||
# Example:
|
||||
# `command: { program: "alacritty", args: ["-e", "vttest"] }`
|
||||
#
|
||||
# Values for `mode`:
|
||||
# - ~AppCursor
|
||||
# - AppCursor
|
||||
# - ~AppKeypad
|
||||
# - AppKeypad
|
||||
key_bindings:
|
||||
# (Windows/Linux only)
|
||||
#- { key: V, mods: Control|Shift, action: Paste }
|
||||
#- { key: C, mods: Control|Shift, action: Copy }
|
||||
#- { key: Insert, mods: Shift, action: PasteSelection }
|
||||
#- { key: Key0, mods: Control, action: ResetFontSize }
|
||||
#- { key: Equals, mods: Control, action: IncreaseFontSize }
|
||||
#- { key: Subtract, mods: Control, action: DecreaseFontSize }
|
||||
|
||||
# (macOS only)
|
||||
#- { key: Key0, mods: Command, action: ResetFontSize }
|
||||
#- { key: Equals, mods: Command, action: IncreaseFontSize }
|
||||
#- { key: Minus, mods: Command, action: DecreaseFontSize }
|
||||
#- { key: K, mods: Command, action: ClearHistory }
|
||||
#- { key: K, mods: Command, chars: "\x0c" }
|
||||
#- { key: V, mods: Command, action: Paste }
|
||||
#- { key: C, mods: Command, action: Copy }
|
||||
#- { key: H, mods: Command, action: Hide }
|
||||
#- { key: Q, mods: Command, action: Quit }
|
||||
#- { key: W, mods: Command, action: Quit }
|
||||
|
||||
- { key: Paste, action: Paste }
|
||||
- { key: Copy, action: Copy }
|
||||
- { key: L, mods: Control, action: ClearLogNotice }
|
||||
- { key: L, mods: Control, chars: "\x0c" }
|
||||
- { key: Home, chars: "\x1bOH", mode: AppCursor }
|
||||
- { key: Home, chars: "\x1b[H", mode: ~AppCursor }
|
||||
- { key: End, chars: "\x1bOF", mode: AppCursor }
|
||||
- { key: End, chars: "\x1b[F", mode: ~AppCursor }
|
||||
- { key: PageUp, mods: Shift, action: ScrollPageUp, mode: ~Alt }
|
||||
- { key: PageUp, mods: Shift, chars: "\x1b[5;2~", mode: Alt }
|
||||
- { key: PageUp, mods: Control, chars: "\x1b[5;5~" }
|
||||
- { key: PageUp, chars: "\x1b[5~" }
|
||||
- { key: PageDown, mods: Shift, action: ScrollPageDown, mode: ~Alt }
|
||||
- { key: PageDown, mods: Shift, chars: "\x1b[6;2~", mode: Alt }
|
||||
- { key: PageDown, mods: Control, chars: "\x1b[6;5~" }
|
||||
- { key: PageDown, chars: "\x1b[6~" }
|
||||
- { key: Tab, mods: Shift, chars: "\x1b[Z" }
|
||||
- { key: Back, chars: "\x7f" }
|
||||
- { key: Back, mods: Alt, chars: "\x1b\x7f" }
|
||||
- { key: Insert, chars: "\x1b[2~" }
|
||||
- { key: Delete, chars: "\x1b[3~" }
|
||||
- { key: Left, mods: Shift, chars: "\x1b[1;2D" }
|
||||
- { key: Left, mods: Control, chars: "\x1b[1;5D" }
|
||||
- { key: Left, mods: Alt, chars: "\x1b[1;3D" }
|
||||
- { key: Left, chars: "\x1b[D", mode: ~AppCursor }
|
||||
- { key: Left, chars: "\x1bOD", mode: AppCursor }
|
||||
- { key: Right, mods: Shift, chars: "\x1b[1;2C" }
|
||||
- { key: Right, mods: Control, chars: "\x1b[1;5C" }
|
||||
- { key: Right, mods: Alt, chars: "\x1b[1;3C" }
|
||||
- { key: Right, chars: "\x1b[C", mode: ~AppCursor }
|
||||
- { key: Right, chars: "\x1bOC", mode: AppCursor }
|
||||
- { key: Up, mods: Shift, chars: "\x1b[1;2A" }
|
||||
- { key: Up, mods: Control, chars: "\x1b[1;5A" }
|
||||
- { key: Up, mods: Alt, chars: "\x1b[1;3A" }
|
||||
- { key: Up, chars: "\x1b[A", mode: ~AppCursor }
|
||||
- { key: Up, chars: "\x1bOA", mode: AppCursor }
|
||||
- { key: Down, mods: Shift, chars: "\x1b[1;2B" }
|
||||
- { key: Down, mods: Control, chars: "\x1b[1;5B" }
|
||||
- { key: Down, mods: Alt, chars: "\x1b[1;3B" }
|
||||
- { key: Down, chars: "\x1b[B", mode: ~AppCursor }
|
||||
- { key: Down, chars: "\x1bOB", mode: AppCursor }
|
||||
- { key: F1, chars: "\x1bOP" }
|
||||
- { key: F2, chars: "\x1bOQ" }
|
||||
- { key: F3, chars: "\x1bOR" }
|
||||
- { key: F4, chars: "\x1bOS" }
|
||||
- { key: F5, chars: "\x1b[15~" }
|
||||
- { key: F6, chars: "\x1b[17~" }
|
||||
- { key: F7, chars: "\x1b[18~" }
|
||||
- { key: F8, chars: "\x1b[19~" }
|
||||
- { key: F9, chars: "\x1b[20~" }
|
||||
- { key: F10, chars: "\x1b[21~" }
|
||||
- { key: F11, chars: "\x1b[23~" }
|
||||
- { key: F12, chars: "\x1b[24~" }
|
||||
- { key: F1, mods: Shift, chars: "\x1b[1;2P" }
|
||||
- { key: F2, mods: Shift, chars: "\x1b[1;2Q" }
|
||||
- { key: F3, mods: Shift, chars: "\x1b[1;2R" }
|
||||
- { key: F4, mods: Shift, chars: "\x1b[1;2S" }
|
||||
- { key: F5, mods: Shift, chars: "\x1b[15;2~" }
|
||||
- { key: F6, mods: Shift, chars: "\x1b[17;2~" }
|
||||
- { key: F7, mods: Shift, chars: "\x1b[18;2~" }
|
||||
- { key: F8, mods: Shift, chars: "\x1b[19;2~" }
|
||||
- { key: F9, mods: Shift, chars: "\x1b[20;2~" }
|
||||
- { key: F10, mods: Shift, chars: "\x1b[21;2~" }
|
||||
- { key: F11, mods: Shift, chars: "\x1b[23;2~" }
|
||||
- { key: F12, mods: Shift, chars: "\x1b[24;2~" }
|
||||
- { key: F1, mods: Control, chars: "\x1b[1;5P" }
|
||||
- { key: F2, mods: Control, chars: "\x1b[1;5Q" }
|
||||
- { key: F3, mods: Control, chars: "\x1b[1;5R" }
|
||||
- { key: F4, mods: Control, chars: "\x1b[1;5S" }
|
||||
- { key: F5, mods: Control, chars: "\x1b[15;5~" }
|
||||
- { key: F6, mods: Control, chars: "\x1b[17;5~" }
|
||||
- { key: F7, mods: Control, chars: "\x1b[18;5~" }
|
||||
- { key: F8, mods: Control, chars: "\x1b[19;5~" }
|
||||
- { key: F9, mods: Control, chars: "\x1b[20;5~" }
|
||||
- { key: F10, mods: Control, chars: "\x1b[21;5~" }
|
||||
- { key: F11, mods: Control, chars: "\x1b[23;5~" }
|
||||
- { key: F12, mods: Control, chars: "\x1b[24;5~" }
|
||||
- { key: F1, mods: Alt, chars: "\x1b[1;6P" }
|
||||
- { key: F2, mods: Alt, chars: "\x1b[1;6Q" }
|
||||
- { key: F3, mods: Alt, chars: "\x1b[1;6R" }
|
||||
- { key: F4, mods: Alt, chars: "\x1b[1;6S" }
|
||||
- { key: F5, mods: Alt, chars: "\x1b[15;6~" }
|
||||
- { key: F6, mods: Alt, chars: "\x1b[17;6~" }
|
||||
- { key: F7, mods: Alt, chars: "\x1b[18;6~" }
|
||||
- { key: F8, mods: Alt, chars: "\x1b[19;6~" }
|
||||
- { key: F9, mods: Alt, chars: "\x1b[20;6~" }
|
||||
- { key: F10, mods: Alt, chars: "\x1b[21;6~" }
|
||||
- { key: F11, mods: Alt, chars: "\x1b[23;6~" }
|
||||
- { key: F12, mods: Alt, chars: "\x1b[24;6~" }
|
||||
- { key: F1, mods: Super, chars: "\x1b[1;3P" }
|
||||
- { key: F2, mods: Super, chars: "\x1b[1;3Q" }
|
||||
- { key: F3, mods: Super, chars: "\x1b[1;3R" }
|
||||
- { key: F4, mods: Super, chars: "\x1b[1;3S" }
|
||||
- { key: F5, mods: Super, chars: "\x1b[15;3~" }
|
||||
- { key: F6, mods: Super, chars: "\x1b[17;3~" }
|
||||
- { key: F7, mods: Super, chars: "\x1b[18;3~" }
|
||||
- { key: F8, mods: Super, chars: "\x1b[19;3~" }
|
||||
- { key: F9, mods: Super, chars: "\x1b[20;3~" }
|
||||
- { key: F10, mods: Super, chars: "\x1b[21;3~" }
|
||||
- { key: F11, mods: Super, chars: "\x1b[23;3~" }
|
||||
- { key: F12, mods: Super, chars: "\x1b[24;3~" }
|
||||
- { key: NumpadEnter, chars: "\n" }
|
|
@ -1,22 +0,0 @@
|
|||
FROM archlinux:latest
|
||||
|
||||
# First build then run this dockerfile with:
|
||||
# `podman build -t bootstrap .`
|
||||
# `podman run -it -v /path/to/my/dotfiles:/path/to/my/dotfiles:ro bootstrap`
|
||||
|
||||
RUN pacman-key --init
|
||||
RUN pacman -Syu --noconfirm git vi base-devel sudo zsh man man-pages
|
||||
|
||||
RUN useradd -m -G wheel -s /usr/bin/zsh marty
|
||||
RUN echo "%wheel ALL=(ALL:ALL) ALL" >> /etc/sudoers
|
||||
RUN echo "marty:password" | chpasswd
|
||||
|
||||
# RUN su marty
|
||||
# RUN cd /home/marty
|
||||
# RUN git clone "https://git.martyoeh.me/Marty/dotfiles" .dotfiles
|
||||
# link it directly from dotfile development dir to experiment
|
||||
|
||||
USER marty
|
||||
VOLUME /home/marty/.dotfiles
|
||||
WORKDIR /home/marty/.dotfiles
|
||||
CMD ["/usr/bin/bash"]
|
|
@ -1,9 +0,0 @@
|
|||
# bootstrap module
|
||||
|
||||
The bootstrapping module mainly concerns the setup of the repository itself -- installation of packages, setting up basic options and maintenance scripts.
|
||||
|
||||
* installs general list of packages, listed [here](bootstrap/packages.tsv)
|
||||
* if githooks are enabled (either through install script, or manually) will compare installed packages with those on the package list on each commit and warn user about differences
|
||||
* contains a simple alias `dotlink` which allows quickly re-linking dotfiles when they have been changed. This is useful to invoke when files have been removed or added and need to be sym-linked by stow again (only works for `~/.dotfiles` dot directory)
|
||||
* contains an `update_package_list.sh` script which I can use to quickly repopulate the list of explicitly installed packages, noting down their source (repositories or AUR) and retaining their target, if I set any (only works for `~/.dotfiles` dot directory)
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
# relink all stowed files from anywhere
|
||||
# grepping is to remove meaningless stderr lines until this bug is fixed:
|
||||
# https://github.com/aspiers/stow/issues/65
|
||||
#
|
||||
# redirection is a neat way to filter stderr msgs by redirecting stderr
|
||||
# to stdout in a subshell, grepping in it, and redirecting back to stderr:
|
||||
# https://stackoverflow.com/a/15936384
|
||||
#
|
||||
# to customize this to your own needs, change the `push folder` to the
|
||||
# location of your dotfiles (stow) repository
|
||||
|
||||
alias dotlink="pushd ~/.dotfiles;\
|
||||
dotter deploy;\
|
||||
popd"
|
|
@ -1,116 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Simple app bootstrapping script
|
||||
|
||||
#=== main function ============================================================
|
||||
# NAME: main
|
||||
# DESCRIPTION: Display usage information for this script.
|
||||
# PARAMETERS: see usage function
|
||||
#==============================================================================
|
||||
PKG_TSV_FILE=${PKG_TSV_FILE:-bootstrap/packages_stable.tsv}
|
||||
packages_repo="${BOOTSTRAP_PACKAGES:-$(grep -e ' R ' "$PKG_TSV_FILE" | cut -f1 -d' ')}"
|
||||
packages_aur="${BOOTSTRAP_PACKAGES_AUR:-$(grep -e ' A ' "$PKG_TSV_FILE" | cut -f1 -d' ')}"
|
||||
packages_pipx="${BOOTSTRAP_PACKAGES_PIPX:-$(grep -e ' P ' "$PKG_TSV_FILE" | cut -f1,5 -d' ')}"
|
||||
|
||||
main() {
|
||||
local cmd=""
|
||||
local ret=0
|
||||
|
||||
case "$1" in
|
||||
-v | --version)
|
||||
printf "Package bootstrap script.\n\n©Marty Oehme\n\nVersion: 0.3\n"
|
||||
;;
|
||||
-h | --help)
|
||||
printf "Usage: install [-f|--force][-v|--version][-h|--help]\n\n-f Do not ask for any confirmations but force update and installation.\n"
|
||||
;;
|
||||
-f | --force)
|
||||
install true
|
||||
;;
|
||||
*)
|
||||
install false
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
|
||||
$cmd "$@"
|
||||
ret=$((ret + $?))
|
||||
exit $ret
|
||||
}
|
||||
|
||||
install_paru() {
|
||||
# check for existing paru installation
|
||||
if type paru >/dev/null 2>&1; then
|
||||
echo "Existing paru installation found ..........................................."
|
||||
return
|
||||
fi
|
||||
|
||||
# use tmp dir to make paru
|
||||
tempdir=".paru"
|
||||
git clone https://aur.archlinux.org/paru.git "$tempdir"
|
||||
pushd "$tempdir" || exit 1
|
||||
makepkg -si
|
||||
popd || exit 1
|
||||
rm -rf "$tempdir"
|
||||
}
|
||||
|
||||
update_repos() {
|
||||
unattended="$1"
|
||||
if "$unattended"; then
|
||||
paru -Sqyy --noconfirm
|
||||
else
|
||||
paru -Syy
|
||||
fi
|
||||
}
|
||||
|
||||
install_packages() {
|
||||
unattended="$1"
|
||||
if "$unattended"; then
|
||||
echo "$packages_repo" "$packages_aur" | paru -Squ --noconfirm --needed -
|
||||
else
|
||||
echo "$packages_repo" | paru -Squ --needed -
|
||||
echo "$packages_aur" | paru -S --needed -
|
||||
fi
|
||||
}
|
||||
|
||||
install_pipx() {
|
||||
if type pipx >/dev/null 2>&1; then
|
||||
echo "Existing pipx installation found .........................................."
|
||||
return
|
||||
fi
|
||||
if "$unattended"; then
|
||||
paru -S --noconfirm python-pipx
|
||||
else
|
||||
paru -S python-pipx
|
||||
fi
|
||||
}
|
||||
|
||||
install_pipx_pkgs() {
|
||||
while IFS= read -r line; do
|
||||
if [ -z "$line" ]; then return; fi
|
||||
prog=$(echo "$line" | cut -f1 -d' ')
|
||||
pipx install "$prog"
|
||||
|
||||
injections=$(echo "$line" | cut -f2 -d' ')
|
||||
for inject_args in ${injections//,/ }; do
|
||||
pipx inject "$prog" "$inject_args"
|
||||
done
|
||||
done <<<"$packages_pipx"
|
||||
}
|
||||
|
||||
install() {
|
||||
unattended=$1
|
||||
echo "Beginning package bootstrap ..............................................."
|
||||
echo "Installing paru ............................................................"
|
||||
install_paru
|
||||
echo "Installing apps ..........................................................."
|
||||
update_repos "$unattended"
|
||||
install_packages "$unattended"
|
||||
echo "Done ......................................................................"
|
||||
echo "Installing pipx ..........................................................."
|
||||
install_pipx
|
||||
echo "Installing pipx packages .................................................."
|
||||
install_pipx_pkgs
|
||||
echo "Done ......................................................................"
|
||||
}
|
||||
|
||||
main "$@"
|
|
@ -1,363 +0,0 @@
|
|||
Name Description Source Target Injections
|
||||
aaxtomp3 Convert Audible's .aax filetype to MP3, FLAC, M4A, or OPUS A
|
||||
acpid A daemon for delivering ACPI power management events with netlink support R
|
||||
afew Initial tagging script for notmuch mail R
|
||||
alias-tips-git An oh-my-zsh plugin to help remembering those aliases you defined once A
|
||||
alsa-utils Advanced Linux Sound Architecture - Utilities R
|
||||
anki-bin Helps you remember facts (like words/phrases in a foreign language) efficiently. Installed with wheel. A
|
||||
ansible Official assortment of Ansible collections R
|
||||
ansible-lint Checks playbooks for practices and behaviour that could potentially be improved. R
|
||||
arch-wiki-lite Arch Wiki without HTML. 1/9 as big, easily searched & viewable on console R
|
||||
arduino Arduino prototyping platform SDK R
|
||||
arduino-avr-core Arduino AVR core with upstream avr-gcc and avrdude R
|
||||
arduino-cli Arduino command line interface R
|
||||
asciinema Record and share terminal sessions R
|
||||
asix-ax88179-dkms A kernel module for ASIX AX88178A AX88179 USB 3.0 network adapters A
|
||||
aspell-de German dictionary for aspell R
|
||||
aspell-en English dictionary for aspell R
|
||||
atool A script for managing file archives of various types R
|
||||
atuin Magical shell history R
|
||||
aubio A tool for extracting annotations from audio signals R
|
||||
autofs A kernel-based automounter for Linux A
|
||||
barrier Open-source KVM software based on Synergy (GUI) R
|
||||
base Minimal package set to define a basic Arch Linux installation R
|
||||
base-devel Basic tools to build Arch Linux packages R
|
||||
bash-bats Bash Automated Testing System R
|
||||
bash-completion Programmable completion for the bash shell R
|
||||
bash-language-server Bash language server implementation based on Tree Sitter and its grammar for Bash R
|
||||
bat Cat clone with syntax highlighting and git integration R
|
||||
bc An arbitrary precision calculator language R
|
||||
beancount A personal double entry accounting and budgeting software P git+https://github.com/bratekarate/beancount-categorizer.git,beancount-dkb,fava,python-magic,smart-importer
|
||||
bearssl Implementation of the SSL/TLS protocol (RFC 5246) written in C R
|
||||
beets Organize your music collection from the command line P beetcamp,deets-describe,beets-ydl,pyacoustid,pylast
|
||||
bemoji-git Emoji picker that remembers your favorites. A
|
||||
bibclean BibTeX and Scribe bibliography prettyprinter and syntax checker A
|
||||
biber A Unicode-capable BibTeX replacement for biblatex users R
|
||||
bibtool A tool for manipulating BibTeX files R
|
||||
bind A complete, highly portable implementation of the DNS protocol R
|
||||
bluetuith-bin A TUI based bluetooth manager A
|
||||
bluez-utils Development and debugging utilities for the bluetooth protocol stack R
|
||||
booster Fast and secure initramfs generator R
|
||||
brightnessctl Lightweight brightness control tool R
|
||||
btop A monitor of system resources, bpytop ported to C++ R
|
||||
caddy Fast web server with automatic HTTPS R
|
||||
calcurse A text-based personal organizer R
|
||||
catdoc A convertor for Microsoft Word, Excel, PowerPoint and RTF Files to text R
|
||||
chafa Image-to-text converter supporting a wide range of symbols and palettes, transparency, animations, etc. R
|
||||
cinny-desktop-bin Matrix client focusing primarily on a simple, elegant and secure interface (binary release) A
|
||||
clipman A simple clipboard manager for Wayland A
|
||||
cups-pdf PDF printer for cups R
|
||||
cups-pk-helper A helper that makes system-config-printer use PolicyKit R
|
||||
dbus-broker Linux D-Bus Message Broker R
|
||||
dcnnt Yet another tool to connect Android phone with desktop similar to KDE Connect A
|
||||
dconf-editor GSettings editor for GNOME R
|
||||
dell-command-configure Configure various BIOS features on Dell laptops A
|
||||
dhcpcd RFC2131 compliant DHCP client daemon R
|
||||
distrobox Use any linux distribution inside your terminal. A
|
||||
dnsmasq Lightweight, easy to configure DNS forwarder and DHCP server R
|
||||
docker Pack, ship and run any application as a lightweight container R
|
||||
docker-compose Fast, isolated development environments using Docker R
|
||||
docx2txt Recovers text from DOCX files, with good formatting. R
|
||||
dos2unix Text file format converter R
|
||||
dotter-rs-bin A dotfile manager and templater written in Rust A
|
||||
duf Disk Usage/Free Utility R
|
||||
dust A more intuitive version of du in rust R
|
||||
efm-langserver General purpose Language Server A
|
||||
enca Charset analyser and converter R
|
||||
entr Run arbitrary commands when files change R
|
||||
euporie View and work with ipnb Python notebooks from the cli P
|
||||
exa ls replacement R
|
||||
exercism-bin Command line client for exercism.io A
|
||||
exfat-utils Utilities for exFAT file system R
|
||||
f3 Simple tool that tests flash cards capacity and performance to see if they live up to claimed specifications A
|
||||
fd Simple, fast and user-friendly alternative to find R
|
||||
ffmpegthumbnailer Lightweight video thumbnailer that can be used by file managers R
|
||||
firefox Standalone web browser from mozilla.org R
|
||||
flavours A simple and easy cli to build and use base16 schemes A
|
||||
fonts-cjk Linux 下的免费商用字体包 A
|
||||
freerdp Free implementation of the Remote Desktop Protocol (RDP) R
|
||||
fwupd Simple daemon to allow session software to update firmware R
|
||||
fzf-tab-bin-git Replace zsh's default completion selection menu with fzf (git version). This package also compiles the optional binary module. A
|
||||
gallery-dl Command-line program to download image-galleries and collections from several image hosting sites A
|
||||
gamemode A daemon/lib combo that allows games to request a set of optimisations be temporarily applied to the host OS R
|
||||
gimp GNU Image Manipulation Program R
|
||||
git-delta Syntax-highlighting pager for git and diff output R
|
||||
git-lfs Git extension for versioning large files R
|
||||
gitlint Git commit message linter A
|
||||
gitui Blazing fast terminal-ui for git written in Rust R
|
||||
gk6x-bin Configure keys, macros, and lighting on GK6X keyboards (GK64, GK84, GK61, etc) A
|
||||
glances CLI curses-based monitoring tool R
|
||||
glfw-wayland A free, open source, portable framework for graphical application development (wayland) R
|
||||
glow Command-line markdown renderer R
|
||||
gnu-netcat GNU rewrite of netcat, the network piping application R
|
||||
gnumeric A GNOME Spreadsheet Program R
|
||||
gnuplot Plotting package which outputs to X11, PostScript, PNG, GIF, and others R
|
||||
go-md2man A markdown to manpage generator R
|
||||
gomuks A terminal based Matrix client written in Go A
|
||||
gopls Language server for Go programming language R
|
||||
gotty-bin Simple command line tool that turns your CLI tools into web applications. A
|
||||
grim Screenshot utility for Wayland R
|
||||
grub GNU GRand Unified Bootloader (2) R
|
||||
gsimplecal Simple and lightweight GTK calendar R
|
||||
gst-plugins-bad Multimedia graph framework - bad plugins R
|
||||
gstreamer-vaapi Multimedia graph framework - vaapi plugin R
|
||||
gucharmap Gnome Unicode Charmap R
|
||||
haveged Entropy harvesting daemon using CPU timings R
|
||||
heimdall Tool suite used to flash firmware (ROMs) onto Samsung Galaxy S devices R
|
||||
htop Interactive process viewer R
|
||||
hugo Fast and Flexible Static Site Generator in Go R
|
||||
iftop Display bandwidth usage on an interface R
|
||||
imapfilter A mail filtering utility for processing IMAP mailboxes A
|
||||
imv Image viewer for Wayland and X11 R
|
||||
intel-ucode Microcode update files for Intel CPUs R
|
||||
iputils Network monitoring tools, including ping R
|
||||
ipython An enhanced Interactive Python shell. R
|
||||
isbntools A variety of tools to work with isbn addresses P
|
||||
iucode-tool Tool to manipulate Intel® IA-32/X86-64 microcode bundles R
|
||||
iwd Internet Wireless Daemon R
|
||||
jiq-bin Interactive JSON query tool using jq expressions A
|
||||
jmtpfs FUSE and libmtp based filesystem for accessing MTP (Media Transfer Protocol) devices A
|
||||
jpdftweak A Swiss Army Knife GUI application for PDF documents A
|
||||
jrnl Collect your thoughts and notes without leaving the command line R
|
||||
jupyter-nbclient A tool for running Jupyter Notebooks in different execution contexts. R
|
||||
kanshi Dynamic output configuration for Wayland WMs R
|
||||
keyd A key remapping daemon for linux. A
|
||||
khal CLI calendar application build around CalDAV R
|
||||
khard Console address book manager R
|
||||
kubo IPFS implementation in Go R
|
||||
lazygit Simple terminal UI for git commands R
|
||||
lib32-gamemode A daemon/lib combo that allows games to request a set of optimisations be temporarily applied to the host OS R
|
||||
libdvdcss Portable abstraction library for DVD decryption R
|
||||
libfido2 Library functionality for FIDO 2.0, including communication with a device over USB R
|
||||
libqalculate Multi-purpose desktop calculator R
|
||||
libreoffice-fresh LibreOffice branch which contains new features and program enhancements R
|
||||
libva-intel-driver VA-API implementation for Intel G45 and HD Graphics family R
|
||||
libvirt API for controlling virtualization engines (openvz,kvm,qemu,virtualbox,xen,etc) R
|
||||
linux The Linux kernel and modules R
|
||||
linux-firmware Firmware files for Linux R
|
||||
linux-headers Headers and scripts for building modules for the Linux kernel R
|
||||
linux-lts The LTS Linux kernel and modules R
|
||||
littler a hash-bang and simple command line pipe front end for GNU R A
|
||||
logrotate Rotates system logs automatically R
|
||||
lsof Lists open files for running Unix processes R
|
||||
lswt List Wayland toplevels A
|
||||
lua-format LuaFormatter - Code formatter for Lua A
|
||||
lua-language-server Lua Language Server coded by Lua R
|
||||
lua51 Powerful lightweight programming language designed for extending applications R
|
||||
luacheck A tool for linting and static analysis of Lua code R
|
||||
lutris Open Gaming Platform R
|
||||
ly TUI display manager A
|
||||
lynx A text browser for the World Wide Web R
|
||||
magic-wormhole Securely transfer data between computers R
|
||||
maim Utility to take a screenshot using imlib2 R
|
||||
mako Lightweight notification daemon for Wayland R
|
||||
man-db A utility for reading man pages R
|
||||
man-pages Linux man pages R
|
||||
markdown-anki-decks Construct and modify anki decks directly with markdown P
|
||||
markdownlint-cli MarkdownLint Command Line Interface A
|
||||
masterpdfeditor-free A complete solution for creation and editing PDF files - Free version without watermark A
|
||||
mbsync-git free (GPL) mailbox synchronization program A
|
||||
mediainfo Supplies technical and tag information about a video or audio file (CLI interface) R
|
||||
mermaid-cli Generation of diagram and flowchart from text in a similar manner as markdown (CLI) A
|
||||
mimeo Open files by MIME-type or file name using regular expressions. A
|
||||
minidlna A DLNA/UPnP-AV Media server (aka ReadyDLNA) R
|
||||
minio-client Replacement for ls, cp, mkdir, diff and rsync commands for filesystems and object storage R
|
||||
mopidy-bandcamp Mopidy backend for Bandcamp A
|
||||
mopidy-iris A Mopidy Web client that utilizes the Spotify and EchoNest frameworks. (Formerly Spotmop) A
|
||||
mopidy-local Mopidy extension for local media playback A
|
||||
mopidy-mpd Mopidy extension for controlling playback from MPD clients A
|
||||
mopidy-mpris Mopidy extension for controlling Mopidy through the MPRIS D-Bus interface A
|
||||
mopidy-scrobbler Mopidy extension for scrobbling played tracks to Last.fm A
|
||||
mopidy-somafm Mopidy extension for playing music from SomaFM A
|
||||
mopidy-youtube Mopidy extension for playing music from Youtube A
|
||||
moreutils A growing collection of the unix tools that nobody thought to write thirty years ago R
|
||||
mosh Mobile shell, surviving disconnects with local echo and line editing R
|
||||
mpv-mpris MPRIS plugin for mpv R
|
||||
msmtp A mini smtp client R
|
||||
mupdf-gl Lightweight PDF and XPS viewer with OpenGL backend R
|
||||
mutt-ics Show calendar event details in mutt A
|
||||
ncmpcpp Almost exact clone of ncmpc with some new features R
|
||||
needrestart Restart daemons after library updates. A
|
||||
neomutt A version of mutt with added features R
|
||||
neovim Fork of Vim aiming to improve user experience, plugins, and GUIs R
|
||||
net-tools Configuration tools for Linux networking R
|
||||
netbird-bin WireGuard-based mesh network A
|
||||
netctl Profile based systemd network management R
|
||||
nethogs A net top tool which displays traffic used per process instead of per IP or interface R
|
||||
network-manager-applet Applet for managing network connections R
|
||||
networkmanager-openconnect NetworkManager VPN plugin for OpenConnect R
|
||||
newsboat RSS/Atom feed reader for text terminals R
|
||||
nextcloud-client Nextcloud desktop client R
|
||||
nfs-utils Support programs for Network File Systems R
|
||||
nmap Utility for network discovery and security auditing R
|
||||
nodejs-pandiff Prose diffs for any document format supported by Pandoc A
|
||||
noto-fonts-emoji Google Noto emoji fonts R
|
||||
npm A package manager for javascript R
|
||||
nss-mdns glibc plugin providing host name resolution via mDNS R
|
||||
nsxiv Neo (or New or Not) Simple (or Small or Suckless) X Image Viewer A
|
||||
ntfs-3g NTFS filesystem driver and utilities R
|
||||
ntp Network Time Protocol reference implementation R
|
||||
nushell A new type of shell R
|
||||
nzbget Download from Usenet using .nzb files R
|
||||
offpunk-git Fork of the command-line Gemini client AV-98 with added offline capabilities A
|
||||
oh-my-zsh-git A community-driven framework for managing your zsh configuration. Includes 180+ optional plugins and over 120 themes to spice up your morning, and an auto-update tool so that makes it easy to keep up with the latest updates from the community A
|
||||
os-prober Utility to detect other OSes on a set of drives R
|
||||
pacman-contrib Contributed scripts and tools for pacman systems R
|
||||
papis Papis is a powerful and highly extensible command-line based document and bibliography manager. P whoosh,papis-zotero,papis-scihub,git+https://git.martyoeh.me/Marty/papis-extract.git,git+https://github.com/supersambo/papis-tui,pybtex-apa-style,git+https://git.martyoeh.me/Marty/papis-bbt-formatter.git
|
||||
parallel A shell tool for executing jobs in parallel R
|
||||
parsec-bin Remotely connect to a gaming pc for a low latency remote computing experience A
|
||||
paru-bin Feature packed AUR helper A
|
||||
pass-coffin A password store extension that hides data inside a signed and encrypted coffin A
|
||||
pass-ssh A pass extension that creates ssh keys with an automatically generated passphrases stored in pass and outputs the public key using fzf or rofi A
|
||||
pavucontrol PulseAudio Volume Control R
|
||||
pbzip2 Parallel implementation of the bzip2 block-sorting file compressor R
|
||||
pdfjs PDF reader in javascript R
|
||||
pdftk Command-line tool for working with PDFs R
|
||||
peek Simple screen recorder with an easy to use interface R
|
||||
perf Linux kernel performance auditing tool R
|
||||
perl-authen-sasl Perl/CPAN Module Authen::SASL : SASL authentication framework R
|
||||
piavpn-bin Private Internet Access client A
|
||||
pigz Parallel implementation of the gzip file compressor R
|
||||
pipewire-alsa Low-latency audio/video router and processor - ALSA configuration R
|
||||
pipewire-roc Low-latency audio/video router and processor - ROC streaming support R
|
||||
playerctl mpris media player controller and lib for spotify, vlc, audacious, bmp, xmms2, and others. R
|
||||
podman Tool and library for running OCI-based containers in pods R
|
||||
powertop A tool to diagnose issues with power consumption and power management R
|
||||
prettier An opinionated code formatter for JS, JSON, CSS, YAML and much more R
|
||||
protonvpn Official ProtonVPN metapackage that installs protonvpn-gui and protonvpn-cli, maintained by the ProtonVPN team. A
|
||||
ptpython Python REPL build on top of prompt_toolkit A
|
||||
pulsemixer CLI and curses mixer for pulseaudio R
|
||||
pv A terminal-based tool for monitoring the progress of data through a pipeline. R
|
||||
pyright Type checker for the Python language R
|
||||
python-adblock Brave's adblock library in Python R
|
||||
python-dictcc commandline tool for dict.cc A
|
||||
python-docs Set of HTML documentation for python R
|
||||
python-html2text A HTML to markdown-structured text converter R
|
||||
python-openpyxl A Python library to read/write Excel 2007 xlsx/xlsm files R
|
||||
python-pagelabels Python library to manipulate PDF page numbers and labels. A
|
||||
python-pancritic CriticMarkdup parser with optional pandoc backend A
|
||||
python-pdfminer Python PDF Parser R
|
||||
python-pip The PyPA recommended tool for installing Python packages R
|
||||
python-pipx Install and Run Python Applications in Isolated Environments R
|
||||
python-pybluez Python wrapper for the BlueZ Bluetooth stack R
|
||||
python-pybtex A BibTeX-compatible bibliography processor written in Python R
|
||||
python-pynvim Python client for Neovim R
|
||||
python-pyqt6-3d Python bindings for Qt3D R
|
||||
python-pyqt6-charts Python bindings for QtChart R
|
||||
python-pyqt6-datavisualization Python bindings for QtDataVisualization R
|
||||
python-pyqt6-networkauth Python bindings for QtNetworkAuth R
|
||||
python-readability-lxml Fast html to text parser (article readability tool) python library R
|
||||
python-slugify A Python slugify application that handles unicode R
|
||||
python-tasklib Python library for interacting with taskwarrior databases R
|
||||
qemu-desktop A QEMU setup for desktop environments R
|
||||
qt5-wayland Provides APIs for Wayland R
|
||||
qt5-xmlpatterns Support for XPath, XQuery, XSLT and XML schema validation R
|
||||
qt6-svg Classes for displaying the contents of SVG files R
|
||||
qt6-wayland Provides APIs for Wayland R
|
||||
qtcurve-gtk2 A configurable set of widget styles for KDE and Gtk R
|
||||
quarto-cli-bin An open-source scientific and technical publishing system built on Pandoc (binary from official repo) A
|
||||
qutebrowser A keyboard-driven, vim-like browser based on Python and Qt R
|
||||
refind An EFI boot manager R
|
||||
refind-btrfs Generate rEFInd manual boot stanzas from Btrfs snapshots A
|
||||
reflector A Python 3 module and script to retrieve and filter the latest Pacman mirror list. R
|
||||
remind A sophisticated calendar and alarm program. R
|
||||
remmina remote desktop client written in GTK+ R
|
||||
restic Fast, secure, efficient backup program R
|
||||
ripgrep-all rga: ripgrep, but also search in PDFs, E-Books, Office documents, zip, tar.gz, etc. R
|
||||
river A dynamic tiling wayland compositor R
|
||||
rivercarro A slightly modified version of rivertile layout generator for river. A
|
||||
rng-tools Random number generator related utilities R
|
||||
sc-im A spreadsheet program based on SC A
|
||||
scc Sloc, Cloc and Code: a very fast accurate code counter with complexity calculations and COCOMO estimates written in pure Go A
|
||||
scrcpy Display and control your Android device R
|
||||
screen Full-screen window manager that multiplexes a physical terminal R
|
||||
sfz A simple static file server A
|
||||
shellcheck-bin Shell script analysis tool (binary release, static) A
|
||||
shfmt Format shell programs R
|
||||
sioyek PDF viewer for research papers and technical books. A
|
||||
slurp Select a region in a Wayland compositor R
|
||||
smartmontools Control and monitor S.M.A.R.T. enabled ATA and SCSI Hard Drives R
|
||||
snap-pac Pacman hooks that use snapper to create pre/post btrfs snapshots like openSUSE's YaST R
|
||||
speedtest-cli Command line interface for testing internet bandwidth using speedtest.net R
|
||||
sshfs FUSE client based on the SSH File Transfer Protocol R
|
||||
starship The cross-shell prompt for astronauts R
|
||||
steam Valve's digital software delivery system R
|
||||
sudo Give certain users the ability to run some commands as root R
|
||||
surfraw Shell Users' Revolutionary Front Rage Against the Web R
|
||||
swaybg Wallpaper tool for Wayland compositors R
|
||||
swayidle Idle management daemon for Wayland R
|
||||
swww Efficient animated wallpaper daemon for wayland, controlled at runtime. A
|
||||
task-spooler Queue up tasks from the shell for batch execution A
|
||||
taskopen Script for taking notes and open urls with taskwarrior A
|
||||
tasksh A shell command that wraps Taskwarrior commands A
|
||||
tea A command line tool to interact with Gitea servers R
|
||||
tectonic Modernized, complete, self-contained TeX/LaTeX engine, powered by XeTeX and TeXLive R
|
||||
tex-gyre-fonts Substitute PostScript fonts in OpenType format R
|
||||
texlab A cross-platform implementation of the Language Server Protocol for LaTeX. R
|
||||
thermald The Linux Thermal Daemon program from 01.org R
|
||||
tidy-viewer CLI csv pretty printer that uses column styling A
|
||||
timew Timewarrior, A command line time tracking application R
|
||||
tlp Linux Advanced Power Management R
|
||||
tmux Terminal multiplexer R
|
||||
toilet free replacement for the FIGlet utility. A
|
||||
topgrade-bin Invoke the upgrade procedure of multiple package managers A
|
||||
traceroute Tracks the route taken by packets over an IP network R
|
||||
translate-shell A command-line interface and interactive shell for Google Translate R
|
||||
transmission-qt Fast, easy, and free BitTorrent client (Qt GUI) R
|
||||
ttf-brill Brill Typeface by John Hudson for Brill Publishing House A
|
||||
ttf-comic-neue Comic Neue aspires to be the casual script choice for everyone including the typographically savvy. A
|
||||
ttf-heuristica A serif latin & cyrillic font, derived from the "Adobe Utopia" font by Apanov A
|
||||
ttf-iosevka-nerd Patched font Iosevka from nerd fonts library R
|
||||
ttf-signika Sans-serif typeface from Google by Anna Giedryś A
|
||||
ttf-victor-mono-nerd Patched font Victor Mono from nerd fonts library R
|
||||
tuir Browse Reddit from your terminal A
|
||||
tut A TUI for Mastodon with vim inspired keys A
|
||||
typescript-language-server Language Server Protocol (LSP) implementation for TypeScript using tsserver R
|
||||
udiskie Removable disk automounter using udisks R
|
||||
ufw Uncomplicated and easy to use CLI tool for managing a netfilter firewall R
|
||||
unrar The RAR uncompression program R
|
||||
unrtf Command-line program which converts RTF documents to other formats R
|
||||
urlview-xdg-git A curses URL parser for text files. Git version, adds support for QUITONLAUNCH option and XDG Base Directory specification compliance. A
|
||||
usql A universal command-line interface for SQL databases A
|
||||
v4l2loopback-dkms v4l2-loopback device – module sources R
|
||||
vagrant Build and distribute virtualized development environments R
|
||||
vdirsyncer Synchronize CalDAV and CardDAV. R
|
||||
viddy A modern watch command A
|
||||
vifm A file manager with curses interface, which provides Vi[m]-like environment R
|
||||
vim-language-server VimScript language server A
|
||||
virt-manager Desktop user interface for managing virtual machines R
|
||||
virtualbox Powerful x86 virtualization for enterprise as well as home use R
|
||||
virtualbox-guest-iso The official VirtualBox Guest Additions ISO image R
|
||||
visidata Terminal spreadsheet multitool for discovering and arranging data R
|
||||
viu Simple terminal image viewer R
|
||||
wallabag-client Command line client for the self hosted read-it-later app Wallabag A
|
||||
wavemon Ncurses-based monitoring application for wireless network devices R
|
||||
waybar Highly customizable Wayland bar for Sway and Wlroots based compositors R
|
||||
waylock A simple screenlocker for wayland compositors R
|
||||
wdisplays GUI display configurator for wlroots compositors A
|
||||
wev tool for debugging wayland events, similar to xev A
|
||||
wezterm A GPU-accelerated cross-platform terminal emulator and multiplexer R
|
||||
wf-recorder Screen recorder for wlroots-based compositors such as sway R
|
||||
wget Network utility to retrieve files from the Web R
|
||||
wireguard-tools next generation secure network tunnel - tools for configuration R
|
||||
wireless_tools Tools allowing to manipulate the Wireless Extensions R
|
||||
wlopm Wayland output power management. A
|
||||
wlsunset Day/night gamma adjustments for Wayland compositors A
|
||||
wpa_actiond Daemon that connects to wpa_supplicant and handles connect and disconnect events A
|
||||
wtype xdotool type for wayland R
|
||||
xdg-user-dirs Manage user directories like ~/Desktop and ~/Music R
|
||||
xsv A CLI for indexing, slicing, analyzing, splitting and joining CSV files R
|
||||
yaml-language-server YAML Language Server R
|
||||
yarn Fast, reliable, and secure dependency management R
|
||||
yt-dlp A youtube-dl fork with additional features and fixes R
|
||||
ytfzf A POSIX script to find and watch youtube videos from the terminal R
|
||||
yubikey-manager Python library and command line tool for configuring a YubiKey R
|
||||
zathura-cb Adds comic book support to zathura R
|
||||
zathura-djvu DjVu support for Zathura R
|
||||
zathura-pdf-mupdf PDF support for Zathura (MuPDF backend) (Supports PDF, ePub, and OpenXPS) R
|
||||
zk A command-line tool helping you to maintain a Zettelkasten or personal wiki R
|
||||
zotero-bin Zotero Standalone. Is a free, easy-to-use tool to help you collect, organize, cite, and share your research sources. A
|
||||
zoxide A smarter cd command for your terminal R
|
||||
zq Tooling for super-structured data A
|
||||
zsh-autosuggestions Fish-like autosuggestions for zsh R
|
||||
zsh-fast-syntax-highlighting Optimized and extended zsh-syntax-highlighting A
|
Can't render this file because it has a wrong number of fields in line 30.
|
|
@ -1,16 +0,0 @@
|
|||
Name Description Source Target
|
||||
adbfs-rootless-git fuse filesystem over adb tool for android devices, no device root required A
|
||||
arch-install-scripts Scripts to aid in installing Arch Linux R
|
||||
blueberry Bluetooth configuration tool R
|
||||
dotter-rs-bin A dotfile manager and templater written in Rust A
|
||||
eza A modern replacement for ls (community fork of exa) R
|
||||
feishin-appimage A modern self-hosted music player. A
|
||||
khal CLI calendar application built around CalDAV R
|
||||
m4b-tool-bin A command line utility to merge, split and chapterize audiobook files such as mp3, ogg, flac, m4a or m4b A
|
||||
nodejs-markmap-cli Create markmaps (mindmaps from markdown) from CLI A
|
||||
pv A terminal-based tool for monitoring the progress of data through a pipeline R
|
||||
qpwgraph PipeWire Graph Qt GUI Interface R
|
||||
texlive-latexextra TeX Live - LaTeX additional packages R
|
||||
toilet Free replacement for the FIGlet utility R
|
||||
vifm A file manager with curses interface, which provides Vi[m]-like environment R
|
||||
woeusb-ng Simple tool that enable you to create your own usb stick with Windows installer. A
|
|
|
@ -1,39 +0,0 @@
|
|||
# Makes capslock to control/escape
|
||||
# insert to paste
|
||||
# right alt to enable German Umlaute (äÄöÖüÜ),
|
||||
# sharp s (ß), and the Euro sign (€).
|
||||
# Needs compose key to be set in xkb to work correctly:
|
||||
# $ setxkbmap -option "compose:menu"
|
||||
|
||||
[ids]
|
||||
|
||||
*
|
||||
|
||||
[main]
|
||||
|
||||
capslock = overload(control, esc)
|
||||
insert = S-insert
|
||||
rightalt = layer(dia)
|
||||
shift = layer(shift)
|
||||
rightshift = layer(shift)
|
||||
|
||||
[shift:S]
|
||||
|
||||
rightalt = layer(shiftedDia)
|
||||
|
||||
[dia]
|
||||
|
||||
shift = layer(shiftedDia)
|
||||
rightshift = layer(shiftedDia)
|
||||
|
||||
a = macro(compose a ")
|
||||
o = macro(compose o ")
|
||||
u = macro(compose u ")
|
||||
s = macro(compose s s)
|
||||
e = macro(compose = e)
|
||||
|
||||
[shiftedDia]
|
||||
|
||||
a = macro(compose A ")
|
||||
o = macro(compose O ")
|
||||
u = macro(compose U ")
|
|
@ -1,100 +0,0 @@
|
|||
#
|
||||
# /etc/pacman.conf
|
||||
#
|
||||
# See the pacman.conf(5) manpage for option and repository directives
|
||||
|
||||
#
|
||||
# GENERAL OPTIONS
|
||||
#
|
||||
[options]
|
||||
# The following paths are commented out with their default values listed.
|
||||
# If you wish to use different paths, uncomment and update the paths.
|
||||
#RootDir = /
|
||||
#DBPath = /var/lib/pacman/
|
||||
#CacheDir = /var/cache/pacman/pkg/
|
||||
#LogFile = /var/log/pacman.log
|
||||
#GPGDir = /etc/pacman.d/gnupg/
|
||||
#HookDir = /etc/pacman.d/hooks/
|
||||
HoldPkg = pacman glibc
|
||||
#XferCommand = /usr/bin/curl -L -C - -f -o %o %u
|
||||
#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
|
||||
#CleanMethod = KeepInstalled
|
||||
Architecture = auto
|
||||
|
||||
# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
|
||||
#IgnorePkg =
|
||||
#IgnoreGroup =
|
||||
|
||||
#NoUpgrade =
|
||||
#NoExtract =
|
||||
|
||||
# Misc options
|
||||
UseSyslog
|
||||
Color
|
||||
#NoProgressBar
|
||||
CheckSpace
|
||||
VerbosePkgLists
|
||||
ParallelDownloads = 5
|
||||
|
||||
# By default, pacman accepts packages signed by keys that its local keyring
|
||||
# trusts (see pacman-key and its man page), as well as unsigned packages.
|
||||
SigLevel = Required DatabaseOptional
|
||||
LocalFileSigLevel = Optional
|
||||
#RemoteFileSigLevel = Required
|
||||
|
||||
# NOTE: You must run `pacman-key --init` before first using pacman; the local
|
||||
# keyring can then be populated with the keys of all official Arch Linux
|
||||
# packagers with `pacman-key --populate archlinux`.
|
||||
|
||||
#
|
||||
# REPOSITORIES
|
||||
# - can be defined here or included from another file
|
||||
# - pacman will search repositories in the order defined here
|
||||
# - local/custom mirrors can be added here or in separate files
|
||||
# - repositories listed first will take precedence when packages
|
||||
# have identical names, regardless of version number
|
||||
# - URLs will have $repo replaced by the name of the current repo
|
||||
# - URLs will have $arch replaced by the name of the architecture
|
||||
#
|
||||
# Repository entries are of the format:
|
||||
# [repo-name]
|
||||
# Server = ServerName
|
||||
# Include = IncludePath
|
||||
#
|
||||
# The header [repo-name] is crucial - it must be present and
|
||||
# uncommented to enable the repo.
|
||||
#
|
||||
|
||||
# The testing repositories are disabled by default. To enable, uncomment the
|
||||
# repo name header and Include lines. You can add preferred servers immediately
|
||||
# after the header, and they will be used before the default mirrors.
|
||||
|
||||
#[testing]
|
||||
#Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[core]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[extra]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
#[community-testing]
|
||||
#Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[community]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
# If you want to run 32 bit applications on your x86_64 system,
|
||||
# enable the multilib repositories as required here.
|
||||
|
||||
#[multilib-testing]
|
||||
#Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
[multilib]
|
||||
Include = /etc/pacman.d/mirrorlist
|
||||
|
||||
# An example of a custom package repository. See the pacman manpage for
|
||||
# tips on creating your own repositories.
|
||||
#[custom]
|
||||
#SigLevel = Optional TrustAll
|
||||
#Server = file:///home/custompkgs
|
|
@ -1,3 +0,0 @@
|
|||
[Sleep]
|
||||
HibernateDelaySec=120min
|
||||
SuspendEstimationSec=30min
|
|
@ -1 +0,0 @@
|
|||
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ATTRS{idVendor}=="0000", ATTRS{idProduct}=="3825", ATTR{power/wakeup}="disabled"
|
|
@ -1,97 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
BOOTSTRAP_DIR=${BOOTSTRAP_DIR:-$(pwd)/bootstrap}
|
||||
INPUTFILES=$(find "${BOOTSTRAP_DIR}" -type f -name 'packages*.tsv')
|
||||
OUTPUTFILE=${BOOTSTRAP_DIR}/packages_testing.tsv
|
||||
|
||||
pkg_all=$(pacman -Qqett)
|
||||
|
||||
pkg_repo=$(pacman -Qqn)
|
||||
pkg_aur=$(pacman -Qqm)
|
||||
|
||||
while getopts "nvhf:" opt; do
|
||||
case "$opt" in
|
||||
n) DRYRUN=true ;;
|
||||
v) VERBOSE=true ;;
|
||||
f) OUTPUTFILE="$OPTARG" ;;
|
||||
h | *)
|
||||
{
|
||||
printf "\nUpdate the list of installed packages.\n\nWill compare packages committed to the dotfile repository\nand those currently installed (on an Arch system, using pacman).\nUpdates the list of committed packages in repository\nand prints out the differences as a diff.\n\nOptions:\n\n\t-h\tDisplay this help.\n\t-v\tShow verbose information.\n\t-n\tPrint out changes without changing anything (dry-run).\n"
|
||||
exit 1
|
||||
}
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# get all existing written packages
|
||||
if [ -n "$INPUTFILES" ]; then
|
||||
INPUT=$(cat $INPUTFILES | grep -v -e '^Name Description Source Target' | sort)
|
||||
else
|
||||
INPUT=""
|
||||
fi
|
||||
|
||||
print_msg() {
|
||||
# shellcheck disable=2059
|
||||
[ -n "$VERBOSE" ] && printf "$@"
|
||||
}
|
||||
|
||||
# tsv file:
|
||||
# packagename, description, source, target
|
||||
# toot a toot manager A D
|
||||
|
||||
if [ -f "${OUTPUTFILE}_TEMP" ]; then
|
||||
rm "${OUTPUTFILE}_TEMP"
|
||||
fi
|
||||
touch "${OUTPUTFILE}_TEMP"
|
||||
|
||||
# create new package list
|
||||
for pkg in $pkg_all; do
|
||||
|
||||
source=""
|
||||
if echo "$pkg_repo" | grep -F -q -x "$pkg"; then
|
||||
source="R"
|
||||
elif echo "$pkg_aur" | grep -F -q -x "$pkg"; then
|
||||
source="A"
|
||||
else
|
||||
echo "ERROR: The package $pkg could not be found in repositories or AUR."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
desc=$(pacman -Qs "$pkg" | grep -A1 --color "local/$pkg\s" | tail -n1)
|
||||
#remove leading whitespace
|
||||
desc="${desc#"${desc%%[![:space:]]*}"}"
|
||||
|
||||
target=""
|
||||
found_line=$(echo "$INPUT" | grep -e "^$pkg")
|
||||
if [ -n "$found_line" ]; then
|
||||
target=$(echo "$found_line" | cut -f4)
|
||||
print_msg "Updating pkg: %s:%s from: %s, for: %s\n" "$pkg" "$desc" "$source" "$target"
|
||||
else
|
||||
print_msg "Adding pkg: %s:%s from: %s, for: %s\n" "$pkg" "$desc" "$source" "$target"
|
||||
fi
|
||||
|
||||
printf "%s\t%s\t%s\t%s\n" "$pkg" "$desc" "$source" "$target" >>"${OUTPUTFILE}_TEMP"
|
||||
done
|
||||
|
||||
# notify on any removed packages
|
||||
while read -r line; do
|
||||
if ! echo "$line" | cut -f1 | xargs -I _ grep -F -q -x _ <(echo "$pkg_all"); then
|
||||
printf "REMOVED: %s\n" "$line"
|
||||
fi
|
||||
done <<<"<(echo $INPUT | tail +2)"
|
||||
|
||||
# show file changes
|
||||
if [ -f "$OUTPUTFILE"_TEMP ]; then
|
||||
changes=$(diff --color=always -y --suppress-common-lines <(echo "$INPUT") <(sort "$OUTPUTFILE"_TEMP | tail -n+2))
|
||||
printf "FILE CHANGES:\n=============\n%s" "$changes"
|
||||
fi
|
||||
|
||||
# actually write changes to file
|
||||
if [ -z "$DRYRUN" ]; then
|
||||
|
||||
while IFS= read -r line; do
|
||||
sed -i -e "/^${line//\//\\/}$/d" "$OUTPUTFILE"_TEMP
|
||||
done <<< "$INPUT"
|
||||
|
||||
cat <(printf "Name\tDescription\tSource\tTarget\n") "${OUTPUTFILE}_TEMP" > "$OUTPUTFILE"
|
||||
fi
|
||||
rm "${OUTPUTFILE}_TEMP"
|
|
@ -1,92 +0,0 @@
|
|||
# Configuration for flavours
|
||||
# https://github.com/Misterio77/flavours
|
||||
#
|
||||
# This file should contain a [[items]] section for each application you want themed
|
||||
# You can also set a shell (outside items) on which to run hooks
|
||||
# Check flavours repository for more information and examples
|
||||
|
||||
|
||||
# Explanation and default values for keys:
|
||||
|
||||
# # Through which shell command hooks will run. The command will be replaced in '{}'
|
||||
shell = "bash -c '{}'"
|
||||
#
|
||||
# [[items]]
|
||||
# # File to inject to, supports tilde and env var expansion. required
|
||||
# file = "~/.config/example"
|
||||
# # Template to use. required
|
||||
# template = "example"
|
||||
#
|
||||
# # Subtemplate to use
|
||||
# subtemplate = "default"
|
||||
# # If not rewriting, on which line (usually a comment) to start replacing
|
||||
# start = "# Start flavours"
|
||||
# # If not rewriting, on which line (usually a comment) to stop replacing
|
||||
# end = "# End flavours"
|
||||
# # Should we rewrite the entire file, instead of using the above delimiters?
|
||||
# rewrite = false
|
||||
# # Command to execute after injecting (goes through shell)
|
||||
# hook = ""
|
||||
# # Whether this hook should be executed when flavours is ran with lightweight flag
|
||||
# light = true
|
||||
|
||||
[[items]]
|
||||
template = "waybar"
|
||||
file = "~/.local/state/waybar/colorscheme.css"
|
||||
rewrite = true
|
||||
light = false
|
||||
hook = "killall -SIGUSR2 waybar"
|
||||
|
||||
[[items]]
|
||||
# Uses custom nvim template to work together with
|
||||
# RRethy base16 neovim plugin
|
||||
template = "nvim"
|
||||
file = "~/.local/state/nvim/colorscheme.lua"
|
||||
rewrite = true
|
||||
|
||||
[[items]]
|
||||
# For newer wezterm versions (missing cursor= field)
|
||||
# make use of my custom wezterm template
|
||||
template = "wezterm"
|
||||
file = "~/.local/state/wezterm/colors.toml"
|
||||
rewrite = true
|
||||
|
||||
[[items]]
|
||||
template = "zathura"
|
||||
file = "~/.local/state/zathura/zathurarc"
|
||||
rewrite = true
|
||||
|
||||
[[items]]
|
||||
template = "qutebrowser"
|
||||
subtemplate = "minimal"
|
||||
file = "~/.local/state/qutebrowser/colorscheme.py"
|
||||
rewrite = true
|
||||
light = false
|
||||
hook = "pgrep -x qutebrowser && qutebrowser :config-source"
|
||||
|
||||
# CSS Webpage styling in qutebrowser
|
||||
[[item]]
|
||||
file = "~/.config/qutebrowser/stylesheets/stylesheet.css"
|
||||
template = "styles"
|
||||
subtemplate = "css-variables"
|
||||
rewrite = false
|
||||
start = "/* Start flavours */"
|
||||
end = "/* End flavours */"
|
||||
|
||||
[[items]]
|
||||
# MAKO DOES NOT SUPPORT INCLUDES YET
|
||||
template = "mako"
|
||||
file = "~/.config/mako/config"
|
||||
light = false
|
||||
rewrite = false
|
||||
start = "# Start flavours"
|
||||
end = "# End flavours"
|
||||
hook = "killall mako"
|
||||
|
||||
[[items]]
|
||||
# SIOYEK does not support includes afaik
|
||||
template = "sioyek"
|
||||
file = "~/.config/sioyek/prefs_user.config"
|
||||
rewrite = false
|
||||
start = "# START FLAVOURS"
|
||||
end = "# END FLAVOURS"
|
|
@ -1,42 +0,0 @@
|
|||
-- base16-nvim (https://github.com/wincent/base16-nvim)
|
||||
-- by Greg Hurrell (https://github.com/wincent)
|
||||
-- based on
|
||||
-- base16-vim (https://github.com/chriskempson/base16-vim)
|
||||
-- by Chris Kempson (https://github.com/chriskempson)
|
||||
-- using nvim-base16 neovim plugin
|
||||
-- by RRethy (https://github.com/RRethy/nvim-base16)
|
||||
-- {{scheme-name}} scheme by {{scheme-author}}
|
||||
|
||||
local function exists(plugin)
|
||||
local status, lib = pcall(require, plugin)
|
||||
if(status) then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
if exists("mini.base16") then
|
||||
require('mini.base16').setup({
|
||||
palette = {
|
||||
base00 = '#{{base00-hex}}',
|
||||
base01 = '#{{base01-hex}}',
|
||||
base02 = '#{{base02-hex}}',
|
||||
base03 = '#{{base03-hex}}',
|
||||
base04 = '#{{base04-hex}}',
|
||||
base05 = '#{{base05-hex}}',
|
||||
base06 = '#{{base06-hex}}',
|
||||
base07 = '#{{base07-hex}}',
|
||||
base08 = '#{{base08-hex}}',
|
||||
base09 = '#{{base09-hex}}',
|
||||
base0A = '#{{base0A-hex}}',
|
||||
base0B = '#{{base0B-hex}}',
|
||||
base0C = '#{{base0C-hex}}',
|
||||
base0D = '#{{base0D-hex}}',
|
||||
base0E = '#{{base0E-hex}}',
|
||||
base0F = '#{{base0F-hex}}'
|
||||
},
|
||||
})
|
||||
end
|
||||
if exists("lualine") then
|
||||
require("lualine").setup()
|
||||
end
|
||||
|
||||
-- vim: filetype=lua
|
|
@ -1,20 +0,0 @@
|
|||
# base16-sioyek (https://github.com/loiccoyle/base16-sioyek)
|
||||
# by Loic Coyle
|
||||
# {{scheme-name}} scheme by{{scheme-author}}
|
||||
|
||||
custom_background_color #{{base00-hex}}
|
||||
custom_color_mode_empty_background_color #{{base00-hex}}
|
||||
custom_text_color #{{base06-hex}}
|
||||
|
||||
page_separator_color #{{base00-hex}}
|
||||
search_highlight_color #{{base0A-hex}}
|
||||
status_bar_color #{{base00-hex}}
|
||||
status_bar_text_color #{{base06-hex}}
|
||||
ui_text_color #{{base06-hex}}
|
||||
ui_selected_text_color #{{base06-hex}}
|
||||
ui_background_color #{{base01-hex}}
|
||||
ui_selected_background_color #{{base03-hex}}
|
||||
visual_mark_color {{base03-dec-r}} {{base03-dec-g}} {{base03-dec-b}} 0.2
|
||||
text_highlight_color #{{base03-hex}}
|
||||
link_highlight_color #{{base0D-hex}}
|
||||
synctex_highlight_color #{{base08-hex}}
|
|
@ -1,44 +0,0 @@
|
|||
# Base16 {{scheme-name}} - wezterm color config
|
||||
# Scheme by {{scheme-author}}
|
||||
|
||||
[colors]
|
||||
foreground = "#{{base05-hex}}"
|
||||
background = "#{{base00-hex}}"
|
||||
cursor_bg = "#{{base05-hex}}"
|
||||
cursor_border = "#{{base05-hex}}"
|
||||
selection_bg = "#{{base05-hex}}"
|
||||
selection_fg = "#{{base00-hex}}"
|
||||
|
||||
ansi = [
|
||||
"#{{base00-hex}}",
|
||||
"#{{base08-hex}}",
|
||||
"#{{base0B-hex}}",
|
||||
"#{{base0A-hex}}",
|
||||
"#{{base0D-hex}}",
|
||||
"#{{base0E-hex}}",
|
||||
"#{{base0C-hex}}",
|
||||
"#{{base05-hex}}"
|
||||
]
|
||||
|
||||
brights = [
|
||||
"#{{base03-hex}}",
|
||||
"#{{base08-hex}}",
|
||||
"#{{base0B-hex}}",
|
||||
"#{{base0A-hex}}",
|
||||
"#{{base0D-hex}}",
|
||||
"#{{base0E-hex}}",
|
||||
"#{{base0C-hex}}",
|
||||
"#{{base07-hex}}"
|
||||
]
|
||||
|
||||
[colors.tab_bar]
|
||||
background = "#{{base00-hex}}"
|
||||
[colors.tab_bar.inactive_tab]
|
||||
bg_color = "#{{base00-hex}}"
|
||||
fg_color = "#{{base05-hex}}"
|
||||
[colors.tab_bar.active_tab]
|
||||
bg_color = "#{{base05-hex}}"
|
||||
fg_color = "#{{base00-hex}}"
|
||||
[colors.tab_bar.new_tab]
|
||||
bg_color = "#{{base05-hex}}"
|
||||
fg_color = "#{{base00-hex}}"
|
|
@ -1,32 +0,0 @@
|
|||
profile docked {
|
||||
output "LG Electronics W2442 0x000075FD" position 1920,0
|
||||
output "LG Electronics W2442 0x000075E1" position 0,0
|
||||
output eDP-1 disable
|
||||
exec notify-send "💻 Display changed" "Applying docked LG profile"
|
||||
}
|
||||
|
||||
profile docked {
|
||||
output "Goldstar Company Ltd W2442 0x000075FD" position 1920,0
|
||||
output "Goldstar Company Ltd W2442 0x000075E1" position 0,0
|
||||
output eDP-1 disable
|
||||
exec notify-send "💻 Display changed" "Applying docked Goldstar profile"
|
||||
}
|
||||
|
||||
profile dockedall {
|
||||
output "LG Electronics W2442 0x000075FD" position 1920,0
|
||||
output "LG Electronics W2442 0x000075E1" position 0,0
|
||||
output eDP-1 enable position 960,1080
|
||||
exec notify-send "💻 Display changed" "Applying docked 3-screen profile"
|
||||
}
|
||||
|
||||
profile portable {
|
||||
output "LG Electronics W2442 0x000075FD" disable
|
||||
output "LG Electronics W2442 0x000075E1" disable
|
||||
output eDP-1 enable position 0,0
|
||||
exec notify-send "💻 Display changed" "Applying portable profile"
|
||||
}
|
||||
|
||||
profile portable {
|
||||
output eDP-1 enable position 0,0
|
||||
exec notify-send "💻 Display changed" "Applying portable profile"
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
sort=-time
|
||||
layer=overlay
|
||||
width=300
|
||||
height=110
|
||||
border-size=2
|
||||
border-radius=15
|
||||
max-icon-size=64
|
||||
default-timeout=5000
|
||||
ignore-timeout=1
|
||||
font=monospace 14
|
||||
|
||||
# Intentionally left empty, automatically filled by flavours
|
||||
# on switching theme.
|
||||
# Start flavours
|
||||
|
||||
# End flavours
|
||||
|
||||
[urgency=critical]
|
||||
#on-notify=exec mpv /usr/share/sounds/freedesktop/stereo/message.oga
|
||||
default-timeout=0
|
||||
|
||||
[mode=do-not-disturb]
|
||||
invisible=1
|
||||
|
||||
[category=mpd]
|
||||
default-timeout=2000
|
||||
group-by=category
|
|
@ -1,297 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
mod="Mod4"
|
||||
modemod="Mod1"
|
||||
term=${TERMINAL:-foot}
|
||||
time_to_lockscreen=300
|
||||
time_to_screendim=600
|
||||
time_to_suspend=900
|
||||
|
||||
## OPTIONS
|
||||
riverctl spawn "dbus-update-activation-environment SEATD_SOCK DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=river"
|
||||
riverctl focus-follows-cursor normal
|
||||
riverctl set-cursor-warp on-output-change
|
||||
riverctl hide-cursor when-typing enabled
|
||||
riverctl attach-mode bottom
|
||||
|
||||
# Set background and border color
|
||||
riverctl background-color 0x000000
|
||||
riverctl border-width 1
|
||||
riverctl border-color-focused 0xffffff
|
||||
riverctl border-color-unfocused 0x586e75
|
||||
# Set repeat rate
|
||||
riverctl set-repeat 75 300
|
||||
# Make certain views start floating
|
||||
riverctl float-filter-add app-id float
|
||||
# riverctl float-filter-add title "popup title with spaces"
|
||||
# # Set app-ids and titles of views which should use client side decorations
|
||||
# riverctl csd-filter-add app-id "gedit"
|
||||
riverctl csd-filter-remove app-id "org.pwmt.zathura"
|
||||
|
||||
## DEBUG
|
||||
# Reload river configuration
|
||||
riverctl map normal $mod+Shift F12 spawn "$HOME/.config/river/init"
|
||||
|
||||
## HOTKEYS
|
||||
# close focused view
|
||||
riverctl map normal $mod+Shift C close
|
||||
|
||||
# Open terminal
|
||||
riverctl map normal $mod Return spawn "$term"
|
||||
# Open floating terminal
|
||||
riverctl map normal $mod+Control Return spawn "$term -e --class float"
|
||||
|
||||
# Open run menu
|
||||
riverctl map normal $mod Space spawn "bemenu-run"
|
||||
|
||||
# Toggle status bar
|
||||
riverctl map normal $mod F7 spawn "killall -SIGUSR1 waybar"
|
||||
|
||||
# Switch to lockscreen
|
||||
riverctl map normal $mod X spawn "lockscreen"
|
||||
|
||||
# Open logout script
|
||||
riverctl map normal $mod backspace spawn "powermenu"
|
||||
|
||||
# Open Bookmark search
|
||||
riverctl map normal None XF86Search spawn "qutedmenu"
|
||||
riverctl map normal $mod+Shift O spawn "qutedmenu"
|
||||
|
||||
# Open clipboard history
|
||||
riverctl map normal $mod+Shift Space spawn "clipman pick --tool=bemenu"
|
||||
|
||||
# Open floating calculator
|
||||
riverctl map normal $mod+Shift R spawn "$term -e --class float qalc"
|
||||
|
||||
# Open emoji picker
|
||||
riverctl map normal $mod+Shift E spawn "bemoji -nt"
|
||||
|
||||
# Open translation helper
|
||||
riverctl map normal $mod+Shift T spawn "bemenu-translate"
|
||||
|
||||
# Open item from library
|
||||
riverctl map normal $mod+Shift L spawn "papis -s picktool dmenu open"
|
||||
|
||||
# Desktop theming
|
||||
# shellcheck disable=SC2016
|
||||
riverctl map normal $mod+Shift S spawn 'flavourchoose'
|
||||
|
||||
# Password dropdown frontend
|
||||
riverctl map normal $mod+Shift P spawn "pass-pick"
|
||||
|
||||
# File upload
|
||||
riverctl map normal $mod+Shift U spawn "$term -e --class float sharefile | xargs notify-send"
|
||||
|
||||
# Open recent downloads
|
||||
riverctl map normal $mod+Shift D spawn "recently-downloaded"
|
||||
|
||||
# # Screenshot
|
||||
riverctl map normal None Print spawn "screenshot"
|
||||
riverctl map normal Shift Print spawn "screenshot | sharefile -"
|
||||
riverctl map normal $mod Print spawn "screenshot region"
|
||||
riverctl map normal $mod+Shift Print spawn "screenshot region | sharefile -"
|
||||
|
||||
# control notification daemon
|
||||
riverctl map normal $mod N spawn "makoctl dismiss"
|
||||
riverctl map normal $mod+Shift N spawn "makoctl dismiss --all"
|
||||
riverctl map normal $mod+Control N spawn "makoctl restore"
|
||||
|
||||
# MOVEMENT
|
||||
# focus the next/previous view in the layout stack
|
||||
riverctl map normal $mod J focus-view next
|
||||
riverctl map normal $mod K focus-view previous
|
||||
|
||||
# swap the focused view with the next/previous view in the layout stack
|
||||
riverctl map normal $mod+Shift J swap next
|
||||
riverctl map normal $mod+Shift K swap previous
|
||||
|
||||
# bump the focused view to the top of the layout stack
|
||||
riverctl map normal $mod+Shift Return zoom
|
||||
|
||||
# change layout orientation
|
||||
riverctl map normal $mod Up send-layout-cmd rivercarro "main-location top"
|
||||
riverctl map normal $mod Right send-layout-cmd rivercarro "main-location right"
|
||||
riverctl map normal $mod Down send-layout-cmd rivercarro "main-location bottom"
|
||||
riverctl map normal $mod Left send-layout-cmd rivercarro "main-location left"
|
||||
|
||||
# snap views to screen edges
|
||||
riverctl map normal $mod+Control H snap left
|
||||
riverctl map normal $mod+Control J snap down
|
||||
riverctl map normal $mod+Control K snap up
|
||||
riverctl map normal $mod+Control L snap right
|
||||
|
||||
# Mod+F to toggle fullscreen
|
||||
riverctl map normal $mod F toggle-fullscreen
|
||||
|
||||
# shellcheck disable=SC2016
|
||||
# Make all connected outputs show the desktop and no windows at all
|
||||
riverctl map normal $mod+Shift M spawn 'for i in $(wlopm | wc -l); do riverctl set-focused-tags $((1 << 10)); riverctl focus-output next; done; riverctl set-focused-tags $((1 << 10)); riverctl focus-output next'
|
||||
|
||||
riverctl map normal $mod+Shift F10 spawn 'riverctl send-layout-cmd rivercarro "gaps 0"'
|
||||
riverctl map normal $mod F10 spawn 'riverctl send-layout-cmd rivercarro "gaps 6"'
|
||||
|
||||
# toggle float
|
||||
riverctl map normal $mod+Shift v toggle-float
|
||||
# Mod + Left Mouse Button to move views
|
||||
riverctl map-pointer normal $mod BTN_LEFT move-view
|
||||
# Mod + Right Mouse Button to resize views
|
||||
riverctl map-pointer normal $mod BTN_RIGHT resize-view
|
||||
|
||||
### Begin resize and moving mode, for floating windows
|
||||
riverctl declare-mode interact_float
|
||||
riverctl map normal $modemod R enter-mode interact_float
|
||||
riverctl map interact_float $modemod R enter-mode normal
|
||||
# resize views on screen
|
||||
riverctl map interact_float $mod H resize horizontal -100
|
||||
riverctl map interact_float $mod J resize vertical 100
|
||||
riverctl map interact_float $mod K resize vertical -100
|
||||
riverctl map interact_float $mod L resize horizontal 100
|
||||
# move views around screen
|
||||
riverctl map interact_float $mod+Shift H move left 100
|
||||
riverctl map interact_float $mod+Shift J move down 100
|
||||
riverctl map interact_float $mod+Shift K move up 100
|
||||
riverctl map interact_float $mod+Shift L move right 100
|
||||
# decrease/increase the main ratio of layout
|
||||
riverctl map interact_float None H send-layout-cmd rivercarro "main-ratio -0.05"
|
||||
riverctl map interact_float None L send-layout-cmd rivercarro "main-ratio +0.05"
|
||||
# increment/decrement the main layout
|
||||
riverctl map interact_float None J send-layout-cmd rivercarro "main-count +1"
|
||||
riverctl map interact_float None K send-layout-cmd rivercarro "main-count -1"
|
||||
### End resize and moving mode
|
||||
|
||||
# focus the next/previous output
|
||||
riverctl map normal $mod Period focus-output next
|
||||
riverctl map normal $mod Comma focus-output previous
|
||||
|
||||
# send the focused view to the next/previous output
|
||||
riverctl map normal $mod+Shift Period send-to-output next
|
||||
riverctl map normal $mod+Shift Comma send-to-output previous
|
||||
|
||||
# set up 10 tags (with '0' opening the 10th one)
|
||||
for i in $(seq 0 9); do
|
||||
tags="$((1 << (i - 1)))"
|
||||
if [ "$i" -eq 0 ]; then tags="$((1 << 9))"; fi
|
||||
|
||||
# Mod+[1-9] to focus tag [0-8]
|
||||
riverctl map normal $mod "$i" set-focused-tags $tags
|
||||
|
||||
# Mod+Shift+[1-9] to tag focused view with tag [0-8]
|
||||
riverctl map normal $mod+Shift "$i" set-view-tags $tags
|
||||
|
||||
# Mod+Ctrl+[1-9] to toggle focus of tag [0-8]
|
||||
riverctl map normal $mod+Control "$i" toggle-focused-tags $tags
|
||||
|
||||
# Mod+Shift+Ctrl+[1-9] to toggle tag [0-8] of focused view
|
||||
riverctl map normal $mod+Shift+Control "$i" toggle-view-tags $tags
|
||||
done
|
||||
|
||||
# focus all tags
|
||||
all_tags=$(((1 << 32) - 1))
|
||||
riverctl map normal $mod equal set-focused-tags $all_tags
|
||||
# tag focused view with all tags
|
||||
riverctl map normal $mod+Shift equal set-view-tags $all_tags
|
||||
|
||||
# Various media key mapping examples for both normal and locked mode which do
|
||||
# not have a modifier
|
||||
for mode in normal locked; do
|
||||
# Eject the optical drive
|
||||
riverctl map $mode None XF86Eject spawn 'eject -T'
|
||||
|
||||
riverctl map -repeat $mode None XF86AudioRaiseVolume spawn 'pactl set-sink-volume @DEFAULT_SINK@ +5%'
|
||||
riverctl map -repeat $mode None XF86AudioLowerVolume spawn 'pactl set-sink-volume @DEFAULT_SINK@ -5%'
|
||||
riverctl map $mode None XF86AudioMute spawn 'pactl set-sink-mute @DEFAULT_SINK@ toggle'
|
||||
|
||||
# Control MPRIS aware media players with playerctl (https://github.com/altdesktop/playerctl)
|
||||
riverctl map $mode None XF86AudioMedia spawn 'playerctl play-pause'
|
||||
riverctl map $mode None XF86AudioPlay spawn 'playerctl play-pause'
|
||||
riverctl map $mode None XF86AudioPrev spawn 'playerctl previous'
|
||||
riverctl map $mode None XF86AudioNext spawn 'playerctl next'
|
||||
|
||||
# You can control screen backlight brighness with light (https://github.com/haikarainen/light); but we prefer brightnessctl
|
||||
riverctl map -repeat $mode None XF86MonBrightnessUp spawn 'brightnessctl set 10%+'
|
||||
riverctl map -repeat $mode None XF86MonBrightnessDown spawn 'brightnessctl set 10%-'
|
||||
done
|
||||
|
||||
# The scratchpad will live on an unused tag. Which tags are used depends on your
|
||||
# config, but rivers default uses the first 9 tags.
|
||||
scratch_tag=$((1 << 20))
|
||||
# Toggle the scratchpad with Super+P
|
||||
riverctl map normal $mod grave toggle-focused-tags ${scratch_tag}
|
||||
# Send windows to the scratchpad with Super+Shift+P
|
||||
riverctl map normal $mod+Shift grave set-view-tags ${scratch_tag}
|
||||
# Set spawn tagmask to ensure new windows don't have the scratchpad tag unless
|
||||
# explicitly set.
|
||||
all_but_scratch_tag=$((((1 << 32) - 1) ^ scratch_tag))
|
||||
riverctl spawn-tagmask ${all_but_scratch_tag}
|
||||
|
||||
# set up scratch pad for todo and 'drop-down' terminal
|
||||
# call scratchpads to current workspace -- scratchpads started on i3 starting (see end of file)
|
||||
# bindsym $mod+t [class="scratchpad" title="dropdown-todo"] scratchpad show
|
||||
# bindsym $mod+Shift+Return [class="scratchpad" title="dropdown-terminal"] scratchpad show
|
||||
|
||||
## INPUT
|
||||
# device (touchscreen)
|
||||
# enable touch clicking for touchpads
|
||||
for pad in $(riverctl list-inputs | grep -i touchpad); do
|
||||
riverctl input "$pad" events enabled
|
||||
riverctl input "$pad" tap enabled
|
||||
done
|
||||
for pad in $(riverctl list-inputs | grep -i touchscreen); do
|
||||
riverctl input "$pad" events enabled
|
||||
riverctl input "$pad" tap enabled
|
||||
riverctl input "$pad" drag enabled
|
||||
riverctl input "$pad" pointer-accel 0.5
|
||||
done
|
||||
|
||||
setxkbmap -option "compose:menu"
|
||||
|
||||
# start dynamic display configuration
|
||||
[ "$(pidof kanshi)" -eq 0 ] || riverctl spawn kanshi
|
||||
|
||||
# set a nice wallpaper
|
||||
if exist swww; then
|
||||
riverctl spawn "swww init"
|
||||
outputs=$(swww query | cut -d':' -f1)
|
||||
if [ "$(echo "$outputs" | grep -c -e '^DP')" -eq 2 ] && [ -e "$HOME/pictures/wall_r.jpg" ]; then
|
||||
swww img -o "$(echo "$outputs" | head -n1)" "$HOME/pictures/wall_l.jpg"
|
||||
swww img -o "$(echo "$outputs" | tail -n1)" "$HOME/pictures/wall_r.jpg"
|
||||
elif [ -e "$HOME/pictures/wall.jpg" ]; then
|
||||
swww img "$HOME/pictures/wall.jpg"
|
||||
fi
|
||||
fi
|
||||
|
||||
# start status bar
|
||||
killall waybar
|
||||
riverctl spawn waybar
|
||||
|
||||
# start redshift-like sundown warming using current location or standard values
|
||||
killall wlsunset
|
||||
loc=$(curl ipinfo.io | grep -e '"loc": ' | sed -e 's/^.*"loc": "\(.*\)",$/\1/')
|
||||
if [ -n "$loc" ]; then
|
||||
riverctl spawn "wlsunset -l \"$(echo "$loc" | cut -d, -f1)\" -L \"$(echo "$loc" | cut -d, -f2)\""
|
||||
else
|
||||
riverctl spawn "wlsunset -S \"09:00\" -s \"21:00\" -d \"3600\""
|
||||
fi
|
||||
unset loc
|
||||
|
||||
# start screen idle locking/dimming/sleep tool
|
||||
killall swayidle
|
||||
riverctl spawn "swayidle \
|
||||
timeout ${time_to_suspend} \"[ $(cat /sys/class/power_supply/AC/online) -eq 0 ] && systemctl suspend-then-hibernate\"
|
||||
timeout ${time_to_screendim} \"wlopm --off '*'\" \
|
||||
resume \"wlopm --on '*'\" \
|
||||
timeout ${time_to_lockscreen} \"pidof waylock || lockscreen\" \
|
||||
after-resume \"wlopm --on '*'\" \
|
||||
before-sleep \"pidof waylock || lockscreen\" &"
|
||||
killall clipman
|
||||
riverctl spawn "wl-paste -t text --watch clipman store"
|
||||
# bash ~/.config/bin/gtktheme # setting our gtk variables
|
||||
# killall polkit-gnome-authentication-agent-1
|
||||
# /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 &
|
||||
# start layouting engine
|
||||
killall rivercarro
|
||||
riverctl spawn "rivercarro -main-ratio 0.65"
|
||||
# River will send the process group of the init executable SIGTERM on exit.
|
||||
riverctl default-layout rivercarro
|
||||
# exec rivercarro -main-ratio 0.65 -view-padding 6 -outer-padding 6 & # -> does not work with current rivercarro version (0.1.4)
|
||||
brightnessctl set 70%
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
export XKB_DEFAULT_OPTIONS='compose:menu'
|
|
@ -1,185 +0,0 @@
|
|||
{
|
||||
"layer": "top",
|
||||
"modules-left": ["river/tags", "custom/events", "custom/vidl", "river/window"],
|
||||
"modules-center": ["clock", "custom/media"],
|
||||
"modules-right": ["river/mode", "custom/wireguard", "custom/archupdates", "pulseaudio", "backlight", "network", "cpu", "memory", "temperature", "battery", "tray"],
|
||||
"custom/archupdates": {
|
||||
"format": "{} {icon}",
|
||||
"format-alt-click": "right",
|
||||
"format-icons": {
|
||||
"default": ""
|
||||
},
|
||||
"return-type": "json",
|
||||
"exec": "~/.config/waybar/modules/archupdates 5 json",
|
||||
"interval": 3600,
|
||||
"on-click": "$TERMINAL start --class float topgrade"
|
||||
},
|
||||
"backlight": {
|
||||
"device": "intel_backlight",
|
||||
"format": "{percent}% {icon}",
|
||||
"format-icons": ["滋", "", "", ""],
|
||||
"on-scroll-up": "brightnessctl set 1%+",
|
||||
"on-scroll-down": "brightnessctl set 1%-"
|
||||
},
|
||||
"battery": {
|
||||
"format": "{capacity}% {icon}",
|
||||
"format-alt":"{capacity}% ({time}) {icon}",
|
||||
"format-alt-click": "click-right",
|
||||
"format-icons": ["", "", "", "", ""],
|
||||
"interval": 60,
|
||||
"states": {
|
||||
"warning": 30,
|
||||
"critical": 15
|
||||
},
|
||||
},
|
||||
"clock": {
|
||||
"format-alt": "{:%a, %d. %b %H:%M}",
|
||||
"format-alt-click": "click-right",
|
||||
"on-click": "gsimplecal"
|
||||
},
|
||||
"cpu": {
|
||||
"interval": 10,
|
||||
"format": "{usage}% ",
|
||||
"max-length": 10,
|
||||
"states": {
|
||||
"warning": 50,
|
||||
"critical": 80
|
||||
},
|
||||
"on-click": "$TERMINAL start --class float top",
|
||||
"on-click-right": "$TERMINAL start --class float glances"
|
||||
},
|
||||
"custom/events": {
|
||||
"format": "{}",
|
||||
"interval": 300,
|
||||
"exec": "~/.config/waybar/modules/khal.py 2>/dev/null",
|
||||
"exec-if": "command -v khal >/dev/null 2>&1",
|
||||
"return-type": "json",
|
||||
"on-click": "$TERMINAL start --class float ikhal"
|
||||
},
|
||||
"memory": {
|
||||
"interval": 30,
|
||||
"format": "{avail:0.1f}G ",
|
||||
"format-alt": "{used:0.1f}G/{total:0.1f}G ",
|
||||
"format-alt-click": "click-right",
|
||||
"max-length": 10
|
||||
},
|
||||
"mpd": {
|
||||
"format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}",
|
||||
"format-disconnected": "ﱙ",
|
||||
"format-stopped": "",
|
||||
"interval": 10,
|
||||
"consume-icons": {
|
||||
"on": " " // Icon shows only when "consume" is on
|
||||
},
|
||||
"random-icons": {
|
||||
"on": " "
|
||||
},
|
||||
"repeat-icons": {
|
||||
"on": " "
|
||||
},
|
||||
"single-icons": {
|
||||
"on": "1 "
|
||||
},
|
||||
"state-icons": {
|
||||
"paused": "",
|
||||
"playing": "",
|
||||
},
|
||||
"tooltip-format": "{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ",
|
||||
"tooltip-format-disconnected": "MPD (disconnected)"
|
||||
},
|
||||
"custom/media": {
|
||||
"format": "{icon}{}",
|
||||
"format-alt-click": "right",
|
||||
"return-type": "json",
|
||||
"format-icons": {
|
||||
"Playing": " ",
|
||||
"Paused": " ",
|
||||
},
|
||||
"escape": true,
|
||||
"max-length":70,
|
||||
"exec": "playerctl -a metadata --format '{\"text\": \"\", \"tooltip\": \"{{playerName}} : {{markup_escape(title)}}\", \"alt\": \"{{status}}\", \"class\": \"{{status}}\"}' -F",
|
||||
"exec-if": "command -v playerctl >/dev/null 2>&1",
|
||||
"on-click": "playerctl play-pause",
|
||||
"on-click-right": "playerctl stop",
|
||||
},
|
||||
"network": {
|
||||
"format": "{ifname}",
|
||||
"format-wifi": "{signalStrength}% ",
|
||||
"format-ethernet": "{ipaddr}/{cidr} ",
|
||||
"format-disconnected": "",
|
||||
"tooltip-format": "{ifname} via {gwaddr} ",
|
||||
"tooltip-format-wifi": "{essid}: {bandwidthDownBits}-{bandwidthUpBits} ({signalStrength}%) {ifname}",
|
||||
"tooltip-format-ethernet": "{ifname} ",
|
||||
"tooltip-format-disconnected": "Disconnected",
|
||||
"max-length": 50,
|
||||
"on-click": "$TERMINAL start --class float nmtui",
|
||||
// "on-click-right": "sudo rfkill toggle wlan"
|
||||
},
|
||||
"pulseaudio": {
|
||||
"format": "{volume}% {icon}",
|
||||
"format-bluetooth": "{volume}% {icon} ",
|
||||
"format-muted": "",
|
||||
"format-icons": {
|
||||
"headphone": "",
|
||||
"hands-free": "",
|
||||
"headset": "",
|
||||
"phone": "",
|
||||
"portable": "",
|
||||
"car": "",
|
||||
"default": ["", ""]
|
||||
},
|
||||
"scroll-step": 1,
|
||||
"on-click": "$TERMINAL start --class float pulsemixer",
|
||||
"on-scroll-up": "pactl set-sink-volume @DEFAULT_SINK@ +1%",
|
||||
"on-scroll-down": "pactl set-sink-volume @DEFAULT_SINK@ -1%"
|
||||
},
|
||||
"river/tags": {
|
||||
"num-tags": 10,
|
||||
"tag-labels": [ "", "", "", "", "", "", "", "", "", "" ]
|
||||
},
|
||||
"river/mode": {
|
||||
"format": "{} ",
|
||||
},
|
||||
"river/window": {
|
||||
"format": " {}",
|
||||
"max-length": 70
|
||||
},
|
||||
"temperature": {
|
||||
// "thermal-zone": 2,
|
||||
"hwmon-path": "/sys/class/hwmon/hwmon5/temp1_input",
|
||||
"critical-threshold": 80,
|
||||
// "format-critical": "{temperatureC}° ",
|
||||
"format": "{temperatureC}° ",
|
||||
"on-click": "$TERMINAL start --class float watch sensors"
|
||||
},
|
||||
"tray": {
|
||||
"icon-size": 21,
|
||||
"spacing": 10
|
||||
},
|
||||
"custom/weather": {
|
||||
"exec": "curl 'https://wttr.in/?format=%t'",
|
||||
"exec-if": "command -v curl >/dev/null 2>&1",
|
||||
"interval": 3600
|
||||
},
|
||||
"custom/wireguard": {
|
||||
"format-icons": {
|
||||
"default": ""
|
||||
},
|
||||
"exec": "~/.config/waybar/modules/wireguard json",
|
||||
"exec-if": "command -v nmcli >/dev/null 2>&1",
|
||||
"return-type": "json",
|
||||
"signal": 6,
|
||||
"interval": 60,
|
||||
},
|
||||
"custom/vidl": {
|
||||
"format": "{} {icon}",
|
||||
"format-alt-click": "right",
|
||||
"format-icons": {
|
||||
"default": ""
|
||||
},
|
||||
"exec": "wc -l ~/.local/share/vidl/vidl_queue | cut -d' ' -f1",
|
||||
"exec-if": "[ -f ~/.local/share/vidl/vidl_queue ]",
|
||||
"interval": 5,
|
||||
"on-click": "$TERMINAL start --class float nvim ~/.local/share/vidl/vidl_queue"
|
||||
},
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Check for available archupdates and return their number.
|
||||
# Checks both repositories and aur,
|
||||
# returns empty string when 0 packages are available, so
|
||||
# that polybar simply displays nothing.
|
||||
# Can be used to generate json-like output for waybar.
|
||||
#
|
||||
# dependencies: yay, (pacman-contrib optional)
|
||||
# optional: jq
|
||||
#
|
||||
# Takes 2 arguments:
|
||||
# Takes an optional integer argument, which is the minimum
|
||||
# numer of package updates for an answer to be returned.
|
||||
min_upd=${1:-0}
|
||||
# Takes as second optional argument the output format
|
||||
# Valid value is "json", everything else returns plain-text.
|
||||
# json output requires jq.
|
||||
format=${2:-plain}
|
||||
|
||||
# prefer checkupdates since it allows checking w/o partial upgrade
|
||||
if command -v "checkupdates" >/dev/null; then
|
||||
updates_repo="$(checkupdates 2>&1)"
|
||||
fi
|
||||
|
||||
# fall back to yay, but be aware it will not find everything
|
||||
# if checkupdates finds nothing or returns an error
|
||||
if [ "$updates_repo" = "" ] || [ -z "${updates_repo##*==> ERROR: Cannot fetch updates*}" ]; then
|
||||
updates_repo="$(yay -Qun 2>/dev/null)"
|
||||
fi
|
||||
updates_repo=$(echo "$updates_repo" | wc -l)
|
||||
|
||||
updates_aur="$(yay -Qum 2>/dev/null | wc -l)"
|
||||
# updates_aur=$(cower -u 2> /dev/null | wc -l)
|
||||
# updates_aur=$(trizen -Su --aur --quiet | wc -l)
|
||||
# updates_aur=$(pikaur -Qua 2> /dev/null | wc -l)
|
||||
# updates_aur=$(rua upgrade --printonly 2> /dev/null | wc -l)
|
||||
|
||||
updates=$((updates_repo + updates_aur))
|
||||
|
||||
text="${updates}"
|
||||
alt="${updates_repo}|${updates_aur}"
|
||||
tooltip="Repositories: ${updates_repo} | AUR: ${updates_aur}"
|
||||
[ "$updates" -gt "$min_upd" ] && class="available" || class="empty"
|
||||
if [ "$format" = "json" ]; then
|
||||
printf "{\"text\": \"%s\", \"alt\": \"%s\", \"tooltip\": \"%s\", \"class\": \"%s\"}" \
|
||||
"$text" \
|
||||
"$alt" \
|
||||
"$tooltip" \
|
||||
"$class"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ "$updates" -gt "$min_upd" ]; then
|
||||
echo "$updates"
|
||||
else
|
||||
echo ""
|
||||
fi
|
|
@ -1,35 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# from https://gist.github.com/bjesus/178a9bd3453470d74803945dbbf9ed40
|
||||
# List upcoming khal events in simple json container fit for waybar
|
||||
|
||||
import subprocess
|
||||
import datetime
|
||||
import json
|
||||
from html import escape
|
||||
|
||||
data = {}
|
||||
|
||||
today = datetime.date.today().strftime("%Y-%m-%d")
|
||||
|
||||
next_week = (datetime.date.today() + datetime.timedelta(days=10)).strftime("%Y-%m-%d")
|
||||
|
||||
output = subprocess.check_output("khal list now " + next_week, shell=True)
|
||||
output = output.decode("utf-8")
|
||||
|
||||
lines = output.split("\n")
|
||||
new_lines = []
|
||||
for line in lines:
|
||||
clean_line = escape(line).split(" ::")[0]
|
||||
if len(clean_line) and not clean_line[0] in ["0", "1", "2"]:
|
||||
clean_line = "\n<b>" + clean_line + "</b>"
|
||||
new_lines.append(clean_line)
|
||||
output = "\n".join(new_lines).strip()
|
||||
|
||||
if today in output:
|
||||
data["text"] = " " + output.split("\n")[1]
|
||||
else:
|
||||
data["text"] = " "
|
||||
|
||||
data["tooltip"] = output
|
||||
|
||||
print(json.dumps(data))
|
|
@ -1,151 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# nmcli WireGuard abstraction layer for use with my waybar module and rofi custom menu script
|
||||
#
|
||||
# requires nmcli on your path
|
||||
# install to the same directory as wireguard-rofi.sh
|
||||
#
|
||||
# usage: ./wireguard.sh [menu|toggle NAME]
|
||||
# no argument: print current connections
|
||||
# json: print waybar-ready json output
|
||||
# menu: print all connections
|
||||
# toggle NAME: toggle connection NAME
|
||||
|
||||
if ! command -v nmcli >/dev/null 2>&1; then
|
||||
echo "err: nmcli not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
nargs=$#
|
||||
showmenu="no"
|
||||
dotoggle="no"
|
||||
printjson="no"
|
||||
if [[ $nargs == 1 ]]; then
|
||||
if [[ $1 == "menu" ]]; then
|
||||
showmenu="yes"
|
||||
elif [[ $1 == "json" ]]; then
|
||||
printjson="yes"
|
||||
fi
|
||||
elif [[ $nargs == 2 ]]; then
|
||||
if [[ $1 == "toggle" ]]; then
|
||||
dotoggle="yes"
|
||||
conn="$2"
|
||||
fi
|
||||
fi
|
||||
|
||||
nmclicmd="nmcli connection"
|
||||
wgconns="$nmclicmd show"
|
||||
wgactive="$wgconns --active"
|
||||
|
||||
connected=()
|
||||
available=()
|
||||
|
||||
function print_as_json() {
|
||||
text="" # only prints a single icon when connected
|
||||
# text="${1}" # use this line to show all output in text
|
||||
alt="${1}"
|
||||
tooltip="${1}"
|
||||
[ -n "$1" ] && class="connected" || class="disconnected"
|
||||
printf "{\"text\": \"%s\", \"alt\": \"%s\", \"tooltip\": \"%s\", \"class\": \"%s\"}" \
|
||||
"$text" \
|
||||
"$alt" \
|
||||
"$tooltip" \
|
||||
"$class"
|
||||
}
|
||||
|
||||
function get_conns {
|
||||
while read -r name _ type device; do
|
||||
if [[ $type != "wireguard" && ($type != "tun" || $device != "proton0") ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ $device != "--" ]]; then
|
||||
while read -r key value; do
|
||||
if [[ $key != "ipv4.addresses:" ]]; then
|
||||
continue
|
||||
fi
|
||||
connected+=("$name: $value")
|
||||
done < <($wgconns "$name")
|
||||
else
|
||||
available+=("$name")
|
||||
fi
|
||||
done < <($1)
|
||||
}
|
||||
|
||||
function get_pia {
|
||||
if ! command -v piactl >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
status=$(piactl get connectionstate)
|
||||
if [[ $status = "Connected" ]]; then
|
||||
connected+=("$(piactl get region): $(piactl get vpnip)")
|
||||
fi
|
||||
}
|
||||
|
||||
function print_conns {
|
||||
local first="yes"
|
||||
local array_print="$1[@]"
|
||||
local array_print=("${!array_print}")
|
||||
if [[ $2 == "list" ]]; then
|
||||
for c in "${array_print[@]}"; do
|
||||
output="$1: $c"
|
||||
done
|
||||
else
|
||||
output=""
|
||||
for c in "${array_print[@]}"; do
|
||||
if [[ "$first" != "yes" ]]; then
|
||||
output+=" | "
|
||||
fi
|
||||
output+="$c"
|
||||
first="no"
|
||||
done
|
||||
fi
|
||||
if [[ "$printjson" == "yes" ]]; then
|
||||
print_as_json "$output"
|
||||
else
|
||||
echo "$output"
|
||||
fi
|
||||
}
|
||||
|
||||
function array_contains {
|
||||
local array_has="$1[@]"
|
||||
local array_has=("${!array_has}")
|
||||
local element="$2"
|
||||
for e in "${array_has[@]}"; do
|
||||
if [[ "$e" == *"$element"* ]]; then
|
||||
echo "yes"
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo "no"
|
||||
}
|
||||
|
||||
if [[ $nargs == 0 ]] || [[ $printjson = "yes" ]]; then
|
||||
get_conns "$wgactive"
|
||||
get_pia
|
||||
print_conns connected
|
||||
|
||||
elif [[ $showmenu == "yes" ]]; then
|
||||
get_conns "$wgconns"
|
||||
get_pia
|
||||
print_conns connected "list"
|
||||
print_conns available "list"
|
||||
|
||||
elif [[ $dotoggle == "yes" ]]; then
|
||||
get_conns "$wgconns"
|
||||
get_pia
|
||||
|
||||
if [[ "$(array_contains connected "$conn")" == "yes" ]]; then
|
||||
$nmclicmd down "$conn"
|
||||
elif [[ "$(array_contains available "$conn")" == "yes" ]]; then
|
||||
$nmclicmd up "$conn"
|
||||
else
|
||||
echo "err: connection not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
else
|
||||
echo "err: wrong args"
|
||||
exit 1
|
||||
fi
|
|
@ -1,162 +0,0 @@
|
|||
* {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
min-height: 0;
|
||||
font-family: Cantarell, Signika, Iosevka Nerd Font, Iosevka, monospace;
|
||||
}
|
||||
|
||||
window#waybar {
|
||||
font-size: 15px;
|
||||
color: @base0F;
|
||||
background-color: @base00;
|
||||
border-bottom: 1px solid @base01;
|
||||
transition-property: background-color;
|
||||
transition-duration: .5s;
|
||||
}
|
||||
|
||||
window#waybar.hidden {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
#tags button {
|
||||
font-size:17px;
|
||||
font-weight:900;
|
||||
background-color: transparent;
|
||||
color: @base03;
|
||||
margin: 0px;
|
||||
padding: 0px 5px;
|
||||
/* Use box-shadow instead of border so the text isn't offset */
|
||||
}
|
||||
/* /1* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect *1/ */
|
||||
#tags button:hover {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
/* box-shadow: inset 0 -1px #d5c4a1; */
|
||||
box-shadow: inset 0 0;
|
||||
}
|
||||
#tags button.occupied {
|
||||
font-weight: 400;
|
||||
color: @base05;
|
||||
}
|
||||
#tags button.focused {
|
||||
font-weight: 400;
|
||||
color: @base0D;
|
||||
}
|
||||
#tags button.urgent {
|
||||
color: @base0C;
|
||||
}
|
||||
|
||||
#clock,
|
||||
#battery,
|
||||
#cpu,
|
||||
#memory,
|
||||
#disk,
|
||||
#temperature,
|
||||
#backlight,
|
||||
#network,
|
||||
#pulseaudio,
|
||||
#tray,
|
||||
#mode,
|
||||
#idle_inhibitor,
|
||||
#mpd,
|
||||
#window,
|
||||
#custom-archupdates,
|
||||
#custom-wireguard,
|
||||
#custom-events,
|
||||
#custom-vidl,
|
||||
#custom-media {
|
||||
padding: 0 10px;
|
||||
margin: 0 5px;
|
||||
color: @base05;
|
||||
background-color: @base01;
|
||||
}
|
||||
|
||||
/* If workspaces is the leftmost module, omit left margin */
|
||||
.modules-left > widget:first-child > #tags {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
/* If workspaces is the rightmost module, omit right margin */
|
||||
.modules-right > widget:last-child > #tags {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#battery.charging, #battery.plugged {
|
||||
background-color: @base02;
|
||||
}
|
||||
|
||||
/* Mark active output through highlighted window background */
|
||||
#window {
|
||||
background-color: transparent;
|
||||
}
|
||||
#window.focused {
|
||||
background-color: @base01;
|
||||
}
|
||||
|
||||
#battery.warning {
|
||||
background-color: @base09;
|
||||
color: @base00;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
to {
|
||||
background-color: @base08;
|
||||
color: @base00;
|
||||
}
|
||||
}
|
||||
|
||||
#battery.critical:not(.charging) {
|
||||
color: @base05;
|
||||
background-color: @base00;
|
||||
animation-name: blink;
|
||||
animation-duration: 0.5s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
|
||||
#cpu.warning {
|
||||
color: @base00;
|
||||
background-color: @base0A;
|
||||
}
|
||||
#cpu.critical {
|
||||
color: @base05;
|
||||
background-color: @base08;
|
||||
}
|
||||
|
||||
#custom-archupdates.empty {
|
||||
background-color: transparent;
|
||||
color: transparent;
|
||||
font-size: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#custom-wireguard.disconnected {
|
||||
background-color: transparent;
|
||||
color: transparent;
|
||||
font-size: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#temperature.critical {
|
||||
background-color: @base08;
|
||||
}
|
||||
|
||||
#tray > .passive {
|
||||
-gtk-icon-effect: dim;
|
||||
}
|
||||
|
||||
|
||||
#tray > .needs-attention {
|
||||
-gtk-icon-effect: highlight;
|
||||
}
|
||||
|
||||
#mpd.disconnected {
|
||||
background-color: transparent;
|
||||
color: transparent;
|
||||
font-size: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
@import "/home/marty/.local/state/waybar/colorscheme.css";
|
|
@ -1,26 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
picker=dmenu
|
||||
if exist bemenu; then
|
||||
picker=bemenu
|
||||
elif exist wofi; then
|
||||
picker=wofi
|
||||
elif exist rofi; then
|
||||
picker=rofi
|
||||
fi
|
||||
|
||||
list=$(flavours list -l)
|
||||
flavour=$(printf "%s\nrandom\nlight" "$list" | "$picker")
|
||||
if [ -z "$flavour" ]; then
|
||||
return
|
||||
elif [ "$flavour" = "random" ]; then
|
||||
flavours apply '*'
|
||||
elif [ "$flavour" = "light" ]; then
|
||||
flavours apply '*light'
|
||||
else
|
||||
flavours apply "$flavour"
|
||||
fi
|
||||
|
||||
if [ "$1" = '-v' ]; then
|
||||
notify-send "Theme set" "set to: $flavour"
|
||||
fi
|
|
@ -1,48 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
# Take a screenshot on wayland
|
||||
# By default takes a screenshot of the whole desktop
|
||||
# If 'region' or 'area' command is passed in will allow selecting a region to screenshot.
|
||||
# Example: `screenshot region`
|
||||
|
||||
TIME="$(date +%Y-%m-%d-%H-%M-%S)"
|
||||
readonly TIME
|
||||
readonly TMPSCREENSHOTDIR="$HOME/.cache/screenshot"
|
||||
readonly TMPIMGPATH="$TMPSCREENSHOTDIR/img-$TIME.png"
|
||||
|
||||
FULLSCREEN=true
|
||||
|
||||
if [ "$1" = "area" ] || [ "$1" = "region" ]; then
|
||||
FULLSCREEN=false
|
||||
fi
|
||||
|
||||
main() {
|
||||
prepare_cache
|
||||
take_screenshot "$FULLSCREEN"
|
||||
if [ -n "$SCREENSHOT_POSTPROCESS" ]; then
|
||||
eval "$SCREENSHOT_POSTPROCESS"
|
||||
else
|
||||
postprocess
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_cache() {
|
||||
if [ ! -d "$TMPSCREENSHOTDIR" ]; then
|
||||
mkdir -p "$TMPSCREENSHOTDIR"
|
||||
fi
|
||||
}
|
||||
|
||||
take_screenshot() {
|
||||
if $1; then
|
||||
grim "$TMPIMGPATH"
|
||||
else
|
||||
grim -g "$(slurp)" "$TMPIMGPATH"
|
||||
fi
|
||||
}
|
||||
|
||||
postprocess() {
|
||||
notify-send -i "$TMPIMGPATH" "Screenshot taken" "$TMPIMGPATH"
|
||||
echo "$TMPIMGPATH" | wl-copy
|
||||
echo "$TMPIMGPATH"
|
||||
}
|
||||
|
||||
main "$@"
|
|
@ -1,94 +0,0 @@
|
|||
# Wayland desktop environment
|
||||
|
||||
Contains:
|
||||
[riverwm](https://github.com/riverwm/river)
|
||||
[waybar](https://github.com/Alexays/Waybar)
|
||||
mako -- notification daemon
|
||||
font settings
|
||||
kanshi -- display output setup
|
||||
|
||||
The thoughts below are overhauled and fairly old - they are still based on my first days and weeks on wayland.
|
||||
Takeaway is this: I love the river wm, waybar is unexciting but does what it's supposed to and the wayland environment is totally worth it.
|
||||
|
||||
---
|
||||
|
||||
My first foray into wayland is based on river,
|
||||
a tiling window manager somewhat based on bspwm.
|
||||
|
||||
This is only a very work-in-progress README file.
|
||||
|
||||
Since wayland handles key presses and so on completely differently
|
||||
from X,
|
||||
I can't for example use sxhkd anymore which is a shame.
|
||||
|
||||
On the other hand, there is an amazing key *re* binding tool
|
||||
(which also works under X I've now found out)
|
||||
`keyd` which takes care of some X functionality (xcape) at a lower level.
|
||||
|
||||
I have not found a good replacement for `clutter`
|
||||
(which automatically hides your mouse cursor after inactivity)
|
||||
which is independent from the window manager.
|
||||
I believe `swaywm` would include similar functionality, but `river` does not.
|
||||
|
||||
|
||||
## River
|
||||
|
||||
River is set up to come close to my old i3 setup.
|
||||
Of course, some mappings are different
|
||||
(especially those for movement between windows),
|
||||
but overall the keys map to the old ones.
|
||||
|
||||
Since the window manager now also takes over the task of compositor
|
||||
and does not pass through all keys to all programs,
|
||||
it takes over the role of `sxhkd` as well and summons other programs.
|
||||
|
||||
I am not entirely sure how I feel about this bundling of tasks into one application,
|
||||
but so far it works.
|
||||
Since river is also, mimicking bspwm, using an executable file
|
||||
(any executable file)
|
||||
as its configuration file it is also reasonable that the setup can be tamed and refactored better than a single i3 configuration file.
|
||||
|
||||
## Waybar
|
||||
|
||||
Waybar replaces the old [polybar](https://gitlab.com/marty-oehme/dotfiles/-/tree/89d1402b3e711c4aa473386e47e84f3593e5ae56/polybar) setup.
|
||||
|
||||
It displays the first 10 tags on its left,
|
||||
with tags highlighted that are either occupied by windows or currently in focus.
|
||||
In the center it displays a clock which can be clicked to open a simple calendar.
|
||||
|
||||
![Simple waybar configuration](.assets/waybar/simple.png)
|
||||
|
||||
To the right is where most of the info modules are:
|
||||
If there are upcoming events listed for the khal application, it will display a calendar module here.
|
||||
If music is playing through an mpris-compatible player, its status is shown here.
|
||||
If a connection through wireguard or over a vpn tunnel is established, it is shown here.
|
||||
Then, from left to right, audio, brightness, wifi, cpu, ram, temperature, and battery information are displayed.
|
||||
|
||||
Some displays have alternative display states, with for example the battery showing remaining time and ram information showing used and total available.
|
||||
|
||||
Clicking on:
|
||||
|
||||
* audio opens pulsemixer
|
||||
* network opens nmtui
|
||||
* cpu opens top (or glances on right-click)
|
||||
* temperature shows sensors
|
||||
|
||||
## keyd
|
||||
|
||||
keyd is set up within `/etc/` and not in the dotfiles themselves.
|
||||
If using the included `./install.sh` file, there is an option to also set up files outside the home directory, including keyd options.
|
||||
It is configured through `/etc/keyd/default.cfg`.
|
||||
Currently, it takes care of mapping `capslock` to both control and escape (depending on if its used alone or with other keys),
|
||||
as well as adding some German characters that I am otherwise missing on my en_US keyboard.
|
||||
Lastly, it allows easy clipboard pasting with the `insert` key.
|
||||
|
||||
## Swaybg
|
||||
|
||||
`swaybg` is used to set the wallpaper from the river configuration file.
|
||||
|
||||
## Missing
|
||||
|
||||
things not yet set up:
|
||||
|
||||
* [-] modes: media, academia (worth?)
|
||||
* [ ] display current desktop mode in status bar
|
|
@ -1 +0,0 @@
|
|||
../udiskie.service
|
|
@ -1,9 +0,0 @@
|
|||
[Unit]
|
||||
Description=Automated mounting of removable devices (udisks)
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/udiskie --config %h/.config/udiskie/config.yml
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
|
@ -1,9 +0,0 @@
|
|||
program_options:
|
||||
tray: auto
|
||||
automount: true
|
||||
notify: true
|
||||
device_config:
|
||||
# ignore any snap loopback devices
|
||||
# see https://github.com/coldfix/udiskie/issues/180
|
||||
- id_type: squashfs
|
||||
ignore: true
|
|
@ -1,9 +0,0 @@
|
|||
# Removable disk configuration
|
||||
|
||||
This simple unit depends on `udiskie2` being installed and enables auto-mounting of any inserted removable media.
|
||||
It will additionally *ignore* any snaps managed by `snapd`, since they would otherwise be detected as loopback devices.
|
||||
|
||||
Automatically installs a `systemd` service which is also enabled to start up on system start (as a user service).
|
||||
|
||||
The end result is `udiskie` running in the background and automatically mounting any inserted media,
|
||||
which will then also show its tray icon so that it can be quickly unmounted as well.
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version='1.0'?>
|
||||
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
|
||||
<fontconfig>
|
||||
<!-- Replacements from http://bohoomil.com/doc/05-fonts/ (until ibfonts-meta-extended) -->
|
||||
<alias>
|
||||
<family>serif</family>
|
||||
<prefer><family>Heuristica</family></prefer>
|
||||
|
@ -11,7 +12,7 @@
|
|||
</alias>
|
||||
<alias>
|
||||
<family>monospace</family>
|
||||
<prefer><family>Iosevka Nerd Font</family></prefer>
|
||||
<prefer><family>Iosevka</family></prefer>
|
||||
</alias>
|
||||
<alias>
|
||||
<family>fantasy</family>
|
||||
|
@ -21,27 +22,40 @@
|
|||
<family>cursive</family>
|
||||
<prefer><family>Comic Neue</family></prefer>
|
||||
</alias>
|
||||
<!-- more advanced replacements (replacing many fonts with open alternatives) see spark/fonts/local.conf -->
|
||||
<match target="font">
|
||||
<edit name="antialias" mode="assign">
|
||||
<bool>true</bool>
|
||||
</edit>
|
||||
</match>
|
||||
<match target="font">
|
||||
<edit name="rgba" mode="assign">
|
||||
<const>rgb</const>
|
||||
</edit>
|
||||
</match>
|
||||
<match target="font">
|
||||
<edit name="lcdfilter" mode="assign">
|
||||
<const>lcddefault</const>
|
||||
</edit>
|
||||
</match>
|
||||
<match target="font">
|
||||
<edit name="hinting" mode="assign">
|
||||
<bool>true</bool>
|
||||
</edit>
|
||||
</match>
|
||||
<match target="font">
|
||||
<edit name="hintstyle" mode="assign">
|
||||
<const>hintfull</const>
|
||||
</edit>
|
||||
</match>
|
||||
<match target="font">
|
||||
<edit name="autohint" mode="assign">
|
||||
<bool>false</bool>
|
||||
</edit>
|
||||
</match>
|
||||
<match target="pattern">
|
||||
<edit name="dpi" mode="assign">
|
||||
<double>93</double>
|
||||
<double>92</double>
|
||||
</edit>
|
||||
</match>
|
||||
</fontconfig>
|
|
@ -1,77 +1,11 @@
|
|||
[user]
|
||||
email = marty.oehme@gmail.com
|
||||
name = Marty Oehme
|
||||
signingkey = 73BA40D5AFAF49C9
|
||||
[init]
|
||||
defaultBranch = main
|
||||
[core]
|
||||
pager = git delta
|
||||
[pager]
|
||||
difftool = true
|
||||
[interactive]
|
||||
diffFilter = git delta --color-only
|
||||
[merge]
|
||||
conflictstyle = diff3
|
||||
[delta]
|
||||
navigate = true
|
||||
line-numbers = true
|
||||
syntax-theme = base16
|
||||
[sendemail]
|
||||
smtpserver = "/usr/bin/msmtp"
|
||||
annotate = yes
|
||||
[alias]
|
||||
ignore = "!gitignore -f"
|
||||
last = "diff HEAD~ HEAD"
|
||||
pushall = "!git remote | xargs -I R git push R" # push to all connected remotes
|
||||
fetchall = "!git remote | xargs -I R git fetch R" # fetch from all connected remotes
|
||||
diffword = "!git diff --word-diff=color --word-diff-regex='[0-9A-Za-z_]+'" # word-wise diff, good for prose
|
||||
diffsyn = "!git difftool --tool difftastic" # add syntax-driven diff using treesitter
|
||||
diffside = "!DELTA_FEATURES='+side-by-side' git diff" # add side-by-side diffing
|
||||
delta = "![ $TERM_DARK = false ] && delta --light || delta" # Take care that we always display right color scheme
|
||||
[commit]
|
||||
gpgsign = true # sign commits as me
|
||||
verbose = true # Always show diff when preparing commit message
|
||||
[fetch]
|
||||
prune = true # remove references to non-existent remote branches
|
||||
[pull]
|
||||
rebase = true # always rebase on pulling, obviates merge commits
|
||||
[diff]
|
||||
colorMoved = zebra # also color stuff that has simply been moved, in a classy zebra-color
|
||||
[difftool]
|
||||
prompt = false
|
||||
[difftool "difftastic"]
|
||||
cmd = difft "$LOCAL" "$REMOTE"
|
||||
[color "diff"]
|
||||
meta = "9"
|
||||
frag = "magenta bold"
|
||||
commit = "yellow bold"
|
||||
old = "red bold"
|
||||
new = "green bold"
|
||||
whitespace = "red reverse"
|
||||
[color "diff-highlight"]
|
||||
oldNormal = "red bold"
|
||||
oldHighlight = "red bold 52"
|
||||
newNormal = "green bold"
|
||||
newHighlight = "green bold 22"
|
||||
[rebase]
|
||||
autostash = true
|
||||
autoSquash = true
|
||||
# Make use of git urls for git{lab,hub}, but only do so for pushing
|
||||
# since pulling will create troubles with some applications
|
||||
[url "git@github.com:"]
|
||||
pushInsteadOf = "https://github.com/"
|
||||
pushInsteadOf = "http://github.com/"
|
||||
pushInsteadOf = "gh:"
|
||||
[url "git@gitlab.com:"]
|
||||
pushInsteadOf = "https://gitlab.com"
|
||||
pushInsteadOf = "http://gitlab.com"
|
||||
[url "git@git.martyoeh.me:"]
|
||||
pushInsteadOf = "https://git.martyoeh.me"
|
||||
pushInsteadOf = "http://git.martyoeh.me"
|
||||
pushInsteadOf = "git@martyoeh.me"
|
||||
pullInsteadOf = "git@martyoeh.me"
|
||||
email = marty.oehme@gmail.com
|
||||
name = Marty Oehme
|
||||
[filter "lfs"]
|
||||
clean = git-lfs clean -- %f
|
||||
smudge = git-lfs smudge -- %f
|
||||
process = git-lfs filter-process
|
||||
required = true
|
||||
clean = git-lfs clean -- %f
|
||||
smudge = git-lfs smudge -- %f
|
||||
process = git-lfs filter-process
|
||||
required = true
|
||||
[alias]
|
||||
ignore = "!gitignore -f"
|
||||
ign = "ignore"
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
|
||||
# Created by https://www.gitignore.io/api/vim,linux,zsh
|
||||
# Edit at https://www.gitignore.io/?templates=vim,linux,zsh
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### Vim ###
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
Sessionx.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
|
||||
# Coc configuration directory
|
||||
.vim
|
||||
|
||||
### Zsh ###
|
||||
# Zsh compiled script + zrecompile backup
|
||||
*.zwc
|
||||
*.zwc.old
|
||||
|
||||
# Zsh completion-optimization dumpfile
|
||||
*zcompdump*
|
||||
|
||||
# Zsh zcalc history
|
||||
.zcalc_history
|
||||
|
||||
# A popular plugin manager's files
|
||||
._zplugin
|
||||
.zplugin_lstupd
|
||||
|
||||
# zdharma/zshelldoc tool's files
|
||||
zsdoc/data
|
||||
|
||||
# robbyrussell/oh-my-zsh/plugins/per-directory-history plugin's files
|
||||
# (when set-up to store the history in the local directory)
|
||||
.directory_history
|
||||
|
||||
# MichaelAquilina/zsh-autoswitch-virtualenv plugin's files
|
||||
# (for Zsh plugins using Python)
|
||||
.venv
|
||||
|
||||
# Zunit tests' output
|
||||
/tests/_output/*
|
||||
!/tests/_output/.gitkeep
|
||||
|
||||
# End of https://www.gitignore.io/api/vim,linux,zsh
|
|
@ -1,115 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
if ! exist git; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# print git version output and get raw version number by stripping prefix
|
||||
git_version=$(git --version 2>/dev/null)
|
||||
git_version="${git_version##git version }"
|
||||
|
||||
alias g='git'
|
||||
|
||||
alias ga='git add'
|
||||
alias gaa='git add --all'
|
||||
alias gai='git add -i'
|
||||
|
||||
alias gc='git commit -v'
|
||||
alias gc!='git commit -v --amend'
|
||||
alias gcn!='git commit -v --no-edit --amend'
|
||||
|
||||
if version_at_least 2.23 "$git_version"; then
|
||||
alias gcm='git switch master 2>/dev/null || git switch main'
|
||||
alias gcd='git switch develop 2>/dev/null || git switch staging'
|
||||
gcb() {
|
||||
git switch "$@" 2>/dev/null || git switch -c "$@"
|
||||
}
|
||||
else
|
||||
alias gcm='git checkout master 2>/dev/null || git checkout main'
|
||||
alias gcd='git checkout develop'
|
||||
alias gcb='git checkout -b'
|
||||
fi
|
||||
alias gco='git checkout'
|
||||
|
||||
alias gi='git ignore'
|
||||
|
||||
# normal diff
|
||||
alias gd='git diff'
|
||||
alias gds='git diff --staged'
|
||||
# word-based diff (with custom word regex)
|
||||
alias gdw='git diffword'
|
||||
alias gdws='git diffword --staged'
|
||||
# side-by-side diff
|
||||
alias gdd='git diffside'
|
||||
alias gdds='git diffside --staged'
|
||||
# syntax-based diff
|
||||
if exist difft; then
|
||||
alias gdy='git diffsyn'
|
||||
alias gdys='git diffsyn --staged'
|
||||
fi
|
||||
alias gdd='git diffside'
|
||||
alias gdds='git diffside --staged'
|
||||
# show last committed content
|
||||
alias gdl='git last'
|
||||
|
||||
# show quick log overview
|
||||
alias glg='git log --oneline --decorate --graph'
|
||||
alias glga='git log --oneline --decorate --graph --remotes --all'
|
||||
# show quick log overview - with dates
|
||||
alias glgd="git log --graph --pretty=format:'%C(auto)%h%Creset %C(cyan)%ar%Creset%C(auto)%d%Creset %s'"
|
||||
alias glgad="git log --graph --remotes --all --pretty=format:'%C(auto)%h%Creset %C(cyan)%ar%Creset%C(auto)%d%Creset %s'"
|
||||
# show detailed log overview
|
||||
alias glog='git log --stat'
|
||||
# show detailed log overview with contents
|
||||
alias gloog='git log --stat -p'
|
||||
|
||||
alias gf='git fetch'
|
||||
alias gfa='git fetchall'
|
||||
alias gl='git pull'
|
||||
|
||||
alias gpn='git push --dry-run'
|
||||
alias gp='git push'
|
||||
alias gpf!='git push --force'
|
||||
alias gpm='git pushmerge'
|
||||
alias gpa='git pushall'
|
||||
|
||||
alias grv='git remote -v'
|
||||
|
||||
alias grs='git restore --staged'
|
||||
alias grs!='git restore'
|
||||
|
||||
alias grb='git rebase'
|
||||
alias grbi='git rebase -i'
|
||||
alias grbc='git rebase --continue'
|
||||
alias grbm='git rebase master || git rebase main'
|
||||
|
||||
alias gst='git status'
|
||||
|
||||
alias gstp='git stash pop'
|
||||
alias gstl='git stash list'
|
||||
alias gstL='git stash list --stat'
|
||||
if version_at_least 2.13 "$git_version"; then
|
||||
alias gsta='git stash push'
|
||||
else
|
||||
alias gsta='git stash save'
|
||||
fi
|
||||
|
||||
if exist git-bug; then
|
||||
gb() {
|
||||
if [ "$#" -eq 1 ]; then
|
||||
git bug show "$1"
|
||||
else
|
||||
git bug ls "$@"
|
||||
fi
|
||||
}
|
||||
alias gbt='git bug termui'
|
||||
|
||||
alias gba='git bug add'
|
||||
alias gbm='git bug comment add'
|
||||
alias gbc='git bug status close'
|
||||
|
||||
alias gbp='git bug push'
|
||||
alias gbl='git bug pull'
|
||||
fi
|
||||
|
||||
unset -v git_version
|
|
@ -1,15 +0,0 @@
|
|||
#compdef _gitignore gitignore
|
||||
#
|
||||
# Requires gitignore script in path
|
||||
#
|
||||
# Enables completion for zsh of gitignore function.
|
||||
|
||||
_gitignore_get_command_list() {
|
||||
curl -sL https://www.gitignore.io/api/list | tr ',' '\n'
|
||||
}
|
||||
|
||||
_gitignore() {
|
||||
_arguments \
|
||||
'1::flags:((-f\:"Save output to .gitignore file in current directory"))' \
|
||||
":listopts: _values -s, 'modules' $(_gitignore_get_command_list)"
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
#
|
||||
# Adds call to gitignore.sh api to automatically
|
||||
# generate a .gitignore file with the individual arguments
|
||||
# passed in included as ignored packages.
|
||||
#
|
||||
# Pass in -f as first argument to save the file as .gitignore
|
||||
# in the current directory in addition to printing to stdout.
|
||||
# Will *overwrite* any previous .gitignore file that exists in
|
||||
# current directory.
|
||||
#
|
||||
# When called without arguments will load all possible arguments
|
||||
# as fzf searchable list if fzf is in path.
|
||||
#
|
||||
# Enables completion for zsh in git/.config/shell/zshrc.d/_gitignore_completions.zsh
|
||||
|
||||
__get_items() {
|
||||
if [ "$1" = "" ]; then
|
||||
echo "gitignore definition generation needs at least one argument."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$savetofile" = "true" ]; then
|
||||
__call_url "$@" >>.gitignore
|
||||
else
|
||||
__call_url "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
__call_url() {
|
||||
IFS=","
|
||||
curl -L -s https://www.gitignore.io/api/"$*"
|
||||
}
|
||||
|
||||
gitignore() {
|
||||
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||
usage
|
||||
exit 0
|
||||
# just print to stdout or save locally?
|
||||
elif [ "$1" = "-f" ] || [ "$1" = "--file" ]; then
|
||||
savetofile=true
|
||||
shift
|
||||
fi
|
||||
|
||||
IFS=","
|
||||
if [ "$#" -eq 0 ]; then
|
||||
if type fzf >/dev/null 2>&1; then
|
||||
for item in $(__get_items list); do
|
||||
echo "$item"
|
||||
done | fzf --multi --ansi | paste -s -d "," - |
|
||||
{ read -r result && __get_items "$result"; }
|
||||
else
|
||||
usage
|
||||
fi
|
||||
else
|
||||
__get_items "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
usage() {
|
||||
printf "%s\n" \
|
||||
"" \
|
||||
" gitignore Quickly generate a gitignore definition." \
|
||||
"" \
|
||||
" Usage: gitignore [-h] [vim] [linux] [javascript] [...]" \
|
||||
"" \
|
||||
" Arguments:" \
|
||||
"" \
|
||||
" -h | --help Print out this help." \
|
||||
"" \
|
||||
" -f | --file Append gitignore definition to .gitignore file" \
|
||||
" instead of stdout." \
|
||||
"" \
|
||||
" Arguments will be passed along to gitignore.io for parsing and " \
|
||||
" gitignore definition generation. By default only prints to stdout." \
|
||||
"" \
|
||||
""
|
||||
}
|
||||
|
||||
gitignore "$@"
|
|
@ -1,44 +0,0 @@
|
|||
# Git module
|
||||
|
||||
[git](https://git-scm.com/) - a distributed version control system
|
||||
|
||||
## What's in this module
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
## Global git settings
|
||||
|
||||
This is probably the first thing that needs to be customized, since it points to a different identity for each git user.
|
||||
I sign all my commits by default, so take out the corresponding lines if you don't, or exchange it with your gpg key.
|
||||
|
||||
Git will rewrite any remotes using http(s) to use the ssh notation for pushes to github and gitlab so that, even if you set up the repository using an https url you can utilize your usual ssh key for pushing.
|
||||
|
||||
Finally, the configuration makes use of a custom pager called `dsf` which is also contained in this module.
|
||||
It tries to use `diff-so-fancy` if that is installed on the path, otherwise uses git's internal diff prettifier.
|
||||
If nothing exists it falls back to the standard output.
|
||||
You can move between changed files in diffs with n/N.
|
||||
|
||||
Otherwise, the git config is prepared to handle lfs repositories, and it has an assortment of smaller quality of life changes, though nothing major.
|
||||
|
||||
## Basic git command aliases
|
||||
|
||||
This module contains a heap of aliases to every-day git commands.
|
||||
Most of them follow a two-to-three letter combination of things to do which corresponds to the mnemonic of the longer git command.
|
||||
As such, they are mostly similar to those found in the Oh My Zsh [git plugin](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/git).
|
||||
Examples of some common aliases are `alias ga=git add`, `alias gst=git status`, `alias gp=git push`, `alias gl=git pull`.
|
||||
|
||||
Two aliases might be of note:
|
||||
`gi` allows quickly generating a gitignore file for the current directory, using the included `gitignore` script.
|
||||
And `gll` quickly pulls up the contents of the last commit. (`ll` since `gl` already pulls from the remote)
|
||||
|
||||
## Gitignore generation script
|
||||
|
||||
Adds a `gitignore` script which pulls relevant .gitignore lines from [gitignore.io](https://www.gitignore.io) and sends them to standard out, or creates a .gitignore file in the current directory.
|
||||
|
||||
[![asciicast](https://asciinema.org/a/298616.svg)](https://asciinema.org/a/298616)
|
||||
|
||||
To show usage of the script run `gitignore -h`. It is fully equipped with zsh auto completions, which will pull a list of all available ignore modules from the website to complete with.
|
||||
|
||||
It can alternatively be run through git itself by invoking `git ignore`, which will always generate a .gitignore file in the current directory.
|
||||
|
||||
When fzf is installed invoke the script without any arguments to generate a fzf-searchable list of git definitions to create. Select multiple definitions with <tab>.
|
|
@ -0,0 +1,20 @@
|
|||
root:
|
||||
askformore: false
|
||||
autoclip: true
|
||||
autoprint: false
|
||||
autoimport: true
|
||||
autosync: false
|
||||
check_recipient_hash: false
|
||||
cliptimeout: 45
|
||||
concurrency: 1
|
||||
editrecipients: false
|
||||
nocolor: false
|
||||
noconfirm: true
|
||||
nopager: false
|
||||
notifications: true
|
||||
path: gpgcli-gitcli-fs+file:///home/marty/.local/share/gopass/passwords
|
||||
recipient_hash:
|
||||
.gpg-id: 353145383639394133414439344537334641363830434541313341433036443341444345413339410a423038433639313143453343313539463643373831324546334641423133324534463946343433390aa69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26
|
||||
safecontent: false
|
||||
usesymbols: false
|
||||
mounts: {}
|
|
@ -0,0 +1,13 @@
|
|||
#
|
||||
# ~/.bash_profile
|
||||
#
|
||||
|
||||
if [ -d $XDG_CONFIG_HOME/shell/login.d ]; then
|
||||
for file in $XDG_CONFIG_HOME/shell/login.d/*.sh; do
|
||||
source $file
|
||||
done
|
||||
fi
|
||||
|
||||
[[ -f ~/.bashrc ]] && . ~/.bashrc
|
||||
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
|
@ -0,0 +1,23 @@
|
|||
#
|
||||
# ~/.bashrc
|
||||
#
|
||||
|
||||
# If not running interactively, don't do anything
|
||||
[[ $- != *i* ]] && return
|
||||
|
||||
# Load files from rc.d
|
||||
if [ -d $XDG_CONFIG_HOME/shell/rc.d ]; then
|
||||
for file in $XDG_CONFIG_HOME/shell/rc.d/*.sh; do
|
||||
source $file
|
||||
done
|
||||
fi
|
||||
|
||||
# Load files from bashrc.d
|
||||
if [ -d $XDG_CONFIG_HOME/shell/bashrc.d ]; then
|
||||
for file in $XDG_CONFIG_HOME/shell/bashrc.d/*.bash; do
|
||||
source $file
|
||||
done
|
||||
fi
|
||||
|
||||
alias ls='ls --color=auto'
|
||||
PS1='[\u@\h \W]\$ '
|
|
@ -3,4 +3,4 @@
|
|||
|
||||
# make zsh source the correct directory
|
||||
export XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-"$HOME/.config"}
|
||||
ZDOTDIR="${XDG_CONFIG_HOME:-"$HOME/.config"}/zsh"
|
||||
ZDOTDIR="$XDG_CONFIG_HOME/zsh"
|
|
@ -0,0 +1,219 @@
|
|||
# This file has been auto-generated by i3-config-wizard(1).
|
||||
# It will not be overwritten, so edit it as you like.
|
||||
#
|
||||
# Should you change your keyboard layout some time, delete
|
||||
# this file and re-run i3-config-wizard(1).
|
||||
#
|
||||
|
||||
# i3 config file (v4)
|
||||
#
|
||||
# Please see https://i3wm.org/docs/userguide.html for a complete reference!
|
||||
|
||||
set $mod Mod4
|
||||
set $modemod Mod1
|
||||
set $font "pango:Fira Code 8"
|
||||
|
||||
# Font for window titles. Will also be used by the bar unless a different font
|
||||
# is used in the bar {} block below.
|
||||
#font pango:monospace 8
|
||||
font $font
|
||||
|
||||
workspace_auto_back_and_forth yes
|
||||
|
||||
# This font is widely installed, provides lots of unicode glyphs, right-to-left
|
||||
# text rendering and scalability on retina/hidpi displays (thanks to pango).
|
||||
#font pango:DejaVu Sans Mono 8
|
||||
|
||||
# Before i3 v4.8, we used to recommend this one as the default:
|
||||
# font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||
# The font above is very space-efficient, that is, it looks good, sharp and
|
||||
# clear in small sizes. However, its unicode glyph coverage is limited, the old
|
||||
# X core fonts rendering does not support right-to-left and this being a bitmap
|
||||
# font, it doesn’t scale on retina/hidpi displays.
|
||||
|
||||
# Use Mouse+$mod to drag floating windows to their wanted position
|
||||
floating_modifier $mod
|
||||
|
||||
# kill focused window
|
||||
bindsym $mod+Shift+c kill
|
||||
|
||||
# change focus
|
||||
bindsym $mod+h focus left
|
||||
bindsym $mod+j focus down
|
||||
bindsym $mod+k focus up
|
||||
bindsym $mod+l focus right
|
||||
|
||||
# alternatively, you can use the cursor keys:
|
||||
bindsym $mod+Left focus left
|
||||
bindsym $mod+Down focus down
|
||||
bindsym $mod+Up focus up
|
||||
bindsym $mod+Right 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
|
||||
|
||||
# alternatively, you can use the cursor keys:
|
||||
bindsym $mod+Shift+Left move left
|
||||
bindsym $mod+Shift+Down move down
|
||||
bindsym $mod+Shift+Up move up
|
||||
bindsym $mod+Shift+Right move right
|
||||
|
||||
# split in horizontal orientation
|
||||
bindsym $mod+Shift+s split v
|
||||
|
||||
# split in vertical orientation
|
||||
bindsym $mod+s split h
|
||||
|
||||
# enter fullscreen mode for the focused container
|
||||
bindsym $mod+f fullscreen toggle
|
||||
|
||||
# change container layout (stacked, tabbed, toggle split)
|
||||
bindsym $mod+Shift+w layout stacking
|
||||
bindsym $mod+w layout tabbed
|
||||
bindsym $mod+e layout toggle split
|
||||
|
||||
# toggle tiling / floating
|
||||
bindsym $mod+Shift+v floating toggle
|
||||
|
||||
# change focus between tiling / floating windows
|
||||
bindsym $mod+v focus mode_toggle
|
||||
|
||||
# focus the parent/child container
|
||||
bindsym $mod+Shift+a focus parent
|
||||
bindsym $mod+a focus child
|
||||
|
||||
# GAP MANAGEMENT
|
||||
# disable titles and borders, necessary for i3gaps to work apparently
|
||||
for_window [class="^.*"] border pixel 0
|
||||
# no gaps if there is only 1 window
|
||||
smart_gaps on
|
||||
# by default set some gaps when multiple windows are on the workspace
|
||||
gaps inner 0
|
||||
gaps outer 0
|
||||
# enable / disable gaps (g disables, G enables)
|
||||
bindsym $mod+shift+g gaps inner current set 15; gaps outer current set 0; exec picom_toggle_inactive_opacity on
|
||||
bindsym $mod+g gaps inner current set 0; gaps outer current set 0; exec picom_toggle_inactive_opacity off
|
||||
|
||||
# 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"
|
||||
set $ws5 "5"
|
||||
set $ws6 "6"
|
||||
set $ws7 "7"
|
||||
set $ws8 "8"
|
||||
set $ws9 "9"
|
||||
set $ws10 "10"
|
||||
|
||||
# switch to workspace
|
||||
bindsym $mod+1 workspace $ws1
|
||||
bindsym $mod+2 workspace $ws2
|
||||
bindsym $mod+3 workspace $ws3
|
||||
bindsym $mod+4 workspace $ws4
|
||||
bindsym $mod+5 workspace $ws5
|
||||
bindsym $mod+6 workspace $ws6
|
||||
bindsym $mod+7 workspace $ws7
|
||||
bindsym $mod+8 workspace $ws8
|
||||
bindsym $mod+9 workspace $ws9
|
||||
bindsym $mod+0 workspace $ws10
|
||||
|
||||
# move focused container to workspace
|
||||
bindsym $mod+Shift+1 move container to workspace $ws1
|
||||
bindsym $mod+Shift+2 move container to workspace $ws2
|
||||
bindsym $mod+Shift+3 move container to workspace $ws3
|
||||
bindsym $mod+Shift+4 move container to workspace $ws4
|
||||
bindsym $mod+Shift+5 move container to workspace $ws5
|
||||
bindsym $mod+Shift+6 move container to workspace $ws6
|
||||
bindsym $mod+Shift+7 move container to workspace $ws7
|
||||
bindsym $mod+Shift+8 move container to workspace $ws8
|
||||
bindsym $mod+Shift+9 move container to workspace $ws9
|
||||
bindsym $mod+Shift+0 move container to workspace $ws10
|
||||
|
||||
|
||||
# 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 Shift+h resize shrink width 30 px or 30 ppt
|
||||
bindsym j resize grow height 10 px or 10 ppt
|
||||
bindsym Shift+j resize grow height 30 px or 30 ppt
|
||||
bindsym k resize shrink height 10 px or 10 ppt
|
||||
bindsym Shift+k resize shrink height 30 px or 30 ppt
|
||||
bindsym l resize grow width 10 px or 10 ppt
|
||||
bindsym Shift+l resize grow width 30 px or 30 ppt
|
||||
|
||||
# same bindings, but for the arrow keys
|
||||
bindsym Left resize shrink width 10 px or 10 ppt
|
||||
bindsym Down resize grow height 10 px or 10 ppt
|
||||
bindsym Up resize shrink height 10 px or 10 ppt
|
||||
bindsym Right 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 $modemod+r mode "default"
|
||||
|
||||
}
|
||||
|
||||
mode "media" {
|
||||
# Pressing left will move 5s back
|
||||
bindsym h exec playerctl position 5-
|
||||
bindsym Shift+h exec playerctl position 15-
|
||||
# Pressing down will move to the next file
|
||||
bindsym j exec playerctl next
|
||||
# Pressing up will move to the previous file
|
||||
bindsym k exec playerctl previous
|
||||
# Pressing right will move 5s forward
|
||||
bindsym l exec playerctl position 5+
|
||||
bindsym Shift+l exec playerctl position 30+
|
||||
|
||||
# Pressing p will play/pause current track
|
||||
bindsym p exec playerctl play-pause
|
||||
|
||||
# Pressing p will play/pause current track
|
||||
bindsym s exec playerctl stop
|
||||
|
||||
#TODO add option to open file, loop, shuffle, show/select players
|
||||
|
||||
# back to normal
|
||||
bindsym Return mode "default"
|
||||
bindsym Escape mode "default"
|
||||
bindsym $modemod+m mode "default"
|
||||
}
|
||||
|
||||
bindsym $modemod+r mode "resize"
|
||||
bindsym $modemod+m mode "media"
|
||||
|
||||
# reload the configuration file
|
||||
bindsym $mod+Shift+y reload
|
||||
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
|
||||
bindsym $mod+Shift+r restart
|
||||
|
||||
# set a pretty wallpaper
|
||||
exec_always --no-startup-id feh --bg-scale ~/pictures/wall.jpg
|
||||
|
||||
# launch polybar (script ensures only 1 instance existing at a time)
|
||||
exec_always --no-startup-id polybar-launch simple-top
|
||||
|
||||
# default workspaces for most used apps
|
||||
# assign [class="^qutebrowser$"] → number 1
|
||||
# spotify needs for_window, see https://i3wm.org/docs/userguide.html#assign_workspace
|
||||
assign [class="^spotify$"] → 10
|
||||
assign [class="^Spotify$"] → 10
|
||||
#fix for spotify not moving to workspace 10
|
||||
for_window [class="^spotify$"] move to workspace 10
|
||||
|
||||
# autostart often used apps
|
||||
# needs to be done like this to both automatically start the apps on correct workspace
|
||||
# and not confine them there forever
|
||||
exec $XDG_CONFIG_HOME/i3/scripts/i3-applications-autostart
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Wait for program coming up
|
||||
wait_for_program() {
|
||||
n=0
|
||||
while true; do
|
||||
# PID of last background command
|
||||
if xdotool search --onlyvisible --pid $! 2>/dev/null; then
|
||||
break
|
||||
else
|
||||
if [ $n -eq 200 ]; then
|
||||
notify-send -u critical "Error during start: last program $! did not started fast enough"
|
||||
break
|
||||
else
|
||||
n=$($n + 1)
|
||||
sleep 0.1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
i3-msg "workspace number 2"
|
||||
alacritty &
|
||||
wait_for_program
|
||||
|
||||
i3-msg "workspace number 10"
|
||||
spotify &
|
||||
wait_for_program
|
||||
|
||||
i3-msg "workspace number 9"
|
||||
keybase &
|
||||
wait_for_program
|
||||
|
||||
i3-msg "workspace number 1"
|
||||
qutebrowser &
|
||||
wait_for_program
|
||||
|
||||
i3-msg "workspace number 3"
|
163
install.sh
163
install.sh
|
@ -2,137 +2,68 @@
|
|||
#
|
||||
# install.sh
|
||||
#
|
||||
# Installs dotfiles and packages for my setup.
|
||||
# Needs to be invoked from containing dotfile directory to work correctly.
|
||||
# Installs dotfiles and packages for my setup
|
||||
#
|
||||
# Will first install yay, then all my used packages (read from bootstrap/packages.txt)
|
||||
# Will first install yay, then all my used packages (read from bootstrap/packages.csv)
|
||||
#
|
||||
# Finally, symlinks all dotfiles into their correct locations using dotter
|
||||
# Will symlink all my dotfiles into their correct places using autostow.sh
|
||||
|
||||
bootstrap_dir="${DOT_BOOTSTRAP_DIR:-./bootstrap}"
|
||||
unattended_install="${DOT_UNATTENDED_INSTALL:-false}"
|
||||
link_systemfiles="${DOT_LINK_SYSTEMFILES:-true}"
|
||||
|
||||
help() {
|
||||
printf "Usage: install [-f|--force][-v|--version][-h|--help]\n\n-f Do not ask for any confirmations but force update and installation.\n"
|
||||
exit "${1:-0}"
|
||||
}
|
||||
bootstrap_dir="${BOOTSTRAP_DIRECTORY:-./_bootstrap}"
|
||||
|
||||
main() {
|
||||
local cmd=""
|
||||
local ret=0
|
||||
case "$1" in
|
||||
-v | --version)
|
||||
printf "Personal system bootstrap script.\n\nby Marty Oehme\n\nv0.2\n"
|
||||
;;
|
||||
-h | --help)
|
||||
help 0
|
||||
;;
|
||||
-f | --force)
|
||||
unattended_install=true
|
||||
;;
|
||||
*)
|
||||
install
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
local cmd=""
|
||||
local ret=0
|
||||
|
||||
$cmd "$@"
|
||||
ret=$((ret + $?))
|
||||
exit $ret
|
||||
case "$1" in
|
||||
-v | --version)
|
||||
printf "Personal system bootstrap script.\n\n©Marty Oehme\n\nVersion: 0.1.1\n"
|
||||
;;
|
||||
-h | --help)
|
||||
printf "Usage: install [-f|--force][-v|--version][-h|--help]\n\n-f Do not ask for any confirmations but force update and installation.\n"
|
||||
;;
|
||||
-f | --force)
|
||||
install true
|
||||
;;
|
||||
*)
|
||||
install false
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
|
||||
$cmd "$@"
|
||||
ret=$((ret + $?))
|
||||
exit $ret
|
||||
}
|
||||
|
||||
# takes default value (y/n), question, abort message as arguments
|
||||
# automatically answers yes if unattended install
|
||||
check_consent() {
|
||||
if [ "$unattended_install" == "true" ]; then
|
||||
true
|
||||
else
|
||||
[[ "$1" == "y" ]] && default_consent="[Y/n]" || default_consent="[y/N]"
|
||||
printf "%b %b " "$2" "$default_consent"
|
||||
read -r answer
|
||||
if [[ "$1" == "n" ]] && [[ "$answer" != y* ]]; then
|
||||
printf "%s\n" "$3"
|
||||
false
|
||||
elif [[ "$1" == "y" ]] && [[ "$answer" == n* ]]; then
|
||||
printf "%s\n" "$3"
|
||||
false
|
||||
else
|
||||
true
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
entry_question() {
|
||||
check_consent n "This will take a while and install many packages and link dotfiles all over the place depending on your selections.\nYou need to be in the base directory of the dotfiles repository.\nProceed?" "Aborting." || exit
|
||||
}
|
||||
|
||||
enable_git_hooks() {
|
||||
check_consent y "Should we enable git hooks for this repository, so that missing or additionally installed packages are automatically compared to the repository when committing?" "Not changing repository settings." || return
|
||||
git config --local core.hooksPath .githooks/
|
||||
echo "Changed repository settings."
|
||||
}
|
||||
|
||||
manage_dotfiles() {
|
||||
check_consent y "Link dot files?" "Not linking dotfiles." || return
|
||||
check_consent n "Link system settings files? This will require sudo access but will not overwrite existing files." "Not touching system files." || link_systemfiles=false
|
||||
if [ "$link_systemfiles" == "false" ]; then
|
||||
dotter deploy
|
||||
echo "Linked dotfiles."
|
||||
else
|
||||
if [ -e "/etc/pacman.conf" ]; then
|
||||
check_consent n "Found an existing pacman.conf file, installation will error if it exists. Remove file?" && run_elevated rm "/etc/pacman.conf"
|
||||
fi
|
||||
dotter deploy -l .dotter/systemwide.toml
|
||||
echo "Linked dotfiles and system files."
|
||||
fi
|
||||
}
|
||||
|
||||
run_elevated() {
|
||||
if command -v doas >/dev/null 2>&1; then
|
||||
doas "$@"
|
||||
elif command -v sudo >/dev/null 2>&1; then
|
||||
sudo "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
install_packages() {
|
||||
check_consent n "Install pre-designated packages? This will take a while, install a lot of packages and require super user privileges." "Not installing packages." || return
|
||||
if [ "$unattended_install" == "true" ]; then
|
||||
"$bootstrap_dir"/install_packages.sh -f
|
||||
else
|
||||
"$bootstrap_dir"/install_packages.sh
|
||||
fi
|
||||
echo "Installed packages."
|
||||
}
|
||||
|
||||
headline() {
|
||||
len="${#1}"
|
||||
target_len=85
|
||||
side_len=$(((target_len - len) / 2))
|
||||
for ((i = 1; i <= side_len; i++)); do printf '='; done
|
||||
printf " %s " "$1"
|
||||
for ((i = 1; i <= side_len; i++)); do printf '='; done
|
||||
printf "\n"
|
||||
echo "This will take a while, install many packages and link dotfiles all over the place. Proceed [y/N]?"
|
||||
read -r yes
|
||||
if [[ "$yes" != y* ]]; then
|
||||
echo "Exiting."
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
if [ "$unattended_install" == false ] ; then
|
||||
entry_question
|
||||
fi
|
||||
unattended=$1
|
||||
if ! "$unattended"; then
|
||||
check_consent
|
||||
fi
|
||||
echo "====================== BEGINNING INSTALLATION ============================="
|
||||
if ! "$unattended"; then
|
||||
export BOOTSTRAP_PACKAGES="bootstrap/packages.csv"
|
||||
"$bootstrap_dir"/install_packages.sh
|
||||
else
|
||||
export BOOTSTRAP_PACKAGES="bootstrap/packages.csv"
|
||||
"$bootstrap_dir"/install_packages.sh -f
|
||||
fi
|
||||
unset BOOTSTRAP_PACKAGES
|
||||
|
||||
headline "BEGINNING PACKAGE INSTALLATION"
|
||||
install_packages
|
||||
echo "=================== BEGINNING DOTFILE MANAGEMENT =========================="
|
||||
"$bootstrap_dir"/autostow.sh -s
|
||||
|
||||
headline "BEGINNING DOTFILE MANAGEMENT"
|
||||
manage_dotfiles
|
||||
echo "====================== INSTALLATION FINISHED =============================="
|
||||
|
||||
headline "ENABLING GIT REPOSITORY HOOKS"
|
||||
enable_git_hooks
|
||||
|
||||
echo "INSTALLATION FINISHED"
|
||||
exit 0
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
# Configuration file for libinput-gestures.
|
||||
#
|
||||
# The default configuration file exists at /etc/libinput-gestures.conf
|
||||
# but a user can create a personal custom configuration file at
|
||||
# ~/.config/libinput-gestures.conf.
|
||||
#
|
||||
# Lines starting with '#' and blank lines are ignored. Currently
|
||||
# "gesture" and "device" configuration keywords are supported as
|
||||
# described below. The keyword can optionally be appended with a ":" (to
|
||||
# maintain compatibility with original format configuration files).
|
||||
#
|
||||
# Each gesture line has 3 [or 4] arguments separated by whitespace:
|
||||
#
|
||||
# action motion [finger_count] command
|
||||
#
|
||||
# where action and motion is either:
|
||||
# swipe up
|
||||
# swipe down
|
||||
# swipe left
|
||||
# swipe right
|
||||
# pinch in
|
||||
# pinch out
|
||||
#
|
||||
# command is the remainder of the line and is any valid shell command +
|
||||
# arguments.
|
||||
#
|
||||
# finger_count is a single numeric digit and is optional (and is
|
||||
# typically 3 or 4). If specified then the command is executed when
|
||||
# exactly that number of fingers is used in the gesture. If not
|
||||
# specified then the command is executed when that gesture is executed
|
||||
# with any number of fingers. Gesture lines specified with finger_count
|
||||
# have priority over the same gesture specified without any
|
||||
# finger_count.
|
||||
#
|
||||
# Typically command will be xdotool, or wmctrl. See "man xdotool" for
|
||||
# the many things you can action with that tool. Note that unfortunately
|
||||
# xdotool does not work with native Wayland clients.
|
||||
|
||||
###############################################################################
|
||||
# SWIPE GESTURES:
|
||||
###############################################################################
|
||||
|
||||
# Gestures with 4 fingers: Move around desktops
|
||||
# next dekstop
|
||||
gesture swipe right 4 _internal ws_up
|
||||
# previous desktop
|
||||
gesture swipe left 4 _internal ws_down
|
||||
# hide all windows (show desktop)
|
||||
gesture swipe down 4 xdotool key Control_L+F12
|
||||
# show all open windows
|
||||
gesture swipe up 4 xdotool key Control_L+F10
|
||||
|
||||
# show desktop grid
|
||||
gesture swipe right_down 3 xdotool key Control_L+F8
|
||||
# show activity manager
|
||||
gesture swipe left_down 3 xdotool key Super_L+q
|
||||
# Show all open windows of desktop
|
||||
gesture swipe up 3 xdotool key Control_L+F9
|
||||
# minimize window (Super_L = Meta; Next = PgDown)
|
||||
gesture swipe down 3 xdotool key Super_L+Next
|
||||
|
||||
|
||||
# 45degree Gestures with 3 fingers: control tab & browsing behavior
|
||||
# next tab
|
||||
gesture swipe right 3 xdotool key Control_L+Tab
|
||||
# previous tab
|
||||
gesture swipe left 3 xdotool key Control_L+Shift+Tab
|
||||
# new tab
|
||||
gesture swipe right_up 3 xdotool key Control_L+t
|
||||
# close tab
|
||||
gesture swipe left_up 3 xdotool key Control_L+w
|
||||
|
||||
# Note the default is an "internal" command that uses wmctrl to switch
|
||||
# workspaces and, unlike xdotool, works on both Xorg and Wayland (via
|
||||
# XWayland). It also can be configured for vertical and horizontal
|
||||
# switching over tabular workspaces, as per the example below. You can
|
||||
# also add "-w" to the internal command to allow wrapping workspaces.
|
||||
|
||||
# Move to next workspace (works for GNOME/KDE/etc on Wayland and Xorg)
|
||||
# gesture swipe up _internal ws_up
|
||||
|
||||
# NOTE ABOUT FINGER COUNT:
|
||||
# The above command will configure this command for all fingers (i.e. 3
|
||||
# for 4) but to configure it for 3 fingers only, change it to:
|
||||
# gesture swipe up 3 _internal ws_up
|
||||
# Then you can configure something else for 4 fingers or leave 4 fingers
|
||||
# unconfigured. You can configure an explicit finger count like this for
|
||||
# all example commands in this configuration file.
|
||||
#
|
||||
# gesture swipe up xdotool key super+Page_Down
|
||||
|
||||
# Move to prev workspace (works for GNOME/KDE/etc on Wayland and Xorg)
|
||||
# gesture swipe down _internal ws_down
|
||||
# gesture swipe down xdotool key super+Page_Up
|
||||
|
||||
# Browser go forward (works only for Xorg, and Xwayland clients)
|
||||
# gesture swipe left xdotool key alt+Right
|
||||
|
||||
# Browser go back (works only for Xorg, and Xwayland clients)
|
||||
# gesture swipe right xdotool key alt+Left
|
||||
|
||||
# NOTE: If you don't use "natural" scrolling direction for your touchpad
|
||||
# then you may want to swap the above default left/right and up/down
|
||||
# configurations.
|
||||
|
||||
# Optional extended swipe gestures, e.g. for browser tab navigation:
|
||||
#
|
||||
# Jump to next open browser tab
|
||||
# gesture swipe right_up xdotool key control+Tab
|
||||
#
|
||||
# Jump to previous open browser tab
|
||||
# gesture swipe left_up xdotool key control+shift+Tab
|
||||
#
|
||||
# Close current browser tab
|
||||
# gesture swipe left_down xdotool key control+w
|
||||
#
|
||||
# Reopen and jump to last closed browser tab
|
||||
# gesture swipe right_down xdotool key control+shift+t
|
||||
|
||||
# Example of 8 static workspaces, e.g. using KDE virtual-desktops,
|
||||
# arranged in 2 rows of 4 across using swipe up/down/left/right to
|
||||
# navigate in fixed planes. Must match how you have configured your
|
||||
# virtual desktops.
|
||||
# gesture swipe up _internal --col=2 ws_up
|
||||
# gesture swipe down _internal --col=2 ws_down
|
||||
# gesture swipe left _internal --row=4 ws_up
|
||||
# gesture swipe right _internal --row=4 ws_down
|
||||
|
||||
# Example virtual desktop switching for Ubuntu Unity/Compiz. The
|
||||
# _internal command does not work for Compiz but you can explicitly
|
||||
# configure the swipe commands to work for a Compiz virtual 2
|
||||
# dimensional desktop as follows:
|
||||
# gesture swipe up xdotool key ctrl+alt+Up
|
||||
# gesture swipe down xdotool key ctrl+alt+Down
|
||||
# gesture swipe left xdotool key ctrl+alt+Left
|
||||
# gesture swipe right xdotool key ctrl+alt+Right
|
||||
|
||||
# Example to change audio volume:
|
||||
# Note this only works on an Xorg desktop (not Wayland).
|
||||
# gesture swipe up xdotool key XF86AudioRaiseVolume
|
||||
# gesture swipe down xdotool key XF86AudioLowerVolume
|
||||
|
||||
###############################################################################
|
||||
# PINCH GESTURES:
|
||||
###############################################################################
|
||||
|
||||
# GNOME SHELL open/close overview (works for GNOME on Xorg only)
|
||||
#gesture pinch in xdotool key super+s
|
||||
#gesture pinch out xdotool key super+s
|
||||
|
||||
# KDE Plasma open/close overview
|
||||
# gesture pinch in xdotool key ctrl+F9
|
||||
# gesture pinch out xdotool key ctrl+F9
|
||||
|
||||
# GNOME SHELL open/close overview (works for GNOME on Wayland and Xorg)
|
||||
# Note since GNOME 3.24 on Wayland this is implemented natively so no
|
||||
# real point configuring for Wayland.
|
||||
# gesture pinch in dbus-send --session --type=method_call --dest=org.gnome.Shell /org/gnome/Shell org.gnome.Shell.Eval string:'Main.overview.toggle();'
|
||||
# gesture pinch out dbus-send --session --type=method_call --dest=org.gnome.Shell /org/gnome/Shell org.gnome.Shell.Eval string:'Main.overview.toggle();'
|
||||
|
||||
# Optional extended pinch gestures:
|
||||
# gesture pinch clockwise <whatever command>
|
||||
# gesture pinch anticlockwise <whatever command>
|
||||
|
||||
###############################################################################
|
||||
# This application normally determines your touchpad device
|
||||
# automatically. Some users may have multiple touchpads but by default
|
||||
# we use only the first one found. However, you can choose to specify
|
||||
# the explicit device name to use. Run "libinput list-devices" to work
|
||||
# out the name of your device (from the "Device:" field). Then add a
|
||||
# device line specifying that name, e.g:
|
||||
#
|
||||
# device DLL0665:01 06CB:76AD Touchpad
|
||||
#
|
||||
# If the device name starts with a '/' then it is instead considered as
|
||||
# the explicit device path although since device paths can change
|
||||
# through reboots this is best to be a symlink. E.g. instead of specifying
|
||||
# /dev/input/event12, use the corresponding full path link under
|
||||
# /dev/input/by-path/*.
|
||||
#
|
||||
# You can choose to use ALL touchpad devices by setting the device name
|
||||
# to "all". E.g. Do this if you have multiple touchpads which you want
|
||||
# to use in parallel. This reduces performance slightly so only set this
|
||||
# if you have to.
|
||||
#
|
||||
# device all
|
||||
|
||||
###############################################################################
|
||||
# You can set a minimum travel distance threshold before swipe gestures
|
||||
# are actioned using the swipe_threshold configuration command.
|
||||
# Specify this value in dots. The default is 0.
|
||||
# E.g. set it to 100 dots with "swipe_threshold 100".
|
||||
# swipe_threshold 0
|
|
@ -1,118 +0,0 @@
|
|||
# much of this such as the whitelist, canonical list for lasgenre
|
||||
# is stolen from https://github.com/montchr/beets-config/ with much gratitude
|
||||
|
||||
directory: ~/media/audio/music
|
||||
library: ~/.local/share/beets/library.db
|
||||
|
||||
threaded: true
|
||||
art_filename: albumart
|
||||
# Use the album's original date instead of the release's date
|
||||
original_date: yes
|
||||
# Use safer pathnames
|
||||
asciify_paths: yes
|
||||
max_filename_length: 255
|
||||
|
||||
# Long format - I don't need all the extra info for each invocation
|
||||
#format_item: '[$id] [$album_id] [$singleton] $albumartist - $title - $album - $original_year - [$format - $bitrate $length $filesize]'
|
||||
#format_album: '[$id] $albumartist - $album - $original_year [$catalognum]'
|
||||
|
||||
clutter:
|
||||
- Thumbs.db
|
||||
- .DS_Store
|
||||
- '*.m3u'
|
||||
- '*.pls'
|
||||
- '*.db'
|
||||
|
||||
import:
|
||||
copy: false
|
||||
move: true
|
||||
write: true
|
||||
bell: true
|
||||
# incremental: true
|
||||
languages:
|
||||
- en
|
||||
log: ~/.cache/beets.log
|
||||
quiet_fallback: skip
|
||||
timid: false
|
||||
|
||||
ignore_hidden: yes
|
||||
paths:
|
||||
default: "%the{$albumartist}/$album/$track $title"
|
||||
singleton: "singletons/%the{$artist - $title}"
|
||||
comp: compilations/$album/$track $title
|
||||
albumtype:soundtrack: soundtracks/$album/$track $title
|
||||
|
||||
item_fields:
|
||||
multidisc: 1 if disctotal > 1 else 0
|
||||
artist_differs: 1 if albumartist != artist else 0
|
||||
|
||||
musicbrainz:
|
||||
extra_tags: [year, catalognum, country, media, label]
|
||||
user: {{multimedia_beets_musicbrainz_user}}
|
||||
pass: {{multimedia_beets_musicbrainz_pass}}
|
||||
auto: yes
|
||||
remove: yes
|
||||
|
||||
match:
|
||||
preferred:
|
||||
countries: ["US", "UK|GB", "DE", "NL", "SE"]
|
||||
media: ["Digital Media|File", "CD"]
|
||||
strong_rec_thresh: 0.15
|
||||
medium_red_thresh: 0.25
|
||||
rec_gap_thresh: 0.25
|
||||
|
||||
plugins:
|
||||
- bandcamp
|
||||
- chroma
|
||||
- deezer
|
||||
- describe
|
||||
- edit
|
||||
- embedart
|
||||
- export
|
||||
- fetchart
|
||||
- fromfilename
|
||||
- ftintitle
|
||||
- fuzzy
|
||||
- importadded
|
||||
- info
|
||||
- inline
|
||||
- lastgenre
|
||||
- lastimport
|
||||
- lyrics
|
||||
- mbcollection
|
||||
- mbsync
|
||||
- missing
|
||||
- spotify
|
||||
- the
|
||||
- ydl
|
||||
|
||||
edit:
|
||||
itemfields: track title artist album
|
||||
albumfields: album albumartist albumtype
|
||||
ignore_fields: id path
|
||||
|
||||
fetchart:
|
||||
sources: filesystem coverart itunes discogs amazon albumart
|
||||
store_source: yes
|
||||
|
||||
lastgenre:
|
||||
auto: true
|
||||
canonical: ~/.config/beets/lastgenre_canonicallist.yaml
|
||||
count: 4
|
||||
fallback: ""
|
||||
force: yes
|
||||
min_weight: 6
|
||||
prefer_specific: false
|
||||
separator: "; "
|
||||
source: album
|
||||
whitelist: ~/.config/beets/lastgenre_whitelist.txt
|
||||
|
||||
lastfm:
|
||||
user: schmitzkater
|
||||
|
||||
lyrics:
|
||||
sources: musixmatch genius
|
||||
fallback: ''
|
||||
|
||||
bandcamp:
|
||||
art: true
|
|
@ -1,783 +0,0 @@
|
|||
- 2-step
|
||||
- acapella
|
||||
- acid
|
||||
- acid house
|
||||
- acid jazz
|
||||
- acid techno
|
||||
- adult contemporary
|
||||
- african
|
||||
- african blues
|
||||
- african heavy metal
|
||||
- african hip hop
|
||||
- afrobeat
|
||||
- aggrotech
|
||||
- alternative country
|
||||
- alternative metal
|
||||
- alternative rock
|
||||
- ambient
|
||||
- ambient dub
|
||||
- ambient house
|
||||
- ambient space jazz
|
||||
- ambient techno
|
||||
- american folk revival
|
||||
- americana
|
||||
- anison
|
||||
- anti-folk
|
||||
- apala
|
||||
- arab pop
|
||||
- asian underground
|
||||
- atlanta hip hop:
|
||||
- snap music
|
||||
- australian country music
|
||||
- avant-garde
|
||||
- avant-garde jazz
|
||||
- axé
|
||||
- bachata
|
||||
- baithak gana
|
||||
- bakersfield sound
|
||||
- balearic beat
|
||||
- ballet
|
||||
- baltimore club
|
||||
- barbershop
|
||||
- baroque pop
|
||||
- baroque:
|
||||
- baroque music
|
||||
- bebop
|
||||
- benga
|
||||
- berlin school:
|
||||
- berlin school of electronic music
|
||||
- berlin-school
|
||||
- big band
|
||||
- big beat
|
||||
- bikutsi
|
||||
- black metal:
|
||||
- viking metal
|
||||
- blue-eyed soul
|
||||
- bluegrass:
|
||||
- progressive bluegrass
|
||||
- reactionary bluegrass
|
||||
- blues
|
||||
- blues country
|
||||
- blues rock
|
||||
- blues shouter
|
||||
- bolero
|
||||
- bongo flava
|
||||
- boogie
|
||||
- boogie-woogie
|
||||
- bossa nova
|
||||
- bounce music
|
||||
- brazilian
|
||||
- brazilian rock
|
||||
- breakbeat:
|
||||
- 4-beat
|
||||
- acid breaks
|
||||
- breakbeat hardcore
|
||||
- broken beat
|
||||
- florida breaks
|
||||
- nu skool breaks
|
||||
- breakcore
|
||||
- brega
|
||||
- british blues
|
||||
- british folk revival
|
||||
- britpop:
|
||||
- post-britpop
|
||||
- bubblegum pop
|
||||
- c-pop:
|
||||
- cantopop
|
||||
- cajun:
|
||||
- cajun fiddle tunes
|
||||
- calypso
|
||||
- canadian blues
|
||||
- cantata
|
||||
- cape jazz
|
||||
- celtic music
|
||||
- chamber jazz
|
||||
- chamber music:
|
||||
- string quartet
|
||||
- chanson
|
||||
- chicago blues
|
||||
- chicago house
|
||||
- chillwave:
|
||||
- chill wave
|
||||
- chimurenga
|
||||
- chiptune:
|
||||
- bitpop
|
||||
- game boy music
|
||||
- nintendocore
|
||||
- video game music
|
||||
- yorkshire bleeps and bass
|
||||
- choro
|
||||
- christian country music
|
||||
- christian hip hop
|
||||
- christian metal
|
||||
- christian pop
|
||||
- christian rock
|
||||
- chutney
|
||||
- chutney soca
|
||||
- classic country
|
||||
- classical crossover
|
||||
- classical:
|
||||
- classical music
|
||||
- orchestra:
|
||||
- orchestral
|
||||
- symphonic
|
||||
- symphony
|
||||
- close harmony
|
||||
- coldwave
|
||||
- comedy:
|
||||
- comedy music
|
||||
- comedy rock
|
||||
- humor
|
||||
- parody music
|
||||
- stand-up
|
||||
- compas
|
||||
- computer music
|
||||
- concerto:
|
||||
- concerto grosso
|
||||
- contemporary folk
|
||||
- contemporary r&b
|
||||
- continental jazz
|
||||
- cool jazz
|
||||
- country
|
||||
- country blues
|
||||
- country pop
|
||||
- country rap
|
||||
- country rock
|
||||
- country soul
|
||||
- country-rap
|
||||
- coupé-décalé
|
||||
- cowpunk
|
||||
- crunkcore
|
||||
- cybergrind
|
||||
- dance-punk
|
||||
- dance-rock
|
||||
- dancehall
|
||||
- dansband music
|
||||
- dark ambient
|
||||
- dark electro
|
||||
- darkwave:
|
||||
- dark wave
|
||||
- death industrial
|
||||
- death metal:
|
||||
- goregrind
|
||||
- deconstructed club
|
||||
- deep house
|
||||
- deep techno
|
||||
- delta blues
|
||||
- detroit blues
|
||||
- detroit techno
|
||||
- digital hardcore:
|
||||
- bouncy house
|
||||
- bouncy techno
|
||||
- hardstyle
|
||||
- jumpstyle
|
||||
- makina
|
||||
- uk hardcore
|
||||
- disco:
|
||||
- disco polo:
|
||||
- euro disco
|
||||
- nu-disco
|
||||
- diva house
|
||||
- dixieland
|
||||
- doo wop
|
||||
- doom metal
|
||||
- doomcore
|
||||
- downtempo:
|
||||
- chill out
|
||||
- ethnic electronica
|
||||
- moombahton
|
||||
- nu jazz
|
||||
- dream pop
|
||||
- drone metal
|
||||
- drone:
|
||||
- drone music
|
||||
- drum and bass:
|
||||
- darkcore
|
||||
- darkstep
|
||||
- drumfunk
|
||||
- drumstep
|
||||
- hardstep
|
||||
- intelligent drum and bass
|
||||
- jump-up
|
||||
- liquid funk
|
||||
- neurofunk
|
||||
- raggacore
|
||||
- sambass
|
||||
- techstep
|
||||
- dub poetry
|
||||
- dub techno
|
||||
- dub:
|
||||
- dub music
|
||||
- dubstep
|
||||
- dubtronica
|
||||
- dungeon synth
|
||||
- dutch house
|
||||
- east coast hip hop:
|
||||
- brick city club
|
||||
- hardcore hip hop
|
||||
- mafioso rap
|
||||
- new jersey hip hop
|
||||
- easy listening:
|
||||
- background music
|
||||
- beautiful music
|
||||
- elevator music
|
||||
- furniture music
|
||||
- middle of the road
|
||||
- ebm:
|
||||
- electronic body music:
|
||||
- futurepop
|
||||
- edm:
|
||||
- electronic dance music
|
||||
- electric blues
|
||||
- electro
|
||||
- electro house
|
||||
- electro-grime
|
||||
- electro-industrial
|
||||
- electroacoustic:
|
||||
- acousmatic music
|
||||
- electroacoustic improvisation
|
||||
- live electronics
|
||||
- electroclash
|
||||
- electrofunk
|
||||
- electronic rock:
|
||||
- alternative dance:
|
||||
- baggy
|
||||
- madchester
|
||||
- electronicore
|
||||
- ethereal wave
|
||||
- new rave
|
||||
- electropop
|
||||
- electropunk
|
||||
- emo
|
||||
- enka
|
||||
- eurodance:
|
||||
- bubblegum dance
|
||||
- italo dance
|
||||
- turbofolk
|
||||
- europop:
|
||||
- austropop
|
||||
- balkan pop
|
||||
- french pop
|
||||
- latin pop
|
||||
- laïkó
|
||||
- nederpop
|
||||
- russian pop
|
||||
- experimental pop
|
||||
- experimental rock
|
||||
- experimental:
|
||||
- experimental music
|
||||
- fann at-tanbura
|
||||
- field recording
|
||||
- fijiri
|
||||
- filmi
|
||||
- folk metal:
|
||||
- celtic metal
|
||||
- medieval metal
|
||||
- folk punk:
|
||||
- celtic punk
|
||||
- gypsy punk
|
||||
- folk rock
|
||||
- folk:
|
||||
- filk music
|
||||
- folk music
|
||||
- folktronica
|
||||
- footwork
|
||||
- forró
|
||||
- fourth world:
|
||||
- ethnic ambient
|
||||
- tribal ambient
|
||||
- franco-country
|
||||
- freak folk
|
||||
- free jazz
|
||||
- free funk
|
||||
- free improvisation
|
||||
- freestyle house
|
||||
- freestyle rap
|
||||
- freestyle:
|
||||
- freestyle music
|
||||
- french house
|
||||
- frevo
|
||||
- fuji music
|
||||
- funk carioca
|
||||
- funk metal
|
||||
- funk:
|
||||
- deep funk
|
||||
- go-go
|
||||
- funky house
|
||||
- g-funk
|
||||
- gabber
|
||||
- gamelan
|
||||
- gangsta rap
|
||||
- garage rock
|
||||
- garage:
|
||||
- 4x4
|
||||
- bassline
|
||||
- breakstep
|
||||
- funky
|
||||
- speed garage
|
||||
- genge
|
||||
- ghetto house
|
||||
- ghettotech
|
||||
- glam metal
|
||||
- glam rock
|
||||
- glitch-hop
|
||||
- glitch:
|
||||
- clicks 'n' cuts
|
||||
- goa:
|
||||
- dark psytranceon
|
||||
- goa trance
|
||||
- psybreaks
|
||||
- psyprog
|
||||
- gospel blues
|
||||
- goth rock:
|
||||
- gothic rock
|
||||
- gothic metal
|
||||
- grime
|
||||
- grindcore:
|
||||
- crustgrind
|
||||
- noisegrind
|
||||
- grunge:
|
||||
- post-grunge
|
||||
- gulf and western
|
||||
- gypsy jazz
|
||||
- happy hardcore
|
||||
- hard bop
|
||||
- hard rock
|
||||
- hardbag
|
||||
- hardcore punk:
|
||||
- street punk
|
||||
- hellbilly music
|
||||
- hi-nrg:
|
||||
- eurobeat
|
||||
- hard nrg
|
||||
- new beat
|
||||
- highlife
|
||||
- hill country blues
|
||||
- hip house
|
||||
- hip-hop:
|
||||
- alternative hip hop
|
||||
- avant-garde hip hop
|
||||
- chap hop
|
||||
- chicago hip hop
|
||||
- conscious hip hop
|
||||
- detroit hip hop
|
||||
- hip hop
|
||||
- hip hop soul
|
||||
- hip pop
|
||||
- horrorcore
|
||||
- hyphy
|
||||
- jazz rap
|
||||
- low bap
|
||||
- lyrical hip hop
|
||||
- merenrap
|
||||
- motswako
|
||||
- new jack swing
|
||||
- new school hip hop
|
||||
- old school hip hop
|
||||
- political hip hop
|
||||
- rap opera
|
||||
- songo-salsa
|
||||
- st. louis hip hop
|
||||
- twin cities hip hop
|
||||
- underground hip hop
|
||||
- urban pasifika
|
||||
- hiplife
|
||||
- hokum
|
||||
- hokum blues
|
||||
- hong kong english pop
|
||||
- honky tonk
|
||||
- horror punk
|
||||
- house
|
||||
- houston hip hop:
|
||||
- chopped and screwed
|
||||
- idm
|
||||
- illbient
|
||||
- indian pop
|
||||
- indie folk
|
||||
- indie pop:
|
||||
- dunedin sound
|
||||
- twee pop
|
||||
- indie rock
|
||||
- indietronica
|
||||
- industrial
|
||||
- industrial dance
|
||||
- industrial folk
|
||||
- industrial hip hop
|
||||
- industrial metal:
|
||||
- neue deutsche härte
|
||||
- industrial rock
|
||||
- instrumental country
|
||||
- instrumental hip hop
|
||||
- iranian pop
|
||||
- isicathamiya
|
||||
- isolationism
|
||||
- italo disco
|
||||
- italo house
|
||||
- j-pop
|
||||
- jazz blues
|
||||
- jazz fusion:
|
||||
- fusion
|
||||
- fusion jazz
|
||||
- jazz rock
|
||||
- jazz-funk
|
||||
- jazz:
|
||||
- british dance band
|
||||
- crossover jazz
|
||||
- cubop
|
||||
- ethno jazz
|
||||
- european free jazz
|
||||
- m-base
|
||||
- mainstream jazz
|
||||
- novelty ragtime
|
||||
- orchestral jazz
|
||||
- shibuya-kei
|
||||
- stride jazz
|
||||
- third stream
|
||||
- trad jazz
|
||||
- vocal jazz
|
||||
- west coast gypsy jazz
|
||||
- west coast jazz
|
||||
- jit
|
||||
- jump blues
|
||||
- jungle:
|
||||
- oldschool jungle:
|
||||
- darkside jungle
|
||||
- ragga jungle
|
||||
- jùjú
|
||||
- k-pop:
|
||||
- korean pop
|
||||
- kansas city blues
|
||||
- kansas city jazz
|
||||
- kapuka
|
||||
- kayōkyoku
|
||||
- khaliji
|
||||
- kizomba
|
||||
- kosmische:
|
||||
- kraut rock
|
||||
- krautrock
|
||||
- kuduro
|
||||
- kwaito
|
||||
- kwela
|
||||
- lambada
|
||||
- latin house
|
||||
- latin jazz
|
||||
- latin:
|
||||
- chicha
|
||||
- criolla
|
||||
- cumbia
|
||||
- huayno
|
||||
- mariachi
|
||||
- ranchera
|
||||
- tejano
|
||||
- live coding
|
||||
- liwa
|
||||
- lo-fi
|
||||
- louisiana blues
|
||||
- lounge:
|
||||
- lounge music
|
||||
- lovers rock
|
||||
- lowercase
|
||||
- lubbock sound
|
||||
- luk thung:
|
||||
- luk krung
|
||||
- makossa
|
||||
- maloya
|
||||
- mambo
|
||||
- mandopop
|
||||
- manila sound
|
||||
- maracatu
|
||||
- marrabenta
|
||||
- martial industrial
|
||||
- mass
|
||||
- math rock
|
||||
- mbalax
|
||||
- mbaqanga
|
||||
- mbube
|
||||
- melodic death metal
|
||||
- memphis blues
|
||||
- metal:
|
||||
- heavy metal
|
||||
- metalcore:
|
||||
- deathcore
|
||||
- mathcore:
|
||||
- djent
|
||||
- mexican pop
|
||||
- miami bass
|
||||
- minimal house
|
||||
- minimal techno
|
||||
- minimal wave
|
||||
- modal jazz
|
||||
- modern classical:
|
||||
- contemporary classical
|
||||
- morlam
|
||||
- morna
|
||||
- museve
|
||||
- musique concrète:
|
||||
- tape music
|
||||
- méringue:
|
||||
- merengue
|
||||
- música popular brasileira
|
||||
- música sertaneja
|
||||
- nashville sound
|
||||
- neo soul
|
||||
- neo-bop jazz
|
||||
- neo-psychedelia
|
||||
- neo-swing
|
||||
- neofolk
|
||||
- neotraditional country
|
||||
- nerdcore
|
||||
- new age:
|
||||
- new age music
|
||||
- new-age
|
||||
- new-age music
|
||||
- new wave
|
||||
- no wave
|
||||
- noise pop
|
||||
- noise rock
|
||||
- noise:
|
||||
- harsh noise
|
||||
- japanoise
|
||||
- noise music
|
||||
- northern soul
|
||||
- nu metal
|
||||
- onkyokei
|
||||
- opera
|
||||
- oratorio
|
||||
- organum
|
||||
- outlaw country
|
||||
- p-funk
|
||||
- pagode
|
||||
- palm-wine
|
||||
- piano blues
|
||||
- piedmont blues
|
||||
- pinoy pop
|
||||
- pop punk
|
||||
- pop rock
|
||||
- pop sunda
|
||||
- pop:
|
||||
- jangle pop
|
||||
- latin ballad
|
||||
- levenslied
|
||||
- louisiana swamp pop
|
||||
- motorpop
|
||||
- new romanticism
|
||||
- pop rap
|
||||
- popera
|
||||
- schlager
|
||||
- sophisti-pop
|
||||
- sunshine pop
|
||||
- traditional pop music
|
||||
- vispop
|
||||
- wonky pop
|
||||
- post-bop
|
||||
- post-disco:
|
||||
- dance-pop
|
||||
- post-hardcore
|
||||
- post-punk
|
||||
- post-punk revival
|
||||
- post-rock:
|
||||
- post-metal
|
||||
- power electronics
|
||||
- power metal
|
||||
- power noise
|
||||
- powerviolence
|
||||
- progressive country
|
||||
- progressive electronic:
|
||||
- progressive breaks
|
||||
- progressive drum & bass
|
||||
- progressive house/trance:
|
||||
- disco house
|
||||
- dream house
|
||||
- space house
|
||||
- progressive techno
|
||||
- progressive folk
|
||||
- progressive house
|
||||
- progressive metal
|
||||
- progressive rock:
|
||||
- canterbury scene
|
||||
- new prog
|
||||
- rock in opposition
|
||||
- psychedelic folk
|
||||
- psychedelic pop
|
||||
- psychedelic rock:
|
||||
- acid rock
|
||||
- freakbeat
|
||||
- raga rock
|
||||
- psychobilly
|
||||
- psychobilly
|
||||
- punk blues
|
||||
- punk jazz
|
||||
- punk:
|
||||
- anarcho punk:
|
||||
- crust punk:
|
||||
- d-beat
|
||||
- art punk
|
||||
- christian punk
|
||||
- deathrock
|
||||
- garage punk
|
||||
- skate punk
|
||||
- punta
|
||||
- punta rock
|
||||
- r&b:
|
||||
- rhythm and blues
|
||||
- ragga:
|
||||
- raggamuffin
|
||||
- ragini
|
||||
- ragtime
|
||||
- rap rock:
|
||||
- rap metal
|
||||
- rapcore
|
||||
- rasin
|
||||
- rave:
|
||||
- rave music
|
||||
- raï
|
||||
- red dirt
|
||||
- reggae:
|
||||
- 2 tone
|
||||
- reggae en español:
|
||||
- reggae 110
|
||||
- reggae bultrón
|
||||
- romantic flow
|
||||
- spanish reggae
|
||||
- reggae fusion
|
||||
- reggaeton
|
||||
- requiem
|
||||
- riot grrrl
|
||||
- rock and roll
|
||||
- rock:
|
||||
- art rock
|
||||
- beat music
|
||||
- chinese rock
|
||||
- dark cabaret
|
||||
- desert rock
|
||||
- paisley underground
|
||||
- power pop
|
||||
- visual kei:
|
||||
- nagoya kei
|
||||
- rockabilly
|
||||
- rocksteady
|
||||
- roots reggae
|
||||
- sacred music:
|
||||
- cantique
|
||||
- gregorian chant
|
||||
- sadcore
|
||||
- sakara
|
||||
- salsa
|
||||
- samba
|
||||
- samba rock
|
||||
- sawt
|
||||
- screamo
|
||||
- sega
|
||||
- seggae
|
||||
- semba
|
||||
- sertanejo
|
||||
- shoegaze
|
||||
- ska
|
||||
- ska jazz
|
||||
- ska punk:
|
||||
- ska-core
|
||||
- slowcore
|
||||
- sludge metal
|
||||
- smooth jazz
|
||||
- soca
|
||||
- soft rock
|
||||
- son
|
||||
- sonata
|
||||
- soukous
|
||||
- soul
|
||||
- soul blues
|
||||
- soul jazz
|
||||
- south and southeast asian:
|
||||
- baila
|
||||
- bhangra
|
||||
- bhojpuri
|
||||
- dangdut
|
||||
- lavani
|
||||
- southern hip hop
|
||||
- southern rock
|
||||
- space age pop
|
||||
- space disco:
|
||||
- cosmic disco
|
||||
- space rock
|
||||
- speed metal
|
||||
- speedcore
|
||||
- st. louis blues
|
||||
- stoner metal
|
||||
- stoner rock
|
||||
- straight-ahead jazz
|
||||
- sufi rock
|
||||
- sung poetry
|
||||
- surf pop
|
||||
- surf rock
|
||||
- swamp blues
|
||||
- swing
|
||||
- swing house
|
||||
- symphonic metal
|
||||
- synthcore
|
||||
- synthpop
|
||||
- synthpunk
|
||||
- taarab
|
||||
- taiwanese pop
|
||||
- tech house
|
||||
- technical death metal
|
||||
- techno-folk
|
||||
- techno:
|
||||
- free tekno
|
||||
- nortec
|
||||
- schranz
|
||||
- techno-dnb
|
||||
- tecno brega
|
||||
- toytown techno
|
||||
- technopop
|
||||
- tecnobrega
|
||||
- teen pop
|
||||
- terrorcore
|
||||
- texas blues
|
||||
- texas country
|
||||
- thai pop
|
||||
- thrash metal:
|
||||
- crossover thrash
|
||||
- groove metal
|
||||
- thrash
|
||||
- thrashcore
|
||||
- timba
|
||||
- traditional country music
|
||||
- trance:
|
||||
- acid trance
|
||||
- classic trance
|
||||
- dream trance
|
||||
- hard trance
|
||||
- progressive trance
|
||||
- psychedelic trance
|
||||
- psytrance
|
||||
- tech trance
|
||||
- uplifting trance:
|
||||
- orchestral uplifting
|
||||
- vocal trance
|
||||
- trap
|
||||
- trip-hop:
|
||||
- trip hop
|
||||
- tropicalia
|
||||
- truck-driving country
|
||||
- turkish pop
|
||||
- turntablism
|
||||
- twoubadou
|
||||
- uk garage
|
||||
- uk hard house
|
||||
- us garage
|
||||
- vaporwave
|
||||
- vocal house
|
||||
- west coast blues
|
||||
- west coast hip hop:
|
||||
- chicano rap
|
||||
- jerkin'
|
||||
- western swing
|
||||
- witch house
|
||||
- world:
|
||||
- world music
|
||||
- worldbeat
|
||||
- world fusion
|
||||
- zouglou
|
||||
- zouk
|
||||
- zouk-lambada
|
||||
- zydeco
|
|
@ -1,491 +0,0 @@
|
|||
2-step
|
||||
acapella
|
||||
acid
|
||||
acid house
|
||||
acid jazz
|
||||
acid techno
|
||||
adult contemporary
|
||||
african
|
||||
african blues
|
||||
african heavy metal
|
||||
african hip hop
|
||||
afrobeat
|
||||
aggrotech
|
||||
alternative country
|
||||
alternative metal
|
||||
alternative rock
|
||||
ambient
|
||||
ambient dub
|
||||
ambient house
|
||||
ambient space jazz
|
||||
ambient techno
|
||||
american folk revival
|
||||
americana
|
||||
anison
|
||||
anti-folk
|
||||
apala
|
||||
arab pop
|
||||
asian underground
|
||||
atlanta hip hop
|
||||
australian country music
|
||||
avant-garde
|
||||
avant-garde jazz
|
||||
axé
|
||||
bachata
|
||||
baithak gana
|
||||
bakersfield sound
|
||||
balearic beat
|
||||
ballet
|
||||
baltimore club
|
||||
barbershop
|
||||
baroque pop
|
||||
baroque
|
||||
bebop
|
||||
benga
|
||||
berlin school
|
||||
big band
|
||||
big beat
|
||||
bikutsi
|
||||
black metal
|
||||
blue-eyed soul
|
||||
bluegrass
|
||||
blues
|
||||
blues country
|
||||
blues rock
|
||||
blues shouter
|
||||
bolero
|
||||
bongo flava
|
||||
boogie
|
||||
boogie-woogie
|
||||
bossa nova
|
||||
bounce music
|
||||
brazilian
|
||||
brazilian rock
|
||||
breakbeat
|
||||
breakcore
|
||||
brega
|
||||
british blues
|
||||
british folk revival
|
||||
britpop
|
||||
bubblegum pop
|
||||
c-pop
|
||||
cajun
|
||||
calypso
|
||||
canadian blues
|
||||
cantata
|
||||
cape jazz
|
||||
celtic music
|
||||
chamber jazz
|
||||
chamber music
|
||||
chanson
|
||||
chicago blues
|
||||
chicago house
|
||||
chillwave
|
||||
chimurenga
|
||||
chiptune
|
||||
choro
|
||||
christian country music
|
||||
christian hip hop
|
||||
christian metal
|
||||
christian pop
|
||||
christian rock
|
||||
chutney
|
||||
chutney soca
|
||||
classic country
|
||||
classical crossover
|
||||
classical
|
||||
close harmony
|
||||
coldwave
|
||||
comedy
|
||||
compas
|
||||
computer music
|
||||
concerto
|
||||
contemporary folk
|
||||
contemporary r&b
|
||||
continental jazz
|
||||
cool jazz
|
||||
country
|
||||
country blues
|
||||
country pop
|
||||
country rap
|
||||
country rock
|
||||
country soul
|
||||
country-rap
|
||||
coupé-décalé
|
||||
cowpunk
|
||||
crunkcore
|
||||
cybergrind
|
||||
dance-punk
|
||||
dance-rock
|
||||
dancehall
|
||||
dansband music
|
||||
dark ambient
|
||||
dark electro
|
||||
darkwave
|
||||
death industrial
|
||||
death metal
|
||||
deconstructed club
|
||||
deep house
|
||||
deep techno
|
||||
delta blues
|
||||
detroit blues
|
||||
detroit techno
|
||||
digital hardcore
|
||||
disco
|
||||
diva house
|
||||
dixieland
|
||||
doo wop
|
||||
doom metal
|
||||
doomcore
|
||||
downtempo
|
||||
dream pop
|
||||
drone metal
|
||||
drone
|
||||
drum and bass
|
||||
dub poetry
|
||||
dub techno
|
||||
dub
|
||||
dubstep
|
||||
dubtronica
|
||||
dungeon synth
|
||||
dutch house
|
||||
east coast hip hop
|
||||
easy listening
|
||||
ebm
|
||||
edm
|
||||
electric blues
|
||||
electro
|
||||
electro house
|
||||
electro-grime
|
||||
electro-industrial
|
||||
electroacoustic
|
||||
electroclash
|
||||
electrofunk
|
||||
electronic rock
|
||||
electropop
|
||||
electropunk
|
||||
emo
|
||||
enka
|
||||
eurodance
|
||||
europop
|
||||
experimental pop
|
||||
experimental rock
|
||||
experimental
|
||||
fann at-tanbura
|
||||
field recording
|
||||
fijiri
|
||||
filmi
|
||||
folk metal
|
||||
folk punk
|
||||
folk rock
|
||||
folk
|
||||
folktronica
|
||||
footwork
|
||||
forró
|
||||
fourth world
|
||||
franco-country
|
||||
freak folk
|
||||
free jazz
|
||||
freestyle house
|
||||
freestyle rap
|
||||
freestyle
|
||||
french house
|
||||
frevo
|
||||
fuji music
|
||||
funk carioca
|
||||
funk metal
|
||||
funk
|
||||
funky house
|
||||
g-funk
|
||||
gabber
|
||||
gamelan
|
||||
gangsta rap
|
||||
garage rock
|
||||
garage
|
||||
genge
|
||||
ghetto house
|
||||
ghettotech
|
||||
glam metal
|
||||
glam rock
|
||||
glitch-hop
|
||||
glitch
|
||||
goa
|
||||
gospel blues
|
||||
goth rock
|
||||
gothic metal
|
||||
grime
|
||||
grindcore
|
||||
grunge
|
||||
gulf and western
|
||||
gypsy jazz
|
||||
happy hardcore
|
||||
hard bop
|
||||
hard rock
|
||||
hardbag
|
||||
hardcore punk
|
||||
hellbilly music
|
||||
hi-nrg
|
||||
highlife
|
||||
hill country blues
|
||||
hip house
|
||||
hip-hop
|
||||
hiplife
|
||||
hokum
|
||||
hokum blues
|
||||
hong kong english pop
|
||||
honky tonk
|
||||
horror punk
|
||||
house
|
||||
houston hip hop
|
||||
idm
|
||||
illbient
|
||||
indian pop
|
||||
indie folk
|
||||
indie pop
|
||||
indie rock
|
||||
indietronica
|
||||
industrial
|
||||
industrial dance
|
||||
industrial folk
|
||||
industrial hip hop
|
||||
industrial metal
|
||||
industrial rock
|
||||
instrumental country
|
||||
instrumental hip hop
|
||||
iranian pop
|
||||
isicathamiya
|
||||
isolationism
|
||||
italo disco
|
||||
italo house
|
||||
j-pop
|
||||
jazz blues
|
||||
jazz fusion
|
||||
jazz rock
|
||||
jazz-funk
|
||||
jazz
|
||||
jit
|
||||
jump blues
|
||||
jungle
|
||||
jùjú
|
||||
k-pop
|
||||
kansas city blues
|
||||
kansas city jazz
|
||||
kapuka
|
||||
kayōkyoku
|
||||
khaliji
|
||||
kizomba
|
||||
kosmische
|
||||
kuduro
|
||||
kwaito
|
||||
kwela
|
||||
lambada
|
||||
latin house
|
||||
latin jazz
|
||||
latin
|
||||
live coding
|
||||
liwa
|
||||
lo-fi
|
||||
louisiana blues
|
||||
lounge
|
||||
lovers rock
|
||||
lowercase
|
||||
lubbock sound
|
||||
luk thung
|
||||
makossa
|
||||
maloya
|
||||
mambo
|
||||
mandopop
|
||||
manila sound
|
||||
maracatu
|
||||
marrabenta
|
||||
martial industrial
|
||||
mass
|
||||
math rock
|
||||
mbalax
|
||||
mbaqanga
|
||||
mbube
|
||||
melodic death metal
|
||||
memphis blues
|
||||
metal
|
||||
metalcore
|
||||
mexican pop
|
||||
miami bass
|
||||
minimal house
|
||||
minimal techno
|
||||
minimal wave
|
||||
modal jazz
|
||||
modern classical
|
||||
morlam
|
||||
morna
|
||||
museve
|
||||
musique concrète
|
||||
méringue
|
||||
música popular brasileira
|
||||
música sertaneja
|
||||
nashville sound
|
||||
neo soul
|
||||
neo-bop jazz
|
||||
neo-psychedelia
|
||||
neo-swing
|
||||
neofolk
|
||||
neotraditional country
|
||||
nerdcore
|
||||
new age
|
||||
new wave
|
||||
no wave
|
||||
noise pop
|
||||
noise rock
|
||||
noise
|
||||
northern soul
|
||||
nu metal
|
||||
onkyokei
|
||||
opera
|
||||
oratorio
|
||||
organum
|
||||
outlaw country
|
||||
p-funk
|
||||
pagode
|
||||
palm-wine
|
||||
piano blues
|
||||
piedmont blues
|
||||
pinoy pop
|
||||
pop punk
|
||||
pop rock
|
||||
pop sunda
|
||||
pop
|
||||
post-bop
|
||||
post-disco
|
||||
post-hardcore
|
||||
post-punk
|
||||
post-punk revival
|
||||
post-rock
|
||||
power electronics
|
||||
power metal
|
||||
power noise
|
||||
powerviolence
|
||||
progressive country
|
||||
progressive electronic
|
||||
progressive folk
|
||||
progressive house
|
||||
progressive metal
|
||||
progressive rock
|
||||
psychedelic folk
|
||||
psychedelic pop
|
||||
psychedelic rock
|
||||
psychobilly
|
||||
psychobilly
|
||||
punk blues
|
||||
punk jazz
|
||||
punk
|
||||
punta
|
||||
punta rock
|
||||
r&b
|
||||
ragga
|
||||
ragini
|
||||
ragtime
|
||||
rap rock
|
||||
rasin
|
||||
rave
|
||||
raï
|
||||
red dirt
|
||||
reggae
|
||||
reggaeton
|
||||
requiem
|
||||
riot grrrl
|
||||
rock and roll
|
||||
rock
|
||||
rockabilly
|
||||
rocksteady
|
||||
roots reggae
|
||||
sacred music
|
||||
sadcore
|
||||
sakara
|
||||
salsa
|
||||
samba
|
||||
samba rock
|
||||
sawt
|
||||
screamo
|
||||
sega
|
||||
seggae
|
||||
semba
|
||||
sertanejo
|
||||
shoegaze
|
||||
ska
|
||||
ska jazz
|
||||
ska punk
|
||||
slowcore
|
||||
sludge metal
|
||||
smooth jazz
|
||||
soca
|
||||
soft rock
|
||||
son
|
||||
sonata
|
||||
soukous
|
||||
soul
|
||||
soul blues
|
||||
soul jazz
|
||||
south and southeast asian
|
||||
southern hip hop
|
||||
southern rock
|
||||
space age pop
|
||||
space disco
|
||||
space rock
|
||||
speed metal
|
||||
speedcore
|
||||
st. louis blues
|
||||
stoner metal
|
||||
stoner rock
|
||||
straight-ahead jazz
|
||||
sufi rock
|
||||
sung poetry
|
||||
surf pop
|
||||
surf rock
|
||||
swamp blues
|
||||
swing
|
||||
swing house
|
||||
symphonic metal
|
||||
synthcore
|
||||
synthpop
|
||||
synthpunk
|
||||
taarab
|
||||
taiwanese pop
|
||||
tech house
|
||||
technical death metal
|
||||
techno-folk
|
||||
techno
|
||||
technopop
|
||||
tecnobrega
|
||||
teen pop
|
||||
terrorcore
|
||||
texas blues
|
||||
texas country
|
||||
thai pop
|
||||
thrash metal
|
||||
thrashcore
|
||||
timba
|
||||
traditional country music
|
||||
trance
|
||||
trap
|
||||
trip-hop
|
||||
tropicalia
|
||||
truck-driving country
|
||||
turkish pop
|
||||
turntablism
|
||||
twoubadou
|
||||
uk garage
|
||||
uk hard house
|
||||
us garage
|
||||
vaporwave
|
||||
vocal house
|
||||
west coast blues
|
||||
west coast hip hop
|
||||
western swing
|
||||
witch house
|
||||
world
|
||||
worldbeat
|
||||
world fusion
|
||||
gregorian chant
|
||||
zouglou
|
||||
zouk
|
||||
zouk-lambada
|
||||
zydeco
|
|
@ -1,20 +0,0 @@
|
|||
[options]
|
||||
background = #080808
|
||||
overlay = false
|
||||
overlay_position_bottom = true
|
||||
overlay_font = monospace:12
|
||||
overlay_text_color = #c6c6c6
|
||||
overlay_background_color = #1c1c1c
|
||||
overlay_background_alpha = ff
|
||||
|
||||
[binds]
|
||||
n = next
|
||||
p = prev
|
||||
o = overlay
|
||||
<Shift+K> = zoom 5
|
||||
<Shift+J> = zoom -5
|
||||
<Shift+R> = rotate by 90
|
||||
z = flip horizontal
|
||||
<Shift+Z> = flip vertical
|
||||
y = exec wl-copy "$imv_current_file"
|
||||
Y = exec wl-copy < "$imv_current_file"
|
|
@ -1,266 +0,0 @@
|
|||
# For further information about options in this file see:
|
||||
# https://docs.mopidy.com/
|
||||
#
|
||||
# The initial commented out values reflect the defaults as of:
|
||||
# Mopidy 3.4.1
|
||||
# Mopidy-Bandcamp 1.1.5
|
||||
# Mopidy-File 3.4.1
|
||||
# Mopidy-HTTP 3.4.1
|
||||
# Mopidy-Iris 3.65.0
|
||||
# Mopidy-Local 3.2.1
|
||||
# Mopidy-M3U 3.4.1
|
||||
# Mopidy-MPD 3.3.0
|
||||
# Mopidy-MPRIS 3.0.3
|
||||
# Mopidy-Scrobbler 2.0.1
|
||||
# Mopidy-SoftwareMixer 3.4.1
|
||||
# Mopidy-SomaFM 2.0.2
|
||||
# Mopidy-Spotify 4.1.1
|
||||
# Mopidy-Stream 3.4.1
|
||||
# Mopidy-YouTube 3.5
|
||||
#
|
||||
# Available options and defaults might have changed since then,
|
||||
# run `mopidy config` to see the current effective config and
|
||||
# `mopidy --version` to check the current version.
|
||||
|
||||
[core]
|
||||
#cache_dir = $XDG_CACHE_DIR/mopidy
|
||||
#config_dir = $XDG_CONFIG_DIR/mopidy
|
||||
#data_dir = $XDG_DATA_DIR/mopidy
|
||||
#max_tracklist_length = 10000
|
||||
#restore_state = false
|
||||
|
||||
[logging]
|
||||
#verbosity = 0
|
||||
#format = %(levelname)-8s %(asctime)s [%(process)d:%(threadName)s] %(name)s\n %(message)s
|
||||
#color = true
|
||||
#config_file =
|
||||
|
||||
[audio]
|
||||
#mixer = software
|
||||
#mixer_volume =
|
||||
#output = autoaudiosink
|
||||
#buffer_time =
|
||||
|
||||
[proxy]
|
||||
#scheme =
|
||||
#hostname =
|
||||
#port =
|
||||
#username =
|
||||
#password =
|
||||
|
||||
[spotify]
|
||||
enabled = false
|
||||
#username =
|
||||
#password =
|
||||
#client_id =
|
||||
#client_secret =
|
||||
#bitrate = 160
|
||||
#volume_normalization = true
|
||||
#private_session = false
|
||||
timeout = 5000
|
||||
#allow_cache = true
|
||||
#allow_network = true
|
||||
#allow_playlists = true
|
||||
#search_album_count = 20
|
||||
#search_artist_count = 10
|
||||
#search_track_count = 50
|
||||
#toplist_countries =
|
||||
|
||||
[iris]
|
||||
#enabled = true
|
||||
#country = NZ
|
||||
#locale = en_NZ
|
||||
#verify_certificates = true
|
||||
#snapcast_enabled = true
|
||||
#snapcast_host = localhost
|
||||
#snapcast_port = 1780
|
||||
#snapcast_ssl = false
|
||||
#snapcast_stream = Default
|
||||
#spotify_authorization_url = https://jamesbarnsley.co.nz/iris/auth_spotify.php
|
||||
#lastfm_authorization_url = https://jamesbarnsley.co.nz/iris/auth_lastfm.php
|
||||
#genius_authorization_url = https://jamesbarnsley.co.nz/iris/auth_genius.php
|
||||
#data_dir = $XDG_DATA_DIR/iris
|
||||
|
||||
[file]
|
||||
enabled = false
|
||||
#media_dirs =
|
||||
# $XDG_MUSIC_DIR|Music
|
||||
# ~/|Home
|
||||
#excluded_file_extensions =
|
||||
# .directory
|
||||
# .html
|
||||
# .jpeg
|
||||
# .jpg
|
||||
# .log
|
||||
# .nfo
|
||||
# .pdf
|
||||
# .png
|
||||
# .txt
|
||||
# .zip
|
||||
#show_dotfiles = false
|
||||
#follow_symlinks = false
|
||||
#metadata_timeout = 1000
|
||||
|
||||
[http]
|
||||
#enabled = true
|
||||
#hostname = 127.0.0.1
|
||||
#port = 6680
|
||||
#zeroconf = Mopidy HTTP server on $hostname
|
||||
#allowed_origins =
|
||||
#csrf_protection = true
|
||||
#default_app = mopidy
|
||||
|
||||
[m3u]
|
||||
#enabled = true
|
||||
#base_dir = $XDG_MUSIC_DIR
|
||||
#default_encoding = latin-1
|
||||
#default_extension = .m3u8
|
||||
#playlists_dir =
|
||||
|
||||
[softwaremixer]
|
||||
#enabled = true
|
||||
|
||||
[stream]
|
||||
#enabled = true
|
||||
#protocols =
|
||||
# http
|
||||
# https
|
||||
# mms
|
||||
# rtmp
|
||||
# rtmps
|
||||
# rtsp
|
||||
#metadata_blacklist =
|
||||
#timeout = 5000
|
||||
|
||||
[mpd]
|
||||
#enabled = true
|
||||
#hostname = 127.0.0.1
|
||||
#port = 6600
|
||||
#password =
|
||||
#max_connections = 20
|
||||
#connection_timeout = 60
|
||||
#zeroconf = Mopidy MPD server on $hostname
|
||||
#command_blacklist =
|
||||
# listall
|
||||
# listallinfo
|
||||
#default_playlist_scheme = m3u
|
||||
|
||||
[local]
|
||||
enabled = false
|
||||
#max_search_results = 100
|
||||
media_dir = $XDG_MUSIC_DIR
|
||||
scan_timeout = 5000
|
||||
#scan_flush_threshold = 100
|
||||
#scan_follow_symlinks = false
|
||||
#included_file_extensions =
|
||||
#excluded_file_extensions =
|
||||
# .cue
|
||||
# .directory
|
||||
# .html
|
||||
# .jpeg
|
||||
# .jpg
|
||||
# .log
|
||||
# .nfo
|
||||
# .pdf
|
||||
# .png
|
||||
# .txt
|
||||
# .zip
|
||||
#directories =
|
||||
# Albums local:directory?type=album
|
||||
# Artists local:directory?type=artist
|
||||
# Composers local:directory?type=artist&role=composer
|
||||
# Genres local:directory?type=genre
|
||||
# Performers local:directory?type=artist&role=performer
|
||||
# Release Years local:directory?type=date&format=%25Y
|
||||
# Tracks local:directory?type=track
|
||||
# Last Week's Updates local:directory?max-age=604800
|
||||
# Last Month's Updates local:directory?max-age=2592000
|
||||
#timeout = 10
|
||||
#use_artist_sortname = false
|
||||
#album_art_files =
|
||||
# *.jpg
|
||||
# *.jpeg
|
||||
# *.png
|
||||
|
||||
[subidy]
|
||||
url={{multimedia_mopidy_subidy_url}}
|
||||
username={{multimedia_mopidy_subidy_user}}
|
||||
password={{multimedia_mopidy_subidy_pass}}
|
||||
|
||||
[mpris]
|
||||
#enabled = true
|
||||
#bus_type = session
|
||||
|
||||
[scrobbler]
|
||||
#enabled = true
|
||||
#username =
|
||||
#password =
|
||||
|
||||
[somafm]
|
||||
#enabled = true
|
||||
#encoding = mp3
|
||||
#quality = fast
|
||||
dj_as_artist = false
|
||||
|
||||
[youtube]
|
||||
#enabled = true
|
||||
allow_cache = true
|
||||
#youtube_api_key =
|
||||
#search_results = 15
|
||||
#playlist_max_videos = 20
|
||||
#api_enabled = false
|
||||
#channel_id =
|
||||
#musicapi_enabled = false
|
||||
#musicapi_cookie =
|
||||
#autoplay_enabled = false
|
||||
#strict_autoplay = false
|
||||
#max_autoplay_length = 600
|
||||
#max_degrees_of_separation = 3
|
||||
youtube_dl_package = yt-dlp
|
||||
|
||||
[bandcamp]
|
||||
enabled = false
|
||||
#discover_pages = 1
|
||||
#collection_items = 50
|
||||
discover_genres =
|
||||
# All
|
||||
# Electronic
|
||||
# Rock
|
||||
# Metal
|
||||
# Alternative
|
||||
# Hip-Hop/Rap
|
||||
# Experimental
|
||||
# Punk
|
||||
# Folk
|
||||
# Pop
|
||||
# Ambient
|
||||
# Soundtrack
|
||||
# World
|
||||
# Jazz
|
||||
# Acoustic
|
||||
# Funk
|
||||
# R&B/Soul
|
||||
# Devotional
|
||||
# Classical
|
||||
# Reggae
|
||||
# Podcasts
|
||||
# Country
|
||||
# Spoken Word
|
||||
# Comedy
|
||||
# Blues
|
||||
# Kids
|
||||
# Audiobooks
|
||||
# Latin
|
||||
discover_tags =
|
||||
Outrun
|
||||
Future Funk
|
||||
Alternative Hip-Hop
|
||||
Cozy Synth
|
||||
Post Metal
|
||||
Post Punk
|
||||
# Tokyo, Japan
|
||||
#image_sizes =
|
||||
# 10
|
||||
# 5
|
||||
# 2
|
||||
#identity =
|
Binary file not shown.
Binary file not shown.
|
@ -1,52 +0,0 @@
|
|||
q quit
|
||||
|
||||
l seek 5
|
||||
L seek 60
|
||||
h seek -5
|
||||
H seek -60
|
||||
|
||||
p cycle pause # play/pause
|
||||
|
||||
J playlist-next
|
||||
K playlist-prev
|
||||
k add volume 5
|
||||
j add volume -5
|
||||
|
||||
+ add volume 2
|
||||
- add volume -2
|
||||
|
||||
a add speed 0.1
|
||||
A add speed -0.1
|
||||
|
||||
d add audio-delay 0.100 # for unsynched audio video
|
||||
D add audio-delay -0.100
|
||||
|
||||
u add sub-delay -0.1 # subtract 100 ms delay from subs
|
||||
U add sub-delay +0.1 # add
|
||||
|
||||
s cycle sub # choose subtitles
|
||||
S cycle sub down # choose subtitles (reverse)
|
||||
|
||||
f cycle fullscreen # toggle fullscreen
|
||||
|
||||
F5 async screenshot # take a screenshot
|
||||
Shift+F5 async screenshot video # screenshot without subtitles
|
||||
|
||||
i script-message playlistmanager show playlist toggle # toggle advanced playlist
|
||||
I script-message playlistmanager show filename
|
||||
R cycle-values video-aspect "16:9" "4:3" "3.25:1" "no" "-1" # cycle aspect ratios
|
||||
|
||||
# uosc definitions and menu
|
||||
o script-message-to uosc toggle-elements timeline
|
||||
O script-binding uosc/toggle-ui
|
||||
m script-binding uosc/menu
|
||||
|
||||
# script-binding uosc/open-file #! Open file
|
||||
# script-binding uosc/load-subtitles #! Load subtitles
|
||||
# script-binding uosc/subtitles #! Select subtitles
|
||||
# script-binding uosc/audio #! Select audio
|
||||
# async screenshot #! Utils > Screenshot
|
||||
# script-binding uosc/playlist #! Utils > Playlist
|
||||
# script-binding uosc/chapters #! Utils > Chapters
|
||||
# script-binding uosc/open-config-directory #! Utils > Open config directory
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
## mostly cobbled together from:
|
||||
## https://github.com/Tsubajashi/mpv-settings/blob/master/mpv.conf
|
||||
## https://github.com/haasn/gentoo-conf/blob/d3fd304fd7d8d816de6e280daf626b1b49a69893/home/nand/.mpv/config
|
||||
## with much gratitude
|
||||
|
||||
#### player settings
|
||||
|
||||
# do not open in fullscreen, do not create border
|
||||
fullscreen=no
|
||||
# if playing the last file in e.g. a playlist, simply pause after finishing
|
||||
keep-open=yes
|
||||
# allow remote operation through ipc, see https://mpv.io/manual/stable/#json-ipc
|
||||
input-ipc-server=/tmp/mpv-socket
|
||||
|
||||
# display a little seek bar in term
|
||||
term-osd-bar=yes
|
||||
# colorful term output
|
||||
msg-color=yes
|
||||
# add module names to term output
|
||||
msg-module=yes
|
||||
|
||||
### screenshots
|
||||
|
||||
screenshot-directory="${XDG_PICTURES_DIR:-~/media/pictures}/screenshots"
|
||||
# filename_HH-MM-SS
|
||||
screenshot-template="screen_%F_%wH-%wM-%wS"
|
||||
screenshot-format=png
|
||||
# default compression rate (0 is no compression)
|
||||
screenshot-png-compression=7
|
||||
screenshot-tag-colorspace=yes
|
||||
|
||||
### OSD
|
||||
|
||||
# 2 options necessary for uosc, see https://github.com/darsain/uosc
|
||||
osc=no
|
||||
osd-bar=no
|
||||
|
||||
osd-font='Iosevka Nerd Font'
|
||||
osd-font-size=15
|
||||
|
||||
### Subtitles
|
||||
|
||||
## options and compatibility
|
||||
# vsfilter backward compatibility
|
||||
sub-ass-vsfilter-blur-compat=yes
|
||||
sub-ass-scale-with-window=no
|
||||
# custom ass style params
|
||||
sub-ass-force-style=Kerning=yes
|
||||
# fuzzy-search available subs in folder
|
||||
sub-auto=fuzzy
|
||||
# search for external subs in these relative subdirectories
|
||||
sub-file-paths-append=ass:srt:sub:subs:subtitles
|
||||
# make sure when seeking through mkv we try to display subs
|
||||
demuxer-mkv-subtitle-preroll=yes
|
||||
# use fonts embedded in the container if available
|
||||
embeddedfonts=yes
|
||||
# don't meddle with subtitle timing by default
|
||||
sub-fix-timing=no
|
||||
# do not blend subtitles, so that we can display them beyond the video borders
|
||||
blend-subtitles=no
|
||||
|
||||
## look and style
|
||||
sub-ass-override=scale
|
||||
sub-scale=0.5
|
||||
sub-font="Liberation Sans"
|
||||
sub-font-size=70
|
||||
# put them a bit above seekbar -- not necessary imo
|
||||
# sub-margin-y=50
|
||||
sub-color="#FFFFFFFF"
|
||||
sub-border-color="#FF151515"
|
||||
sub-border-size=6
|
||||
sub-shadow-offset=1
|
||||
sub-shadow-color="#33000000"
|
||||
sub-spacing=0.5
|
||||
|
||||
### Audio
|
||||
|
||||
volume=80
|
||||
volume-max=150
|
||||
# find audio files even if slightly mismatched
|
||||
audio-file-auto=fuzzy
|
||||
# playing at different speed will pitch-correct
|
||||
audio-pitch-correction=yes
|
||||
|
||||
### Video
|
||||
|
||||
hwdec=auto
|
||||
profile=opengl-hq
|
||||
opengl-early-flush=auto
|
||||
opengl-pbo=no
|
||||
# ever so slightly up saturation
|
||||
saturation=12
|
||||
# interpolation options, will take some more cpu
|
||||
interpolation=yes
|
||||
video-sync=display-resample
|
||||
|
||||
### Cache
|
||||
|
||||
# use cache if it seems like a networked connection
|
||||
cache=auto
|
||||
|
||||
### youtube-dl setup
|
||||
|
||||
ytdl=yes
|
||||
# never go beyond 1080p if it is avoidable
|
||||
ytdl-format=(bestvideo[vcodec=vp9.2][height<=1080]/bestvideo[vcodec=vp9][fps>30][height<=1080]/bestvideo[vcodec=vp9][height<=1080]/bestvideo[fps>30][height<=1080]/bestvideo[height>720])+(bestaudio[acodec=opus]/bestaudio)/best
|
||||
|
||||
### Languages
|
||||
|
||||
# prefer english subtitles, use german if you have to
|
||||
slang=en,eng,de,deu,ger
|
||||
# prefer original language, use dub if necessary
|
||||
alang=ja,jp,jpn,en,eng,de,deu,ger
|
||||
|
||||
### Protocol configuration
|
||||
|
||||
[network]
|
||||
# don't wait for buffering to be complete to show the window
|
||||
force-window=immediate
|
||||
cache=yes
|
||||
# create a huge cache to buffer most of videos
|
||||
demuxer-max-bytes=3000MiB
|
||||
demuxer-readahead-secs=500
|
||||
|
||||
[protocol.http]
|
||||
profile=network
|
||||
|
||||
[protocol.https]
|
||||
profile=network
|
||||
|
||||
[protocol.ytdl]
|
||||
profile=network
|
||||
|
||||
[extension.gif]
|
||||
interpolation=no
|
||||
loop-file=yes
|
||||
|
||||
# for those yt playlists that are created in reverse order
|
||||
[reverse]
|
||||
ytdl-raw-options=playlist-reverse=
|
||||
|
||||
[lowquality]
|
||||
scale=bilinear
|
||||
cscale=ewa_lanczossharp
|
||||
interpolation=no
|
||||
video-sync=audio
|
||||
|
||||
[highquality]
|
||||
scale=ewa_lanczossharp
|
||||
cscale=ewa_lanczossharp
|
||||
video-sync=display-resample
|
||||
interpolation=yes
|
||||
tscale=oversample
|
||||
|
||||
# default to hq
|
||||
profile=highquality
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
#navigation keybindings force override only while playlist is visible
|
||||
#if "no" then you can display the playlist by any of the navigation keys
|
||||
dynamic_binds=yes
|
||||
|
||||
#dynamic keybind keys, they should not be re-bound in input.conf
|
||||
#to bind multiple keys separate them by a space
|
||||
key_moveup=UP
|
||||
key_movedown=DOWN
|
||||
key_selectfile=RIGHT LEFT
|
||||
key_unselectfile=
|
||||
key_playfile=ENTER
|
||||
key_removefile=BS
|
||||
key_closeplaylist=ESC
|
||||
|
||||
#Use ~ for home directory. Leave as empty to use mpv/playlists
|
||||
playlist_savepath=~/.local/share/mpv/playlists
|
||||
|
||||
#2 shows playlist, 1 shows current file(filename strip applied), 0 shows nothing
|
||||
show_playlist_on_fileload=1
|
||||
|
||||
#call youtube-dl to resolve the titles of urls in the playlist
|
||||
resolve_titles=yes
|
|
@ -1,2 +0,0 @@
|
|||
timeline_size_max=20
|
||||
timeline_size_min=1
|
|
@ -1,2 +0,0 @@
|
|||
# use fork instead
|
||||
ytdl_path=yt-dlp
|
|
@ -1,268 +0,0 @@
|
|||
-- 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
|
||||
|
||||
--]]
|
||||
|
||||
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,
|
||||
}
|
||||
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)
|
||||
if 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)
|
|
@ -1,37 +0,0 @@
|
|||
-- If the laptop is on battery, the profile 'lq' will be loaded; otherwise 'hq' is used
|
||||
local mp = require("mp")
|
||||
|
||||
local SHOULD_ADJUST = false
|
||||
|
||||
local lqprofile = "lowquality"
|
||||
local hqprofile = "highquality"
|
||||
|
||||
local function powerstate()
|
||||
local f = io.open("/sys/class/power_supply/AC/online")
|
||||
if f == nil then
|
||||
return
|
||||
end
|
||||
local t = f:read("*n")
|
||||
f:close()
|
||||
return t
|
||||
end
|
||||
|
||||
local function adjust()
|
||||
if not SHOULD_ADJUST then
|
||||
return
|
||||
end
|
||||
|
||||
local state = powerstate()
|
||||
-- this actually overrides automatically applied profiles
|
||||
-- like 'protocol.http'
|
||||
if state == 0 then
|
||||
mp.set_property("profile", lqprofile)
|
||||
mp.msg.info("[quality] running battery, setting low-quality options.")
|
||||
mp.osd_message("[quality] LQ")
|
||||
else
|
||||
mp.set_property("profile", hqprofile)
|
||||
mp.msg.info("[quality] running ac, setting high-quality options.")
|
||||
mp.osd_message("[quality] HQ")
|
||||
end
|
||||
end
|
||||
mp.add_hook("on_load", 1, adjust)
|
|
@ -1,83 +0,0 @@
|
|||
local mp = require("mp")
|
||||
require("mp.msg")
|
||||
|
||||
-- Copy the current time of the video to clipboard.
|
||||
|
||||
WINDOWS = 2
|
||||
UNIX = 3
|
||||
KEY_BIND = "y"
|
||||
|
||||
local function platform_type()
|
||||
local utils = require("mp.utils")
|
||||
local workdir = utils.to_string(mp.get_property_native("working-directory"))
|
||||
if string.find(workdir, "\\") then
|
||||
return WINDOWS
|
||||
else
|
||||
return UNIX
|
||||
end
|
||||
end
|
||||
|
||||
local function command_exists(cmd)
|
||||
local pipe = io.popen("type " .. cmd .. ' > /dev/null 2> /dev/null; printf "$?"', "r")
|
||||
if not pipe then
|
||||
return
|
||||
end
|
||||
local exists = pipe:read() == "0"
|
||||
pipe:close()
|
||||
return exists
|
||||
end
|
||||
|
||||
local function get_clipboard_cmd()
|
||||
if command_exists("xclip") then
|
||||
return "xclip -silent -in -selection clipboard"
|
||||
elseif command_exists("wl-copy") then
|
||||
return "wl-copy"
|
||||
elseif command_exists("pbcopy") then
|
||||
return "pbcopy"
|
||||
else
|
||||
mp.msg.error("No supported clipboard command found")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function divmod(a, b)
|
||||
return a / b, a % b
|
||||
end
|
||||
|
||||
local function set_clipboard(text)
|
||||
if platform == WINDOWS then
|
||||
mp.commandv("run", "powershell", "set-clipboard", text)
|
||||
return true
|
||||
elseif platform == UNIX and clipboard_cmd then
|
||||
local pipe = io.popen(clipboard_cmd, "w")
|
||||
if not pipe then
|
||||
return
|
||||
end
|
||||
pipe:write(text)
|
||||
pipe:close()
|
||||
return true
|
||||
else
|
||||
mp.msg.error("Set_clipboard error")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function copyTime()
|
||||
local time_pos = mp.get_property_number("time-pos")
|
||||
local minutes, remainder = divmod(time_pos, 60)
|
||||
local hours, minutes = divmod(minutes, 60)
|
||||
local seconds = math.floor(remainder)
|
||||
local milliseconds = math.floor((remainder - seconds) * 1000)
|
||||
local time = string.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds)
|
||||
if set_clipboard(time) then
|
||||
mp.osd_message(string.format("[copytime] %s", time))
|
||||
else
|
||||
mp.osd_message("[copytime] failed")
|
||||
end
|
||||
end
|
||||
|
||||
platform = platform_type()
|
||||
if platform == UNIX then
|
||||
clipboard_cmd = get_clipboard_cmd()
|
||||
end
|
||||
mp.add_key_binding(KEY_BIND, "copyTime", copyTime)
|
|
@ -1,33 +0,0 @@
|
|||
-- gallery-dl_hook.lua
|
||||
--
|
||||
-- load online image galleries as playlists using gallery-dl
|
||||
-- https://github.com/mikf/gallery-dl
|
||||
--
|
||||
-- to use, prepend the gallery url with: gallery-dl://
|
||||
-- e.g.
|
||||
-- `mpv gallery-dl://https://imgur.com/....`
|
||||
|
||||
local mp = require("mp")
|
||||
local utils = require("mp.utils")
|
||||
local msg = require("mp.msg")
|
||||
|
||||
local function exec(args)
|
||||
local ret = utils.subprocess({ args = args })
|
||||
return ret.status, ret.stdout, ret
|
||||
end
|
||||
|
||||
mp.add_hook("on_load", 15, function()
|
||||
local fn = mp.get_property("stream-open-filename", "")
|
||||
if fn:find("gdl://") ~= 1 then
|
||||
msg.debug("not a gdl:// url: " .. fn)
|
||||
return
|
||||
end
|
||||
local url = string.gsub(url, "gdl://", "")
|
||||
|
||||
local es, urls, result = exec({ "gallery-dl", "-g", url })
|
||||
if (es < 0) or (urls == nil) or (urls == "") then
|
||||
msg.error("failed to get album list.")
|
||||
end
|
||||
|
||||
mp.commandv("loadlist", "memory://" .. urls)
|
||||
end)
|
File diff suppressed because it is too large
Load Diff
|
@ -1,97 +0,0 @@
|
|||
-- sponsorblock_minimal.lua
|
||||
--
|
||||
-- This script skips sponsored segments of YouTube videos
|
||||
-- using data from https://github.com/ajayyy/SponsorBlock
|
||||
--
|
||||
-- original from https://codeberg.org/jouni/mpv_sponsorblock_minimal
|
||||
-- adapted for local playback skipping and some refactoring by me
|
||||
local mp = require("mp")
|
||||
|
||||
local options = {
|
||||
API = "https://sponsor.ajay.app/api/skipSegments",
|
||||
|
||||
-- Categories to fetch and skip
|
||||
categories = '"sponsor","intro","outro","interaction","selfpromo"',
|
||||
}
|
||||
|
||||
local function getranges()
|
||||
local args = {
|
||||
"curl",
|
||||
"-s",
|
||||
"-d",
|
||||
"videoID=" .. Youtube_id,
|
||||
"-d",
|
||||
"categories=[" .. options.categories .. "]",
|
||||
"-G",
|
||||
options.API,
|
||||
}
|
||||
local sponsors = mp.command_native({
|
||||
name = "subprocess",
|
||||
capture_stdout = true,
|
||||
playback_only = false,
|
||||
args = args,
|
||||
})
|
||||
|
||||
if string.match(sponsors.stdout, "%[(.-)%]") then
|
||||
Ranges = {}
|
||||
for i in string.gmatch(string.sub(sponsors.stdout, 2, -2), "%[(.-)%]") do
|
||||
local k, v = string.match(i, "(%d+.?%d*),(%d+.?%d*)")
|
||||
Ranges[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function skip_ads(_, pos)
|
||||
if pos ~= nil then
|
||||
for k, v in pairs(Ranges) do
|
||||
if tonumber(k) <= pos and tonumber(v) > pos then
|
||||
-- this message may sometimes be wrong
|
||||
-- it only seems to be a visual thing though
|
||||
mp.osd_message(
|
||||
"[sponsorblock] skipping forward " .. math.floor(tonumber(v) - mp.get_property("time-pos")) .. "s"
|
||||
)
|
||||
-- need to do the +0.01 otherwise mpv will start spamming skip sometimes
|
||||
-- example: https://www.youtube.com/watch?v=4ypMJzeNooo
|
||||
mp.set_property("time-pos", tonumber(v) + 0.01)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function file_loaded()
|
||||
local video_path = mp.get_property("path")
|
||||
local youtube_id1 = string.match(video_path, "https?://youtu%.be/([%w-_]+).*")
|
||||
local youtube_id2 = string.match(video_path, "https?://w?w?w?%.?youtube%.com/v/([%w-_]+).*")
|
||||
local youtube_id3 = string.match(video_path, "/watch.*[?&]v=([%w-_]+).*")
|
||||
local youtube_id4 = string.match(video_path, "/embed/([%w-_]+).*")
|
||||
local localytfile = string.match(video_path, "-([%a%d%-_]+)%.[mw][kpe][v4b][m]?$")
|
||||
Youtube_id = youtube_id1 or youtube_id2 or youtube_id3 or youtube_id4 or localytfile
|
||||
if not Youtube_id or string.len(Youtube_id) < 11 then
|
||||
return
|
||||
end
|
||||
Youtube_id = string.sub(Youtube_id, 1, 11)
|
||||
|
||||
getranges()
|
||||
if Ranges then
|
||||
ON = true
|
||||
mp.add_key_binding("b", "sponsorblock", toggle)
|
||||
mp.observe_property("time-pos", "native", skip_ads)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local function toggle()
|
||||
if ON then
|
||||
mp.unobserve_property(skip_ads)
|
||||
mp.osd_message("[sponsorblock] off")
|
||||
ON = false
|
||||
return
|
||||
end
|
||||
mp.observe_property("time-pos", "native", skip_ads)
|
||||
mp.osd_message("[sponsorblock] on")
|
||||
ON = true
|
||||
return
|
||||
end
|
||||
|
||||
mp.register_event("file-loaded", file_loaded)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,37 +0,0 @@
|
|||
local Element = require('uosc_shared/elements/Element')
|
||||
|
||||
---@class BufferingIndicator : Element
|
||||
local BufferingIndicator = class(Element)
|
||||
|
||||
function BufferingIndicator:new() return Class.new(self) --[[@as BufferingIndicator]] end
|
||||
function BufferingIndicator:init()
|
||||
Element.init(self, 'buffer_indicator')
|
||||
self.ignores_menu = true
|
||||
self.enabled = false
|
||||
end
|
||||
|
||||
function BufferingIndicator:decide_enabled()
|
||||
local cache = state.cache_underrun or state.cache_buffering and state.cache_buffering < 100
|
||||
local player = state.core_idle and not state.eof_reached
|
||||
if self.enabled then
|
||||
if not player or (state.pause and not cache) then self.enabled = false end
|
||||
elseif player and cache and state.uncached_ranges then self.enabled = true end
|
||||
end
|
||||
|
||||
function BufferingIndicator:on_prop_pause() self:decide_enabled() end
|
||||
function BufferingIndicator:on_prop_core_idle() self:decide_enabled() end
|
||||
function BufferingIndicator:on_prop_eof_reached() self:decide_enabled() end
|
||||
function BufferingIndicator:on_prop_uncached_ranges() self:decide_enabled() end
|
||||
function BufferingIndicator:on_prop_cache_buffering() self:decide_enabled() end
|
||||
function BufferingIndicator:on_prop_cache_underrun() self:decide_enabled() end
|
||||
|
||||
function BufferingIndicator:render()
|
||||
local ass = assdraw.ass_new()
|
||||
ass:rect(0, 0, display.width, display.height, {color = bg, opacity = 0.3})
|
||||
local size = round(30 + math.min(display.width, display.height) / 10)
|
||||
local opacity = (Elements.menu and not Elements.menu.is_closing) and 0.3 or 0.8
|
||||
ass:spinner(display.width / 2, display.height / 2, size, {color = fg, opacity = opacity})
|
||||
return ass
|
||||
end
|
||||
|
||||
return BufferingIndicator
|
|
@ -1,90 +0,0 @@
|
|||
local Element = require('uosc_shared/elements/Element')
|
||||
|
||||
---@alias ButtonProps {icon: string; on_click: function; anchor_id?: string; active?: boolean; badge?: string|number; foreground?: string; background?: string; tooltip?: string}
|
||||
|
||||
---@class Button : Element
|
||||
local Button = class(Element)
|
||||
|
||||
---@param id string
|
||||
---@param props ButtonProps
|
||||
function Button:new(id, props) return Class.new(self, id, props) --[[@as Button]] end
|
||||
---@param id string
|
||||
---@param props ButtonProps
|
||||
function Button:init(id, props)
|
||||
self.icon = props.icon
|
||||
self.active = props.active
|
||||
self.tooltip = props.tooltip
|
||||
self.badge = props.badge
|
||||
self.foreground = props.foreground or fg
|
||||
self.background = props.background or bg
|
||||
---@type fun()
|
||||
self.on_click = props.on_click
|
||||
Element.init(self, id, props)
|
||||
end
|
||||
|
||||
function Button:on_coordinates() self.font_size = round((self.by - self.ay) * 0.7) end
|
||||
function Button:handle_cursor_down()
|
||||
-- We delay the callback to next tick, otherwise we are risking race
|
||||
-- conditions as we are in the middle of event dispatching.
|
||||
-- For example, handler might add a menu to the end of the element stack, and that
|
||||
-- than picks up this click event we are in right now, and instantly closes itself.
|
||||
mp.add_timeout(0.01, self.on_click)
|
||||
end
|
||||
|
||||
function Button:render()
|
||||
local visibility = self:get_visibility()
|
||||
if visibility <= 0 then return end
|
||||
if self.proximity_raw == 0 then
|
||||
cursor.on_primary_down = function() self:handle_cursor_down() end
|
||||
end
|
||||
|
||||
local ass = assdraw.ass_new()
|
||||
local is_hover = self.proximity_raw == 0
|
||||
local is_hover_or_active = is_hover or self.active
|
||||
local foreground = self.active and self.background or self.foreground
|
||||
local background = self.active and self.foreground or self.background
|
||||
|
||||
-- Background
|
||||
if is_hover_or_active then
|
||||
ass:rect(self.ax, self.ay, self.bx, self.by, {
|
||||
color = self.active and background or foreground, radius = 2,
|
||||
opacity = visibility * (self.active and 1 or 0.3),
|
||||
})
|
||||
end
|
||||
|
||||
-- Tooltip on hover
|
||||
if is_hover and self.tooltip then ass:tooltip(self, self.tooltip) end
|
||||
|
||||
-- Badge
|
||||
local icon_clip
|
||||
if self.badge then
|
||||
local badge_font_size = self.font_size * 0.6
|
||||
local badge_opts = {size = badge_font_size, color = background, opacity = visibility}
|
||||
local badge_width = text_width(self.badge, badge_opts)
|
||||
local width, height = math.ceil(badge_width + (badge_font_size / 7) * 2), math.ceil(badge_font_size * 0.93)
|
||||
local bx, by = self.bx - 1, self.by - 1
|
||||
ass:rect(bx - width, by - height, bx, by, {
|
||||
color = foreground, radius = 2, opacity = visibility,
|
||||
border = self.active and 0 or 1, border_color = background,
|
||||
})
|
||||
ass:txt(bx - width / 2, by - height / 2, 5, self.badge, badge_opts)
|
||||
|
||||
local clip_border = math.max(self.font_size / 20, 1)
|
||||
local clip_path = assdraw.ass_new()
|
||||
clip_path:round_rect_cw(
|
||||
math.floor((bx - width) - clip_border), math.floor((by - height) - clip_border), bx, by, 3
|
||||
)
|
||||
icon_clip = '\\iclip(' .. clip_path.scale .. ', ' .. clip_path.text .. ')'
|
||||
end
|
||||
|
||||
-- Icon
|
||||
local x, y = round(self.ax + (self.bx - self.ax) / 2), round(self.ay + (self.by - self.ay) / 2)
|
||||
ass:icon(x, y, self.font_size, self.icon, {
|
||||
color = foreground, border = self.active and 0 or options.text_border, border_color = background,
|
||||
opacity = visibility, clip = icon_clip,
|
||||
})
|
||||
|
||||
return ass
|
||||
end
|
||||
|
||||
return Button
|
|
@ -1,329 +0,0 @@
|
|||
local Element = require('uosc_shared/elements/Element')
|
||||
local Button = require('uosc_shared/elements/Button')
|
||||
local CycleButton = require('uosc_shared/elements/CycleButton')
|
||||
local Speed = require('uosc_shared/elements/Speed')
|
||||
|
||||
-- `scale` - `options.controls_size` scale factor.
|
||||
-- `ratio` - Width/height ratio of a static or dynamic element.
|
||||
-- `ratio_min` Min ratio for 'dynamic' sized element.
|
||||
---@alias ControlItem {element?: Element; kind: string; sizing: 'space' | 'static' | 'dynamic'; scale: number; ratio?: number; ratio_min?: number; hide: boolean; dispositions?: table<string, boolean>}
|
||||
|
||||
---@class Controls : Element
|
||||
local Controls = class(Element)
|
||||
|
||||
function Controls:new() return Class.new(self) --[[@as Controls]] end
|
||||
function Controls:init()
|
||||
Element.init(self, 'controls')
|
||||
---@type ControlItem[] All control elements serialized from `options.controls`.
|
||||
self.controls = {}
|
||||
---@type ControlItem[] Only controls that match current dispositions.
|
||||
self.layout = {}
|
||||
|
||||
-- Serialize control elements
|
||||
local shorthands = {
|
||||
menu = 'command:menu:script-binding uosc/menu-blurred?Menu',
|
||||
subtitles = 'command:subtitles:script-binding uosc/subtitles#sub>0?Subtitles',
|
||||
audio = 'command:graphic_eq:script-binding uosc/audio#audio>1?Audio',
|
||||
['audio-device'] = 'command:speaker:script-binding uosc/audio-device?Audio device',
|
||||
video = 'command:theaters:script-binding uosc/video#video>1?Video',
|
||||
playlist = 'command:list_alt:script-binding uosc/playlist?Playlist',
|
||||
chapters = 'command:bookmark:script-binding uosc/chapters#chapters>0?Chapters',
|
||||
['editions'] = 'command:bookmarks:script-binding uosc/editions#editions>1?Editions',
|
||||
['stream-quality'] = 'command:high_quality:script-binding uosc/stream-quality?Stream quality',
|
||||
['open-file'] = 'command:file_open:script-binding uosc/open-file?Open file',
|
||||
['items'] = 'command:list_alt:script-binding uosc/items?Playlist/Files',
|
||||
prev = 'command:arrow_back_ios:script-binding uosc/prev?Previous',
|
||||
next = 'command:arrow_forward_ios:script-binding uosc/next?Next',
|
||||
first = 'command:first_page:script-binding uosc/first?First',
|
||||
last = 'command:last_page:script-binding uosc/last?Last',
|
||||
['loop-playlist'] = 'cycle:repeat:loop-playlist:no/inf!?Loop playlist',
|
||||
['loop-file'] = 'cycle:repeat_one:loop-file:no/inf!?Loop file',
|
||||
shuffle = 'toggle:shuffle:shuffle?Shuffle',
|
||||
fullscreen = 'cycle:crop_free:fullscreen:no/yes=fullscreen_exit!?Fullscreen',
|
||||
}
|
||||
|
||||
-- Parse out disposition/config pairs
|
||||
local items = {}
|
||||
local in_disposition = false
|
||||
local current_item = nil
|
||||
for c in options.controls:gmatch('.') do
|
||||
if not current_item then current_item = {disposition = '', config = ''} end
|
||||
if c == '<' and #current_item.config == 0 then in_disposition = true
|
||||
elseif c == '>' and #current_item.config == 0 then in_disposition = false
|
||||
elseif c == ',' and not in_disposition then
|
||||
items[#items + 1] = current_item
|
||||
current_item = nil
|
||||
else
|
||||
local prop = in_disposition and 'disposition' or 'config'
|
||||
current_item[prop] = current_item[prop] .. c
|
||||
end
|
||||
end
|
||||
items[#items + 1] = current_item
|
||||
|
||||
-- Create controls
|
||||
self.controls = {}
|
||||
for i, item in ipairs(items) do
|
||||
local config = shorthands[item.config] and shorthands[item.config] or item.config
|
||||
local config_tooltip = split(config, ' *%? *')
|
||||
local tooltip = config_tooltip[2]
|
||||
config = shorthands[config_tooltip[1]]
|
||||
and split(shorthands[config_tooltip[1]], ' *%? *')[1] or config_tooltip[1]
|
||||
local config_badge = split(config, ' *# *')
|
||||
config = config_badge[1]
|
||||
local badge = config_badge[2]
|
||||
local parts = split(config, ' *: *')
|
||||
local kind, params = parts[1], itable_slice(parts, 2)
|
||||
|
||||
-- Serialize dispositions
|
||||
local dispositions = {}
|
||||
for _, definition in ipairs(split(item.disposition, ' *, *')) do
|
||||
if #definition > 0 then
|
||||
local value = definition:sub(1, 1) ~= '!'
|
||||
local name = not value and definition:sub(2) or definition
|
||||
local prop = name:sub(1, 4) == 'has_' and name or 'is_' .. name
|
||||
dispositions[prop] = value
|
||||
end
|
||||
end
|
||||
|
||||
-- Convert toggles into cycles
|
||||
if kind == 'toggle' then
|
||||
kind = 'cycle'
|
||||
params[#params + 1] = 'no/yes!'
|
||||
end
|
||||
|
||||
-- Create a control element
|
||||
local control = {dispositions = dispositions, kind = kind}
|
||||
|
||||
if kind == 'space' then
|
||||
control.sizing = 'space'
|
||||
elseif kind == 'gap' then
|
||||
table_assign(control, {sizing = 'dynamic', scale = 1, ratio = params[1] or 0.3, ratio_min = 0})
|
||||
elseif kind == 'command' then
|
||||
if #params ~= 2 then
|
||||
mp.error(string.format(
|
||||
'command button needs 2 parameters, %d received: %s', #params, table.concat(params, '/')
|
||||
))
|
||||
else
|
||||
local element = Button:new('control_' .. i, {
|
||||
icon = params[1],
|
||||
anchor_id = 'controls',
|
||||
on_click = function() mp.command(params[2]) end,
|
||||
tooltip = tooltip,
|
||||
count_prop = 'sub',
|
||||
})
|
||||
table_assign(control, {element = element, sizing = 'static', scale = 1, ratio = 1})
|
||||
if badge then self:register_badge_updater(badge, element) end
|
||||
end
|
||||
elseif kind == 'cycle' then
|
||||
if #params ~= 3 then
|
||||
mp.error(string.format(
|
||||
'cycle button needs 3 parameters, %d received: %s',
|
||||
#params, table.concat(params, '/')
|
||||
))
|
||||
else
|
||||
local state_configs = split(params[3], ' */ *')
|
||||
local states = {}
|
||||
|
||||
for _, state_config in ipairs(state_configs) do
|
||||
local active = false
|
||||
if state_config:sub(-1) == '!' then
|
||||
active = true
|
||||
state_config = state_config:sub(1, -2)
|
||||
end
|
||||
local state_params = split(state_config, ' *= *')
|
||||
local value, icon = state_params[1], state_params[2] or params[1]
|
||||
states[#states + 1] = {value = value, icon = icon, active = active}
|
||||
end
|
||||
|
||||
local element = CycleButton:new('control_' .. i, {
|
||||
prop = params[2], anchor_id = 'controls', states = states, tooltip = tooltip,
|
||||
})
|
||||
table_assign(control, {element = element, sizing = 'static', scale = 1, ratio = 1})
|
||||
if badge then self:register_badge_updater(badge, element) end
|
||||
end
|
||||
elseif kind == 'speed' then
|
||||
if not Elements.speed then
|
||||
local element = Speed:new({anchor_id = 'controls'})
|
||||
table_assign(control, {
|
||||
element = element, sizing = 'dynamic', scale = params[1] or 1.3, ratio = 3.5, ratio_min = 2,
|
||||
})
|
||||
else
|
||||
msg.error('there can only be 1 speed slider')
|
||||
end
|
||||
else
|
||||
msg.error('unknown element kind "' .. kind .. '"')
|
||||
break
|
||||
end
|
||||
|
||||
self.controls[#self.controls + 1] = control
|
||||
end
|
||||
|
||||
self:reflow()
|
||||
end
|
||||
|
||||
function Controls:reflow()
|
||||
-- Populate the layout only with items that match current disposition
|
||||
self.layout = {}
|
||||
for _, control in ipairs(self.controls) do
|
||||
local matches = true
|
||||
for prop, value in pairs(control.dispositions) do
|
||||
if state[prop] ~= value then
|
||||
matches = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if control.element then control.element.enabled = matches end
|
||||
if matches then self.layout[#self.layout + 1] = control end
|
||||
end
|
||||
|
||||
self:update_dimensions()
|
||||
Elements:trigger('controls_reflow')
|
||||
end
|
||||
|
||||
---@param badge string
|
||||
---@param element Element An element that supports `badge` property.
|
||||
function Controls:register_badge_updater(badge, element)
|
||||
local prop_and_limit = split(badge, ' *> *')
|
||||
local prop, limit = prop_and_limit[1], tonumber(prop_and_limit[2] or -1)
|
||||
local observable_name, serializer, is_external_prop = prop, nil, false
|
||||
|
||||
if itable_index_of({'sub', 'audio', 'video'}, prop) then
|
||||
observable_name = 'track-list'
|
||||
serializer = function(value)
|
||||
local count = 0
|
||||
for _, track in ipairs(value) do if track.type == prop then count = count + 1 end end
|
||||
return count
|
||||
end
|
||||
else
|
||||
local parts = split(prop, '@')
|
||||
-- Support both new `prop@owner` and old `@prop` syntaxes
|
||||
if #parts > 1 then prop, is_external_prop = parts[1] ~= '' and parts[1] or parts[2], true end
|
||||
serializer = function(value) return value and (type(value) == 'table' and #value or tostring(value)) or nil end
|
||||
end
|
||||
|
||||
local function handler(_, value)
|
||||
local new_value = serializer(value) --[[@as nil|string|integer]]
|
||||
local value_number = tonumber(new_value)
|
||||
if value_number then new_value = value_number > limit and value_number or nil end
|
||||
element.badge = new_value
|
||||
request_render()
|
||||
end
|
||||
|
||||
if is_external_prop then element['on_external_prop_' .. prop] = function(_, value) handler(prop, value) end
|
||||
else mp.observe_property(observable_name, 'native', handler) end
|
||||
end
|
||||
|
||||
function Controls:get_visibility()
|
||||
return (Elements.speed and Elements.speed.dragging) and 1 or Elements.timeline:get_is_hovered()
|
||||
and -1 or Element.get_visibility(self)
|
||||
end
|
||||
|
||||
function Controls:update_dimensions()
|
||||
local window_border = Elements.window_border.size
|
||||
local size = state.fullormaxed and options.controls_size_fullscreen or options.controls_size
|
||||
local spacing = options.controls_spacing
|
||||
local margin = options.controls_margin
|
||||
|
||||
-- Disable when not enough space
|
||||
local available_space = display.height - Elements.window_border.size * 2
|
||||
if Elements.top_bar.enabled then available_space = available_space - Elements.top_bar.size end
|
||||
if Elements.timeline.enabled then available_space = available_space - Elements.timeline.size_max end
|
||||
self.enabled = available_space > size + 10
|
||||
|
||||
-- Reset hide/enabled flags
|
||||
for c, control in ipairs(self.layout) do
|
||||
control.hide = false
|
||||
if control.element then control.element.enabled = self.enabled end
|
||||
end
|
||||
|
||||
if not self.enabled then return end
|
||||
|
||||
-- Container
|
||||
self.bx = display.width - window_border - margin
|
||||
self.by = (Elements.timeline.enabled and Elements.timeline.ay or display.height - window_border) - margin
|
||||
self.ax, self.ay = window_border + margin, self.by - size
|
||||
|
||||
-- Controls
|
||||
local available_width = self.bx - self.ax
|
||||
local statics_width = (#self.layout - 1) * spacing
|
||||
local min_content_width = statics_width
|
||||
local max_dynamics_width, dynamic_units, spaces = 0, 0, 0
|
||||
|
||||
-- Calculate statics_width, min_content_width, and count spaces
|
||||
for c, control in ipairs(self.layout) do
|
||||
if control.sizing == 'space' then
|
||||
spaces = spaces + 1
|
||||
elseif control.sizing == 'static' then
|
||||
local width = size * control.scale * control.ratio
|
||||
statics_width = statics_width + width
|
||||
min_content_width = min_content_width + width
|
||||
elseif control.sizing == 'dynamic' then
|
||||
min_content_width = min_content_width + size * control.scale * control.ratio_min
|
||||
max_dynamics_width = max_dynamics_width + size * control.scale * control.ratio
|
||||
dynamic_units = dynamic_units + control.scale * control.ratio
|
||||
end
|
||||
end
|
||||
|
||||
-- Hide & disable elements in the middle until we fit into available width
|
||||
if min_content_width > available_width then
|
||||
local i = math.ceil(#self.layout / 2 + 0.1)
|
||||
for a = 0, #self.layout - 1, 1 do
|
||||
i = i + (a * (a % 2 == 0 and 1 or -1))
|
||||
local control = self.layout[i]
|
||||
|
||||
if control.kind ~= 'gap' and control.kind ~= 'space' then
|
||||
control.hide = true
|
||||
if control.element then control.element.enabled = false end
|
||||
if control.sizing == 'static' then
|
||||
local width = size * control.scale * control.ratio
|
||||
min_content_width = min_content_width - width - spacing
|
||||
statics_width = statics_width - width - spacing
|
||||
elseif control.sizing == 'dynamic' then
|
||||
min_content_width = min_content_width - size * control.scale * control.ratio_min - spacing
|
||||
max_dynamics_width = max_dynamics_width - size * control.scale * control.ratio
|
||||
dynamic_units = dynamic_units - control.scale * control.ratio
|
||||
end
|
||||
|
||||
if min_content_width < available_width then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Lay out the elements
|
||||
local current_x = self.ax
|
||||
local width_for_dynamics = available_width - statics_width
|
||||
local space_width = (width_for_dynamics - max_dynamics_width) / spaces
|
||||
|
||||
for c, control in ipairs(self.layout) do
|
||||
if not control.hide then
|
||||
local sizing, element, scale, ratio = control.sizing, control.element, control.scale, control.ratio
|
||||
local width, height = 0, 0
|
||||
|
||||
if sizing == 'space' then
|
||||
if space_width > 0 then width = space_width end
|
||||
elseif sizing == 'static' then
|
||||
height = size * scale
|
||||
width = height * ratio
|
||||
elseif sizing == 'dynamic' then
|
||||
height = size * scale
|
||||
width = max_dynamics_width < width_for_dynamics
|
||||
and height * ratio or width_for_dynamics * ((scale * ratio) / dynamic_units)
|
||||
end
|
||||
|
||||
local bx = current_x + width
|
||||
if element then element:set_coordinates(round(current_x), round(self.by - height), bx, self.by) end
|
||||
current_x = bx + spacing
|
||||
end
|
||||
end
|
||||
|
||||
Elements:update_proximities()
|
||||
request_render()
|
||||
end
|
||||
|
||||
function Controls:on_dispositions() self:reflow() end
|
||||
function Controls:on_display() self:update_dimensions() end
|
||||
function Controls:on_prop_border() self:update_dimensions() end
|
||||
function Controls:on_prop_fullormaxed() self:update_dimensions() end
|
||||
function Controls:on_timeline_enabled() self:update_dimensions() end
|
||||
|
||||
return Controls
|
|
@ -1,35 +0,0 @@
|
|||
local Element = require('uosc_shared/elements/Element')
|
||||
|
||||
---@class Curtain : Element
|
||||
local Curtain = class(Element)
|
||||
|
||||
function Curtain:new() return Class.new(self) --[[@as Curtain]] end
|
||||
function Curtain:init()
|
||||
Element.init(self, 'curtain', {ignores_menu = true})
|
||||
self.opacity = 0
|
||||
---@type string[]
|
||||
self.dependents = {}
|
||||
end
|
||||
|
||||
---@param id string
|
||||
function Curtain:register(id)
|
||||
self.dependents[#self.dependents + 1] = id
|
||||
if #self.dependents == 1 then self:tween_property('opacity', self.opacity, 1) end
|
||||
end
|
||||
|
||||
---@param id string
|
||||
function Curtain:unregister(id)
|
||||
self.dependents = itable_filter(self.dependents, function(item) return item ~= id end)
|
||||
if #self.dependents == 0 then self:tween_property('opacity', self.opacity, 0) end
|
||||
end
|
||||
|
||||
function Curtain:render()
|
||||
if self.opacity == 0 or options.curtain_opacity == 0 then return end
|
||||
local ass = assdraw.ass_new()
|
||||
ass:rect(0, 0, display.width, display.height, {
|
||||
color = '000000', opacity = options.curtain_opacity * self.opacity,
|
||||
})
|
||||
return ass
|
||||
end
|
||||
|
||||
return Curtain
|
|
@ -1,64 +0,0 @@
|
|||
local Button = require('uosc_shared/elements/Button')
|
||||
|
||||
---@alias CycleState {value: any; icon: string; active?: boolean}
|
||||
---@alias CycleButtonProps {prop: string; states: CycleState[]; anchor_id?: string; tooltip?: string}
|
||||
|
||||
---@class CycleButton : Button
|
||||
local CycleButton = class(Button)
|
||||
|
||||
---@param id string
|
||||
---@param props CycleButtonProps
|
||||
function CycleButton:new(id, props) return Class.new(self, id, props) --[[@as CycleButton]] end
|
||||
---@param id string
|
||||
---@param props CycleButtonProps
|
||||
function CycleButton:init(id, props)
|
||||
local is_state_prop = itable_index_of({'shuffle'}, props.prop)
|
||||
self.prop = props.prop
|
||||
self.states = props.states
|
||||
|
||||
Button.init(self, id, props)
|
||||
|
||||
self.icon = self.states[1].icon
|
||||
self.active = self.states[1].active
|
||||
self.current_state_index = 1
|
||||
self.on_click = function()
|
||||
local new_state = self.states[self.current_state_index + 1] or self.states[1]
|
||||
local new_value = new_state.value
|
||||
if self.owner then
|
||||
mp.commandv('script-message-to', self.owner, 'set', self.prop, new_value)
|
||||
elseif is_state_prop then
|
||||
if itable_index_of({'yes', 'no'}, new_value) then new_value = new_value == 'yes' end
|
||||
set_state(self.prop, new_value)
|
||||
else
|
||||
mp.set_property(self.prop, new_value)
|
||||
end
|
||||
end
|
||||
|
||||
self.handle_change = function(name, value)
|
||||
if is_state_prop and type(value) == 'boolean' then value = value and 'yes' or 'no' end
|
||||
local index = itable_find(self.states, function(state) return state.value == value end)
|
||||
self.current_state_index = index or 1
|
||||
self.icon = self.states[self.current_state_index].icon
|
||||
self.active = self.states[self.current_state_index].active
|
||||
request_render()
|
||||
end
|
||||
|
||||
local prop_parts = split(self.prop, '@')
|
||||
if #prop_parts == 2 then -- External prop with a script owner
|
||||
self.prop, self.owner = prop_parts[1], prop_parts[2]
|
||||
self['on_external_prop_' .. self.prop] = function(_, value) self.handle_change(self.prop, value) end
|
||||
self.handle_change(self.prop, external[self.prop])
|
||||
elseif is_state_prop then -- uosc's state props
|
||||
self['on_prop_' .. self.prop] = function(self, value) self.handle_change(self.prop, value) end
|
||||
self.handle_change(self.prop, state[self.prop])
|
||||
else
|
||||
mp.observe_property(self.prop, 'string', self.handle_change)
|
||||
end
|
||||
end
|
||||
|
||||
function CycleButton:destroy()
|
||||
Button.destroy(self)
|
||||
mp.unobserve_property(self.handle_change)
|
||||
end
|
||||
|
||||
return CycleButton
|
|
@ -1,154 +0,0 @@
|
|||
---@alias ElementProps {enabled?: boolean; ax?: number; ay?: number; bx?: number; by?: number; ignores_menu?: boolean; anchor_id?: string;}
|
||||
|
||||
-- Base class all elements inherit from.
|
||||
---@class Element : Class
|
||||
local Element = class()
|
||||
|
||||
---@param id string
|
||||
---@param props? ElementProps
|
||||
function Element:init(id, props)
|
||||
self.id = id
|
||||
-- `false` means element won't be rendered, or receive events
|
||||
self.enabled = true
|
||||
-- Element coordinates
|
||||
self.ax, self.ay, self.bx, self.by = 0, 0, 0, 0
|
||||
-- Relative proximity from `0` - mouse outside `proximity_max` range, to `1` - mouse within `proximity_min` range.
|
||||
self.proximity = 0
|
||||
-- Raw proximity in pixels.
|
||||
self.proximity_raw = INFINITY
|
||||
---@type number `0-1` factor to force min visibility. Used for toggling element's permanent visibility.
|
||||
self.min_visibility = 0
|
||||
---@type number `0-1` factor to force a visibility value. Used for flashing, fading out, and other animations
|
||||
self.forced_visibility = nil
|
||||
---@type boolean Render this element even when menu is open.
|
||||
self.ignores_menu = false
|
||||
---@type nil|string ID of an element from which this one should inherit visibility.
|
||||
self.anchor_id = nil
|
||||
|
||||
if props then table_assign(self, props) end
|
||||
|
||||
-- Flash timer
|
||||
self._flash_out_timer = mp.add_timeout(options.flash_duration / 1000, function()
|
||||
local function getTo() return self.proximity end
|
||||
local function onTweenEnd() self.forced_visibility = nil end
|
||||
if self.enabled then self:tween_property('forced_visibility', 1, getTo, onTweenEnd)
|
||||
else onTweenEnd() end
|
||||
end)
|
||||
self._flash_out_timer:kill()
|
||||
|
||||
Elements:add(self)
|
||||
end
|
||||
|
||||
function Element:destroy()
|
||||
self.destroyed = true
|
||||
Elements:remove(self)
|
||||
end
|
||||
|
||||
function Element:reset_proximity() self.proximity, self.proximity_raw = 0, INFINITY end
|
||||
|
||||
---@param ax number
|
||||
---@param ay number
|
||||
---@param bx number
|
||||
---@param by number
|
||||
function Element:set_coordinates(ax, ay, bx, by)
|
||||
self.ax, self.ay, self.bx, self.by = ax, ay, bx, by
|
||||
Elements:update_proximities()
|
||||
self:maybe('on_coordinates')
|
||||
end
|
||||
|
||||
function Element:update_proximity()
|
||||
if cursor.hidden then
|
||||
self:reset_proximity()
|
||||
else
|
||||
local range = options.proximity_out - options.proximity_in
|
||||
self.proximity_raw = get_point_to_rectangle_proximity(cursor, self)
|
||||
self.proximity = 1 - (clamp(0, self.proximity_raw - options.proximity_in, range) / range)
|
||||
end
|
||||
end
|
||||
|
||||
function Element:is_persistent()
|
||||
local persist = config[self.id .. '_persistency']
|
||||
return persist and (
|
||||
(persist.audio and state.is_audio)
|
||||
or (persist.paused and state.pause and (not Elements.timeline.pressed or Elements.timeline.pressed.pause))
|
||||
or (persist.video and state.is_video)
|
||||
or (persist.image and state.is_image)
|
||||
or (persist.idle and state.is_idle)
|
||||
)
|
||||
end
|
||||
|
||||
-- Decide elements visibility based on proximity and various other factors
|
||||
function Element:get_visibility()
|
||||
-- Hide when menu is open, unless this is a menu
|
||||
---@diagnostic disable-next-line: undefined-global
|
||||
if not self.ignores_menu and Menu and Menu:is_open() then return 0 end
|
||||
|
||||
-- Persistency
|
||||
if self:is_persistent() then return 1 end
|
||||
|
||||
-- Forced visibility
|
||||
if self.forced_visibility then return math.max(self.forced_visibility, self.min_visibility) end
|
||||
|
||||
-- Anchor inheritance
|
||||
-- If anchor returns -1, it means all attached elements should force hide.
|
||||
local anchor = self.anchor_id and Elements[self.anchor_id]
|
||||
local anchor_visibility = anchor and anchor:get_visibility() or 0
|
||||
|
||||
return anchor_visibility == -1 and 0 or math.max(self.proximity, anchor_visibility, self.min_visibility)
|
||||
end
|
||||
|
||||
-- Call method if it exists
|
||||
function Element:maybe(name, ...)
|
||||
if self[name] then return self[name](self, ...) end
|
||||
end
|
||||
|
||||
-- Attach a tweening animation to this element
|
||||
---@param from number
|
||||
---@param to number|fun():number
|
||||
---@param setter fun(value: number)
|
||||
---@param factor_or_callback? number|fun()
|
||||
---@param callback? fun() Called either on animation end, or when animation is killed.
|
||||
function Element:tween(from, to, setter, factor_or_callback, callback)
|
||||
self:tween_stop()
|
||||
self._kill_tween = self.enabled and tween(
|
||||
from, to, setter, factor_or_callback,
|
||||
function()
|
||||
self._kill_tween = nil
|
||||
if callback then callback() end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function Element:is_tweening() return self and self._kill_tween end
|
||||
function Element:tween_stop() self:maybe('_kill_tween') end
|
||||
|
||||
-- Animate an element property between 2 values.
|
||||
---@param prop string
|
||||
---@param from number
|
||||
---@param to number|fun():number
|
||||
---@param factor_or_callback? number|fun()
|
||||
---@param callback? fun() Called either on animation end, or when animation is killed.
|
||||
function Element:tween_property(prop, from, to, factor_or_callback, callback)
|
||||
self:tween(from, to, function(value) self[prop] = value end, factor_or_callback, callback)
|
||||
end
|
||||
|
||||
---@param name string
|
||||
function Element:trigger(name, ...)
|
||||
local result = self:maybe('on_' .. name, ...)
|
||||
request_render()
|
||||
return result
|
||||
end
|
||||
|
||||
-- Briefly flashes the element for `options.flash_duration` milliseconds.
|
||||
-- Useful to visualize changes of volume and timeline when changed via hotkeys.
|
||||
function Element:flash()
|
||||
if self.enabled and options.flash_duration > 0 and (self.proximity < 1 or self._flash_out_timer:is_enabled()) then
|
||||
self:tween_stop()
|
||||
self.forced_visibility = 1
|
||||
request_render()
|
||||
self._flash_out_timer:kill()
|
||||
self._flash_out_timer:resume()
|
||||
end
|
||||
end
|
||||
|
||||
return Element
|
|
@ -1,125 +0,0 @@
|
|||
local Elements = {itable = {}}
|
||||
|
||||
---@param element Element
|
||||
function Elements:add(element)
|
||||
if not element.id then
|
||||
msg.error('attempt to add element without "id" property')
|
||||
return
|
||||
end
|
||||
|
||||
if self:has(element.id) then Elements:remove(element.id) end
|
||||
|
||||
self.itable[#self.itable + 1] = element
|
||||
self[element.id] = element
|
||||
|
||||
request_render()
|
||||
end
|
||||
|
||||
function Elements:remove(idOrElement)
|
||||
if not idOrElement then return end
|
||||
local id = type(idOrElement) == 'table' and idOrElement.id or idOrElement
|
||||
local element = Elements[id]
|
||||
if element then
|
||||
if not element.destroyed then element:destroy() end
|
||||
element.enabled = false
|
||||
self.itable = itable_remove(self.itable, self[id])
|
||||
self[id] = nil
|
||||
request_render()
|
||||
end
|
||||
end
|
||||
|
||||
function Elements:update_proximities()
|
||||
local menu_only = Elements.menu ~= nil
|
||||
local mouse_leave_elements = {}
|
||||
local mouse_enter_elements = {}
|
||||
|
||||
-- Calculates proximities and opacities for defined elements
|
||||
for _, element in self:ipairs() do
|
||||
if element.enabled then
|
||||
local previous_proximity_raw = element.proximity_raw
|
||||
|
||||
-- If menu is open, all other elements have to be disabled
|
||||
if menu_only then
|
||||
if element.ignores_menu then element:update_proximity()
|
||||
else element:reset_proximity() end
|
||||
else
|
||||
element:update_proximity()
|
||||
end
|
||||
|
||||
if element.proximity_raw == 0 then
|
||||
-- Mouse entered element area
|
||||
if previous_proximity_raw ~= 0 then
|
||||
mouse_enter_elements[#mouse_enter_elements + 1] = element
|
||||
end
|
||||
else
|
||||
-- Mouse left element area
|
||||
if previous_proximity_raw == 0 then
|
||||
mouse_leave_elements[#mouse_leave_elements + 1] = element
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Trigger `mouse_leave` and `mouse_enter` events
|
||||
for _, element in ipairs(mouse_leave_elements) do element:trigger('mouse_leave') end
|
||||
for _, element in ipairs(mouse_enter_elements) do element:trigger('mouse_enter') end
|
||||
end
|
||||
|
||||
-- Toggles passed elements' min visibilities between 0 and 1.
|
||||
---@param ids string[] IDs of elements to peek.
|
||||
function Elements:toggle(ids)
|
||||
local has_invisible = itable_find(ids, function(id) return Elements[id] and Elements[id]:get_visibility() ~= 1 end)
|
||||
self:set_min_visibility(has_invisible and 1 or 0, ids)
|
||||
-- Reset proximities when toggling off. Has to happen after `set_min_visibility`,
|
||||
-- as that is using proximity as a tween starting point.
|
||||
if not has_invisible then
|
||||
for _, id in ipairs(ids) do
|
||||
if Elements[id] then Elements[id]:reset_proximity() end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Set (animate) elements' min visibilities to passed value.
|
||||
---@param visibility number 0-1 floating point.
|
||||
---@param ids string[] IDs of elements to peek.
|
||||
function Elements:set_min_visibility(visibility, ids)
|
||||
for _, id in ipairs(ids) do
|
||||
local element = Elements[id]
|
||||
if element then
|
||||
local from = math.max(0, element:get_visibility())
|
||||
element:tween_property('min_visibility', from, visibility)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Flash passed elements.
|
||||
---@param ids string[] IDs of elements to peek.
|
||||
function Elements:flash(ids)
|
||||
local elements = itable_filter(self.itable, function(element) return itable_index_of(ids, element.id) ~= nil end)
|
||||
for _, element in ipairs(elements) do element:flash() end
|
||||
end
|
||||
|
||||
---@param name string Event name.
|
||||
function Elements:trigger(name, ...)
|
||||
for _, element in self:ipairs() do element:trigger(name, ...) end
|
||||
end
|
||||
|
||||
-- Trigger two events, `name` and `global_name`, depending on element-cursor proximity.
|
||||
-- Disabled elements don't receive these events.
|
||||
---@param name string Event name.
|
||||
function Elements:proximity_trigger(name, ...)
|
||||
for i = #self.itable, 1, -1 do
|
||||
local element = self.itable[i]
|
||||
if element.enabled then
|
||||
if element.proximity_raw == 0 then
|
||||
if element:trigger(name, ...) == 'stop_propagation' then break end
|
||||
end
|
||||
if element:trigger('global_' .. name, ...) == 'stop_propagation' then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Elements:has(id) return self[id] ~= nil end
|
||||
function Elements:ipairs() return ipairs(self.itable) end
|
||||
|
||||
return Elements
|
|
@ -1,854 +0,0 @@
|
|||
local Element = require('uosc_shared/elements/Element')
|
||||
|
||||
-- Menu data structure accepted by `Menu:open(menu)`.
|
||||
---@alias MenuData {type?: string; title?: string; hint?: string; keep_open?: boolean; separator?: boolean; items?: MenuDataItem[]; selected_index?: integer;}
|
||||
---@alias MenuDataItem MenuDataValue|MenuData
|
||||
---@alias MenuDataValue {title?: string; hint?: string; icon?: string; value: any; bold?: boolean; italic?: boolean; muted?: boolean; active?: boolean; keep_open?: boolean; separator?: boolean;}
|
||||
---@alias MenuOptions {mouse_nav?: boolean; on_open?: fun(); on_close?: fun(); on_back?: fun(); on_move_item?: fun(from_index: integer, to_index: integer, submenu_path: integer[]); on_delete_item?: fun(index: integer, submenu_path: integer[])}
|
||||
|
||||
-- Internal data structure created from `Menu`.
|
||||
---@alias MenuStack {id?: string; type?: string; title?: string; hint?: string; selected_index?: number; keep_open?: boolean; separator?: boolean; items: MenuStackItem[]; parent_menu?: MenuStack; submenu_path: integer[]; active?: boolean; width: number; height: number; top: number; scroll_y: number; scroll_height: number; title_width: number; hint_width: number; max_width: number; is_root?: boolean; fling?: Fling}
|
||||
---@alias MenuStackItem MenuStackValue|MenuStack
|
||||
---@alias MenuStackValue {title?: string; hint?: string; icon?: string; value: any; active?: boolean; bold?: boolean; italic?: boolean; muted?: boolean; keep_open?: boolean; separator?: boolean; title_width: number; hint_width: number}
|
||||
---@alias Fling {y: number, distance: number, time: number, easing: fun(x: number), duration: number, update_cursor?: boolean}
|
||||
|
||||
---@alias Modifiers {shift?: boolean, ctrl?: boolean, alt?: boolean}
|
||||
---@alias MenuCallbackMeta {modifiers: Modifiers}
|
||||
---@alias MenuCallback fun(value: any, meta: MenuCallbackMeta)
|
||||
|
||||
---@class Menu : Element
|
||||
local Menu = class(Element)
|
||||
|
||||
---@param data MenuData
|
||||
---@param callback MenuCallback
|
||||
---@param opts? MenuOptions
|
||||
function Menu:open(data, callback, opts)
|
||||
local open_menu = self:is_open()
|
||||
if open_menu then
|
||||
open_menu.is_being_replaced = true
|
||||
open_menu:close(true)
|
||||
end
|
||||
return Menu:new(data, callback, opts)
|
||||
end
|
||||
|
||||
---@param menu_type? string
|
||||
---@return Menu|nil
|
||||
function Menu:is_open(menu_type)
|
||||
return Elements.menu and (not menu_type or Elements.menu.type == menu_type) and Elements.menu or nil
|
||||
end
|
||||
|
||||
---@param immediate? boolean Close immediately without fadeout animation.
|
||||
---@param callback? fun() Called after the animation (if any) ends and element is removed and destroyed.
|
||||
---@overload fun(callback: fun())
|
||||
function Menu:close(immediate, callback)
|
||||
if type(immediate) ~= 'boolean' then callback = immediate end
|
||||
|
||||
local menu = self == Menu and Elements.menu or self
|
||||
|
||||
if menu and not menu.destroyed then
|
||||
if menu.is_closing then
|
||||
menu:tween_stop()
|
||||
return
|
||||
end
|
||||
|
||||
local function close()
|
||||
Elements:remove('menu')
|
||||
menu.is_closing, menu.stack, menu.current, menu.all, menu.by_id = false, nil, nil, {}, {}
|
||||
menu:disable_key_bindings()
|
||||
Elements:update_proximities()
|
||||
cursor.queue_autohide()
|
||||
if callback then callback() end
|
||||
request_render()
|
||||
end
|
||||
|
||||
menu.is_closing = true
|
||||
|
||||
if immediate then close()
|
||||
else menu:fadeout(close) end
|
||||
end
|
||||
end
|
||||
|
||||
---@param data MenuData
|
||||
---@param callback MenuCallback
|
||||
---@param opts? MenuOptions
|
||||
---@return Menu
|
||||
function Menu:new(data, callback, opts) return Class.new(self, data, callback, opts) --[[@as Menu]] end
|
||||
---@param data MenuData
|
||||
---@param callback MenuCallback
|
||||
---@param opts? MenuOptions
|
||||
function Menu:init(data, callback, opts)
|
||||
Element.init(self, 'menu', {ignores_menu = true})
|
||||
|
||||
-----@type fun()
|
||||
self.callback = callback
|
||||
self.opts = opts or {}
|
||||
self.offset_x = 0 -- Used for submenu transition animation.
|
||||
self.mouse_nav = self.opts.mouse_nav -- Stops pre-selecting items
|
||||
---@type Modifiers|nil
|
||||
self.modifiers = nil
|
||||
self.item_height = nil
|
||||
self.item_spacing = 1
|
||||
self.item_padding = nil
|
||||
self.font_size = nil
|
||||
self.font_size_hint = nil
|
||||
self.scroll_step = nil -- Item height + item spacing.
|
||||
self.scroll_height = nil -- Items + spacings - container height.
|
||||
self.opacity = 0 -- Used to fade in/out.
|
||||
self.type = data.type
|
||||
---@type MenuStack Root MenuStack.
|
||||
self.root = nil
|
||||
---@type MenuStack Current MenuStack.
|
||||
self.current = nil
|
||||
---@type MenuStack[] All menus in a flat array.
|
||||
self.all = nil
|
||||
---@type table<string, MenuStack> Map of submenus by their ids, such as `'Tools > Aspect ratio'`.
|
||||
self.by_id = {}
|
||||
self.key_bindings = {}
|
||||
self.is_being_replaced = false
|
||||
self.is_closing, self.is_closed = false, false
|
||||
---@type {y: integer, time: number}[]
|
||||
self.drag_data = nil
|
||||
self.is_dragging = false
|
||||
|
||||
self:update(data)
|
||||
|
||||
if self.mouse_nav then
|
||||
if self.current then self.current.selected_index = nil end
|
||||
else
|
||||
for _, menu in ipairs(self.all) do self:scroll_to_index(menu.selected_index, menu) end
|
||||
end
|
||||
|
||||
self:tween_property('opacity', 0, 1)
|
||||
self:enable_key_bindings()
|
||||
Elements.curtain:register('menu')
|
||||
if self.opts.on_open then self.opts.on_open() end
|
||||
end
|
||||
|
||||
function Menu:destroy()
|
||||
Element.destroy(self)
|
||||
self:disable_key_bindings()
|
||||
self.is_closed = true
|
||||
if not self.is_being_replaced then Elements.curtain:unregister('menu') end
|
||||
if self.opts.on_close then self.opts.on_close() end
|
||||
end
|
||||
|
||||
---@param data MenuData
|
||||
function Menu:update(data)
|
||||
self.type = data.type
|
||||
|
||||
local new_root = {is_root = true, submenu_path = {}}
|
||||
local new_all = {}
|
||||
local new_by_id = {}
|
||||
local menus_to_serialize = {{new_root, data}}
|
||||
local old_current_id = self.current and self.current.id
|
||||
|
||||
table_assign(new_root, data, {'type', 'title', 'hint', 'keep_open'})
|
||||
|
||||
local i = 0
|
||||
while i < #menus_to_serialize do
|
||||
i = i + 1
|
||||
local menu, menu_data = menus_to_serialize[i][1], menus_to_serialize[i][2]
|
||||
local parent_id = menu.parent_menu and not menu.parent_menu.is_root and menu.parent_menu.id
|
||||
if not menu.is_root then
|
||||
menu.id = (parent_id and parent_id .. ' > ' or '') .. (menu_data.title or i)
|
||||
end
|
||||
menu.icon = 'chevron_right'
|
||||
|
||||
-- Update items
|
||||
local first_active_index = nil
|
||||
menu.items = {}
|
||||
|
||||
for i, item_data in ipairs(menu_data.items or {}) do
|
||||
if item_data.active and not first_active_index then first_active_index = i end
|
||||
|
||||
local item = {}
|
||||
table_assign(item, item_data, {
|
||||
'title', 'icon', 'hint', 'active', 'bold', 'italic', 'muted', 'value', 'keep_open', 'separator',
|
||||
})
|
||||
if item.keep_open == nil then item.keep_open = menu.keep_open end
|
||||
|
||||
-- Submenu
|
||||
if item_data.items then
|
||||
item.parent_menu = menu
|
||||
item.submenu_path = itable_join(menu.submenu_path, {i})
|
||||
menus_to_serialize[#menus_to_serialize + 1] = {item, item_data}
|
||||
end
|
||||
|
||||
menu.items[i] = item
|
||||
end
|
||||
|
||||
if menu.is_root then menu.selected_index = menu_data.selected_index or first_active_index end
|
||||
|
||||
-- Retain old state
|
||||
local old_menu = self.by_id[menu.is_root and '__root__' or menu.id]
|
||||
if old_menu then table_assign(menu, old_menu, {'selected_index', 'scroll_y', 'fling'}) end
|
||||
|
||||
new_all[#new_all + 1] = menu
|
||||
new_by_id[menu.is_root and '__root__' or menu.id] = menu
|
||||
end
|
||||
|
||||
self.root, self.all, self.by_id = new_root, new_all, new_by_id
|
||||
self.current = self.by_id[old_current_id] or self.root
|
||||
|
||||
self:update_content_dimensions()
|
||||
self:reset_navigation()
|
||||
end
|
||||
|
||||
---@param items MenuDataItem[]
|
||||
function Menu:update_items(items)
|
||||
local data = table_shallow_copy(self.root)
|
||||
data.items = items
|
||||
self:update(data)
|
||||
end
|
||||
|
||||
function Menu:update_content_dimensions()
|
||||
self.item_height = state.fullormaxed and options.menu_item_height_fullscreen or options.menu_item_height
|
||||
self.font_size = round(self.item_height * 0.48 * options.font_scale)
|
||||
self.font_size_hint = self.font_size - 1
|
||||
self.item_padding = round((self.item_height - self.font_size) * 0.6)
|
||||
self.scroll_step = self.item_height + self.item_spacing
|
||||
|
||||
local title_opts = {size = self.font_size, italic = false, bold = false}
|
||||
local hint_opts = {size = self.font_size_hint}
|
||||
|
||||
for _, menu in ipairs(self.all) do
|
||||
title_opts.bold, title_opts.italic = true, false
|
||||
local max_width = text_width(menu.title, title_opts) + 2 * self.item_padding
|
||||
|
||||
-- Estimate width of a widest item
|
||||
for _, item in ipairs(menu.items) do
|
||||
local icon_width = item.icon and self.font_size or 0
|
||||
item.title_width = text_width(item.title, title_opts)
|
||||
item.hint_width = text_width(item.hint, hint_opts)
|
||||
local spacings_in_item = 1 + (item.title_width > 0 and 1 or 0)
|
||||
+ (item.hint_width > 0 and 1 or 0) + (icon_width > 0 and 1 or 0)
|
||||
local estimated_width = item.title_width + item.hint_width + icon_width
|
||||
+ (self.item_padding * spacings_in_item)
|
||||
if estimated_width > max_width then max_width = estimated_width end
|
||||
end
|
||||
|
||||
menu.max_width = max_width
|
||||
end
|
||||
|
||||
self:update_dimensions()
|
||||
end
|
||||
|
||||
function Menu:update_dimensions()
|
||||
-- Coordinates and sizes are of the scrollable area to make
|
||||
-- consuming values in rendering and collisions easier. Title is rendered
|
||||
-- above it, so we need to account for that in max_height and ay position.
|
||||
local min_width = state.fullormaxed and options.menu_min_width_fullscreen or options.menu_min_width
|
||||
|
||||
for _, menu in ipairs(self.all) do
|
||||
menu.width = round(clamp(min_width, menu.max_width, display.width * 0.9))
|
||||
local title_height = (menu.is_root and menu.title) and self.scroll_step or 0
|
||||
local max_height = round((display.height - title_height) * 0.9)
|
||||
local content_height = self.scroll_step * #menu.items
|
||||
menu.height = math.min(content_height - self.item_spacing, max_height)
|
||||
menu.top = round(math.max((display.height - menu.height) / 2, title_height * 1.5))
|
||||
menu.scroll_height = math.max(content_height - menu.height - self.item_spacing, 0)
|
||||
menu.scroll_y = menu.scroll_y or 0
|
||||
self:scroll_to(menu.scroll_y, menu) -- clamps scroll_y to scroll limits
|
||||
end
|
||||
|
||||
self:update_coordinates()
|
||||
end
|
||||
|
||||
-- Updates element coordinates to match currently open (sub)menu.
|
||||
function Menu:update_coordinates()
|
||||
local ax = round((display.width - self.current.width) / 2) + self.offset_x
|
||||
self:set_coordinates(ax, self.current.top, ax + self.current.width, self.current.top + self.current.height)
|
||||
end
|
||||
|
||||
function Menu:reset_navigation()
|
||||
local menu = self.current
|
||||
|
||||
-- Reset indexes and scroll
|
||||
self:scroll_to(menu.scroll_y) -- clamps scroll_y to scroll limits
|
||||
if self.mouse_nav then
|
||||
self:select_item_below_cursor()
|
||||
else
|
||||
self:select_index((menu.items and #menu.items > 0) and clamp(1, menu.selected_index or 1, #menu.items) or nil)
|
||||
end
|
||||
|
||||
-- Walk up the parent menu chain and activate items that lead to current menu
|
||||
local parent = menu.parent_menu
|
||||
while parent do
|
||||
parent.selected_index = itable_index_of(parent.items, menu)
|
||||
menu, parent = parent, parent.parent_menu
|
||||
end
|
||||
|
||||
request_render()
|
||||
end
|
||||
|
||||
function Menu:set_offset_x(offset)
|
||||
local delta = offset - self.offset_x
|
||||
self.offset_x = offset
|
||||
self:set_coordinates(self.ax + delta, self.ay, self.bx + delta, self.by)
|
||||
end
|
||||
|
||||
function Menu:fadeout(callback) self:tween_property('opacity', 1, 0, callback) end
|
||||
|
||||
function Menu:get_item_index_below_cursor()
|
||||
local menu = self.current
|
||||
if #menu.items < 1 or self.proximity_raw > 0 then return nil end
|
||||
return math.max(1, math.min(math.ceil((cursor.y - self.ay + menu.scroll_y) / self.scroll_step), #menu.items))
|
||||
end
|
||||
|
||||
function Menu:get_first_active_index(menu)
|
||||
menu = menu or self.current
|
||||
for index, item in ipairs(self.current.items) do
|
||||
if item.active then return index end
|
||||
end
|
||||
end
|
||||
|
||||
---@param pos? number
|
||||
---@param menu? MenuStack
|
||||
function Menu:set_scroll_to(pos, menu)
|
||||
menu = menu or self.current
|
||||
menu.scroll_y = clamp(0, pos or 0, menu.scroll_height)
|
||||
request_render()
|
||||
end
|
||||
|
||||
---@param delta? number
|
||||
---@param menu? MenuStack
|
||||
function Menu:set_scroll_by(delta, menu)
|
||||
menu = menu or self.current
|
||||
self:set_scroll_to(menu.scroll_y + delta, menu)
|
||||
end
|
||||
|
||||
---@param pos? number
|
||||
---@param menu? MenuStack
|
||||
---@param fling_options? table
|
||||
function Menu:scroll_to(pos, menu, fling_options)
|
||||
menu = menu or self.current
|
||||
menu.fling = {
|
||||
y = menu.scroll_y, distance = clamp(-menu.scroll_y, pos - menu.scroll_y, menu.scroll_height - menu.scroll_y),
|
||||
time = mp.get_time(), duration = 0.1, easing = ease_out_sext,
|
||||
}
|
||||
if fling_options then table_assign(menu.fling, fling_options) end
|
||||
request_render()
|
||||
end
|
||||
|
||||
---@param delta? number
|
||||
---@param menu? MenuStack
|
||||
---@param fling_options? Fling
|
||||
function Menu:scroll_by(delta, menu, fling_options)
|
||||
menu = menu or self.current
|
||||
self:scroll_to((menu.fling and (menu.fling.y + menu.fling.distance) or menu.scroll_y) + delta, menu, fling_options)
|
||||
end
|
||||
|
||||
---@param index? integer
|
||||
---@param menu? MenuStack
|
||||
---@param immediate? boolean
|
||||
function Menu:scroll_to_index(index, menu, immediate)
|
||||
menu = menu or self.current
|
||||
if (index and index >= 1 and index <= #menu.items) then
|
||||
local position = round((self.scroll_step * (index - 1)) - ((menu.height - self.scroll_step) / 2))
|
||||
if immediate then self:set_scroll_to(position, menu)
|
||||
else self:scroll_to(position, menu) end
|
||||
end
|
||||
end
|
||||
|
||||
---@param index? integer
|
||||
---@param menu? MenuStack
|
||||
function Menu:select_index(index, menu)
|
||||
menu = menu or self.current
|
||||
menu.selected_index = (index and index >= 1 and index <= #menu.items) and index or nil
|
||||
request_render()
|
||||
end
|
||||
|
||||
---@param value? any
|
||||
---@param menu? MenuStack
|
||||
function Menu:select_value(value, menu)
|
||||
menu = menu or self.current
|
||||
local index = itable_find(menu.items, function(item) return item.value == value end)
|
||||
self:select_index(index)
|
||||
end
|
||||
|
||||
---@param menu? MenuStack
|
||||
function Menu:deactivate_items(menu)
|
||||
menu = menu or self.current
|
||||
for _, item in ipairs(menu.items) do item.active = false end
|
||||
request_render()
|
||||
end
|
||||
|
||||
---@param index? integer
|
||||
---@param menu? MenuStack
|
||||
function Menu:activate_index(index, menu)
|
||||
menu = menu or self.current
|
||||
if index and index >= 1 and index <= #menu.items then menu.items[index].active = true end
|
||||
request_render()
|
||||
end
|
||||
|
||||
---@param index? integer
|
||||
---@param menu? MenuStack
|
||||
function Menu:activate_one_index(index, menu)
|
||||
self:deactivate_items(menu)
|
||||
self:activate_index(index, menu)
|
||||
end
|
||||
|
||||
---@param value? any
|
||||
---@param menu? MenuStack
|
||||
function Menu:activate_value(value, menu)
|
||||
menu = menu or self.current
|
||||
local index = itable_find(menu.items, function(item) return item.value == value end)
|
||||
self:activate_index(index, menu)
|
||||
end
|
||||
|
||||
---@param value? any
|
||||
---@param menu? MenuStack
|
||||
function Menu:activate_one_value(value, menu)
|
||||
menu = menu or self.current
|
||||
local index = itable_find(menu.items, function(item) return item.value == value end)
|
||||
self:activate_one_index(index, menu)
|
||||
end
|
||||
|
||||
---@param menu MenuStack One of menus in `self.all`.
|
||||
function Menu:activate_menu(menu)
|
||||
if itable_index_of(self.all, menu) then
|
||||
self.current = menu
|
||||
self:update_coordinates()
|
||||
self:reset_navigation()
|
||||
request_render()
|
||||
else
|
||||
msg.error('Attempt to open a menu not in `self.all` list.')
|
||||
end
|
||||
end
|
||||
|
||||
---@param id string
|
||||
function Menu:activate_submenu(id)
|
||||
local submenu = self.by_id[id]
|
||||
if submenu then self:activate_menu(submenu)
|
||||
else msg.error(string.format('Requested submenu id "%s" doesn\'t exist', id)) end
|
||||
end
|
||||
|
||||
---@param index? integer
|
||||
---@param menu? MenuStack
|
||||
function Menu:delete_index(index, menu)
|
||||
menu = menu or self.current
|
||||
if (index and index >= 1 and index <= #menu.items) then
|
||||
table.remove(menu.items, index)
|
||||
self:update_content_dimensions()
|
||||
self:scroll_to_index(menu.selected_index, menu)
|
||||
end
|
||||
end
|
||||
|
||||
---@param value? any
|
||||
---@param menu? MenuStack
|
||||
function Menu:delete_value(value, menu)
|
||||
menu = menu or self.current
|
||||
local index = itable_find(menu.items, function(item) return item.value == value end)
|
||||
self:delete_index(index)
|
||||
end
|
||||
|
||||
---@param menu? MenuStack
|
||||
function Menu:prev(menu)
|
||||
menu = menu or self.current
|
||||
menu.selected_index = math.max(menu.selected_index and menu.selected_index - 1 or #menu.items, 1)
|
||||
self:scroll_to_index(menu.selected_index, menu, true)
|
||||
end
|
||||
|
||||
---@param menu? MenuStack
|
||||
function Menu:next(menu)
|
||||
menu = menu or self.current
|
||||
menu.selected_index = math.min(menu.selected_index and menu.selected_index + 1 or 1, #menu.items)
|
||||
self:scroll_to_index(menu.selected_index, menu, true)
|
||||
end
|
||||
|
||||
function Menu:back()
|
||||
if self.opts.on_back then
|
||||
self.opts.on_back()
|
||||
if self.is_closed then return end
|
||||
end
|
||||
|
||||
local menu = self.current
|
||||
local parent = menu.parent_menu
|
||||
|
||||
if parent then
|
||||
menu.selected_index = nil
|
||||
self:activate_menu(parent)
|
||||
self:tween(self.offset_x - menu.width / 2, 0, function(offset) self:set_offset_x(offset) end)
|
||||
self.opacity = 1 -- in case tween above canceled fade in animation
|
||||
else
|
||||
self:close()
|
||||
end
|
||||
end
|
||||
|
||||
---@param opts? {keep_open?: boolean, preselect_submenu_item?: boolean}
|
||||
function Menu:open_selected_item(opts)
|
||||
opts = opts or {}
|
||||
local menu = self.current
|
||||
if menu.selected_index then
|
||||
local item = menu.items[menu.selected_index]
|
||||
-- Is submenu
|
||||
if item.items then
|
||||
if opts.preselect_submenu_item then
|
||||
item.selected_index = #item.items > 0 and 1 or nil
|
||||
end
|
||||
self:activate_menu(item)
|
||||
self:tween(self.offset_x + menu.width / 2, 0, function(offset) self:set_offset_x(offset) end)
|
||||
self.opacity = 1 -- in case tween above canceled fade in animation
|
||||
else
|
||||
self.callback(item.value, {modifiers = self.modifiers or {}})
|
||||
if not item.keep_open and not opts.keep_open then self:close() end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Menu:open_selected_item_soft() self:open_selected_item({keep_open = true}) end
|
||||
function Menu:open_selected_item_preselect() self:open_selected_item({preselect_submenu_item = true}) end
|
||||
function Menu:select_item_below_cursor() self.current.selected_index = self:get_item_index_below_cursor() end
|
||||
|
||||
---@param index integer
|
||||
function Menu:move_selected_item_to(index)
|
||||
local from, callback = self.current.selected_index, self.opts.on_move_item
|
||||
if callback and from and from ~= index and index >= 1 and index <= #self.current.items then
|
||||
callback(from, index, self.current.submenu_path)
|
||||
self.current.selected_index = index
|
||||
request_render()
|
||||
end
|
||||
end
|
||||
|
||||
function Menu:move_selected_item_up()
|
||||
if self.current.selected_index then self:move_selected_item_to(self.current.selected_index - 1) end
|
||||
end
|
||||
|
||||
function Menu:move_selected_item_down()
|
||||
if self.current.selected_index then self:move_selected_item_to(self.current.selected_index + 1) end
|
||||
end
|
||||
|
||||
function Menu:delete_selected_item()
|
||||
local index, callback = self.current.selected_index, self.opts.on_delete_item
|
||||
if callback and index then callback(index, self.current.submenu_path) end
|
||||
end
|
||||
|
||||
function Menu:on_display() self:update_dimensions() end
|
||||
function Menu:on_prop_fullormaxed() self:update_content_dimensions() end
|
||||
|
||||
function Menu:handle_cursor_down()
|
||||
if self.proximity_raw == 0 then
|
||||
self.drag_data = {{y = cursor.y, time = mp.get_time()}}
|
||||
self.current.fling = nil
|
||||
else
|
||||
if cursor.x < self.ax and self.current.parent_menu then self:back()
|
||||
else self:close() end
|
||||
end
|
||||
end
|
||||
|
||||
function Menu:fling_distance()
|
||||
local first, last = self.drag_data[1], self.drag_data[#self.drag_data]
|
||||
if mp.get_time() - last.time > 0.05 then return 0 end
|
||||
for i = #self.drag_data - 1, 1, -1 do
|
||||
local drag = self.drag_data[i]
|
||||
if last.time - drag.time > 0.03 then return ((drag.y - last.y) / ((last.time - drag.time) / 0.03)) * 10 end
|
||||
end
|
||||
return #self.drag_data < 2 and 0 or ((first.y - last.y) / ((first.time - last.time) / 0.03)) * 10
|
||||
end
|
||||
|
||||
function Menu:handle_cursor_up()
|
||||
if self.proximity_raw == 0 and self.drag_data and not self.is_dragging then
|
||||
self:select_item_below_cursor()
|
||||
self:open_selected_item({preselect_submenu_item = false, keep_open = self.modifiers and self.modifiers.shift})
|
||||
end
|
||||
if self.is_dragging then
|
||||
local distance = self:fling_distance()
|
||||
if math.abs(distance) > 50 then
|
||||
self.current.fling = {
|
||||
y = self.current.scroll_y, distance = distance, time = self.drag_data[#self.drag_data].time,
|
||||
easing = ease_out_quart, duration = 0.5, update_cursor = true,
|
||||
}
|
||||
end
|
||||
end
|
||||
self.is_dragging = false
|
||||
self.drag_data = nil
|
||||
end
|
||||
|
||||
|
||||
function Menu:on_global_mouse_move()
|
||||
self.mouse_nav = true
|
||||
if self.drag_data then
|
||||
self.is_dragging = self.is_dragging or math.abs(cursor.y - self.drag_data[1].y) >= 10
|
||||
local distance = self.drag_data[#self.drag_data].y - cursor.y
|
||||
if distance ~= 0 then self:set_scroll_by(distance) end
|
||||
self.drag_data[#self.drag_data + 1] = {y = cursor.y, time = mp.get_time()}
|
||||
end
|
||||
if self.proximity_raw == 0 or self.is_dragging then self:select_item_below_cursor()
|
||||
else self.current.selected_index = nil end
|
||||
request_render()
|
||||
end
|
||||
|
||||
function Menu:handle_wheel_up() self:scroll_by(self.scroll_step * -3, nil, {update_cursor = true}) end
|
||||
function Menu:handle_wheel_down() self:scroll_by(self.scroll_step * 3, nil, {update_cursor = true}) end
|
||||
|
||||
function Menu:on_pgup()
|
||||
local menu = self.current
|
||||
local items_per_page = round((menu.height / self.scroll_step) * 0.4)
|
||||
local paged_index = (menu.selected_index and menu.selected_index or #menu.items) - items_per_page
|
||||
menu.selected_index = clamp(1, paged_index, #menu.items)
|
||||
if menu.selected_index > 0 then self:scroll_to_index(menu.selected_index) end
|
||||
end
|
||||
|
||||
function Menu:on_pgdwn()
|
||||
local menu = self.current
|
||||
local items_per_page = round((menu.height / self.scroll_step) * 0.4)
|
||||
local paged_index = (menu.selected_index and menu.selected_index or 1) + items_per_page
|
||||
menu.selected_index = clamp(1, paged_index, #menu.items)
|
||||
if menu.selected_index > 0 then self:scroll_to_index(menu.selected_index) end
|
||||
end
|
||||
|
||||
function Menu:on_home()
|
||||
self.current.selected_index = math.min(1, #self.current.items)
|
||||
if self.current.selected_index > 0 then self:scroll_to_index(self.current.selected_index) end
|
||||
end
|
||||
|
||||
function Menu:on_end()
|
||||
self.current.selected_index = #self.current.items
|
||||
if self.current.selected_index > 0 then self:scroll_to_index(self.current.selected_index) end
|
||||
end
|
||||
|
||||
function Menu:add_key_binding(key, name, fn, flags)
|
||||
self.key_bindings[#self.key_bindings + 1] = name
|
||||
mp.add_forced_key_binding(key, name, fn, flags)
|
||||
end
|
||||
|
||||
function Menu:enable_key_bindings()
|
||||
-- The `mp.set_key_bindings()` method would be easier here, but that
|
||||
-- doesn't support 'repeatable' flag, so we are stuck with this monster.
|
||||
self:add_key_binding('up', 'menu-prev1', self:create_key_action('prev'), 'repeatable')
|
||||
self:add_key_binding('down', 'menu-next1', self:create_key_action('next'), 'repeatable')
|
||||
self:add_key_binding('ctrl+up', 'menu-move-up', self:create_key_action('move_selected_item_up'), 'repeatable')
|
||||
self:add_key_binding('ctrl+down', 'menu-move-down', self:create_key_action('move_selected_item_down'), 'repeatable')
|
||||
self:add_key_binding('left', 'menu-back1', self:create_key_action('back'))
|
||||
self:add_key_binding('right', 'menu-select1', self:create_key_action('open_selected_item_preselect'))
|
||||
self:add_key_binding('shift+right', 'menu-select-soft1',
|
||||
self:create_key_action('open_selected_item_soft', {shift = true}))
|
||||
self:add_key_binding('shift+mbtn_left', 'menu-select3', self:create_modified_mbtn_left_handler({shift = true}))
|
||||
self:add_key_binding('ctrl+mbtn_left', 'menu-select4', self:create_modified_mbtn_left_handler({ctrl = true}))
|
||||
self:add_key_binding('mbtn_back', 'menu-back-alt3', self:create_key_action('back'))
|
||||
self:add_key_binding('bs', 'menu-back-alt4', self:create_key_action('back'))
|
||||
self:add_key_binding('enter', 'menu-select-alt3', self:create_key_action('open_selected_item_preselect'))
|
||||
self:add_key_binding('kp_enter', 'menu-select-alt4', self:create_key_action('open_selected_item_preselect'))
|
||||
self:add_key_binding('ctrl+enter', 'menu-select-ctrl1',
|
||||
self:create_key_action('open_selected_item_preselect', {ctrl = true}))
|
||||
self:add_key_binding('ctrl+kp_enter', 'menu-select-ctrl2',
|
||||
self:create_key_action('open_selected_item_preselect', {ctrl = true}))
|
||||
self:add_key_binding('shift+enter', 'menu-select-alt5',
|
||||
self:create_key_action('open_selected_item_soft', {shift = true}))
|
||||
self:add_key_binding('shift+kp_enter', 'menu-select-alt6',
|
||||
self:create_key_action('open_selected_item_soft', {shift = true}))
|
||||
self:add_key_binding('esc', 'menu-close', self:create_key_action('close'))
|
||||
self:add_key_binding('pgup', 'menu-page-up', self:create_key_action('on_pgup'), 'repeatable')
|
||||
self:add_key_binding('pgdwn', 'menu-page-down', self:create_key_action('on_pgdwn'), 'repeatable')
|
||||
self:add_key_binding('home', 'menu-home', self:create_key_action('on_home'))
|
||||
self:add_key_binding('end', 'menu-end', self:create_key_action('on_end'))
|
||||
self:add_key_binding('del', 'menu-delete-item', self:create_key_action('delete_selected_item'))
|
||||
end
|
||||
|
||||
function Menu:disable_key_bindings()
|
||||
for _, name in ipairs(self.key_bindings) do mp.remove_key_binding(name) end
|
||||
self.key_bindings = {}
|
||||
end
|
||||
|
||||
---@param modifiers Modifiers
|
||||
function Menu:create_modified_mbtn_left_handler(modifiers)
|
||||
return function()
|
||||
self.mouse_nav = true
|
||||
self.modifiers = modifiers
|
||||
self:handle_cursor_down()
|
||||
self:handle_cursor_up()
|
||||
self.modifiers = nil
|
||||
end
|
||||
end
|
||||
|
||||
---@param name string
|
||||
---@param modifiers? Modifiers
|
||||
function Menu:create_key_action(name, modifiers)
|
||||
return function()
|
||||
self.mouse_nav = false
|
||||
self.modifiers = modifiers
|
||||
self:maybe(name)
|
||||
self.modifiers = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Menu:render()
|
||||
local update_cursor = false
|
||||
for _, menu in ipairs(self.all) do
|
||||
if menu.fling then
|
||||
update_cursor = update_cursor or menu.fling.update_cursor or false
|
||||
local time_delta = state.render_last_time - menu.fling.time
|
||||
local progress = menu.fling.easing(math.min(time_delta / menu.fling.duration, 1))
|
||||
self:set_scroll_to(round(menu.fling.y + menu.fling.distance * progress), menu)
|
||||
if progress < 1 then request_render() else menu.fling = nil end
|
||||
end
|
||||
end
|
||||
if update_cursor then self:select_item_below_cursor() end
|
||||
|
||||
cursor.on_primary_down = function() self:handle_cursor_down() end
|
||||
cursor.on_primary_up = function() self:handle_cursor_up() end
|
||||
if self.proximity_raw == 0 then
|
||||
cursor.on_wheel_down = function() self:handle_wheel_down() end
|
||||
cursor.on_wheel_up = function() self:handle_wheel_up() end
|
||||
end
|
||||
|
||||
local ass = assdraw.ass_new()
|
||||
local opacity = options.menu_opacity * self.opacity
|
||||
local spacing = self.item_padding
|
||||
local icon_size = self.font_size
|
||||
|
||||
function draw_menu(menu, x, y, opacity)
|
||||
local ax, ay, bx, by = x, y, x + menu.width, y + menu.height
|
||||
local draw_title = menu.is_root and menu.title
|
||||
local scroll_clip = '\\clip(0,' .. ay .. ',' .. display.width .. ',' .. by .. ')'
|
||||
local start_index = math.floor(menu.scroll_y / self.scroll_step) + 1
|
||||
local end_index = math.ceil((menu.scroll_y + menu.height) / self.scroll_step)
|
||||
local selected_index = menu.selected_index or -1
|
||||
-- remove menu_opacity to start off with full opacity, but still decay for parent menus
|
||||
local text_opacity = opacity / options.menu_opacity
|
||||
|
||||
-- Background
|
||||
ass:rect(ax, ay - (draw_title and self.item_height or 0) - 2, bx, by + 2, {
|
||||
color = bg, opacity = opacity, radius = 4,
|
||||
})
|
||||
|
||||
for index = start_index, end_index, 1 do
|
||||
local item = menu.items[index]
|
||||
local next_item = menu.items[index + 1]
|
||||
local is_highlighted = selected_index == index or item.active
|
||||
local next_is_active = next_item and next_item.active
|
||||
local next_is_highlighted = selected_index == index + 1 or next_is_active
|
||||
|
||||
if not item then break end
|
||||
|
||||
local item_ay = ay - menu.scroll_y + self.scroll_step * (index - 1)
|
||||
local item_by = item_ay + self.item_height
|
||||
local item_center_y = item_ay + (self.item_height / 2)
|
||||
local item_clip = (item_ay < ay or item_by > by) and scroll_clip or nil
|
||||
local content_ax, content_bx = ax + spacing, bx - spacing
|
||||
local font_color = item.active and fgt or bgt
|
||||
local shadow_color = item.active and fg or bg
|
||||
|
||||
-- Separator
|
||||
local separator_ay = item.separator and item_by - 1 or item_by
|
||||
local separator_by = item_by + (item.separator and 2 or 1)
|
||||
if is_highlighted then separator_ay = item_by + 1 end
|
||||
if next_is_highlighted then separator_by = item_by end
|
||||
if separator_by - separator_ay > 0 and item_by < by then
|
||||
ass:rect(ax + spacing / 2, separator_ay, bx - spacing / 2, separator_by, {
|
||||
color = fg, opacity = opacity * (item.separator and 0.08 or 0.06),
|
||||
})
|
||||
end
|
||||
|
||||
-- Highlight
|
||||
local highlight_opacity = 0 + (item.active and 0.8 or 0) + (selected_index == index and 0.15 or 0)
|
||||
if highlight_opacity > 0 then
|
||||
ass:rect(ax + 2, item_ay, bx - 2, item_by, {
|
||||
radius = 2, color = fg, opacity = highlight_opacity * text_opacity,
|
||||
clip = item_clip,
|
||||
})
|
||||
end
|
||||
|
||||
-- Icon
|
||||
if item.icon then
|
||||
local x, y = content_bx - (icon_size / 2), item_center_y
|
||||
if item.icon == 'spinner' then
|
||||
ass:spinner(x, y, icon_size * 1.5, {color = font_color, opacity = text_opacity * 0.8})
|
||||
else
|
||||
ass:icon(x, y, icon_size * 1.5, item.icon, {
|
||||
color = font_color, opacity = text_opacity, clip = item_clip,
|
||||
shadow = 1, shadow_color = shadow_color,
|
||||
})
|
||||
end
|
||||
content_bx = content_bx - icon_size - spacing
|
||||
end
|
||||
|
||||
local title_cut_x = content_bx
|
||||
if item.hint_width > 0 then
|
||||
-- controls title & hint clipping proportional to the ratio of their widths
|
||||
local title_content_ratio = item.title_width / (item.title_width + item.hint_width)
|
||||
title_cut_x = round(content_ax + (content_bx - content_ax - spacing) * title_content_ratio
|
||||
+ (item.title_width > 0 and spacing / 2 or 0))
|
||||
end
|
||||
|
||||
-- Hint
|
||||
if item.hint then
|
||||
item.ass_safe_hint = item.ass_safe_hint or ass_escape(item.hint)
|
||||
local clip = '\\clip(' .. title_cut_x .. ',' ..
|
||||
math.max(item_ay, ay) .. ',' .. bx .. ',' .. math.min(item_by, by) .. ')'
|
||||
ass:txt(content_bx, item_center_y, 6, item.ass_safe_hint, {
|
||||
size = self.font_size_hint, color = font_color, wrap = 2, opacity = 0.5 * opacity, clip = clip,
|
||||
shadow = 1, shadow_color = shadow_color,
|
||||
})
|
||||
end
|
||||
|
||||
-- Title
|
||||
if item.title then
|
||||
item.ass_safe_title = item.ass_safe_title or ass_escape(item.title)
|
||||
local clip = '\\clip(' .. ax .. ',' .. math.max(item_ay, ay) .. ','
|
||||
.. title_cut_x .. ',' .. math.min(item_by, by) .. ')'
|
||||
ass:txt(content_ax, item_center_y, 4, item.ass_safe_title, {
|
||||
size = self.font_size, color = font_color, italic = item.italic, bold = item.bold, wrap = 2,
|
||||
opacity = text_opacity * (item.muted and 0.5 or 1), clip = clip,
|
||||
shadow = 1, shadow_color = shadow_color,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Menu title
|
||||
if draw_title then
|
||||
local title_ay = ay - self.item_height
|
||||
local title_height = self.item_height - 3
|
||||
menu.ass_safe_title = menu.ass_safe_title or ass_escape(menu.title)
|
||||
|
||||
-- Background
|
||||
ass:rect(ax + 2, title_ay, bx - 2, title_ay + title_height, {
|
||||
color = fg, opacity = opacity * 0.8, radius = 2,
|
||||
})
|
||||
ass:texture(ax + 2, title_ay, bx - 2, title_ay + title_height, 'n', {
|
||||
size = 80, color = bg, opacity = opacity * 0.1,
|
||||
})
|
||||
|
||||
-- Title
|
||||
ass:txt(ax + menu.width / 2, title_ay + (title_height / 2), 5, menu.ass_safe_title, {
|
||||
size = self.font_size, bold = true, color = bg, wrap = 2, opacity = opacity,
|
||||
clip = '\\clip(' .. ax .. ',' .. title_ay .. ',' .. bx .. ',' .. ay .. ')',
|
||||
})
|
||||
end
|
||||
|
||||
-- Scrollbar
|
||||
if menu.scroll_height > 0 then
|
||||
local groove_height = menu.height - 2
|
||||
local thumb_height = math.max((menu.height / (menu.scroll_height + menu.height)) * groove_height, 40)
|
||||
local thumb_y = ay + 1 + ((menu.scroll_y / menu.scroll_height) * (groove_height - thumb_height))
|
||||
ass:rect(bx - 3, thumb_y, bx - 1, thumb_y + thumb_height, {color = fg, opacity = opacity * 0.8})
|
||||
end
|
||||
end
|
||||
|
||||
-- Main menu
|
||||
draw_menu(self.current, self.ax, self.ay, opacity)
|
||||
|
||||
-- Parent menus
|
||||
local parent_menu = self.current.parent_menu
|
||||
local parent_offset_x = self.ax
|
||||
local parent_opacity_factor = options.menu_parent_opacity
|
||||
local menu_gap = 2
|
||||
|
||||
while parent_menu do
|
||||
parent_offset_x = parent_offset_x - parent_menu.width - menu_gap
|
||||
draw_menu(parent_menu, parent_offset_x, parent_menu.top, parent_opacity_factor * opacity)
|
||||
parent_opacity_factor = parent_opacity_factor * parent_opacity_factor
|
||||
parent_menu = parent_menu.parent_menu
|
||||
end
|
||||
|
||||
-- Selected menu
|
||||
local selected_menu = self.current.items[self.current.selected_index]
|
||||
|
||||
if selected_menu and selected_menu.items then
|
||||
draw_menu(selected_menu, self.bx + menu_gap, selected_menu.top, options.menu_parent_opacity * opacity)
|
||||
end
|
||||
|
||||
return ass
|
||||
end
|
||||
|
||||
return Menu
|
|
@ -1,80 +0,0 @@
|
|||
local Element = require('uosc_shared/elements/Element')
|
||||
|
||||
---@class PauseIndicator : Element
|
||||
local PauseIndicator = class(Element)
|
||||
|
||||
function PauseIndicator:new() return Class.new(self) --[[@as PauseIndicator]] end
|
||||
function PauseIndicator:init()
|
||||
Element.init(self, 'pause_indicator')
|
||||
self.ignores_menu = true
|
||||
self.base_icon_opacity = options.pause_indicator == 'flash' and 1 or 0.8
|
||||
self.paused = state.pause
|
||||
self.type = options.pause_indicator
|
||||
self.is_manual = options.pause_indicator == 'manual'
|
||||
self.fadeout_requested = false
|
||||
self.opacity = 0
|
||||
|
||||
mp.observe_property('pause', 'bool', function(_, paused)
|
||||
if Elements.timeline.pressed then return end
|
||||
if options.pause_indicator == 'flash' then
|
||||
if self.paused == paused then return end
|
||||
self:flash()
|
||||
elseif options.pause_indicator == 'static' then
|
||||
self:decide()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function PauseIndicator:flash()
|
||||
if not self.is_manual and self.type ~= 'flash' then return end
|
||||
-- can't wait for pause property event listener to set this, because when this is used inside a binding like:
|
||||
-- cycle pause; script-binding uosc/flash-pause-indicator
|
||||
-- the pause event is not fired fast enough, and indicator starts rendering with old icon
|
||||
self.paused = mp.get_property_native('pause')
|
||||
if self.is_manual then self.type = 'flash' end
|
||||
self.opacity = 1
|
||||
self:tween_property('opacity', 1, 0, 0.15)
|
||||
end
|
||||
|
||||
-- decides whether static indicator should be visible or not
|
||||
function PauseIndicator:decide()
|
||||
if not self.is_manual and self.type ~= 'static' then return end
|
||||
self.paused = mp.get_property_native('pause') -- see flash() for why this line is necessary
|
||||
if self.is_manual then self.type = 'static' end
|
||||
self.opacity = self.paused and 1 or 0
|
||||
request_render()
|
||||
|
||||
-- Workaround for an mpv race condition bug during pause on windows builds, which causes osd updates to be ignored.
|
||||
-- .03 was still loosing renders, .04 was fine, but to be safe I added 10ms more
|
||||
mp.add_timeout(.05, function() osd:update() end)
|
||||
end
|
||||
|
||||
function PauseIndicator:render()
|
||||
if self.opacity == 0 then return end
|
||||
|
||||
local ass = assdraw.ass_new()
|
||||
local is_static = self.type == 'static'
|
||||
|
||||
-- Background fadeout
|
||||
if is_static then
|
||||
ass:rect(0, 0, display.width, display.height, {color = bg, opacity = self.opacity * 0.3})
|
||||
end
|
||||
|
||||
-- Icon
|
||||
local size = round(math.min(display.width, display.height) * (is_static and 0.20 or 0.15))
|
||||
size = size + size * (1 - self.opacity)
|
||||
|
||||
if self.paused then
|
||||
ass:icon(display.width / 2, display.height / 2, size, 'pause',
|
||||
{border = 1, opacity = self.base_icon_opacity * self.opacity}
|
||||
)
|
||||
else
|
||||
ass:icon(display.width / 2, display.height / 2, size * 1.2, 'play_arrow',
|
||||
{border = 1, opacity = self.base_icon_opacity * self.opacity}
|
||||
)
|
||||
end
|
||||
|
||||
return ass
|
||||
end
|
||||
|
||||
return PauseIndicator
|
|
@ -1,192 +0,0 @@
|
|||
local Element = require('uosc_shared/elements/Element')
|
||||
|
||||
---@alias Dragging { start_time: number; start_x: number; distance: number; speed_distance: number; start_speed: number; }
|
||||
|
||||
---@class Speed : Element
|
||||
local Speed = class(Element)
|
||||
|
||||
---@param props? ElementProps
|
||||
function Speed:new(props) return Class.new(self, props) --[[@as Speed]] end
|
||||
function Speed:init(props)
|
||||
Element.init(self, 'speed', props)
|
||||
|
||||
self.width = 0
|
||||
self.height = 0
|
||||
self.notches = 10
|
||||
self.notch_every = 0.1
|
||||
---@type number
|
||||
self.notch_spacing = nil
|
||||
---@type number
|
||||
self.font_size = nil
|
||||
---@type Dragging|nil
|
||||
self.dragging = nil
|
||||
end
|
||||
|
||||
function Speed:on_coordinates()
|
||||
self.height, self.width = self.by - self.ay, self.bx - self.ax
|
||||
self.notch_spacing = self.width / (self.notches + 1)
|
||||
self.font_size = round(self.height * 0.48 * options.font_scale)
|
||||
end
|
||||
|
||||
function Speed:speed_step(speed, up)
|
||||
if options.speed_step_is_factor then
|
||||
if up then
|
||||
return speed * options.speed_step
|
||||
else
|
||||
return speed * 1 / options.speed_step
|
||||
end
|
||||
else
|
||||
if up then
|
||||
return speed + options.speed_step
|
||||
else
|
||||
return speed - options.speed_step
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Speed:handle_cursor_down()
|
||||
self:tween_stop() -- Stop and cleanup possible ongoing animations
|
||||
self.dragging = {
|
||||
start_time = mp.get_time(),
|
||||
start_x = cursor.x,
|
||||
distance = 0,
|
||||
speed_distance = 0,
|
||||
start_speed = state.speed,
|
||||
}
|
||||
end
|
||||
|
||||
function Speed:on_global_mouse_move()
|
||||
if not self.dragging then return end
|
||||
|
||||
self.dragging.distance = cursor.x - self.dragging.start_x
|
||||
self.dragging.speed_distance = (-self.dragging.distance / self.notch_spacing * self.notch_every)
|
||||
|
||||
local speed_current = state.speed
|
||||
local speed_drag_current = self.dragging.start_speed + self.dragging.speed_distance
|
||||
speed_drag_current = clamp(0.01, speed_drag_current, 100)
|
||||
local drag_dir_up = speed_drag_current > speed_current
|
||||
|
||||
local speed_step_next = speed_current
|
||||
local speed_drag_diff = math.abs(speed_drag_current - speed_current)
|
||||
while math.abs(speed_step_next - speed_current) < speed_drag_diff do
|
||||
speed_step_next = self:speed_step(speed_step_next, drag_dir_up)
|
||||
end
|
||||
local speed_step_prev = self:speed_step(speed_step_next, not drag_dir_up)
|
||||
|
||||
local speed_new = speed_step_prev
|
||||
local speed_next_diff = math.abs(speed_drag_current - speed_step_next)
|
||||
local speed_prev_diff = math.abs(speed_drag_current - speed_step_prev)
|
||||
if speed_next_diff < speed_prev_diff then
|
||||
speed_new = speed_step_next
|
||||
end
|
||||
|
||||
if speed_new ~= speed_current then
|
||||
mp.set_property_native('speed', speed_new)
|
||||
end
|
||||
end
|
||||
|
||||
function Speed:handle_cursor_up()
|
||||
if self.proximity_raw == 0 then
|
||||
-- Reset speed on short clicks
|
||||
if self.dragging and math.abs(self.dragging.distance) < 6 and mp.get_time() - self.dragging.start_time < 0.15 then
|
||||
mp.set_property_native('speed', 1)
|
||||
end
|
||||
end
|
||||
self.dragging = nil
|
||||
request_render()
|
||||
end
|
||||
|
||||
function Speed:on_global_mouse_leave()
|
||||
self.dragging = nil
|
||||
request_render()
|
||||
end
|
||||
|
||||
function Speed:handle_wheel_up() mp.set_property_native('speed', self:speed_step(state.speed, true)) end
|
||||
function Speed:handle_wheel_down() mp.set_property_native('speed', self:speed_step(state.speed, false)) end
|
||||
|
||||
function Speed:render()
|
||||
local visibility = self:get_visibility()
|
||||
local opacity = self.dragging and 1 or visibility
|
||||
|
||||
if opacity <= 0 then return end
|
||||
|
||||
if self.proximity_raw == 0 then
|
||||
cursor.on_primary_down = function()
|
||||
self:handle_cursor_down()
|
||||
cursor.on_primary_up = function() self:handle_cursor_up() end
|
||||
end
|
||||
cursor.on_wheel_down = function() self:handle_wheel_down() end
|
||||
cursor.on_wheel_up = function() self:handle_wheel_up() end
|
||||
end
|
||||
if self.dragging then
|
||||
cursor.on_primary_up = function() self:handle_cursor_up() end
|
||||
end
|
||||
|
||||
local ass = assdraw.ass_new()
|
||||
|
||||
-- Background
|
||||
ass:rect(self.ax, self.ay, self.bx, self.by, {color = bg, radius = 2, opacity = opacity * options.speed_opacity})
|
||||
|
||||
-- Coordinates
|
||||
local ax, ay = self.ax, self.ay
|
||||
local bx, by = self.bx, ay + self.height
|
||||
local half_width = (self.width / 2)
|
||||
local half_x = ax + half_width
|
||||
|
||||
-- Notches
|
||||
local speed_at_center = state.speed
|
||||
if self.dragging then
|
||||
speed_at_center = self.dragging.start_speed + self.dragging.speed_distance
|
||||
speed_at_center = clamp(0.01, speed_at_center, 100)
|
||||
end
|
||||
local nearest_notch_speed = round(speed_at_center / self.notch_every) * self.notch_every
|
||||
local nearest_notch_x = half_x + (((nearest_notch_speed - speed_at_center) / self.notch_every) * self.notch_spacing)
|
||||
local guide_size = math.floor(self.height / 7.5)
|
||||
local notch_by = by - guide_size
|
||||
local notch_ay_big = ay + round(self.font_size * 1.1)
|
||||
local notch_ay_medium = notch_ay_big + ((notch_by - notch_ay_big) * 0.2)
|
||||
local notch_ay_small = notch_ay_big + ((notch_by - notch_ay_big) * 0.4)
|
||||
local from_to_index = math.floor(self.notches / 2)
|
||||
|
||||
for i = -from_to_index, from_to_index do
|
||||
local notch_speed = nearest_notch_speed + (i * self.notch_every)
|
||||
|
||||
if notch_speed >= 0 and notch_speed <= 100 then
|
||||
local notch_x = nearest_notch_x + (i * self.notch_spacing)
|
||||
local notch_thickness = 1
|
||||
local notch_ay = notch_ay_small
|
||||
if (notch_speed % (self.notch_every * 10)) < 0.00000001 then
|
||||
notch_ay = notch_ay_big
|
||||
notch_thickness = 1.5
|
||||
elseif (notch_speed % (self.notch_every * 5)) < 0.00000001 then
|
||||
notch_ay = notch_ay_medium
|
||||
end
|
||||
|
||||
ass:rect(notch_x - notch_thickness, notch_ay, notch_x + notch_thickness, notch_by, {
|
||||
color = fg, border = 1, border_color = bg,
|
||||
opacity = math.min(1.2 - (math.abs((notch_x - ax - half_width) / half_width)), 1) * opacity,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Center guide
|
||||
ass:new_event()
|
||||
ass:append('{\\rDefault\\an7\\blur0\\bord1\\shad0\\1c&H' .. fg .. '\\3c&H' .. bg .. '}')
|
||||
ass:opacity(opacity)
|
||||
ass:pos(0, 0)
|
||||
ass:draw_start()
|
||||
ass:move_to(half_x, by - 2 - guide_size)
|
||||
ass:line_to(half_x + guide_size, by - 2)
|
||||
ass:line_to(half_x - guide_size, by - 2)
|
||||
ass:draw_stop()
|
||||
|
||||
-- Speed value
|
||||
local speed_text = (round(state.speed * 100) / 100) .. 'x'
|
||||
ass:txt(half_x, ay + (notch_ay_big - ay) / 2, 5, speed_text, {
|
||||
size = self.font_size, color = bgt, border = options.text_border, border_color = bg, opacity = opacity,
|
||||
})
|
||||
|
||||
return ass
|
||||
end
|
||||
|
||||
return Speed
|
|
@ -1,430 +0,0 @@
|
|||
local Element = require('uosc_shared/elements/Element')
|
||||
|
||||
---@class Timeline : Element
|
||||
local Timeline = class(Element)
|
||||
|
||||
function Timeline:new() return Class.new(self) --[[@as Timeline]] end
|
||||
function Timeline:init()
|
||||
Element.init(self, 'timeline')
|
||||
---@type false|{pause: boolean, distance: number, last: {x: number, y: number}}
|
||||
self.pressed = false
|
||||
self.obstructed = false
|
||||
self.size_max = 0
|
||||
self.size_min = 0
|
||||
self.size_min_override = options.timeline_start_hidden and 0 or nil
|
||||
self.font_size = 0
|
||||
self.top_border = options.timeline_border
|
||||
self.is_hovered = false
|
||||
self.has_thumbnail = false
|
||||
|
||||
-- Delayed seeking timer
|
||||
self.seek_timer = mp.add_timeout(0.05, function() self:set_from_cursor() end)
|
||||
self.seek_timer:kill()
|
||||
|
||||
-- Release any dragging when file gets unloaded
|
||||
mp.register_event('end-file', function() self.pressed = false end)
|
||||
end
|
||||
|
||||
function Timeline:get_visibility()
|
||||
return Elements.controls and math.max(Elements.controls.proximity, Element.get_visibility(self))
|
||||
or Element.get_visibility(self)
|
||||
end
|
||||
|
||||
function Timeline:decide_enabled()
|
||||
local previous = self.enabled
|
||||
self.enabled = not self.obstructed and state.duration ~= nil and state.duration > 0 and state.time ~= nil
|
||||
if self.enabled ~= previous then Elements:trigger('timeline_enabled', self.enabled) end
|
||||
end
|
||||
|
||||
function Timeline:get_effective_size_min()
|
||||
return self.size_min_override or self.size_min
|
||||
end
|
||||
|
||||
function Timeline:get_effective_size()
|
||||
if Elements.speed and Elements.speed.dragging then return self.size_max end
|
||||
local size_min = self:get_effective_size_min()
|
||||
return size_min + math.ceil((self.size_max - size_min) * self:get_visibility())
|
||||
end
|
||||
|
||||
function Timeline:get_effective_line_width()
|
||||
return state.fullormaxed and options.timeline_line_width_fullscreen or options.timeline_line_width
|
||||
end
|
||||
|
||||
function Timeline:get_is_hovered() return self.enabled and self.is_hovered end
|
||||
|
||||
function Timeline:update_dimensions()
|
||||
if state.fullormaxed then
|
||||
self.size_min = options.timeline_size_min_fullscreen
|
||||
self.size_max = options.timeline_size_max_fullscreen
|
||||
else
|
||||
self.size_min = options.timeline_size_min
|
||||
self.size_max = options.timeline_size_max
|
||||
end
|
||||
self.font_size = math.floor(math.min((self.size_max + 60) * 0.2, self.size_max * 0.96) * options.font_scale)
|
||||
self.ax = Elements.window_border.size
|
||||
self.ay = display.height - Elements.window_border.size - self.size_max - self.top_border
|
||||
self.bx = display.width - Elements.window_border.size
|
||||
self.by = display.height - Elements.window_border.size
|
||||
self.width = self.bx - self.ax
|
||||
self.chapter_size = math.max((self.by - self.ay) / 10, 3)
|
||||
self.chapter_size_hover = self.chapter_size * 2
|
||||
|
||||
-- Disable if not enough space
|
||||
local available_space = display.height - Elements.window_border.size * 2
|
||||
if Elements.top_bar.enabled then available_space = available_space - Elements.top_bar.size end
|
||||
self.obstructed = available_space < self.size_max + 10
|
||||
self:decide_enabled()
|
||||
end
|
||||
|
||||
function Timeline:get_time_at_x(x)
|
||||
local line_width = (options.timeline_style == 'line' and self:get_effective_line_width() - 1 or 0)
|
||||
local time_width = self.width - line_width - 1
|
||||
local fax = (time_width) * state.time / state.duration
|
||||
local fbx = fax + line_width
|
||||
-- time starts 0.5 pixels in
|
||||
x = x - self.ax - 0.5
|
||||
if x > fbx then x = x - line_width
|
||||
elseif x > fax then x = fax end
|
||||
local progress = clamp(0, x / time_width, 1)
|
||||
return state.duration * progress
|
||||
end
|
||||
|
||||
---@param fast? boolean
|
||||
function Timeline:set_from_cursor(fast)
|
||||
if state.time and state.duration then
|
||||
mp.commandv('seek', self:get_time_at_x(cursor.x), fast and 'absolute+keyframes' or 'absolute+exact')
|
||||
end
|
||||
end
|
||||
|
||||
function Timeline:clear_thumbnail()
|
||||
mp.commandv('script-message-to', 'thumbfast', 'clear')
|
||||
self.has_thumbnail = false
|
||||
end
|
||||
|
||||
function Timeline:handle_cursor_down()
|
||||
self.pressed = {pause = state.pause, distance = 0, last = {x = cursor.x, y = cursor.y}}
|
||||
mp.set_property_native('pause', true)
|
||||
self:set_from_cursor()
|
||||
cursor.on_primary_up = function() self:handle_cursor_up() end
|
||||
end
|
||||
function Timeline:on_prop_duration() self:decide_enabled() end
|
||||
function Timeline:on_prop_time() self:decide_enabled() end
|
||||
function Timeline:on_prop_border() self:update_dimensions() end
|
||||
function Timeline:on_prop_fullormaxed() self:update_dimensions() end
|
||||
function Timeline:on_display() self:update_dimensions() end
|
||||
function Timeline:handle_cursor_up()
|
||||
self.seek_timer:kill()
|
||||
if self.pressed then
|
||||
mp.set_property_native('pause', self.pressed.pause)
|
||||
self.pressed = false
|
||||
end
|
||||
end
|
||||
function Timeline:on_global_mouse_leave()
|
||||
self.pressed = false
|
||||
end
|
||||
|
||||
function Timeline:on_global_mouse_move()
|
||||
if self.pressed then
|
||||
self.pressed.distance = self.pressed.distance + get_point_to_point_proximity(self.pressed.last, cursor)
|
||||
self.pressed.last.x, self.pressed.last.y = cursor.x, cursor.y
|
||||
if self.width / state.duration < 10 then
|
||||
self:set_from_cursor(true)
|
||||
self.seek_timer:kill()
|
||||
self.seek_timer:resume()
|
||||
else self:set_from_cursor() end
|
||||
end
|
||||
end
|
||||
function Timeline:handle_wheel_up() mp.commandv('seek', options.timeline_step) end
|
||||
function Timeline:handle_wheel_down() mp.commandv('seek', -options.timeline_step) end
|
||||
|
||||
function Timeline:render()
|
||||
if self.size_max == 0 then return end
|
||||
|
||||
local size_min = self:get_effective_size_min()
|
||||
local size = self:get_effective_size()
|
||||
local visibility = self:get_visibility()
|
||||
self.is_hovered = false
|
||||
|
||||
if size < 1 then
|
||||
if self.has_thumbnail then self:clear_thumbnail() end
|
||||
return
|
||||
end
|
||||
|
||||
if self.proximity_raw == 0 then
|
||||
self.is_hovered = true
|
||||
cursor.on_primary_down = function() self:handle_cursor_down() end
|
||||
cursor.on_wheel_down = function() self:handle_wheel_down() end
|
||||
cursor.on_wheel_up = function() self:handle_wheel_up() end
|
||||
end
|
||||
|
||||
if self.pressed then
|
||||
cursor.on_primary_up = function() self:handle_cursor_up() end
|
||||
end
|
||||
|
||||
local ass = assdraw.ass_new()
|
||||
|
||||
-- Text opacity rapidly drops to 0 just before it starts overflowing, or before it reaches timeline.size_min
|
||||
local hide_text_below = math.max(self.font_size * 0.8, size_min * 2)
|
||||
local hide_text_ramp = hide_text_below / 2
|
||||
local text_opacity = clamp(0, size - hide_text_below, hide_text_ramp) / hide_text_ramp
|
||||
|
||||
local spacing = math.max(math.floor((self.size_max - self.font_size) / 2.5), 4)
|
||||
local progress = state.time / state.duration
|
||||
local is_line = options.timeline_style == 'line'
|
||||
|
||||
-- Foreground & Background bar coordinates
|
||||
local bax, bay, bbx, bby = self.ax, self.by - size - self.top_border, self.bx, self.by
|
||||
local fax, fay, fbx, fby = 0, bay + self.top_border, 0, bby
|
||||
local fcy = fay + (size / 2)
|
||||
|
||||
local line_width = 0
|
||||
|
||||
if is_line then
|
||||
local minimized_fraction = 1 - math.min((size - size_min) / ((self.size_max - size_min) / 8), 1)
|
||||
local line_width_max = self:get_effective_line_width()
|
||||
local max_min_width_delta = size_min > 0
|
||||
and line_width_max - line_width_max * options.timeline_line_width_minimized_scale
|
||||
or 0
|
||||
line_width = line_width_max - (max_min_width_delta * minimized_fraction)
|
||||
fax = bax + (self.width - line_width) * progress
|
||||
fbx = fax + line_width
|
||||
line_width = line_width - 1
|
||||
else
|
||||
fax, fbx = bax, bax + self.width * progress
|
||||
end
|
||||
|
||||
local foreground_size = fby - fay
|
||||
local foreground_coordinates = round(fax) .. ',' .. fay .. ',' .. round(fbx) .. ',' .. fby -- for clipping
|
||||
|
||||
-- time starts 0.5 pixels in
|
||||
local time_ax = bax + 0.5
|
||||
local time_width = self.width - line_width - 1
|
||||
|
||||
-- time to x: calculates x coordinate so that it never lies inside of the line
|
||||
local function t2x(time)
|
||||
local x = time_ax + time_width * time / state.duration
|
||||
return time <= state.time and x or x + line_width
|
||||
end
|
||||
|
||||
-- Background
|
||||
ass:new_event()
|
||||
ass:pos(0, 0)
|
||||
ass:append('{\\rDefault\\an7\\blur0\\bord0\\1c&H' .. bg .. '}')
|
||||
ass:opacity(options.timeline_opacity)
|
||||
ass:draw_start()
|
||||
ass:rect_cw(bax, bay, fax, bby) --left of progress
|
||||
ass:rect_cw(fbx, bay, bbx, bby) --right of progress
|
||||
ass:rect_cw(fax, bay, fbx, fay) --above progress
|
||||
ass:draw_stop()
|
||||
|
||||
-- Progress
|
||||
ass:rect(fax, fay, fbx, fby, {opacity = options.timeline_opacity})
|
||||
|
||||
-- Uncached ranges
|
||||
local buffered_playtime = nil
|
||||
if state.uncached_ranges then
|
||||
local opts = {size = 80, anchor_y = fby}
|
||||
local texture_char = visibility > 0 and 'b' or 'a'
|
||||
local offset = opts.size / (visibility > 0 and 24 or 28)
|
||||
for _, range in ipairs(state.uncached_ranges) do
|
||||
if not buffered_playtime and (range[1] > state.time or range[2] > state.time) then
|
||||
buffered_playtime = (range[1] - state.time) / (state.speed or 1)
|
||||
end
|
||||
if options.timeline_cache then
|
||||
local ax = range[1] < 0.5 and bax or math.floor(t2x(range[1]))
|
||||
local bx = range[2] > state.duration - 0.5 and bbx or math.ceil(t2x(range[2]))
|
||||
opts.color, opts.opacity, opts.anchor_x = 'ffffff', 0.4 - (0.2 * visibility), bax
|
||||
ass:texture(ax, fay, bx, fby, texture_char, opts)
|
||||
opts.color, opts.opacity, opts.anchor_x = '000000', 0.6 - (0.2 * visibility), bax + offset
|
||||
ass:texture(ax, fay, bx, fby, texture_char, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Custom ranges
|
||||
for _, chapter_range in ipairs(state.chapter_ranges) do
|
||||
local rax = chapter_range.start < 0.1 and bax or t2x(chapter_range.start)
|
||||
local rbx = chapter_range['end'] > state.duration - 0.1 and bbx
|
||||
or t2x(math.min(chapter_range['end'], state.duration))
|
||||
ass:rect(rax, fay, rbx, fby, {color = chapter_range.color, opacity = chapter_range.opacity})
|
||||
end
|
||||
|
||||
-- Chapters
|
||||
local hovered_chapter = nil
|
||||
if (options.timeline_chapters_opacity > 0
|
||||
and (#state.chapters > 0 or state.ab_loop_a or state.ab_loop_b)
|
||||
) then
|
||||
local diamond_radius = foreground_size < 3 and foreground_size or self.chapter_size
|
||||
local diamond_radius_hovered = diamond_radius * 2
|
||||
local diamond_border = options.timeline_border and math.max(options.timeline_border, 1) or 1
|
||||
|
||||
if diamond_radius > 0 then
|
||||
local function draw_chapter(time, radius)
|
||||
local chapter_x, chapter_y = t2x(time), fay - 1
|
||||
ass:new_event()
|
||||
ass:append(string.format(
|
||||
'{\\pos(0,0)\\rDefault\\an7\\blur0\\yshad0.01\\bord%f\\1c&H%s\\3c&H%s\\4c&H%s\\1a&H%X&\\3a&H00&\\4a&H00&}',
|
||||
diamond_border, fg, bg, bg, opacity_to_alpha(options.timeline_opacity * options.timeline_chapters_opacity)
|
||||
))
|
||||
ass:draw_start()
|
||||
ass:move_to(chapter_x - radius, chapter_y)
|
||||
ass:line_to(chapter_x, chapter_y - radius)
|
||||
ass:line_to(chapter_x + radius, chapter_y)
|
||||
ass:line_to(chapter_x, chapter_y + radius)
|
||||
ass:draw_stop()
|
||||
end
|
||||
|
||||
if #state.chapters > 0 then
|
||||
-- Find hovered chapter indicator
|
||||
local closest_delta = INFINITY
|
||||
|
||||
if self.proximity_raw < diamond_radius_hovered then
|
||||
for i, chapter in ipairs(state.chapters) do
|
||||
local chapter_x, chapter_y = t2x(chapter.time), fay - 1
|
||||
local cursor_chapter_delta = math.sqrt((cursor.x - chapter_x) ^ 2 + (cursor.y - chapter_y) ^ 2)
|
||||
if cursor_chapter_delta <= diamond_radius_hovered and cursor_chapter_delta < closest_delta then
|
||||
hovered_chapter, closest_delta = chapter, cursor_chapter_delta
|
||||
self.is_hovered = true
|
||||
cursor.on_primary_down = function()
|
||||
mp.commandv('seek', hovered_chapter.time, 'absolute+exact')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i, chapter in ipairs(state.chapters) do
|
||||
if chapter ~= hovered_chapter then draw_chapter(chapter.time, diamond_radius) end
|
||||
end
|
||||
|
||||
-- Render hovered chapter above others
|
||||
if hovered_chapter then draw_chapter(hovered_chapter.time, diamond_radius_hovered) end
|
||||
end
|
||||
|
||||
-- A-B loop indicators
|
||||
local has_a, has_b = state.ab_loop_a and state.ab_loop_a >= 0, state.ab_loop_b and state.ab_loop_b > 0
|
||||
local ab_radius = round(math.min(math.max(8, foreground_size * 0.25), foreground_size))
|
||||
|
||||
---@param time number
|
||||
---@param kind 'a'|'b'
|
||||
local function draw_ab_indicator(time, kind)
|
||||
local x = t2x(time)
|
||||
ass:new_event()
|
||||
ass:append(string.format(
|
||||
'{\\pos(0,0)\\rDefault\\an7\\blur0\\yshad0.01\\bord%f\\1c&H%s\\3c&H%s\\4c&H%s\\1a&H%X&\\3a&H00&\\4a&H00&}',
|
||||
diamond_border, fg, bg, bg, opacity_to_alpha(options.timeline_opacity * options.timeline_chapters_opacity)
|
||||
))
|
||||
ass:draw_start()
|
||||
ass:move_to(x, fby - ab_radius)
|
||||
if kind == 'b' then ass:line_to(x + 3, fby - ab_radius) end
|
||||
ass:line_to(x + (kind == 'a' and 0 or ab_radius), fby)
|
||||
ass:line_to(x - (kind == 'b' and 0 or ab_radius), fby)
|
||||
if kind == 'a' then ass:line_to(x - 3, fby - ab_radius) end
|
||||
ass:draw_stop()
|
||||
end
|
||||
|
||||
if has_a then draw_ab_indicator(state.ab_loop_a, 'a') end
|
||||
if has_b then draw_ab_indicator(state.ab_loop_b, 'b') end
|
||||
end
|
||||
end
|
||||
|
||||
local function draw_timeline_text(x, y, align, text, opts)
|
||||
opts.color, opts.border_color = fgt, fg
|
||||
opts.clip = '\\clip(' .. foreground_coordinates .. ')'
|
||||
ass:txt(x, y, align, text, opts)
|
||||
opts.color, opts.border_color = bgt, bg
|
||||
opts.clip = '\\iclip(' .. foreground_coordinates .. ')'
|
||||
ass:txt(x, y, align, text, opts)
|
||||
end
|
||||
|
||||
-- Time values
|
||||
if text_opacity > 0 then
|
||||
local time_opts = {size = self.font_size, opacity = text_opacity, border = 2}
|
||||
-- Upcoming cache time
|
||||
if buffered_playtime and options.buffered_time_threshold > 0
|
||||
and buffered_playtime < options.buffered_time_threshold then
|
||||
local x, align = fbx + 5, 4
|
||||
local cache_opts = {size = self.font_size * 0.8, opacity = text_opacity * 0.6, border = 1}
|
||||
local human = round(math.max(buffered_playtime, 0)) .. 's'
|
||||
local width = text_width(human, cache_opts)
|
||||
local time_width = timestamp_width(state.time_human, time_opts)
|
||||
local time_width_end = timestamp_width(state.destination_time_human, time_opts)
|
||||
local min_x, max_x = bax + spacing + 5 + time_width, bbx - spacing - 5 - time_width_end
|
||||
if x < min_x then x = min_x elseif x + width > max_x then x, align = max_x, 6 end
|
||||
draw_timeline_text(x, fcy, align, human, cache_opts)
|
||||
end
|
||||
|
||||
-- Elapsed time
|
||||
if state.time_human then
|
||||
draw_timeline_text(bax + spacing, fcy, 4, state.time_human, time_opts)
|
||||
end
|
||||
|
||||
-- End time
|
||||
if state.destination_time_human then
|
||||
draw_timeline_text(bbx - spacing, fcy, 6, state.destination_time_human, time_opts)
|
||||
end
|
||||
end
|
||||
|
||||
-- Hovered time and chapter
|
||||
local rendered_thumbnail = false
|
||||
if (self.proximity_raw == 0 or self.pressed or hovered_chapter) and
|
||||
not (Elements.speed and Elements.speed.dragging) then
|
||||
local cursor_x = hovered_chapter and t2x(hovered_chapter.time) or cursor.x
|
||||
local hovered_seconds = hovered_chapter and hovered_chapter.time or self:get_time_at_x(cursor.x)
|
||||
|
||||
-- Cursor line
|
||||
-- 0.5 to switch when the pixel is half filled in
|
||||
local color = ((fax - 0.5) < cursor_x and cursor_x < (fbx + 0.5)) and bg or fg
|
||||
local ax, ay, bx, by = cursor_x - 0.5, fay, cursor_x + 0.5, fby
|
||||
ass:rect(ax, ay, bx, by, {color = color, opacity = 0.2})
|
||||
local tooltip_anchor = {ax = ax, ay = ay, bx = bx, by = by}
|
||||
|
||||
-- Timestamp
|
||||
local offset = #state.chapters > 0 and 10 or 4
|
||||
local opts = {size = self.font_size, offset = offset}
|
||||
local hovered_time_human = format_time(hovered_seconds, state.duration)
|
||||
opts.width_overwrite = timestamp_width(hovered_time_human, opts)
|
||||
ass:tooltip(tooltip_anchor, hovered_time_human, opts)
|
||||
tooltip_anchor.ay = tooltip_anchor.ay - self.font_size - offset
|
||||
|
||||
-- Thumbnail
|
||||
if not thumbnail.disabled
|
||||
and (not self.pressed or self.pressed.distance < 5)
|
||||
and thumbnail.width ~= 0
|
||||
and thumbnail.height ~= 0
|
||||
then
|
||||
local scale_x, scale_y = display.scale_x, display.scale_y
|
||||
local border, margin_x, margin_y = math.ceil(2 * scale_x), round(10 * scale_x), round(5 * scale_y)
|
||||
local thumb_x_margin, thumb_y_margin = border + margin_x, border + margin_y
|
||||
local thumb_width, thumb_height = thumbnail.width, thumbnail.height
|
||||
local thumb_x = round(clamp(
|
||||
thumb_x_margin, cursor_x * scale_x - thumb_width / 2,
|
||||
display.width * scale_x - thumb_width - thumb_x_margin
|
||||
))
|
||||
local thumb_y = round(tooltip_anchor.ay * scale_y - thumb_y_margin - thumb_height)
|
||||
local ax, ay = (thumb_x - border) / scale_x, (thumb_y - border) / scale_y
|
||||
local bx, by = (thumb_x + thumb_width + border) / scale_x, (thumb_y + thumb_height + border) / scale_y
|
||||
ass:rect(ax, ay, bx, by, {color = bg, border = 1, border_color = fg, border_opacity = 0.08, radius = 2})
|
||||
mp.commandv('script-message-to', 'thumbfast', 'thumb', hovered_seconds, thumb_x, thumb_y)
|
||||
self.has_thumbnail, rendered_thumbnail = true, true
|
||||
tooltip_anchor.ax, tooltip_anchor.bx, tooltip_anchor.ay = ax, bx, ay
|
||||
end
|
||||
|
||||
-- Chapter title
|
||||
if #state.chapters > 0 then
|
||||
local _, chapter = itable_find(state.chapters, function(c) return hovered_seconds >= c.time end, true)
|
||||
if chapter and not chapter.is_end_only then
|
||||
ass:tooltip(tooltip_anchor, chapter.title_wrapped, {
|
||||
size = self.font_size, offset = 10, responsive = false, bold = true,
|
||||
width_overwrite = chapter.title_wrapped_width * self.font_size,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Clear thumbnail
|
||||
if not rendered_thumbnail and self.has_thumbnail then self:clear_thumbnail() end
|
||||
|
||||
return ass
|
||||
end
|
||||
|
||||
return Timeline
|
|
@ -1,253 +0,0 @@
|
|||
local Element = require('uosc_shared/elements/Element')
|
||||
|
||||
---@alias TopBarButtonProps {icon: string; background: string; anchor_id?: string; command: string|fun()}
|
||||
|
||||
---@class TopBarButton : Element
|
||||
local TopBarButton = class(Element)
|
||||
|
||||
---@param id string
|
||||
---@param props TopBarButtonProps
|
||||
function TopBarButton:new(id, props) return Class.new(self, id, props) --[[@as TopBarButton]] end
|
||||
function TopBarButton:init(id, props)
|
||||
Element.init(self, id, props)
|
||||
self.anchor_id = 'top_bar'
|
||||
self.icon = props.icon
|
||||
self.background = props.background
|
||||
self.command = props.command
|
||||
end
|
||||
|
||||
function TopBarButton:handle_cursor_down()
|
||||
mp.command(type(self.command) == 'function' and self.command() or self.command)
|
||||
end
|
||||
|
||||
function TopBarButton:render()
|
||||
local visibility = self:get_visibility()
|
||||
if visibility <= 0 then return end
|
||||
local ass = assdraw.ass_new()
|
||||
|
||||
-- Background on hover
|
||||
if self.proximity_raw == 0 then
|
||||
ass:rect(self.ax, self.ay, self.bx, self.by, {color = self.background, opacity = visibility})
|
||||
cursor.on_primary_down = function() self:handle_cursor_down() end
|
||||
end
|
||||
|
||||
local width, height = self.bx - self.ax, self.by - self.ay
|
||||
local icon_size = math.min(width, height) * 0.5
|
||||
ass:icon(self.ax + width / 2, self.ay + height / 2, icon_size, self.icon, {
|
||||
opacity = visibility, border = options.text_border,
|
||||
})
|
||||
|
||||
return ass
|
||||
end
|
||||
|
||||
--[[ TopBar ]]
|
||||
|
||||
---@class TopBar : Element
|
||||
local TopBar = class(Element)
|
||||
|
||||
function TopBar:new() return Class.new(self) --[[@as TopBar]] end
|
||||
function TopBar:init()
|
||||
Element.init(self, 'top_bar')
|
||||
self.size = 0
|
||||
self.icon_size, self.spacing, self.font_size, self.title_bx, self.title_by = 1, 1, 1, 1, 1
|
||||
self.show_alt_title = false
|
||||
self.main_title, self.alt_title = nil, nil
|
||||
|
||||
local function get_maximized_command()
|
||||
return state.border
|
||||
and (state.fullscreen and 'set fullscreen no;cycle window-maximized' or 'cycle window-maximized')
|
||||
or 'set window-maximized no;cycle fullscreen'
|
||||
end
|
||||
|
||||
-- Order aligns from right to left
|
||||
self.buttons = {
|
||||
TopBarButton:new('tb_close', {icon = 'close', background = '2311e8', command = 'quit'}),
|
||||
TopBarButton:new('tb_max', {icon = 'crop_square', background = '222222', command = get_maximized_command}),
|
||||
TopBarButton:new('tb_min', {icon = 'minimize', background = '222222', command = 'cycle window-minimized'}),
|
||||
}
|
||||
|
||||
self:decide_titles()
|
||||
end
|
||||
|
||||
function TopBar:decide_enabled()
|
||||
if options.top_bar == 'no-border' then
|
||||
self.enabled = not state.border or state.fullscreen
|
||||
else
|
||||
self.enabled = options.top_bar == 'always'
|
||||
end
|
||||
self.enabled = self.enabled and (options.top_bar_controls or options.top_bar_title)
|
||||
for _, element in ipairs(self.buttons) do
|
||||
element.enabled = self.enabled and options.top_bar_controls
|
||||
end
|
||||
end
|
||||
|
||||
function TopBar:decide_titles()
|
||||
self.alt_title = state.alt_title ~= '' and state.alt_title or nil
|
||||
self.main_title = state.title ~= '' and state.title or nil
|
||||
|
||||
-- Fall back to alt title if main is empty
|
||||
if not self.main_title then
|
||||
self.main_title, self.alt_title = self.alt_title, nil
|
||||
end
|
||||
|
||||
-- Deduplicate the main and alt titles by checking if one completely
|
||||
-- contains the other, and using only the longer one.
|
||||
if self.main_title and self.alt_title and not self.show_alt_title then
|
||||
local longer_title, shorter_title
|
||||
if #self.main_title < #self.alt_title then
|
||||
longer_title, shorter_title = self.alt_title, self.main_title
|
||||
else
|
||||
longer_title, shorter_title = self.main_title, self.alt_title
|
||||
end
|
||||
|
||||
local escaped_shorter_title = string.gsub(shorter_title --[[@as string]], "[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1")
|
||||
if string.match(longer_title --[[@as string]], escaped_shorter_title) then
|
||||
self.main_title, self.alt_title = longer_title, nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TopBar:update_dimensions()
|
||||
self.size = state.fullormaxed and options.top_bar_size_fullscreen or options.top_bar_size
|
||||
self.icon_size = round(self.size * 0.5)
|
||||
self.spacing = math.ceil(self.size * 0.25)
|
||||
self.font_size = math.floor((self.size - (self.spacing * 2)) * options.font_scale)
|
||||
self.button_width = round(self.size * 1.15)
|
||||
self.ay = Elements.window_border.size
|
||||
self.bx = display.width - Elements.window_border.size
|
||||
self.by = self.size + Elements.window_border.size
|
||||
self.title_bx = self.bx - (options.top_bar_controls and (self.button_width * 3) or 0)
|
||||
self.ax = options.top_bar_title and Elements.window_border.size or self.title_bx
|
||||
|
||||
local button_bx = self.bx
|
||||
for _, element in pairs(self.buttons) do
|
||||
element.ax, element.bx = button_bx - self.button_width, button_bx
|
||||
element.ay, element.by = self.ay, self.by
|
||||
button_bx = button_bx - self.button_width
|
||||
end
|
||||
end
|
||||
|
||||
function TopBar:toggle_title()
|
||||
if options.top_bar_alt_title_place ~= 'toggle' then return end
|
||||
self.show_alt_title = not self.show_alt_title
|
||||
end
|
||||
|
||||
function TopBar:on_prop_title() self:decide_titles() end
|
||||
function TopBar:on_prop_alt_title() self:decide_titles() end
|
||||
|
||||
function TopBar:on_prop_border()
|
||||
self:decide_enabled()
|
||||
self:update_dimensions()
|
||||
end
|
||||
|
||||
function TopBar:on_prop_fullscreen()
|
||||
self:decide_enabled()
|
||||
self:update_dimensions()
|
||||
end
|
||||
|
||||
function TopBar:on_prop_maximized()
|
||||
self:decide_enabled()
|
||||
self:update_dimensions()
|
||||
end
|
||||
|
||||
function TopBar:on_display() self:update_dimensions() end
|
||||
|
||||
function TopBar:render()
|
||||
local visibility = self:get_visibility()
|
||||
if visibility <= 0 then return end
|
||||
local ass = assdraw.ass_new()
|
||||
|
||||
-- Window title
|
||||
if options.top_bar_title and (state.title or state.has_playlist) then
|
||||
local bg_margin = math.floor((self.size - self.font_size) / 4)
|
||||
local padding = self.font_size / 2
|
||||
local title_ax = self.ax + bg_margin
|
||||
local title_ay = self.ay + bg_margin
|
||||
local max_bx = self.title_bx - self.spacing
|
||||
|
||||
-- Playlist position
|
||||
if state.has_playlist then
|
||||
local text = state.playlist_pos .. '' .. state.playlist_count
|
||||
local formatted_text = '{\\b1}' .. state.playlist_pos .. '{\\b0\\fs' .. self.font_size * 0.9 .. '}/'
|
||||
.. state.playlist_count
|
||||
local opts = {size = self.font_size, wrap = 2, color = fgt, opacity = visibility}
|
||||
local bx = round(title_ax + text_width(text, opts) + padding * 2)
|
||||
ass:rect(title_ax, title_ay, bx, self.by - bg_margin, {color = fg, opacity = visibility, radius = 2})
|
||||
ass:txt(title_ax + (bx - title_ax) / 2, self.ay + (self.size / 2), 5, formatted_text, opts)
|
||||
title_ax = bx + bg_margin
|
||||
local rect = {ax = self.ax, ay = self.ay, bx = bx, by = self.by}
|
||||
|
||||
if get_point_to_rectangle_proximity(cursor, rect) == 0 then
|
||||
cursor.on_primary_down = function() mp.command('script-binding uosc/playlist') end
|
||||
end
|
||||
end
|
||||
|
||||
-- Skip rendering titles if there's not enough horizontal space
|
||||
if max_bx - title_ax > self.font_size * 3 then
|
||||
-- Main title
|
||||
local main_title = self.show_alt_title and self.alt_title or self.main_title
|
||||
if main_title then
|
||||
local opts = {
|
||||
size = self.font_size, wrap = 2, color = bgt, border = 1, border_color = bg, opacity = visibility,
|
||||
clip = string.format('\\clip(%d, %d, %d, %d)', self.ax, self.ay, max_bx, self.by),
|
||||
}
|
||||
local bx = math.min(max_bx, title_ax + text_width(main_title, opts) + padding * 2)
|
||||
local by = self.by - bg_margin
|
||||
local rect = {ax = title_ax, ay = self.ay, bx = self.title_bx, by = self.by}
|
||||
|
||||
if get_point_to_rectangle_proximity(cursor, rect) == 0 then
|
||||
cursor.on_primary_down = function() self:toggle_title() end
|
||||
end
|
||||
|
||||
ass:rect(title_ax, title_ay, bx, by, {
|
||||
color = bg, opacity = visibility * options.top_bar_title_opacity, radius = 2,
|
||||
})
|
||||
ass:txt(title_ax + padding, self.ay + (self.size / 2), 4, main_title, opts)
|
||||
title_ay = by + 1
|
||||
end
|
||||
|
||||
-- Alt title
|
||||
if self.alt_title and options.top_bar_alt_title_place == 'below' then
|
||||
local font_size = self.font_size * 0.9
|
||||
local height = font_size * 1.3
|
||||
local by = title_ay + height
|
||||
local opts = {
|
||||
size = font_size, wrap = 2, color = bgt, border = 1, border_color = bg, opacity = visibility
|
||||
}
|
||||
local bx = math.min(max_bx, title_ax + text_width(self.alt_title, opts) + padding * 2)
|
||||
opts.clip = string.format('\\clip(%d, %d, %d, %d)', title_ax, title_ay, bx, by)
|
||||
ass:rect(title_ax, title_ay, bx, by, {
|
||||
color = bg, opacity = visibility * options.top_bar_title_opacity, radius = 2,
|
||||
})
|
||||
ass:txt(title_ax + padding, title_ay + height / 2, 4, self.alt_title, opts)
|
||||
title_ay = by + 1
|
||||
end
|
||||
|
||||
-- Subtitle: current chapter
|
||||
if state.current_chapter then
|
||||
local font_size = self.font_size * 0.8
|
||||
local height = font_size * 1.3
|
||||
local text = '└ ' .. state.current_chapter.index .. ': ' .. state.current_chapter.title
|
||||
local by = title_ay + height
|
||||
local opts = {
|
||||
size = font_size, italic = true, wrap = 2, color = bgt,
|
||||
border = 1, border_color = bg, opacity = visibility * 0.8,
|
||||
}
|
||||
local bx = math.min(max_bx, title_ax + text_width(text, opts) + padding * 2)
|
||||
opts.clip = string.format('\\clip(%d, %d, %d, %d)', title_ax, title_ay, bx, by)
|
||||
ass:rect(title_ax, title_ay, bx, by, {
|
||||
color = bg, opacity = visibility * options.top_bar_title_opacity, radius = 2,
|
||||
})
|
||||
ass:txt(title_ax + padding, title_ay + height / 2, 4, text, opts)
|
||||
title_ay = by + 1
|
||||
end
|
||||
end
|
||||
self.title_by = title_ay - 1
|
||||
else
|
||||
self.title_by = self.ay
|
||||
end
|
||||
|
||||
return ass
|
||||
end
|
||||
|
||||
return TopBar
|
|
@ -1,252 +0,0 @@
|
|||
local Element = require('uosc_shared/elements/Element')
|
||||
|
||||
--[[ MuteButton ]]
|
||||
|
||||
---@class MuteButton : Element
|
||||
local MuteButton = class(Element)
|
||||
---@param props? ElementProps
|
||||
function MuteButton:new(props) return Class.new(self, 'volume_mute', props) --[[@as MuteButton]] end
|
||||
function MuteButton:get_visibility() return Elements.volume:get_visibility(self) end
|
||||
function MuteButton:render()
|
||||
local visibility = self:get_visibility()
|
||||
if visibility <= 0 then return end
|
||||
if self.proximity_raw == 0 then
|
||||
cursor.on_primary_down = function() mp.commandv('cycle', 'mute') end
|
||||
end
|
||||
local ass = assdraw.ass_new()
|
||||
local icon_name = state.mute and 'volume_off' or 'volume_up'
|
||||
local width = self.bx - self.ax
|
||||
ass:icon(self.ax + (width / 2), self.by, width * 0.7, icon_name,
|
||||
{border = options.text_border, opacity = options.volume_opacity * visibility, align = 2}
|
||||
)
|
||||
return ass
|
||||
end
|
||||
|
||||
--[[ VolumeSlider ]]
|
||||
|
||||
---@class VolumeSlider : Element
|
||||
local VolumeSlider = class(Element)
|
||||
---@param props? ElementProps
|
||||
function VolumeSlider:new(props) return Class.new(self, props) --[[@as VolumeSlider]] end
|
||||
function VolumeSlider:init(props)
|
||||
Element.init(self, 'volume_slider', props)
|
||||
self.pressed = false
|
||||
self.nudge_y = 0 -- vertical position where volume overflows 100
|
||||
self.nudge_size = 0
|
||||
self.draw_nudge = false
|
||||
self.spacing = 0
|
||||
self.radius = 1
|
||||
end
|
||||
|
||||
function VolumeSlider:get_visibility() return Elements.volume:get_visibility(self) end
|
||||
|
||||
function VolumeSlider:set_volume(volume)
|
||||
volume = round(volume / options.volume_step) * options.volume_step
|
||||
if state.volume == volume then return end
|
||||
mp.commandv('set', 'volume', clamp(0, volume, state.volume_max))
|
||||
end
|
||||
|
||||
function VolumeSlider:set_from_cursor()
|
||||
local volume_fraction = (self.by - cursor.y - options.volume_border) / (self.by - self.ay - options.volume_border)
|
||||
self:set_volume(volume_fraction * state.volume_max)
|
||||
end
|
||||
|
||||
function VolumeSlider:on_coordinates()
|
||||
if type(state.volume_max) ~= 'number' or state.volume_max <= 0 then return end
|
||||
local width = self.bx - self.ax
|
||||
self.nudge_y = self.by - round((self.by - self.ay) * (100 / state.volume_max))
|
||||
self.nudge_size = round(width * 0.18)
|
||||
self.draw_nudge = self.ay < self.nudge_y
|
||||
self.spacing = round(width * 0.2)
|
||||
self.radius = math.max(2, (self.bx - self.ax) / 10)
|
||||
end
|
||||
function VolumeSlider:on_global_mouse_move()
|
||||
if self.pressed then self:set_from_cursor() end
|
||||
end
|
||||
function VolumeSlider:handle_wheel_up() self:set_volume(state.volume + options.volume_step) end
|
||||
function VolumeSlider:handle_wheel_down() self:set_volume(state.volume - options.volume_step) end
|
||||
|
||||
function VolumeSlider:render()
|
||||
local visibility = self:get_visibility()
|
||||
local ax, ay, bx, by = self.ax, self.ay, self.bx, self.by
|
||||
local width, height = bx - ax, by - ay
|
||||
|
||||
if width <= 0 or height <= 0 or visibility <= 0 then return end
|
||||
|
||||
if self.proximity_raw == 0 then
|
||||
cursor.on_primary_down = function()
|
||||
self.pressed = true
|
||||
self:set_from_cursor()
|
||||
cursor.on_primary_up = function() self.pressed = false end
|
||||
end
|
||||
cursor.on_wheel_down = function() self:handle_wheel_down() end
|
||||
cursor.on_wheel_up = function() self:handle_wheel_up() end
|
||||
end
|
||||
if self.pressed then cursor.on_primary_up = function()
|
||||
self.pressed = false end
|
||||
end
|
||||
|
||||
local ass = assdraw.ass_new()
|
||||
local nudge_y, nudge_size = self.draw_nudge and self.nudge_y or -INFINITY, self.nudge_size
|
||||
local volume_y = self.ay + options.volume_border +
|
||||
((height - (options.volume_border * 2)) * (1 - math.min(state.volume / state.volume_max, 1)))
|
||||
|
||||
-- Draws a rectangle with nudge at requested position
|
||||
---@param p number Padding from slider edges.
|
||||
---@param cy? number A y coordinate where to clip the path from the bottom.
|
||||
function create_nudged_path(p, cy)
|
||||
cy = cy or ay + p
|
||||
local ax, bx, by = ax + p, bx - p, by - p
|
||||
local r = math.max(1, self.radius - p)
|
||||
local d, rh = r * 2, r / 2
|
||||
local nudge_size = ((QUARTER_PI_SIN * (nudge_size - p)) + p) / QUARTER_PI_SIN
|
||||
local path = assdraw.ass_new()
|
||||
path:move_to(bx - r, by)
|
||||
path:line_to(ax + r, by)
|
||||
if cy > by - d then
|
||||
local subtracted_radius = (d - (cy - (by - d))) / 2
|
||||
local xbd = (r - subtracted_radius * 1.35) -- x bezier delta
|
||||
path:bezier_curve(ax + xbd, by, ax + xbd, cy, ax + r, cy)
|
||||
path:line_to(bx - r, cy)
|
||||
path:bezier_curve(bx - xbd, cy, bx - xbd, by, bx - r, by)
|
||||
else
|
||||
path:bezier_curve(ax + rh, by, ax, by - rh, ax, by - r)
|
||||
local nudge_bottom_y = nudge_y + nudge_size
|
||||
|
||||
if cy + rh <= nudge_bottom_y then
|
||||
path:line_to(ax, nudge_bottom_y)
|
||||
if cy <= nudge_y then
|
||||
path:line_to((ax + nudge_size), nudge_y)
|
||||
local nudge_top_y = nudge_y - nudge_size
|
||||
if cy <= nudge_top_y then
|
||||
local r, rh = r, rh
|
||||
if cy > nudge_top_y - r then
|
||||
r = nudge_top_y - cy
|
||||
rh = r / 2
|
||||
end
|
||||
path:line_to(ax, nudge_top_y)
|
||||
path:line_to(ax, cy + r)
|
||||
path:bezier_curve(ax, cy + rh, ax + rh, cy, ax + r, cy)
|
||||
path:line_to(bx - r, cy)
|
||||
path:bezier_curve(bx - rh, cy, bx, cy + rh, bx, cy + r)
|
||||
path:line_to(bx, nudge_top_y)
|
||||
else
|
||||
local triangle_side = cy - nudge_top_y
|
||||
path:line_to((ax + triangle_side), cy)
|
||||
path:line_to((bx - triangle_side), cy)
|
||||
end
|
||||
path:line_to((bx - nudge_size), nudge_y)
|
||||
else
|
||||
local triangle_side = nudge_bottom_y - cy
|
||||
path:line_to((ax + triangle_side), cy)
|
||||
path:line_to((bx - triangle_side), cy)
|
||||
end
|
||||
path:line_to(bx, nudge_bottom_y)
|
||||
else
|
||||
path:line_to(ax, cy + r)
|
||||
path:bezier_curve(ax, cy + rh, ax + rh, cy, ax + r, cy)
|
||||
path:line_to(bx - r, cy)
|
||||
path:bezier_curve(bx - rh, cy, bx, cy + rh, bx, cy + r)
|
||||
end
|
||||
path:line_to(bx, by - r)
|
||||
path:bezier_curve(bx, by - rh, bx - rh, by, bx - r, by)
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
-- BG & FG paths
|
||||
local bg_path = create_nudged_path(0)
|
||||
local fg_path = create_nudged_path(options.volume_border, volume_y)
|
||||
|
||||
-- Background
|
||||
ass:new_event()
|
||||
ass:append('{\\rDefault\\an7\\blur0\\bord0\\1c&H' .. bg ..
|
||||
'\\iclip(' .. fg_path.scale .. ', ' .. fg_path.text .. ')}')
|
||||
ass:opacity(options.volume_opacity, visibility)
|
||||
ass:pos(0, 0)
|
||||
ass:draw_start()
|
||||
ass:append(bg_path.text)
|
||||
ass:draw_stop()
|
||||
|
||||
-- Foreground
|
||||
ass:new_event()
|
||||
ass:append('{\\rDefault\\an7\\blur0\\bord0\\1c&H' .. fg .. '}')
|
||||
ass:opacity(options.volume_opacity, visibility)
|
||||
ass:pos(0, 0)
|
||||
ass:draw_start()
|
||||
ass:append(fg_path.text)
|
||||
ass:draw_stop()
|
||||
|
||||
-- Current volume value
|
||||
local volume_string = tostring(round(state.volume * 10) / 10)
|
||||
local font_size = round(((width * 0.6) - (#volume_string * (width / 20))) * options.font_scale)
|
||||
if volume_y < self.by - self.spacing then
|
||||
ass:txt(self.ax + (width / 2), self.by - self.spacing, 2, volume_string, {
|
||||
size = font_size, color = fgt, opacity = visibility,
|
||||
clip = '\\clip(' .. fg_path.scale .. ', ' .. fg_path.text .. ')',
|
||||
})
|
||||
end
|
||||
if volume_y > self.by - self.spacing - font_size then
|
||||
ass:txt(self.ax + (width / 2), self.by - self.spacing, 2, volume_string, {
|
||||
size = font_size, color = bgt, opacity = visibility,
|
||||
clip = '\\iclip(' .. fg_path.scale .. ', ' .. fg_path.text .. ')',
|
||||
})
|
||||
end
|
||||
|
||||
-- Disabled stripes for no audio
|
||||
if not state.has_audio then
|
||||
local fg_100_path = create_nudged_path(options.volume_border)
|
||||
local texture_opts = {
|
||||
size = 200, color = 'ffffff', opacity = visibility * 0.1, anchor_x = ax,
|
||||
clip = '\\clip(' .. fg_100_path.scale .. ',' .. fg_100_path.text .. ')',
|
||||
}
|
||||
ass:texture(ax, ay, bx, by, 'a', texture_opts)
|
||||
texture_opts.color = '000000'
|
||||
texture_opts.anchor_x = ax + texture_opts.size / 28
|
||||
ass:texture(ax, ay, bx, by, 'a', texture_opts)
|
||||
end
|
||||
|
||||
return ass
|
||||
end
|
||||
|
||||
--[[ Volume ]]
|
||||
|
||||
---@class Volume : Element
|
||||
local Volume = class(Element)
|
||||
|
||||
function Volume:new() return Class.new(self) --[[@as Volume]] end
|
||||
function Volume:init()
|
||||
Element.init(self, 'volume')
|
||||
self.mute = MuteButton:new({anchor_id = 'volume'})
|
||||
self.slider = VolumeSlider:new({anchor_id = 'volume'})
|
||||
end
|
||||
|
||||
function Volume:get_visibility()
|
||||
return self.slider.pressed and 1 or Elements.timeline:get_is_hovered() and -1 or Element.get_visibility(self)
|
||||
end
|
||||
|
||||
function Volume:update_dimensions()
|
||||
local width = state.fullormaxed and options.volume_size_fullscreen or options.volume_size
|
||||
local controls, timeline, top_bar = Elements.controls, Elements.timeline, Elements.top_bar
|
||||
local min_y = top_bar.enabled and top_bar.by or 0
|
||||
local max_y = (controls and controls.enabled and controls.ay) or (timeline.enabled and timeline.ay)
|
||||
or display.height - top_bar.size
|
||||
local available_height = max_y - min_y
|
||||
local max_height = available_height * 0.8
|
||||
local height = round(math.min(width * 8, max_height))
|
||||
self.enabled = height > width * 2 -- don't render if too small
|
||||
local margin = (width / 2) + Elements.window_border.size
|
||||
self.ax = round(options.volume == 'left' and margin or display.width - margin - width)
|
||||
self.ay = min_y + round((available_height - height) / 2)
|
||||
self.bx = round(self.ax + width)
|
||||
self.by = round(self.ay + height)
|
||||
self.mute.enabled, self.slider.enabled = self.enabled, self.enabled
|
||||
self.mute:set_coordinates(self.ax, self.by - round(width * 0.8), self.bx, self.by)
|
||||
self.slider:set_coordinates(self.ax, self.ay, self.bx, self.mute.ay)
|
||||
end
|
||||
|
||||
function Volume:on_display() self:update_dimensions() end
|
||||
function Volume:on_prop_border() self:update_dimensions() end
|
||||
function Volume:on_controls_reflow() self:update_dimensions() end
|
||||
|
||||
return Volume
|
|
@ -1,33 +0,0 @@
|
|||
local Element = require('uosc_shared/elements/Element')
|
||||
|
||||
---@class WindowBorder : Element
|
||||
local WindowBorder = class(Element)
|
||||
|
||||
function WindowBorder:new() return Class.new(self) --[[@as WindowBorder]] end
|
||||
function WindowBorder:init()
|
||||
Element.init(self, 'window_border')
|
||||
self.ignores_menu = true
|
||||
self.size = 0
|
||||
end
|
||||
|
||||
function WindowBorder:decide_enabled()
|
||||
self.enabled = options.window_border_size > 0 and not state.fullormaxed and not state.border
|
||||
self.size = self.enabled and options.window_border_size or 0
|
||||
end
|
||||
|
||||
function WindowBorder:on_prop_border() self:decide_enabled() end
|
||||
function WindowBorder:on_prop_fullormaxed() self:decide_enabled() end
|
||||
|
||||
function WindowBorder:render()
|
||||
if self.size > 0 then
|
||||
local ass = assdraw.ass_new()
|
||||
local clip = '\\iclip(' .. self.size .. ',' .. self.size .. ',' ..
|
||||
(display.width - self.size) .. ',' .. (display.height - self.size) .. ')'
|
||||
ass:rect(0, 0, display.width + 1, display.height + 1, {
|
||||
color = bg, clip = clip, opacity = options.window_border_opacity,
|
||||
})
|
||||
return ass
|
||||
end
|
||||
end
|
||||
|
||||
return WindowBorder
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue