From f87828af15cd8efc0ba78474289485b148fcad83 Mon Sep 17 00:00:00 2001 From: Clay Gomera Date: Sat, 3 Aug 2024 02:35:58 -0400 Subject: [PATCH] Updated --- README.md | 42 +- config/.bash_logout | 2 + config/.bash_profile | 2 +- config/.bashrc | 20 +- config/.config/X11/xinitrc | 4 +- config/.config/btop/btop.conf | 2 +- config/.config/gtk-2.0/gtkrc-2.0 | 4 +- config/.config/gtk-3.0/settings.ini | 4 +- config/.config/lvim/config.lua | 28 +- config/.config/newsboat/urls | 323 +++--- config/.config/qutebrowser/bookmarks/urls | 27 + config/.config/qutebrowser/config.py | 188 ++++ config/.config/qutebrowser/gruvbox.py | 333 ++++++ .../.local/share/applications/es-de.desktop | 17 + config/etc/bash.bash_logout | 6 + config/etc/crypttab | 14 + config/etc/default/grub | 66 +- config/etc/dracut.conf | 4 + config/etc/issue | 24 +- config/etc/mkinitcpio.conf | 81 -- config/etc/pacman.conf | 98 -- source/dmenu/scripts/dmenu_power | 60 +- source/dwm/autostart | 38 +- source/dwm/config.h | 24 +- source/dwmblocks/scripts/block_wifi | 2 +- source/nsxiv/LICENSE | 339 ------ source/nsxiv/Makefile | 95 -- source/nsxiv/README.md | 222 ---- source/nsxiv/autoreload.c | 152 --- source/nsxiv/commands.c | 479 -------- source/nsxiv/commands.h | 99 -- source/nsxiv/config.h | 214 ---- source/nsxiv/config.mk | 35 - source/nsxiv/etc/CHANGELOG.md | 499 -------- source/nsxiv/etc/CONTRIBUTING.md | 112 -- source/nsxiv/etc/examples/image-info | 21 - source/nsxiv/etc/examples/key-handler | 41 - source/nsxiv/etc/examples/thumb-info | 20 - source/nsxiv/etc/examples/win-title | 27 - source/nsxiv/etc/nsxiv.1 | 592 ---------- source/nsxiv/etc/nsxiv.desktop | 8 - source/nsxiv/etc/woodpecker/CFLAGS | 13 - source/nsxiv/etc/woodpecker/analysis.sh | 24 - source/nsxiv/etc/woodpecker/analysis.yml | 12 - source/nsxiv/etc/woodpecker/build.yml | 31 - source/nsxiv/etc/woodpecker/clang-tidy-checks | 16 - source/nsxiv/etc/woodpecker/spell.yml | 13 - source/nsxiv/etc/woodpecker/try-all-builds.sh | 38 - source/nsxiv/icon/128x128.png | Bin 1066 -> 0 bytes source/nsxiv/icon/16x16.png | Bin 262 -> 0 bytes source/nsxiv/icon/32x32.png | Bin 421 -> 0 bytes source/nsxiv/icon/48x48.png | Bin 507 -> 0 bytes source/nsxiv/icon/64x64.png | Bin 620 -> 0 bytes source/nsxiv/icon/README.md | 43 - source/nsxiv/icon/data.gen.h | 255 ----- source/nsxiv/icon/data.h | 14 - source/nsxiv/icon/img2data.c | 157 --- source/nsxiv/image.c | 790 ------------- source/nsxiv/main.c | 1000 ----------------- source/nsxiv/nsxiv.h | 473 -------- source/nsxiv/options.c | 280 ----- source/nsxiv/optparse.h | 403 ------- source/nsxiv/thumbs.c | 592 ---------- source/nsxiv/utf8.h | 68 -- source/nsxiv/util.c | 294 ----- source/nsxiv/window.c | 547 --------- source/st/st | Bin 0 -> 133464 bytes 67 files changed, 946 insertions(+), 8485 deletions(-) create mode 100644 config/.bash_logout create mode 100644 config/.config/qutebrowser/bookmarks/urls create mode 100644 config/.config/qutebrowser/config.py create mode 100644 config/.config/qutebrowser/gruvbox.py create mode 100644 config/.local/share/applications/es-de.desktop create mode 100644 config/etc/bash.bash_logout create mode 100644 config/etc/crypttab create mode 100644 config/etc/dracut.conf delete mode 100644 config/etc/mkinitcpio.conf delete mode 100644 config/etc/pacman.conf delete mode 100644 source/nsxiv/LICENSE delete mode 100644 source/nsxiv/Makefile delete mode 100644 source/nsxiv/README.md delete mode 100644 source/nsxiv/autoreload.c delete mode 100644 source/nsxiv/commands.c delete mode 100644 source/nsxiv/commands.h delete mode 100644 source/nsxiv/config.h delete mode 100644 source/nsxiv/config.mk delete mode 100644 source/nsxiv/etc/CHANGELOG.md delete mode 100644 source/nsxiv/etc/CONTRIBUTING.md delete mode 100755 source/nsxiv/etc/examples/image-info delete mode 100755 source/nsxiv/etc/examples/key-handler delete mode 100755 source/nsxiv/etc/examples/thumb-info delete mode 100755 source/nsxiv/etc/examples/win-title delete mode 100644 source/nsxiv/etc/nsxiv.1 delete mode 100644 source/nsxiv/etc/nsxiv.desktop delete mode 100644 source/nsxiv/etc/woodpecker/CFLAGS delete mode 100755 source/nsxiv/etc/woodpecker/analysis.sh delete mode 100644 source/nsxiv/etc/woodpecker/analysis.yml delete mode 100644 source/nsxiv/etc/woodpecker/build.yml delete mode 100644 source/nsxiv/etc/woodpecker/clang-tidy-checks delete mode 100644 source/nsxiv/etc/woodpecker/spell.yml delete mode 100755 source/nsxiv/etc/woodpecker/try-all-builds.sh delete mode 100644 source/nsxiv/icon/128x128.png delete mode 100644 source/nsxiv/icon/16x16.png delete mode 100644 source/nsxiv/icon/32x32.png delete mode 100644 source/nsxiv/icon/48x48.png delete mode 100644 source/nsxiv/icon/64x64.png delete mode 100644 source/nsxiv/icon/README.md delete mode 100644 source/nsxiv/icon/data.gen.h delete mode 100644 source/nsxiv/icon/data.h delete mode 100644 source/nsxiv/icon/img2data.c delete mode 100644 source/nsxiv/image.c delete mode 100644 source/nsxiv/main.c delete mode 100644 source/nsxiv/nsxiv.h delete mode 100644 source/nsxiv/options.c delete mode 100644 source/nsxiv/optparse.h delete mode 100644 source/nsxiv/thumbs.c delete mode 100644 source/nsxiv/utf8.h delete mode 100644 source/nsxiv/util.c delete mode 100644 source/nsxiv/window.c create mode 100755 source/st/st diff --git a/README.md b/README.md index ac5e9618c..0b722eccf 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Other configuration files included in this project are available at the config f * `.config/vifm`: [vifm](https://vifm.info/) is the best terminal file manager with everything you will and may need, with vim-like keybindings and image previews (with ueberzug). * `.config/zathura`: [zathura](https://git.pwmt.org/pwmt/zathura) is my document viewer of choice, also with vim-like keybindings. -## Installation & How To Modify +## Dependencies Make sure to have these dependencies installed in your system, in this case package names are from Void Linux, you'll have to look for the package names in your distribution: @@ -116,13 +116,53 @@ ueberzug - `bitwarden`: launched with flatpak by default - `btop` - `pulsemixer` +- `alsa-utils` - `newsboat` - `arandr` +- `mpv` +- `zathura` - `qalculate-gtk`: launched with flatpak by default - `ytfzf`: [download here](https://github.com/pystardust/ytfzf) - `ani-cli`: [download here](https://github.com/pystardust/ani-cli) - `flix-cli`: [download here](https://github.com/d4r1us-drk/flix-cli) +### System stuff (just here to keep track on stuff I need to rebuild the system) + +* **System dependencies** (stuff required for the main installation) + +``` +xorg-server +xf86-input-libinput +pipewire +wireplumber +alsa-pipewire +gstreamer1 +gstreamermm +gstreamer-vaapi +gst-plugins-bad1 +gst-plugins-good1 +gst-plugins-base1 +gst-plugins-ugly1 +bluez +bluez-alsa +libspa-alsa +libspa-bluetooth +mesa +mesa-vaapi +``` + +* **Intel stuff** (I mainly use intel devices) + +``` +xf86-video-intel +mesa-intel-dri +mesa-vulkan-intel +intel-media-driver +libva-intel-driver +``` + +## Installation & How To Modify + After installing them with your package manager of choice, you can do the following to get the source code and start to modify it to your liking. ```bash diff --git a/config/.bash_logout b/config/.bash_logout new file mode 100644 index 000000000..1380a21b0 --- /dev/null +++ b/config/.bash_logout @@ -0,0 +1,2 @@ +# ~/.bash_logout +clear diff --git a/config/.bash_profile b/config/.bash_profile index 2ace670b6..f5c063190 100644 --- a/config/.bash_profile +++ b/config/.bash_profile @@ -24,7 +24,7 @@ export BASHRC="$HOME/.bashrc" # Default apps export TERMINAL="st" export EDITOR="$HOME/.local/bin/lvim" -export VISUAL="st -e $EDITOR" +export VISUAL="st -n editor -c editor -e $EDITOR" export BROWSER="flatpak run com.brave.Browser" export VIEWER="zathura" diff --git a/config/.bashrc b/config/.bashrc index ce2488c69..4b15c5f50 100644 --- a/config/.bashrc +++ b/config/.bashrc @@ -147,10 +147,11 @@ fi # function to detect os and assign aliases to package managers alias \ - pkg-update="sudo pacman -Syyu" \ - pkg-install="sudo pacman -S" \ - pkg-remove="sudo pacman -Rcns" \ - pkg-search="sudo pacman -Ss" \ + pkg-update="sudo xbps-install -Su" \ + pkg-install="sudo xbps-install -S" \ + pkg-remove="sudo xbps-remove -R" \ + pkg-autoremove="sudo xbps-remove -Oo" \ + pkg-search="sudo xbps-query -s" \ # colorize grep output (good for log files) alias \ @@ -298,11 +299,10 @@ function parse_git_dirty { export PS1="[\[\e[31m\]\u\[\e[m\]\[\e[35m\]@\[\e[m\]\[\e[32m\]\h\[\e[m\]] [\[\e[33m\]\W\[\e[m\]\[\e[34m\]\`parse_git_branch\`\[\e[m\]] 󱞪 " -# initialize ssh-agent -if ! pgrep -u "$USER" ssh-agent > /dev/null; then - ssh-agent -t 1h > "$XDG_RUNTIME_DIR/ssh-agent.env" +export SSH_AUTH_SOCK=${HOME}/.ssh/agent +if ! pgrep -u ${USER} ssh-agent > /dev/null; then + rm -f ${SSH_AUTH_SOCK} fi -if [[ ! -f "$SSH_AUTH_SOCK" ]]; then - source "$XDG_RUNTIME_DIR/ssh-agent.env" >/dev/null +if [ ! -S ${SSH_AUTH_SOCK} ]; then + eval $(ssh-agent -a ${SSH_AUTH_SOCK} 2> /dev/null) fi - diff --git a/config/.config/X11/xinitrc b/config/.config/X11/xinitrc index ab7b1a13c..dbb003b99 100755 --- a/config/.config/X11/xinitrc +++ b/config/.config/X11/xinitrc @@ -36,10 +36,10 @@ export XDG_SESSION_TYPE=X11 # GTK & cursor stuff export XCURSOR_PATH="$XDG_DATA_HOME/icons/" export XCURSOR_THEME="Simp1e-Gruvbox-Dark" -export GTK_THEME="Gruvbox-Dark-BL-LB" +export GTK_THEME="Gruvbox-Dark" export GTK_ICON_THEME="Papirus-Dark" export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/gcr/ssh" # initialize dwm -exec dwm +dbus-launch dwm diff --git a/config/.config/btop/btop.conf b/config/.config/btop/btop.conf index 5fde20a46..8548fed23 100644 --- a/config/.config/btop/btop.conf +++ b/config/.config/btop/btop.conf @@ -57,7 +57,7 @@ update_ms = 500 #* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct", #* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly. -proc_sorting = "pid" +proc_sorting = "cpu direct" #* Reverse sorting order, True or False. proc_reversed = False diff --git a/config/.config/gtk-2.0/gtkrc-2.0 b/config/.config/gtk-2.0/gtkrc-2.0 index aa7263f62..a7c6c15d0 100644 --- a/config/.config/gtk-2.0/gtkrc-2.0 +++ b/config/.config/gtk-2.0/gtkrc-2.0 @@ -1,6 +1,6 @@ -gtk-theme-name="Gruvbox-Dark-BL-LB" +gtk-theme-name="Gruvbox-Dark" gtk-icon-theme-name="Papirus-Dark" -gtk-font-name="Cantarell 10" +gtk-font-name="Inter 10" gtk-cursor-theme-name="Simp1e-Gruvbox-Dark" gtk-cursor-theme-size=0 gtk-toolbar-style=GTK_TOOLBAR_BOTH diff --git a/config/.config/gtk-3.0/settings.ini b/config/.config/gtk-3.0/settings.ini index c42829b76..c0ac43703 100644 --- a/config/.config/gtk-3.0/settings.ini +++ b/config/.config/gtk-3.0/settings.ini @@ -1,7 +1,7 @@ [Settings] -gtk-theme-name=Gruvbox-Dark-BL-LB +gtk-theme-name=Gruvbox-Dark gtk-icon-theme-name=Papirus-Dark -gtk-font-name=Cantarell 10 +gtk-font-name=Inter 10 gtk-cursor-theme-name=Simp1e-Gruvbox-Dark gtk-cursor-theme-size=0 gtk-toolbar-style=GTK_TOOLBAR_BOTH diff --git a/config/.config/lvim/config.lua b/config/.config/lvim/config.lua index d3d365ca9..56c14978f 100644 --- a/config/.config/lvim/config.lua +++ b/config/.config/lvim/config.lua @@ -1,28 +1,3 @@ --- neovide options -vim.o.guifont = "mononoki Nerd Font:h12" -vim.g.neovide_hide_mouse_when_typing = true -vim.g.neovide_no_idle = true -vim.g.neovide_confirm_quit = true -vim.g.neovide_input_use_logo = true - -vim.g.neovide_cursor_antialiasing = true -vim.g.neovide_cursor_animate_in_insert_mode = true -vim.g.neovide_cursor_vfx_mode = "pixiedust" -vim.g.neovide_cursor_vfx_particle_speed = 20.0 - -vim.g.neovide_padding_top = 0 -vim.g.neovide_padding_bottom = 0 -vim.g.neovide_padding_right = 0 -vim.g.neovide_padding_left = 0 - --- Helper function for transparency formatting -local alpha = function() - return string.format("%x", math.floor(255 * (vim.g.transparency or 0.98))) -end -vim.g.neovide_transparency = 0.95 -vim.g.transparency = 0.95 -vim.g.neovide_background_color = "#1d2021" .. alpha() - -- nvim options vim.opt.shiftwidth = 4 vim.opt.tabstop = 4 @@ -40,7 +15,7 @@ lvim.log.level = "info" -- change theme settings lvim.colorscheme = "gruvbox" -lvim.transparent_window = false +lvim.transparent_window = true lvim.builtin.alpha.active = true lvim.builtin.alpha.mode = "dashboard" lvim.builtin.terminal.active = true @@ -52,7 +27,6 @@ lvim.builtin.treesitter.ensure_installed = { "c", "javascript", "json", - "lua", "python", "typescript", "tsx", diff --git a/config/.config/newsboat/urls b/config/.config/newsboat/urls index 8ce477e24..2d27ca901 100644 --- a/config/.config/newsboat/urls +++ b/config/.config/newsboat/urls @@ -19,7 +19,6 @@ https://blog.zorin.com/index.xml "~Zorin OS Blog" http://blog.linuxmint.com/?feed=rss2 "~Linux Mint Blog" https://www.gamingonlinux.com/article_rss.php "~Gaming on linux" https://hackaday.com/blog/feed/ "~Hackaday" -https://linux.softpedia.com/backend.xml "~Softpedia Linux" https://www.phoronix.com/rss.php "~Phoronix" https://betanews.com/feed "~Betanews Linux" http://lxer.com/module/newswire/headlines.rss "~Lxer" @@ -37,148 +36,180 @@ https://www.eff.org/rss/updates.xml "~EFF - Updates" https://victorhckinthefreeworld.com/feed/ "~Victorhck in the free world" https://theprivacydad.com/feed/ "~Welcome to The Privacy Dad's Blog!" https://proton.me/blog/feed "~Proton Blog" - -https://iv.ggtyler.dev/feed/channel/UC-ErgHYY0_Yjhjz2MN1E1lg "~YT - RETRO Hardware" -https://iv.ggtyler.dev/feed/channel/UC-shHw-IWdyL7Y6JUz_mUMQ "~YT - SORRYLAG" -https://iv.ggtyler.dev/feed/channel/UC0W_BIuwk8D0Bv4THbVZZOQ "~YT - Surveillance Report" -https://iv.ggtyler.dev/feed/channel/UC0woBco6Dgcxt0h8SwyyOmw "~YT - Megaprojects" -https://iv.ggtyler.dev/feed/channel/UC1D3yD4wlPMico0dss264XA "~YT - NileBlue" -https://iv.ggtyler.dev/feed/channel/UC1JTQBa5QxZCpXrFSkMxmPw "~YT - Raycevick" -https://iv.ggtyler.dev/feed/channel/UC1_uAIS3r8Vu6JjXWvastJg "~YT - Mathologer" -https://iv.ggtyler.dev/feed/channel/UC1s1OsWNYDFgbROPV-q5arg "~YT - Michael Horn" -https://iv.ggtyler.dev/feed/channel/UC2WHjPDvbE6O328n17ZGcfg "~YT - ForrestKnight" -https://iv.ggtyler.dev/feed/channel/UC2eYFnH61tmytImy1mTYvhA "~YT - Luke Smith" -https://iv.ggtyler.dev/feed/channel/UC36xmz34q02JYaZYKrMwXng "~YT - Nate Gentile" -https://iv.ggtyler.dev/feed/channel/UC3Wn3dABlgESm8Bzn8Vamgg "~YT - Sideprojects" -https://iv.ggtyler.dev/feed/channel/UC3jSNmKWYA04R47fDcc1ImA "~YT - InfinitelyGalactic" -https://iv.ggtyler.dev/feed/channel/UC3yaWWA9FF9OBog5U9ml68A "~YT - SavvyNik" -https://iv.ggtyler.dev/feed/channel/UC52tAgsocr9DgrPec_iw1eg "~YT - Portfolio Courses" -https://iv.ggtyler.dev/feed/channel/UC5I2hjZYiW9gZPVkvzM8_Cw "~YT - Techmoan" -https://iv.ggtyler.dev/feed/channel/UC5KDiSAFxrDWhmysBcNqtMA "~YT - Eric Murphy" -https://iv.ggtyler.dev/feed/channel/UC5UAwBUum7CPN5buc-_N1Fw "~YT - The Linux Experiment" -https://iv.ggtyler.dev/feed/channel/UC5nlKFUNoskvV5XdW6PbgYw "~YT - A Well-Rested Dog" -https://iv.ggtyler.dev/feed/channel/UC6WOxrSKLW8VagrNkfvi6EQ "~YT - THE SQUIDD" -https://iv.ggtyler.dev/feed/channel/UC6biysICWOJ-C3P4Tyeggzg "~YT - Low Level Learning" -https://iv.ggtyler.dev/feed/channel/UC7YOGHUfC1Tb6E4pudI9STA "~YT - Mental Outlaw" -https://iv.ggtyler.dev/feed/channel/UC7qPftDWPw9XuExpSgfkmJQ "~YT - Nostalgia Nerd" -https://iv.ggtyler.dev/feed/channel/UC8679JjreNLfSCgA-2Fb4kA "~YT - SpineCard" -https://iv.ggtyler.dev/feed/channel/UC8ENHE5xdFSwx71u3fDH5Xw "~YT - ThePrimeagen" -https://iv.ggtyler.dev/feed/channel/UC8bCGC81i_jYlL041-iAFSA "~YT - JWulen" -https://iv.ggtyler.dev/feed/channel/UC8uT9cgJorJPWu7ITLGo9Ww "~YT - The 8-Bit Guy" -https://iv.ggtyler.dev/feed/channel/UC9-y-6csu5WGm29I7JiwpnA "~YT - Computerphile" -https://iv.ggtyler.dev/feed/channel/UC910yxBmXzGDH_2cx0XE0Xw "~YT - Plano de Juego" -https://iv.ggtyler.dev/feed/channel/UC9RM-iSvTu1uPJb8X5yp3EQ "~YT - Wendover Productions" -https://iv.ggtyler.dev/feed/channel/UC9bORzxOWiewqMXxkmhAwAg "~YT - Gingy" -https://iv.ggtyler.dev/feed/channel/UC9eM3jqq5IfxbGHbuRR_jRw "~YT - Sfdx Show" -https://iv.ggtyler.dev/feed/channel/UC9h8BDcXwkhZtnqoQJ7PggA "~YT - Warographics" -https://iv.ggtyler.dev/feed/channel/UC9rU_5YnMDUPc0IP8NwZOig "~YT - Elevated Systems" -https://iv.ggtyler.dev/feed/channel/UCAMu6Dso0ENoNm3sKpQsy0g "~YT - Nir Lichtman" -https://iv.ggtyler.dev/feed/channel/UCAiEWppTvoNSHU939xhMb2g "~YT - hoser" -https://iv.ggtyler.dev/feed/channel/UCAiiOTio8Yu69c3XnR7nQBQ "~YT - System Crafters" -https://iv.ggtyler.dev/feed/channel/UCBq5p-xOla8xhnrbhu8AIAg "~YT - Tech Over Tea" -https://iv.ggtyler.dev/feed/channel/UCD_SqY872aJSEjh8CcBFB7g "~YT - Userlandia" -https://iv.ggtyler.dev/feed/channel/UCE0H52NoucHL2JkhPdZ1ykA "~YT - Jwlar" -https://iv.ggtyler.dev/feed/channel/UCEFymXY4eFCo_AchSpxwyrg "~YT - MetalJesusRocks" -https://iv.ggtyler.dev/feed/channel/UCEIwxahdLz7bap-VDs9h35A "~YT - Steve Mould" -https://iv.ggtyler.dev/feed/channel/UCEp20NgOZHmgWdbQdHSxgjw "~YT - This Does Not Compute" -https://iv.ggtyler.dev/feed/channel/UCFhXFikryT4aFcLkLw2LBLA "~YT - NileRed" -https://iv.ggtyler.dev/feed/channel/UCFzGNDHEZ5-7d5UXxfHUcRg "~YT - jujalag" -https://iv.ggtyler.dev/feed/channel/UCGE-JpLbibXJg3W_N2hbo8g "~YT - Sethorven" -https://iv.ggtyler.dev/feed/channel/UCGGAYsWFerrs-ZqyXw7fQWg "~YT - Resumidito." -https://iv.ggtyler.dev/feed/channel/UCH5DsMZAgdx5Fkk9wwMNwCA "~YT - The New Oil" -https://iv.ggtyler.dev/feed/channel/UCHnyfMqiRRG1u-2MsSQLbXA "~YT - Veritasium" -https://iv.ggtyler.dev/feed/channel/UCHvDhwNuq-h2hZQRR6BwbLQ "~YT - Tech With Nikola" -https://iv.ggtyler.dev/feed/channel/UCI6pj4e-_6Km-dmLLYpmlaA "~YT - Alejandro Julián" -https://iv.ggtyler.dev/feed/channel/UCJ0-OtVpF0wOKEqT2Z1HEtA "~YT - ElectroBOOM" -https://iv.ggtyler.dev/feed/channel/UCJYJgj7rzsn0vdR7fkgjuIA "~YT - styropyro" -https://iv.ggtyler.dev/feed/channel/UCLEoyoOKZK0idGqSc6Pi23w "~YT - RMC - The Cave" -https://iv.ggtyler.dev/feed/channel/UCLx053rWZxCiYWsBETgdKrQ "~YT - LGR" -https://iv.ggtyler.dev/feed/channel/UCMbQbVilo-nezMvwf1BZfAA "~YT - CienciaDeSofa" -https://iv.ggtyler.dev/feed/channel/UCMiyV_Ib77XLpzHPQH_q0qQ "~YT - Veronica Explains" -https://iv.ggtyler.dev/feed/channel/UCMnZ3qm76jc3SUi9Z-5OdcA "~YT - Leyendas & Videojuegos" -https://iv.ggtyler.dev/feed/channel/UCNnNCBgckxzqIh1Txw5cgSg "~YT - BaityBait" -https://iv.ggtyler.dev/feed/channel/UCNzszbnvQeFzObW0ghk0Ckw "~YT - Dave's Garage" -https://iv.ggtyler.dev/feed/channel/UCONH73CdRXUjlh3-DdLGCPw "~YT - Nicco Loves Linux" -https://iv.ggtyler.dev/feed/channel/UCOxmlaJURX3nq8eLuJPbl3A "~YT - Psivewri" -https://iv.ggtyler.dev/feed/channel/UCP5tjEmvPItGyLhmjdwP7Ww "~YT - RealLifeLore" -https://iv.ggtyler.dev/feed/channel/UCQ-W1KE9EYfdxhL6S4twUNw "~YT - The Cherno" -https://iv.ggtyler.dev/feed/channel/UCQX_MZRCaluNKxkywkLEgfA "~YT - Date un Vlog" -https://iv.ggtyler.dev/feed/channel/UCR1IuLEqb6UEA_zQ81kwXfg "~YT - Real Engineering" -https://iv.ggtyler.dev/feed/channel/UCRYeRa2iUMd8An1WTPIP2bw "~YT - aChair Leg" -https://iv.ggtyler.dev/feed/channel/UCS-WzPVpAAli-1IfEG2lN8A "~YT - Michael MJD" -https://iv.ggtyler.dev/feed/channel/UCS0N5baNlQWJCUrhCEo8WlA "~YT - Ben Eater" -https://iv.ggtyler.dev/feed/channel/UCSju5G2aFaWMqn-_0YBtq5A "~YT - Stand-up Maths" -https://iv.ggtyler.dev/feed/channel/UCSp-OaMpsO8K0KkOqyBl7_w "~YT - Let's Get Rusty" -https://iv.ggtyler.dev/feed/channel/UCSuHzQ3GrHSzoBbwrIq3LLA "~YT - Naomi Brockwell TV" -https://iv.ggtyler.dev/feed/channel/UCT35hFvV0j8SR3SBGJChKZQ "~YT - Jota." -https://iv.ggtyler.dev/feed/channel/UCUMwY9iS8oMyWDYIe6_RmoA "~YT - No Boilerplate" -https://iv.ggtyler.dev/feed/channel/UCUyeluBRhGPCW4rPe_UvBZQ "~YT - ThePrimeTime" -https://iv.ggtyler.dev/feed/channel/UCVk4b-svNJoeytrrlOixebQ "~YT - TheVimeagen" -https://iv.ggtyler.dev/feed/channel/UCVls1GmFKf6WlTraIb_IaJg "~YT - DistroTube" -https://iv.ggtyler.dev/feed/channel/UCW-HHEyt67RhZ6q21n4p2zQ "~YT - Mac84" -https://iv.ggtyler.dev/feed/channel/UCW0gH2G-cMKAEjEkI4YhnPA "~YT - Nerd of the Rings" -https://iv.ggtyler.dev/feed/channel/UCWDGyt5hy6UA6Br-hAar03A "~YT - El Robot de Colón" -https://iv.ggtyler.dev/feed/channel/UCWMV8dD0N3tAM2W6GuNCUCg "~YT - theunrealtarik" -https://iv.ggtyler.dev/feed/channel/UCWQaM7SpSECp9FELz-cHzuQ "~YT - Dreams of Code" -https://iv.ggtyler.dev/feed/channel/UCWcp1Mwm7_bJ-mVoZb8TdkQ "~YT - TuberViejuner" -https://iv.ggtyler.dev/feed/channel/UCWyrVfwRL-2DOkzsqrbjo5Q "~YT - NCommander" -https://iv.ggtyler.dev/feed/channel/UCYO_jab_esuFRV4b17AJtAw "~YT - 3Blue1Brown" -https://iv.ggtyler.dev/feed/channel/UCYVU6rModlGxvJbszCclGGw "~YT - Rob Braxman Tech" -https://iv.ggtyler.dev/feed/channel/UCZ4AMrDcNrfy3X6nsU8-rPg "~YT - Economics Explained" -https://iv.ggtyler.dev/feed/channel/UCZkPWp4MVoIVGLVLYC1Vjlg "~YT - Guille Ghemi" -https://iv.ggtyler.dev/feed/channel/UCa6V1UVOXN4wDm7RDQDoa6g "~YT - El Traductor de Ingeniería" -https://iv.ggtyler.dev/feed/channel/UCaSCt8s_4nfkRglWCvNSDrg "~YT - CodeAesthetic" -https://iv.ggtyler.dev/feed/channel/UCaVPhFg-Ax873wvhbNitsrQ "~YT - El Robot de Platón" -https://iv.ggtyler.dev/feed/channel/UCbdSYaPD-lr1kW27UJuk8Pw "~YT - QuantumFracture" -https://iv.ggtyler.dev/feed/channel/UCbiGcwDWZjz05njNPrJU7jA "~YT - ExplainingComputers" -https://iv.ggtyler.dev/feed/channel/UCcAy1o8VUCkdowxRYbc0XRw "~YT - Sebi's Random Tech" -https://iv.ggtyler.dev/feed/channel/UCcQWoBTNG__OwfZk9znWgCQ "~YT - The Rusty Bits" -https://iv.ggtyler.dev/feed/channel/UCd4XwUn2Lure2NHHjukoCwA "~YT - Linux For Everyone" -https://iv.ggtyler.dev/feed/channel/UCdiJKSDwwvy4rMUVc9CgS-g "~YT - DaniboubeTV" -https://iv.ggtyler.dev/feed/channel/UCdp4_l1vPmpN-gDbUwhaRUQ "~YT - Branch Education" -https://iv.ggtyler.dev/feed/channel/UCeCtLnoV0nmE2cBbbftCCiw "~YT - Las reviews de Camilo" -https://iv.ggtyler.dev/feed/channel/UCeHOkFGW-7uAZFvq3BXb8YA "~YT - :3ildcat" -https://iv.ggtyler.dev/feed/channel/UCerEIdrEW-IqwvlH8lTQUJQ "~YT - Tech Tangents" -https://iv.ggtyler.dev/feed/channel/UCf-U0uPVQZtcqXUWa_Hl4Mw "~YT - Into the Shadows" -https://iv.ggtyler.dev/feed/channel/UCg6gPGh8HU2U01vaFCAsvmQ "~YT - Chris Titus Tech" -https://iv.ggtyler.dev/feed/channel/UCgNg3vwj3xt7QOrcIDaHdFg "~YT - PolyMatter" -https://iv.ggtyler.dev/feed/channel/UChI0q9a-ZcbZh7dAu_-J-hg "~YT - Upper Echelon" -https://iv.ggtyler.dev/feed/channel/UCj8orMezFWVcoN-4S545Wtw "~YT - Max Derrat" -https://iv.ggtyler.dev/feed/channel/UCjFaPUcJU1vwk193mnW_w1w "~YT - Modern Vintage Gamer" -https://iv.ggtyler.dev/feed/channel/UCjSEJkpGbcZhvo0lr-44X_w "~YT - TechHut" -https://iv.ggtyler.dev/feed/channel/UCjgS6Uyg8ok4Jd_lH_MUKgg "~YT - Claus Kellerman" -https://iv.ggtyler.dev/feed/channel/UCl2mFZoRqjw_ELax4Yisf6w "~YT - Louis Rossmann" -https://iv.ggtyler.dev/feed/channel/UCl_dlV_7ofr4qeP1drJQ-qg "~YT - Tantacrul" -https://iv.ggtyler.dev/feed/channel/UCld68syR8Wi-GY_n4CaoJGA "~YT - Brodie Robertson" -https://iv.ggtyler.dev/feed/channel/UClnDI2sdehVm1zm_LmUHsjQ "~YT - Biographics" -https://iv.ggtyler.dev/feed/channel/UCmGSJVG3mCRXVOP4yZrU1Dw "~YT - Johnny Harris" -https://iv.ggtyler.dev/feed/channel/UCmw-QGOHbHA5cDAvwwqUTKQ "~YT - Zaney" -https://iv.ggtyler.dev/feed/channel/UCmyGZ0689ODyReHw3rsKLtQ "~YT - Michael Tunnell" -https://iv.ggtyler.dev/feed/channel/UCnw3aIEiz60S6O3XcztCVkQ "~YT - PatricianTV" -https://iv.ggtyler.dev/feed/channel/UCoL8olX-259lS1N6QPyP4IQ "~YT - Action Retro" -https://iv.ggtyler.dev/feed/channel/UCoryWpk4QVYKFCJul9KBdyw "~YT - Switched to Linux" -https://iv.ggtyler.dev/feed/channel/UCoxcjq-8xIDTYp3uz647V5A "~YT - Numberphile" -https://iv.ggtyler.dev/feed/channel/UCpuKDBw8IVIdKWPhiB2VDNQ "~YT - Cinematix" -https://iv.ggtyler.dev/feed/channel/UCpuLiczP2Aqq11Gtf4k_fkw "~YT - Futurasound Productions" -https://iv.ggtyler.dev/feed/channel/UCqoGR_EedlhKDVuWNwYWRbg "~YT - Voices of the Past" -https://iv.ggtyler.dev/feed/channel/UCqxM9T6ksiOVKIkb88S2r7Q "~YT - Zac Builds" -https://iv.ggtyler.dev/feed/channel/UCrkPsvLGln62OMZRO6K-llg "~YT - Nick Chapsas" -https://iv.ggtyler.dev/feed/channel/UCs6KfncB4OV6Vug4o_bzijg "~YT - Techlore" -https://iv.ggtyler.dev/feed/channel/UCs7nPQIEba0T3tGOWWsZpJQ "~YT - Like Stories of Old" -https://iv.ggtyler.dev/feed/channel/UCsnGwSIHyoYN0kiINAGUKxg "~YT - Wolfgang's Channel" -https://iv.ggtyler.dev/feed/channel/UCtMVHI3AJD4Qk4hcbZnI9ZQ "~YT - SomeOrdinaryGamers" -https://iv.ggtyler.dev/feed/channel/UCtYKe7-XbaDjpUwcU5x0bLg "~YT - neo" -https://iv.ggtyler.dev/feed/channel/UCtYg149E_wUGVmjGz-TgyNA "~YT - Titus Tech Talk" -https://iv.ggtyler.dev/feed/channel/UCtZX_67hSjOIPu0HTmFi6aw "~YT - Macintosh Librarian" -https://iv.ggtyler.dev/feed/channel/UCvjgXvBlbQiydffZU7m1_aw "~YT - The Coding Train" -https://iv.ggtyler.dev/feed/channel/UCxQKHvKbmSzGMvUrVtJYnUA "~YT - Learn Linux TV" -https://iv.ggtyler.dev/feed/channel/UCxdZ7XCQVMRMipj3gGemQfw "~YT - GNULectures" -https://iv.ggtyler.dev/feed/channel/UCxoz4YfS4M3H3C57FD4jW4Q "~YT - Science Time" -https://iv.ggtyler.dev/feed/channel/UCy0tKL1T7wFoYcxCe0xjN6Q "~YT - Technology Connections" -https://iv.ggtyler.dev/feed/channel/UCy6pDeDwRFi1kmFekSpMDUA "~YT - La Poción Roja" -https://iv.ggtyler.dev/feed/channel/UCybBViio_TH_uiFFDJuz5tg "~YT - Einzelgänger" -https://iv.ggtyler.dev/feed/channel/UCylGUf9BvQooEFjgdNudoQg "~YT - The Linux Cast" -https://iv.ggtyler.dev/feed/channel/UCz1oFxMrgrQ82-276UCOU9w "~YT - Atlas Pro" -https://iv.ggtyler.dev/feed/channel/UCzGMBzt6UOMoQe_dqOfShZw "~YT - Cultura VJ" -https://iv.ggtyler.dev/feed/channel/UCzR-rom72PHN9Zg7RML9EbA "~YT - PBS Eons" -https://iv.ggtyler.dev/feed/channel/UCzXsTSZDoAPSjfHr8IZM9Ew "~YT - FloatyMonkey" +https://www.youtube.com/feeds/videos.xml?channel_id=UC-ErgHYY0_Yjhjz2MN1E1lg "~YT - RETRO Hardware" +https://www.youtube.com/feeds/videos.xml?channel_id=UC-shHw-IWdyL7Y6JUz_mUMQ "~YT - SORRYLAG" +https://www.youtube.com/feeds/videos.xml?channel_id=UC0W_BIuwk8D0Bv4THbVZZOQ "~YT - Surveillance Report" +https://www.youtube.com/feeds/videos.xml?channel_id=UC0woBco6Dgcxt0h8SwyyOmw "~YT - Megaprojects" +https://www.youtube.com/feeds/videos.xml?channel_id=UC1D3yD4wlPMico0dss264XA "~YT - NileBlue" +https://www.youtube.com/feeds/videos.xml?channel_id=UC1JTQBa5QxZCpXrFSkMxmPw "~YT - Raycevick" +https://www.youtube.com/feeds/videos.xml?channel_id=UC1_uAIS3r8Vu6JjXWvastJg "~YT - Mathologer" +https://www.youtube.com/feeds/videos.xml?channel_id=UC1s1OsWNYDFgbROPV-q5arg "~YT - Michael Horn" +https://www.youtube.com/feeds/videos.xml?channel_id=UC2WHjPDvbE6O328n17ZGcfg "~YT - ForrestKnight" +https://www.youtube.com/feeds/videos.xml?channel_id=UC2avWDLN1EI3r1RZ_dlSxCw "~YT - Integza" +https://www.youtube.com/feeds/videos.xml?channel_id=UC2eYFnH61tmytImy1mTYvhA "~YT - Luke Smith" +https://www.youtube.com/feeds/videos.xml?channel_id=UC36xmz34q02JYaZYKrMwXng "~YT - Nate Gentile" +https://www.youtube.com/feeds/videos.xml?channel_id=UC3Wn3dABlgESm8Bzn8Vamgg "~YT - Sideprojects" +https://www.youtube.com/feeds/videos.xml?channel_id=UC3jSNmKWYA04R47fDcc1ImA "~YT - InfinitelyGalactic" +https://www.youtube.com/feeds/videos.xml?channel_id=UC3yaWWA9FF9OBog5U9ml68A "~YT - SavvyNik" +https://www.youtube.com/feeds/videos.xml?channel_id=UC4qG04RcOZ8J1Ncw5CPqsLA "~YT - Claus Kellerman POV" +https://www.youtube.com/feeds/videos.xml?channel_id=UC4w1YQAJMWOz4qtxinq55LQ "~YT - Level1Techs" +https://www.youtube.com/feeds/videos.xml?channel_id=UC52tAgsocr9DgrPec_iw1eg "~YT - Portfolio Courses" +https://www.youtube.com/feeds/videos.xml?channel_id=UC5I2hjZYiW9gZPVkvzM8_Cw "~YT - Techmoan" +https://www.youtube.com/feeds/videos.xml?channel_id=UC5KDiSAFxrDWhmysBcNqtMA "~YT - Eric Murphy" +https://www.youtube.com/feeds/videos.xml?channel_id=UC5UAwBUum7CPN5buc-_N1Fw "~YT - The Linux Experiment" +https://www.youtube.com/feeds/videos.xml?channel_id=UC5nlKFUNoskvV5XdW6PbgYw "~YT - A Well-Rested Dog" +https://www.youtube.com/feeds/videos.xml?channel_id=UC6WOxrSKLW8VagrNkfvi6EQ "~YT - THE SQUIDD" +https://www.youtube.com/feeds/videos.xml?channel_id=UC6biysICWOJ-C3P4Tyeggzg "~YT - Low Level Learning" +https://www.youtube.com/feeds/videos.xml?channel_id=UC7WDD6yHgzdqijHluCi1z-Q "~YT - The Act Man" +https://www.youtube.com/feeds/videos.xml?channel_id=UC7YOGHUfC1Tb6E4pudI9STA "~YT - Mental Outlaw" +https://www.youtube.com/feeds/videos.xml?channel_id=UC7qPftDWPw9XuExpSgfkmJQ "~YT - Nostalgia Nerd" +https://www.youtube.com/feeds/videos.xml?channel_id=UC8679JjreNLfSCgA-2Fb4kA "~YT - SpineCard" +https://www.youtube.com/feeds/videos.xml?channel_id=UC8ENHE5xdFSwx71u3fDH5Xw "~YT - ThePrimeagen" +https://www.youtube.com/feeds/videos.xml?channel_id=UC8bCGC81i_jYlL041-iAFSA "~YT - JWulen" +https://www.youtube.com/feeds/videos.xml?channel_id=UC8uT9cgJorJPWu7ITLGo9Ww "~YT - The 8-Bit Guy" +https://www.youtube.com/feeds/videos.xml?channel_id=UC9-y-6csu5WGm29I7JiwpnA "~YT - Computerphile" +https://www.youtube.com/feeds/videos.xml?channel_id=UC910yxBmXzGDH_2cx0XE0Xw "~YT - Plano de Juego" +https://www.youtube.com/feeds/videos.xml?channel_id=UC9RM-iSvTu1uPJb8X5yp3EQ "~YT - Wendover Productions" +https://www.youtube.com/feeds/videos.xml?channel_id=UC9bORzxOWiewqMXxkmhAwAg "~YT - Gingy" +https://www.youtube.com/feeds/videos.xml?channel_id=UC9eM3jqq5IfxbGHbuRR_jRw "~YT - Sfdx Show" +https://www.youtube.com/feeds/videos.xml?channel_id=UC9h8BDcXwkhZtnqoQJ7PggA "~YT - Warographics" +https://www.youtube.com/feeds/videos.xml?channel_id=UC9rU_5YnMDUPc0IP8NwZOig "~YT - Elevated Systems" +https://www.youtube.com/feeds/videos.xml?channel_id=UCAMu6Dso0ENoNm3sKpQsy0g "~YT - Nir Lichtman" +https://www.youtube.com/feeds/videos.xml?channel_id=UCAiEWppTvoNSHU939xhMb2g "~YT - hoser" +https://www.youtube.com/feeds/videos.xml?channel_id=UCAiiOTio8Yu69c3XnR7nQBQ "~YT - System Crafters" +https://www.youtube.com/feeds/videos.xml?channel_id=UCBdwqWmXhupzV5q8TfD-kNQ "~YT - The Critical Gamer" +https://www.youtube.com/feeds/videos.xml?channel_id=UCBq5p-xOla8xhnrbhu8AIAg "~YT - Tech Over Tea" +https://www.youtube.com/feeds/videos.xml?channel_id=UCDZsyOkn-WTiTwgAvZSQ_cg "~YT - El Rincón De Giorgio" +https://www.youtube.com/feeds/videos.xml?channel_id=UCD_SqY872aJSEjh8CcBFB7g "~YT - Userlandia" +https://www.youtube.com/feeds/videos.xml?channel_id=UCE0H52NoucHL2JkhPdZ1ykA "~YT - Jwlar" +https://www.youtube.com/feeds/videos.xml?channel_id=UCEEVcDuBRDiwxfXAgQjLGug "~YT - Dreams of Autonomy" +https://www.youtube.com/feeds/videos.xml?channel_id=UCEFymXY4eFCo_AchSpxwyrg "~YT - MetalJesusRocks" +https://www.youtube.com/feeds/videos.xml?channel_id=UCEIwxahdLz7bap-VDs9h35A "~YT - Steve Mould" +https://www.youtube.com/feeds/videos.xml?channel_id=UCEp20NgOZHmgWdbQdHSxgjw "~YT - This Does Not Compute" +https://www.youtube.com/feeds/videos.xml?channel_id=UCFhXFikryT4aFcLkLw2LBLA "~YT - NileRed" +https://www.youtube.com/feeds/videos.xml?channel_id=UCFzGNDHEZ5-7d5UXxfHUcRg "~YT - jujalag" +https://www.youtube.com/feeds/videos.xml?channel_id=UCGE-JpLbibXJg3W_N2hbo8g "~YT - Sethorven" +https://www.youtube.com/feeds/videos.xml?channel_id=UCGGAYsWFerrs-ZqyXw7fQWg "~YT - Resumidito." +https://www.youtube.com/feeds/videos.xml?channel_id=UCH5DsMZAgdx5Fkk9wwMNwCA "~YT - The New Oil" +https://www.youtube.com/feeds/videos.xml?channel_id=UCH_7doiCkWeq0v3ycWE5lDw "~YT - Any Austin" +https://www.youtube.com/feeds/videos.xml?channel_id=UCH_zBfX_o5GDZwevkh1jl9Q "~YT - Nate Live" +https://www.youtube.com/feeds/videos.xml?channel_id=UCHnyfMqiRRG1u-2MsSQLbXA "~YT - Veritasium" +https://www.youtube.com/feeds/videos.xml?channel_id=UCHvDhwNuq-h2hZQRR6BwbLQ "~YT - Tech With Nikola" +https://www.youtube.com/feeds/videos.xml?channel_id=UCI6pj4e-_6Km-dmLLYpmlaA "~YT - Alejandro Julián" +https://www.youtube.com/feeds/videos.xml?channel_id=UCIjIAXXsX4YMYeFj-LP42-Q "~YT - Anthony GG" +https://www.youtube.com/feeds/videos.xml?channel_id=UCJ0-OtVpF0wOKEqT2Z1HEtA "~YT - ElectroBOOM" +https://www.youtube.com/feeds/videos.xml?channel_id=UCJYJgj7rzsn0vdR7fkgjuIA "~YT - styropyro" +https://www.youtube.com/feeds/videos.xml?channel_id=UCLEoyoOKZK0idGqSc6Pi23w "~YT - RMC - The Cave" +https://www.youtube.com/feeds/videos.xml?channel_id=UCLx053rWZxCiYWsBETgdKrQ "~YT - LGR" +https://www.youtube.com/feeds/videos.xml?channel_id=UCM1og9pwIfAlg2jEtWh-Z7A "~YT - Sakharu Baguette" +https://www.youtube.com/feeds/videos.xml?channel_id=UCMbQbVilo-nezMvwf1BZfAA "~YT - CienciaDeSofa" +https://www.youtube.com/feeds/videos.xml?channel_id=UCMiyV_Ib77XLpzHPQH_q0qQ "~YT - Veronica Explains" +https://www.youtube.com/feeds/videos.xml?channel_id=UCMnZ3qm76jc3SUi9Z-5OdcA "~YT - Leyendas & Videojuegos" +https://www.youtube.com/feeds/videos.xml?channel_id=UCNYW2vfGrUE6R5mIJYzkRyQ "~YT - DrossRotzank" +https://www.youtube.com/feeds/videos.xml?channel_id=UCNnNCBgckxzqIh1Txw5cgSg "~YT - BaityBait" +https://www.youtube.com/feeds/videos.xml?channel_id=UCNzszbnvQeFzObW0ghk0Ckw "~YT - Dave's Garage" +https://www.youtube.com/feeds/videos.xml?channel_id=UCODHrzPMGbNv67e84WDZhQQ "~YT - fern" +https://www.youtube.com/feeds/videos.xml?channel_id=UCONH73CdRXUjlh3-DdLGCPw "~YT - Nicco Loves Linux" +https://www.youtube.com/feeds/videos.xml?channel_id=UCOxmlaJURX3nq8eLuJPbl3A "~YT - Psivewri" +https://www.youtube.com/feeds/videos.xml?channel_id=UCP5tjEmvPItGyLhmjdwP7Ww "~YT - RealLifeLore" +https://www.youtube.com/feeds/videos.xml?channel_id=UCQ-W1KE9EYfdxhL6S4twUNw "~YT - The Cherno" +https://www.youtube.com/feeds/videos.xml?channel_id=UCQX_MZRCaluNKxkywkLEgfA "~YT - Date un Vlog" +https://www.youtube.com/feeds/videos.xml?channel_id=UCR1IuLEqb6UEA_zQ81kwXfg "~YT - Real Engineering" +https://www.youtube.com/feeds/videos.xml?channel_id=UCRYeRa2iUMd8An1WTPIP2bw "~YT - aChair Leg" +https://www.youtube.com/feeds/videos.xml?channel_id=UCRrHl7MO7gcwWguTTqDzBHw "~YT - LoneVaultWanderer" +https://www.youtube.com/feeds/videos.xml?channel_id=UCS-WzPVpAAli-1IfEG2lN8A "~YT - Michael MJD" +https://www.youtube.com/feeds/videos.xml?channel_id=UCS0N5baNlQWJCUrhCEo8WlA "~YT - Ben Eater" +https://www.youtube.com/feeds/videos.xml?channel_id=UCSJPFQdZwrOutnmSFYtbstA "~YT - The Critical Drinker" +https://www.youtube.com/feeds/videos.xml?channel_id=UCSju5G2aFaWMqn-_0YBtq5A "~YT - Stand-up Maths" +https://www.youtube.com/feeds/videos.xml?channel_id=UCSp-OaMpsO8K0KkOqyBl7_w "~YT - Let's Get Rusty" +https://www.youtube.com/feeds/videos.xml?channel_id=UCSuHzQ3GrHSzoBbwrIq3LLA "~YT - Naomi Brockwell TV" +https://www.youtube.com/feeds/videos.xml?channel_id=UCT35hFvV0j8SR3SBGJChKZQ "~YT - Jota." +https://www.youtube.com/feeds/videos.xml?channel_id=UCT6LaAC9VckZYJUzutUW3PQ "~YT - Game Sack" +https://www.youtube.com/feeds/videos.xml?channel_id=UCTrSsPMmZavLbc3Ex7VhjDg "~YT - Brandon Herrera" +https://www.youtube.com/feeds/videos.xml?channel_id=UCUMwY9iS8oMyWDYIe6_RmoA "~YT - No Boilerplate" +https://www.youtube.com/feeds/videos.xml?channel_id=UCUyeluBRhGPCW4rPe_UvBZQ "~YT - ThePrimeTime" +https://www.youtube.com/feeds/videos.xml?channel_id=UCVIcSx0JX1lBXnyezabutrA "~YT - Shawn Wildermuth" +https://www.youtube.com/feeds/videos.xml?channel_id=UCVk4b-svNJoeytrrlOixebQ "~YT - TheVimeagen" +https://www.youtube.com/feeds/videos.xml?channel_id=UCVls1GmFKf6WlTraIb_IaJg "~YT - DistroTube" +https://www.youtube.com/feeds/videos.xml?channel_id=UCW-HHEyt67RhZ6q21n4p2zQ "~YT - Mac84" +https://www.youtube.com/feeds/videos.xml?channel_id=UCW0gH2G-cMKAEjEkI4YhnPA "~YT - Nerd of the Rings" +https://www.youtube.com/feeds/videos.xml?channel_id=UCWDGyt5hy6UA6Br-hAar03A "~YT - El Robot de Colón" +https://www.youtube.com/feeds/videos.xml?channel_id=UCWMV8dD0N3tAM2W6GuNCUCg "~YT - theunrealtarik" +https://www.youtube.com/feeds/videos.xml?channel_id=UCWQaM7SpSECp9FELz-cHzuQ "~YT - Dreams of Code" +https://www.youtube.com/feeds/videos.xml?channel_id=UCWcp1Mwm7_bJ-mVoZb8TdkQ "~YT - TuberViejuner" +https://www.youtube.com/feeds/videos.xml?channel_id=UCWyrVfwRL-2DOkzsqrbjo5Q "~YT - NCommander" +https://www.youtube.com/feeds/videos.xml?channel_id=UCXp5qAXAmdQ8n3RUZaME1QQ "~YT - ItsYaBoyBrandyBoy" +https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw "~YT - 3Blue1Brown" +https://www.youtube.com/feeds/videos.xml?channel_id=UCYVU6rModlGxvJbszCclGGw "~YT - Rob Braxman Tech" +https://www.youtube.com/feeds/videos.xml?channel_id=UCZ4AMrDcNrfy3X6nsU8-rPg "~YT - Economics Explained" +https://www.youtube.com/feeds/videos.xml?channel_id=UCZkPWp4MVoIVGLVLYC1Vjlg "~YT - Guille Ghemi" +https://www.youtube.com/feeds/videos.xml?channel_id=UCa6V1UVOXN4wDm7RDQDoa6g "~YT - El Traductor de Ingeniería" +https://www.youtube.com/feeds/videos.xml?channel_id=UCaSCt8s_4nfkRglWCvNSDrg "~YT - CodeAesthetic" +https://www.youtube.com/feeds/videos.xml?channel_id=UCaVPhFg-Ax873wvhbNitsrQ "~YT - El Robot de Platón" +https://www.youtube.com/feeds/videos.xml?channel_id=UCb0XKayhXq2JdoAHC2nr28Q "~YT - JARM" +https://www.youtube.com/feeds/videos.xml?channel_id=UCbJEWVgCAO-gyHcQ7U7RPCg "~YT - Axel 1UP" +https://www.youtube.com/feeds/videos.xml?channel_id=UCbdSYaPD-lr1kW27UJuk8Pw "~YT - QuantumFracture" +https://www.youtube.com/feeds/videos.xml?channel_id=UCbiGcwDWZjz05njNPrJU7jA "~YT - ExplainingComputers" +https://www.youtube.com/feeds/videos.xml?channel_id=UCcAy1o8VUCkdowxRYbc0XRw "~YT - Sebi's Random Tech" +https://www.youtube.com/feeds/videos.xml?channel_id=UCcQWoBTNG__OwfZk9znWgCQ "~YT - The Rusty Bits" +https://www.youtube.com/feeds/videos.xml?channel_id=UCd4XwUn2Lure2NHHjukoCwA "~YT - Linux For Everyone" +https://www.youtube.com/feeds/videos.xml?channel_id=UCdB41UXrNAU_J7A7OnU4KSQ "~YT - Japan Gemu" +https://www.youtube.com/feeds/videos.xml?channel_id=UCdiJKSDwwvy4rMUVc9CgS-g "~YT - DaniboubeTV" +https://www.youtube.com/feeds/videos.xml?channel_id=UCdp4_l1vPmpN-gDbUwhaRUQ "~YT - Branch Education" +https://www.youtube.com/feeds/videos.xml?channel_id=UCeCtLnoV0nmE2cBbbftCCiw "~YT - Las reviews de Camilo" +https://www.youtube.com/feeds/videos.xml?channel_id=UCeHOkFGW-7uAZFvq3BXb8YA "~YT - :3ildcat" +https://www.youtube.com/feeds/videos.xml?channel_id=UCerEIdrEW-IqwvlH8lTQUJQ "~YT - Tech Tangents" +https://www.youtube.com/feeds/videos.xml?channel_id=UCf-U0uPVQZtcqXUWa_Hl4Mw "~YT - Into the Shadows" +https://www.youtube.com/feeds/videos.xml?channel_id=UCg6gPGh8HU2U01vaFCAsvmQ "~YT - Chris Titus Tech" +https://www.youtube.com/feeds/videos.xml?channel_id=UCgNg3vwj3xt7QOrcIDaHdFg "~YT - PolyMatter" +https://www.youtube.com/feeds/videos.xml?channel_id=UCgdTVe88YVSrOZ9qKumhULQ "~YT - Hardware Haven" +https://www.youtube.com/feeds/videos.xml?channel_id=UChI0q9a-ZcbZh7dAu_-J-hg "~YT - Upper Echelon" +https://www.youtube.com/feeds/videos.xml?channel_id=UChIs72whgZI9w6d6FhwGGHA "~YT - Gamers Nexus" +https://www.youtube.com/feeds/videos.xml?channel_id=UCj8mAcR6Mu1dvaXQ6BdShRQ "~YT - The Masked Man" +https://www.youtube.com/feeds/videos.xml?channel_id=UCj8orMezFWVcoN-4S545Wtw "~YT - Max Derrat" +https://www.youtube.com/feeds/videos.xml?channel_id=UCjFaPUcJU1vwk193mnW_w1w "~YT - Modern Vintage Gamer" +https://www.youtube.com/feeds/videos.xml?channel_id=UCjSEJkpGbcZhvo0lr-44X_w "~YT - TechHut" +https://www.youtube.com/feeds/videos.xml?channel_id=UCjgS6Uyg8ok4Jd_lH_MUKgg "~YT - Claus Kellerman" +https://www.youtube.com/feeds/videos.xml?channel_id=UCjinkxv899vPCJ65BWpmPIA "~YT - Scott's Snippets" +https://www.youtube.com/feeds/videos.xml?channel_id=UCkK9UDm_ZNrq_rIXCz3xCGA "~YT - Bryan Lunduke" +https://www.youtube.com/feeds/videos.xml?channel_id=UCl2mFZoRqjw_ELax4Yisf6w "~YT - Louis Rossmann" +https://www.youtube.com/feeds/videos.xml?channel_id=UCl_dlV_7ofr4qeP1drJQ-qg "~YT - Tantacrul" +https://www.youtube.com/feeds/videos.xml?channel_id=UClb90NQQcskPUGDIXsQEz5Q "~YT - developedbyed" +https://www.youtube.com/feeds/videos.xml?channel_id=UCld68syR8Wi-GY_n4CaoJGA "~YT - Brodie Robertson" +https://www.youtube.com/feeds/videos.xml?channel_id=UClnDI2sdehVm1zm_LmUHsjQ "~YT - Biographics" +https://www.youtube.com/feeds/videos.xml?channel_id=UCm8EsftbfNzSiRHzc7I59KQ "~YT - Kevin Fang" +https://www.youtube.com/feeds/videos.xml?channel_id=UCmGSJVG3mCRXVOP4yZrU1Dw "~YT - Johnny Harris" +https://www.youtube.com/feeds/videos.xml?channel_id=UCmw-QGOHbHA5cDAvwwqUTKQ "~YT - Zaney" +https://www.youtube.com/feeds/videos.xml?channel_id=UCmyGZ0689ODyReHw3rsKLtQ "~YT - Michael Tunnell" +https://www.youtube.com/feeds/videos.xml?channel_id=UCnw3aIEiz60S6O3XcztCVkQ "~YT - PatricianTV" +https://www.youtube.com/feeds/videos.xml?channel_id=UCoL8olX-259lS1N6QPyP4IQ "~YT - Action Retro" +https://www.youtube.com/feeds/videos.xml?channel_id=UCoryWpk4QVYKFCJul9KBdyw "~YT - Switched to Linux" +https://www.youtube.com/feeds/videos.xml?channel_id=UCoxcjq-8xIDTYp3uz647V5A "~YT - Numberphile" +https://www.youtube.com/feeds/videos.xml?channel_id=UCpuKDBw8IVIdKWPhiB2VDNQ "~YT - Cinematix" +https://www.youtube.com/feeds/videos.xml?channel_id=UCpuLiczP2Aqq11Gtf4k_fkw "~YT - Futurasound Productions" +https://www.youtube.com/feeds/videos.xml?channel_id=UCq8LldVrjqe61KQttZlLW8g "~YT - Patrick God" +https://www.youtube.com/feeds/videos.xml?channel_id=UCqoGR_EedlhKDVuWNwYWRbg "~YT - Voices of the Past" +https://www.youtube.com/feeds/videos.xml?channel_id=UCqxM9T6ksiOVKIkb88S2r7Q "~YT - Zac Builds" +https://www.youtube.com/feeds/videos.xml?channel_id=UCrkPsvLGln62OMZRO6K-llg "~YT - Nick Chapsas" +https://www.youtube.com/feeds/videos.xml?channel_id=UCs6KfncB4OV6Vug4o_bzijg "~YT - Techlore" +https://www.youtube.com/feeds/videos.xml?channel_id=UCs7nPQIEba0T3tGOWWsZpJQ "~YT - Like Stories of Old" +https://www.youtube.com/feeds/videos.xml?channel_id=UCsBjURrPoezykLs9EqgamOA "~YT - Fireship" +https://www.youtube.com/feeds/videos.xml?channel_id=UCsC--WvN66vZlDoYShzn4sA "~YT - Oxhorn" +https://www.youtube.com/feeds/videos.xml?channel_id=UCsnGwSIHyoYN0kiINAGUKxg "~YT - Wolfgang's Channel" +https://www.youtube.com/feeds/videos.xml?channel_id=UCtApDZWXv8fQaxZAk6dxYkw "~YT - Ghostcharm" +https://www.youtube.com/feeds/videos.xml?channel_id=UCtMVHI3AJD4Qk4hcbZnI9ZQ "~YT - SomeOrdinaryGamers" +https://www.youtube.com/feeds/videos.xml?channel_id=UCtYKe7-XbaDjpUwcU5x0bLg "~YT - neo" +https://www.youtube.com/feeds/videos.xml?channel_id=UCtYg149E_wUGVmjGz-TgyNA "~YT - Titus Tech Talk" +https://www.youtube.com/feeds/videos.xml?channel_id=UCtZX_67hSjOIPu0HTmFi6aw "~YT - Macintosh Librarian" +https://www.youtube.com/feeds/videos.xml?channel_id=UCvjgXvBlbQiydffZU7m1_aw "~YT - The Coding Train" +https://www.youtube.com/feeds/videos.xml?channel_id=UCxQKHvKbmSzGMvUrVtJYnUA "~YT - Learn Linux TV" +https://www.youtube.com/feeds/videos.xml?channel_id=UCxdZ7XCQVMRMipj3gGemQfw "~YT - GNULectures" +https://www.youtube.com/feeds/videos.xml?channel_id=UCxoz4YfS4M3H3C57FD4jW4Q "~YT - Science Time" +https://www.youtube.com/feeds/videos.xml?channel_id=UCy0tKL1T7wFoYcxCe0xjN6Q "~YT - Technology Connections" +https://www.youtube.com/feeds/videos.xml?channel_id=UCy6pDeDwRFi1kmFekSpMDUA "~YT - La Poción Roja" +https://www.youtube.com/feeds/videos.xml?channel_id=UCybBViio_TH_uiFFDJuz5tg "~YT - Einzelgänger" +https://www.youtube.com/feeds/videos.xml?channel_id=UCylGUf9BvQooEFjgdNudoQg "~YT - The Linux Cast" +https://www.youtube.com/feeds/videos.xml?channel_id=UCz1oFxMrgrQ82-276UCOU9w "~YT - Atlas Pro" +https://www.youtube.com/feeds/videos.xml?channel_id=UCzGMBzt6UOMoQe_dqOfShZw "~YT - Cultura VJ" +https://www.youtube.com/feeds/videos.xml?channel_id=UCzR-rom72PHN9Zg7RML9EbA "~YT - PBS Eons" +https://www.youtube.com/feeds/videos.xml?channel_id=UCzXsTSZDoAPSjfHr8IZM9Ew "~YT - FloatyMonkey" diff --git a/config/.config/qutebrowser/bookmarks/urls b/config/.config/qutebrowser/bookmarks/urls new file mode 100644 index 000000000..59badff4c --- /dev/null +++ b/config/.config/qutebrowser/bookmarks/urls @@ -0,0 +1,27 @@ +https://procesos.intec.edu.do/ intec-procesos +https://campusvirtual.intec.edu.do/ intec-aula-virtual +https://outlook.office.com/mail/ intec-correo +https://app.tuta.com/mail tuta +https://mail.proton.me/ protonmail +https://mail.google.com/ gmail +https://outlook.live.com/ outlook +https://floss.social/ mastodon +https://beehaw.org/ beehaw +https://pixelfed.social/ pixelfed +https://www.edx.org/ edx +https://khanacademy.org/ khanacademy +https://clientes.eps.com.do/ eps +https://www.amazon.com/ amazon +https://annas-archive.org/ annas-archive +https://cockpit.lianslair.com/ cockpit +https://cloud.lianslair.com/ nextcloud +https://vault.lianslair.com/ bitwarden +https://library.lianslair.com/ kavita +https://rss.lianslair.com/ freshrss +https://search.lianslair.com/ search +https://portainer.lianslair.com/ portainer +https://cloud.digitalocean.com/ digitalocean +https://github.com/ github +https://mega.nz/ mega +https://drive.google.com/drive/my-drive google-drive +https://web.whatsapp.com/ whatsapp diff --git a/config/.config/qutebrowser/config.py b/config/.config/qutebrowser/config.py new file mode 100644 index 000000000..b52ccd7a8 --- /dev/null +++ b/config/.config/qutebrowser/config.py @@ -0,0 +1,188 @@ +## ____ __ +## / __ \_________ _/ /_____ +## / / / / ___/ __ `/ //_/ _ \ +## / /_/ / / / /_/ / ,< / __/ Clay Gomera (Drake) +## /_____/_/ \__,_/_/|_|\___/ My custom qutebrowser config +## + +# NOTE: config.py is intended for advanced users who are comfortable +# with manually migrating the config file on qutebrowser upgrades. If +# you prefer, you can also configure qutebrowser using the +# :set/:bind/:config-* commands without having to write a config.py +# file. +# +# Documentation: +# qute://help/configuring.html +# qute://help/settings.html + +# Uncomment this to still load settings configured via autoconfig.yml +# config.load_autoconfig() +# Or uncomment this line to load settings from config.py +config.load_autoconfig(False) + +# Aliases for commands. The keys of the given dictionary are the +# aliases, while the values are the commands they map to. +# Type: Dict +c.aliases = {'q': 'quit', 'w': 'session-save', 'wq': 'quit --save'} + +# Setting dark mode +config.set("colors.webpage.darkmode.enabled", True) + +# Which cookies to accept. With QtWebEngine, this setting also controls +# other features with tracking capabilities similar to those of cookies; +# including IndexedDB, DOM storage, filesystem API, service workers, and +# AppCache. Note that with QtWebKit, only `all` and `never` are +# supported as per-domain values. Setting `no-3rdparty` or `no- +# unknown-3rdparty` per-domain on QtWebKit will have the same effect as +# `all`. +# Type: String +# Valid values: +# - all: Accept all cookies. +# - no-3rdparty: Accept cookies from the same origin only. This is known to break some sites, such as GMail. +# - no-unknown-3rdparty: Accept cookies from the same origin only, unless a cookie is already set for the domain. On QtWebEngine, this is the same as no-3rdparty. +# - never: Don't accept cookies at all. +config.set('content.cookies.accept', 'all', 'chrome-devtools://*') + +# Which cookies to accept. With QtWebEngine, this setting also controls +# other features with tracking capabilities similar to those of cookies; +# including IndexedDB, DOM storage, filesystem API, service workers, and +# AppCache. Note that with QtWebKit, only `all` and `never` are +# supported as per-domain values. Setting `no-3rdparty` or `no- +# unknown-3rdparty` per-domain on QtWebKit will have the same effect as +# `all`. +# Type: String +# Valid values: +# - all: Accept all cookies. +# - no-3rdparty: Accept cookies from the same origin only. This is known to break some sites, such as GMail. +# - no-unknown-3rdparty: Accept cookies from the same origin only, unless a cookie is already set for the domain. On QtWebEngine, this is the same as no-3rdparty. +# - never: Don't accept cookies at all. +config.set('content.cookies.accept', 'all', 'devtools://*') + +# User agent to send. The following placeholders are defined: * +# `{os_info}`: Something like "X11; Linux x86_64". * `{webkit_version}`: +# The underlying WebKit version (set to a fixed value with +# QtWebEngine). * `{qt_key}`: "Qt" for QtWebKit, "QtWebEngine" for +# QtWebEngine. * `{qt_version}`: The underlying Qt version. * +# `{upstream_browser_key}`: "Version" for QtWebKit, "Chrome" for +# QtWebEngine. * `{upstream_browser_version}`: The corresponding +# Safari/Chrome version. * `{qutebrowser_version}`: The currently +# running qutebrowser version. The default value is equal to the +# unchanged user agent of QtWebKit/QtWebEngine. Note that the value +# read from JavaScript is always the global value. With QtWebEngine +# between 5.12 and 5.14 (inclusive), changing the value exposed to +# JavaScript requires a restart. +# Type: FormatString +#config.set('content.headers.user_agent', 'Mozilla/5.0 ({os_info}) AppleWebKit/{webkit_version} (KHTML, like Gecko) {upstream_browser_key}/{upstream_browser_version} Safari/{webkit_version}', 'https://web.whatsapp.com/') +#config.set('content.headers.user_agent', 'Mozilla/5.0 ({os_info}; rv:71.0) Gecko/20100101 Firefox/71.0', 'https://accounts.google.com/*') +#config.set('content.headers.user_agent', 'Mozilla/5.0 ({os_info}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99 Safari/537.36', 'https://*.slack.com/*') +#config.set('content.headers.user_agent', 'Mozilla/5.0 ({os_info}; rv:71.0) Gecko/20100101 Firefox/71.0', 'https://docs.google.com/*') +#config.set('content.headers.user_agent', 'Mozilla/5.0 ({os_info}; rv:71.0) Gecko/20100101 Firefox/71.0', 'https://drive.google.com/*') + +# Load images automatically in web pages. +# Type: Bool +config.set('content.images', True, 'chrome-devtools://*') + +# Load images automatically in web pages. +# Type: Bool +config.set('content.images', True, 'devtools://*') + +# Enable JavaScript. +# Type: Bool +config.set('content.javascript.enabled', True, 'chrome-devtools://*') + +# Enable JavaScript. +# Type: Bool +config.set('content.javascript.enabled', True, 'devtools://*') + +# Enable JavaScript. +# Type: Bool +config.set('content.javascript.enabled', True, 'chrome://*/*') + +# Enable JavaScript. +# Type: Bool +config.set('content.javascript.enabled', True, 'qute://*/*') + +# Directory to save downloads to. If unset, a sensible OS-specific +# default is used. +# Type: Directory +c.downloads.location.directory = '~/Downloads' + +# When to show the tab bar. +# Type: String +# Valid values: +# - always: Always show the tab bar. +# - never: Always hide the tab bar. +# - multiple: Hide the tab bar if only one tab is open. +# - switching: Show the tab bar when switching tabs. +c.tabs.show = 'always' + +# Setting default page for when opening new tabs or new windows with +# commands like :open -t and :open -w . +c.url.default_page = 'https://search.lianslair.com' +c.url.start_pages = 'https://search.lianslair.com' + +# Search engines which can be used via the address bar. Maps a search +# engine name (such as `DEFAULT`, or `ddg`) to a URL with a `{}` +# placeholder. The placeholder will be replaced by the search term, use +# `{{` and `}}` for literal `{`/`}` braces. The following further +# placeholds are defined to configure how special characters in the +# search terms are replaced by safe characters (called 'quoting'): * +# `{}` and `{semiquoted}` quote everything except slashes; this is the +# most sensible choice for almost all search engines (for the search +# term `slash/and&` this placeholder expands to `slash/and%26amp`). +# * `{quoted}` quotes all characters (for `slash/and&` this +# placeholder expands to `slash%2Fand%26amp`). * `{unquoted}` quotes +# nothing (for `slash/and&` this placeholder expands to +# `slash/and&`). The search engine named `DEFAULT` is used when +# `url.auto_search` is turned on and something else than a URL was +# entered to be opened. Other search engines can be used by prepending +# the search engine name to the search term, e.g. `:open google +# qutebrowser`. +# Type: Dict +c.url.searchengines = {'DEFAULT': 'https://search.lianslair.com/search?q={}', 'aw': 'https://wiki.archlinux.org/?search={}', 'ub': 'https://www.urbandictionary.com/define.php?term={}', 'wiki': 'https://en.wikipedia.org/wiki/{}'} + +# Default font families to use. Whenever "default_family" is used in a +# font setting, it's replaced with the fonts listed here. If set to an +# empty value, a system-specific monospace default is used. +# Type: List of Font, or Font +c.fonts.default_family = '"Inter"' + +# Default font size to use. Whenever "default_size" is used in a font +# setting, it's replaced with the size listed here. Valid values are +# either a float value with a "pt" suffix, or an integer value with a +# "px" suffix. +# Type: String +c.fonts.default_size = '10pt' + +# Font used in the completion widget. +# Type: Font +c.fonts.completion.entry = '10pt "mononoki Nerd Font"' + +# Font used for the debugging console. +# Type: Font +c.fonts.debug_console = '10pt "mononoki Nerd Font"' + +# Font used for prompts. +# Type: Font +c.fonts.prompts = 'default_size sans-serif' + +# Font used in the statusbar. +# Type: Font +c.fonts.statusbar = '10pt "Inter"' + +config.source('gruvbox.py') + +# Bindings for normal mode +config.bind('M', 'hint links spawn mpv {hint-url}') +config.bind('Z', 'hint links spawn st -e youtube-dl {hint-url}') +config.bind('t', 'set-cmd-text -s :open -t') +config.bind('xb', 'config-cycle statusbar.show always never') +config.bind('xt', 'config-cycle tabs.show always never') +config.bind('xx', 'config-cycle statusbar.show always never;; config-cycle tabs.show always never') + +# configs +config.set('content.notifications.enabled', False) +config.set('content.blocking.method', 'both') +config.set('content.blocking.enabled', True) +config.set('content.autoplay', False) +config.set('content.register_protocol_handler', False) diff --git a/config/.config/qutebrowser/gruvbox.py b/config/.config/qutebrowser/gruvbox.py new file mode 100644 index 000000000..89577b37f --- /dev/null +++ b/config/.config/qutebrowser/gruvbox.py @@ -0,0 +1,333 @@ +# gruvbox dark hard qutebrowser theme by Florian Bruhin +# +# Originally based on: +# base16-qutebrowser (https://github.com/theova/base16-qutebrowser) +# Base16 qutebrowser template by theova and Daniel Mulford +# Gruvbox dark, hard scheme by Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox) + +bg0_hard = "#1d2021" +bg0_soft = '#32302f' +bg0_normal = '#282828' + +bg0 = bg0_normal +bg1 = "#3c3836" +bg2 = "#504945" +bg3 = "#665c54" +bg4 = "#7c6f64" + +fg0 = "#fbf1c7" +fg1 = "#ebdbb2" +fg2 = "#d5c4a1" +fg3 = "#bdae93" +fg4 = "#a89984" + +bright_red = "#fb4934" +bright_green = "#b8bb26" +bright_yellow = "#fabd2f" +bright_blue = "#83a598" +bright_purple = "#d3869b" +bright_aqua = "#8ec07c" +bright_gray = "#928374" +bright_orange = "#fe8019" + +dark_red = "#cc241d" +dark_green = "#98971a" +dark_yellow = "#d79921" +dark_blue = "#458588" +dark_purple = "#b16286" +dark_aqua = "#689d6a" +dark_gray = "#a89984" +dark_orange = "#d65d0e" + +### Completion + +# Text color of the completion widget. May be a single color to use for +# all columns or a list of three colors, one for each column. +c.colors.completion.fg = [fg1, bright_aqua, bright_yellow] + +# Background color of the completion widget for odd rows. +c.colors.completion.odd.bg = bg0 + +# Background color of the completion widget for even rows. +c.colors.completion.even.bg = c.colors.completion.odd.bg + +# Foreground color of completion widget category headers. +c.colors.completion.category.fg = bright_blue + +# Background color of the completion widget category headers. +c.colors.completion.category.bg = bg1 + +# Top border color of the completion widget category headers. +c.colors.completion.category.border.top = c.colors.completion.category.bg + +# Bottom border color of the completion widget category headers. +c.colors.completion.category.border.bottom = c.colors.completion.category.bg + +# Foreground color of the selected completion item. +c.colors.completion.item.selected.fg = fg0 + +# Background color of the selected completion item. +c.colors.completion.item.selected.bg = bg4 + +# Top border color of the selected completion item. +c.colors.completion.item.selected.border.top = bg2 + +# Bottom border color of the selected completion item. +c.colors.completion.item.selected.border.bottom = c.colors.completion.item.selected.border.top + +# Foreground color of the matched text in the selected completion item. +c.colors.completion.item.selected.match.fg = bright_orange + +# Foreground color of the matched text in the completion. +c.colors.completion.match.fg = c.colors.completion.item.selected.match.fg + +# Color of the scrollbar handle in the completion view. +c.colors.completion.scrollbar.fg = c.colors.completion.item.selected.fg + +# Color of the scrollbar in the completion view. +c.colors.completion.scrollbar.bg = c.colors.completion.category.bg + +### Context menu + +# Background color of disabled items in the context menu. +c.colors.contextmenu.disabled.bg = bg3 + +# Foreground color of disabled items in the context menu. +c.colors.contextmenu.disabled.fg = fg3 + +# Background color of the context menu. If set to null, the Qt default is used. +c.colors.contextmenu.menu.bg = bg0 + +# Foreground color of the context menu. If set to null, the Qt default is used. +c.colors.contextmenu.menu.fg = fg2 + +# Background color of the context menu's selected item. If set to null, the Qt default is used. +c.colors.contextmenu.selected.bg = bg2 + +#Foreground color of the context menu's selected item. If set to null, the Qt default is used. +c.colors.contextmenu.selected.fg = c.colors.contextmenu.menu.fg + +### Downloads + +# Background color for the download bar. +c.colors.downloads.bar.bg = bg0 + +# Color gradient start for download text. +c.colors.downloads.start.fg = bg0 + +# Color gradient start for download backgrounds. +c.colors.downloads.start.bg = bright_blue + +# Color gradient end for download text. +c.colors.downloads.stop.fg = c.colors.downloads.start.fg + +# Color gradient stop for download backgrounds. +c.colors.downloads.stop.bg = bright_aqua + +# Foreground color for downloads with errors. +c.colors.downloads.error.fg = bright_red + +### Hints + +# Font color for hints. +c.colors.hints.fg = bg0 + +# Background color for hints. +c.colors.hints.bg = 'rgba(250, 191, 47, 200)' # bright_yellow + +# Font color for the matched part of hints. +c.colors.hints.match.fg = bg4 + +### Keyhint widget + +# Text color for the keyhint widget. +c.colors.keyhint.fg = fg4 + +# Highlight color for keys to complete the current keychain. +c.colors.keyhint.suffix.fg = fg0 + +# Background color of the keyhint widget. +c.colors.keyhint.bg = bg0 + +### Messages + +# Foreground color of an error message. +c.colors.messages.error.fg = bg0 + +# Background color of an error message. +c.colors.messages.error.bg = bright_red + +# Border color of an error message. +c.colors.messages.error.border = c.colors.messages.error.bg + +# Foreground color of a warning message. +c.colors.messages.warning.fg = bg0 + +# Background color of a warning message. +c.colors.messages.warning.bg = bright_purple + +# Border color of a warning message. +c.colors.messages.warning.border = c.colors.messages.warning.bg + +# Foreground color of an info message. +c.colors.messages.info.fg = fg2 + +# Background color of an info message. +c.colors.messages.info.bg = bg0 + +# Border color of an info message. +c.colors.messages.info.border = c.colors.messages.info.bg + +### Prompts + +# Foreground color for prompts. +c.colors.prompts.fg = fg2 + +# Border used around UI elements in prompts. +c.colors.prompts.border = f'1px solid {bg1}' + +# Background color for prompts. +c.colors.prompts.bg = bg3 + +# Background color for the selected item in filename prompts. +c.colors.prompts.selected.bg = bg2 + +### Statusbar + +# Foreground color of the statusbar. +c.colors.statusbar.normal.fg = fg2 + +# Background color of the statusbar. +c.colors.statusbar.normal.bg = bg0 + +# Foreground color of the statusbar in insert mode. +c.colors.statusbar.insert.fg = bg0 + +# Background color of the statusbar in insert mode. +c.colors.statusbar.insert.bg = dark_aqua + +# Foreground color of the statusbar in passthrough mode. +c.colors.statusbar.passthrough.fg = bg0 + +# Background color of the statusbar in passthrough mode. +c.colors.statusbar.passthrough.bg = dark_blue + +# Foreground color of the statusbar in private browsing mode. +c.colors.statusbar.private.fg = bright_purple + +# Background color of the statusbar in private browsing mode. +c.colors.statusbar.private.bg = bg0 + +# Foreground color of the statusbar in command mode. +c.colors.statusbar.command.fg = fg3 + +# Background color of the statusbar in command mode. +c.colors.statusbar.command.bg = bg1 + +# Foreground color of the statusbar in private browsing + command mode. +c.colors.statusbar.command.private.fg = c.colors.statusbar.private.fg + +# Background color of the statusbar in private browsing + command mode. +c.colors.statusbar.command.private.bg = c.colors.statusbar.command.bg + +# Foreground color of the statusbar in caret mode. +c.colors.statusbar.caret.fg = bg0 + +# Background color of the statusbar in caret mode. +c.colors.statusbar.caret.bg = dark_purple + +# Foreground color of the statusbar in caret mode with a selection. +c.colors.statusbar.caret.selection.fg = c.colors.statusbar.caret.fg + +# Background color of the statusbar in caret mode with a selection. +c.colors.statusbar.caret.selection.bg = bright_purple + +# Background color of the progress bar. +c.colors.statusbar.progress.bg = bright_blue + +# Default foreground color of the URL in the statusbar. +c.colors.statusbar.url.fg = fg4 + +# Foreground color of the URL in the statusbar on error. +c.colors.statusbar.url.error.fg = dark_red + +# Foreground color of the URL in the statusbar for hovered links. +c.colors.statusbar.url.hover.fg = bright_orange + +# Foreground color of the URL in the statusbar on successful load +# (http). +c.colors.statusbar.url.success.http.fg = bright_red + +# Foreground color of the URL in the statusbar on successful load +# (https). +c.colors.statusbar.url.success.https.fg = fg0 + +# Foreground color of the URL in the statusbar when there's a warning. +c.colors.statusbar.url.warn.fg = bright_purple + +### tabs + +# Background color of the tab bar. +c.colors.tabs.bar.bg = bg0 + +# Color gradient start for the tab indicator. +c.colors.tabs.indicator.start = bright_blue + +# Color gradient end for the tab indicator. +c.colors.tabs.indicator.stop = bright_aqua + +# Color for the tab indicator on errors. +c.colors.tabs.indicator.error = bright_red + +# Foreground color of unselected odd tabs. +c.colors.tabs.odd.fg = fg2 + +# Background color of unselected odd tabs. +c.colors.tabs.odd.bg = bg2 + +# Foreground color of unselected even tabs. +c.colors.tabs.even.fg = c.colors.tabs.odd.fg + +# Background color of unselected even tabs. +c.colors.tabs.even.bg = bg3 + +# Foreground color of selected odd tabs. +c.colors.tabs.selected.odd.fg = fg2 + +# Background color of selected odd tabs. +c.colors.tabs.selected.odd.bg = bg0 + +# Foreground color of selected even tabs. +c.colors.tabs.selected.even.fg = c.colors.tabs.selected.odd.fg + +# Background color of selected even tabs. +c.colors.tabs.selected.even.bg = bg0 + +# Background color of pinned unselected even tabs. +c.colors.tabs.pinned.even.bg = bright_green + +# Foreground color of pinned unselected even tabs. +c.colors.tabs.pinned.even.fg = bg2 + +# Background color of pinned unselected odd tabs. +c.colors.tabs.pinned.odd.bg = bright_green + +# Foreground color of pinned unselected odd tabs. +c.colors.tabs.pinned.odd.fg = c.colors.tabs.pinned.even.fg + +# Background color of pinned selected even tabs. +c.colors.tabs.pinned.selected.even.bg = bg0 + +# Foreground color of pinned selected even tabs. +c.colors.tabs.pinned.selected.even.fg = c.colors.tabs.selected.odd.fg + +# Background color of pinned selected odd tabs. +c.colors.tabs.pinned.selected.odd.bg = c.colors.tabs.pinned.selected.even.bg + +# Foreground color of pinned selected odd tabs. +c.colors.tabs.pinned.selected.odd.fg = c.colors.tabs.selected.odd.fg + +# Background color for webpages if unset (or empty to use the theme's +# color). +#c.colors.webpage.bg = bg4 + diff --git a/config/.local/share/applications/es-de.desktop b/config/.local/share/applications/es-de.desktop new file mode 100644 index 000000000..65550b2c5 --- /dev/null +++ b/config/.local/share/applications/es-de.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +Type=Application +Icon=org.gnome.Game +Name=Emulation Station +Name[en]=Emulation Station +Name[en_US]=Emulation Station +Name[en_US.UTF-8]=Emulation Station +Comment=Emulator frontend +Comment[en]=Emulator frontend +Comment[en_US]=Emulator frontend +Comment[en_US.UTF-8]=Emulator frontend +Exec=/home/drk/.local/bin/ES-DE_x64.AppImage +Hidden=false +NoDisplay=false +StartupNotify=false +Terminal=false + diff --git a/config/etc/bash.bash_logout b/config/etc/bash.bash_logout new file mode 100644 index 000000000..5aad3995d --- /dev/null +++ b/config/etc/bash.bash_logout @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# ~/.bash_logout: executed by bash(1) when login shell exits. + +if [ "$SHLVL" = 1 ]; then + [ -x /usr/bin/clear ] && /usr/bin/clear -q +fi diff --git a/config/etc/crypttab b/config/etc/crypttab new file mode 100644 index 000000000..844a9ab92 --- /dev/null +++ b/config/etc/crypttab @@ -0,0 +1,14 @@ +# crypttab: mappings for encrypted partitions +# +# Each mapped device will be created in /dev/mapper, so your /etc/fstab +# should use the /dev/mapper/ paths for encrypted devices. +# +# NOTE: Do not list your root (/) partition here. + +# +# home /dev/hda4 /etc/mypassword1 +# data1 /dev/hda3 /etc/mypassword2 +# data2 /dev/hda5 /etc/cryptfs.key +# swap /dev/hdx4 /dev/urandom swap,cipher=aes-cbc-essiv:sha256,size=256 +# vol /dev/hdb7 none +thklvm /dev/nvme0n1p2 /boot/volume.key luks diff --git a/config/etc/default/grub b/config/etc/default/grub index f5d7630a0..2ba18315e 100644 --- a/config/etc/default/grub +++ b/config/etc/default/grub @@ -1,64 +1,22 @@ -# GRUB boot loader configuration - +# +# Configuration file for GRUB. +# GRUB_DEFAULT=0 +GRUB_HIDDEN_TIMEOUT=1 +GRUB_HIDDEN_TIMEOUT_QUIET=true GRUB_TIMEOUT=0 -GRUB_DISTRIBUTOR="Arch" -GRUB_CMDLINE_LINUX_DEFAULT="rd.luks.name=41091a77-695d-4b2e-b60e-439b00504c77=thklvm rd.luks.key=41091a77-695d-4b2e-b60e-439b00504c77=/boot/volume.key loglevel=3 quiet systemd.show_status=auto rd.udev.log_level=3" -GRUB_CMDLINE_LINUX="" - -# Preload both GPT and MBR modules so that they are not missed -GRUB_PRELOAD_MODULES="part_gpt part_msdos" - -# Uncomment to enable booting from LUKS encrypted devices -GRUB_ENABLE_CRYPTODISK=y - -# Set to 'countdown' or 'hidden' to change timeout behavior, -# press ESC key to display menu. -GRUB_TIMEOUT_STYLE=menu - +GRUB_DISTRIBUTOR="Void" +GRUB_CMDLINE_LINUX_DEFAULT="rd.lvm.vg=thkvg rd.luks.uuid=a2d27caa-9675-4e07-ad78-51d796c879de loglevel=3 quiet" # Uncomment to use basic console -GRUB_TERMINAL_INPUT=console - +#GRUB_TERMINAL_INPUT="console" # Uncomment to disable graphical terminal #GRUB_TERMINAL_OUTPUT=console - -# The resolution used on graphical terminal -# note that you can use only modes which your graphic card supports via VBE -# you can see them in real GRUB with the command `videoinfo' -GRUB_GFXMODE=auto - -# Uncomment to allow the kernel use the same resolution used by grub -GRUB_GFXPAYLOAD_LINUX=keep - -# Uncomment if you want GRUB to pass to the Linux kernel the old parameter -# format "root=/dev/xxx" instead of "root=/dev/disk/by-uuid/xxx" +#GRUB_BACKGROUND=/usr/share/void-artwork/splash.png +#GRUB_GFXMODE=1920x1080x32 #GRUB_DISABLE_LINUX_UUID=true - -# Uncomment to disable generation of recovery mode menu entries -GRUB_DISABLE_RECOVERY=true - +#GRUB_DISABLE_RECOVERY=true # Uncomment and set to the desired menu colors. Used by normal and wallpaper # modes only. Entries specified as foreground/background. #GRUB_COLOR_NORMAL="light-blue/black" #GRUB_COLOR_HIGHLIGHT="light-cyan/blue" - -# Uncomment one of them for the gfx desired, a image background or a gfxtheme -#GRUB_BACKGROUND="/path/to/wallpaper" -#GRUB_THEME="/path/to/gfxtheme" - -# Uncomment to get a beep at GRUB start -#GRUB_INIT_TUNE="480 440 1" - -# Uncomment to make GRUB remember the last selection. This requires -# setting 'GRUB_DEFAULT=saved' above. -#GRUB_SAVEDEFAULT=true - -# Uncomment to disable submenus in boot menu -#GRUB_DISABLE_SUBMENU=y - -# Probing for other operating systems is disabled for security reasons. Read -# documentation on GRUB_DISABLE_OS_PROBER, if still want to enable this -# functionality install os-prober and uncomment to detect and include other -# operating systems. -#GRUB_DISABLE_OS_PROBER=false - +GRUB_ENABLE_CRYPTODISK=y diff --git a/config/etc/dracut.conf b/config/etc/dracut.conf new file mode 100644 index 000000000..76d652c7b --- /dev/null +++ b/config/etc/dracut.conf @@ -0,0 +1,4 @@ +# PUT YOUR CONFIG IN separate files +# in /etc/dracut.conf.d named ".conf" +# SEE man dracut.conf(5) for options +add_drivers+=" i915 " diff --git a/config/etc/issue b/config/etc/issue index 96a033257..82609729f 100644 --- a/config/etc/issue +++ b/config/etc/issue @@ -1,13 +1,13 @@ -  -       ### ### ## -     ## ## - ┌ #### ### ###   ####   ## ### ## ### ### ### ### ###  ### ### - │  ##  ### ##  ##   ###  ## ## ## ### ## ## ##   ## ## - │ #####  ##   ##   ## ## ## ## ## ## ## ##   ### - │ ## ##  ##   ##   ## ## ## ## ## ## ##  ##   ## ##  - │ ######  ##   ####  ### ### #### #### ### ### ######  ###   ### - └─────────────────────────────────────────── A simple, lightweight distribution. -  - OS: Arch\s Kernel: \r Processor: \m TTY: \l - + +\e[H\e[2J +\e[0;32m + _______ + \\_____ `- ____ ____ .__ .___ .____ .__ + /\\ ___ `- \\ \\ \\ / /___ |__| __| _/ | | |__| ____ __ _____ ___ + | | / \\ | | \\ Y / _ \\| |/ __ | | | | |/ \\| | \\ \\/ / + | | \\___/ | | \\ ( <_> ) / /_/ | | |___| | | \\ | /> < + \\ `-_____ \\/ \\___/ \\____/|__\\____ | |_______ \\__|___| /____//__/\\_ \\ + `-______\\ \\/ \\/ \\/ \\/ +\e[0m +Welcome Back! Linux Version: \r (\n) (\l) diff --git a/config/etc/mkinitcpio.conf b/config/etc/mkinitcpio.conf deleted file mode 100644 index 6feb6169c..000000000 --- a/config/etc/mkinitcpio.conf +++ /dev/null @@ -1,81 +0,0 @@ -# vim:set ft=sh -# MODULES -# The following modules are loaded before any boot hooks are -# run. Advanced users may wish to specify all system modules -# in this array. For instance: -# MODULES=(usbhid xhci_hcd) -MODULES=(i915) - -# BINARIES -# This setting includes any additional binaries a given user may -# wish into the CPIO image. This is run last, so it may be used to -# override the actual binaries included by a given hook -# BINARIES are dependency parsed, so you may safely ignore libraries -BINARIES=() - -# FILES -# This setting is similar to BINARIES above, however, files are added -# as-is and are not parsed in any way. This is useful for config files. -FILES=(/boot/volume.key) - -# HOOKS -# This is the most important setting in this file. The HOOKS control the -# modules and scripts added to the image, and what happens at boot time. -# Order is important, and it is recommended that you do not change the -# order in which HOOKS are added. Run 'mkinitcpio -H ' for -# help on a given hook. -# 'base' is _required_ unless you know precisely what you are doing. -# 'udev' is _required_ in order to automatically load modules -# 'filesystems' is _required_ unless you specify your fs modules in MODULES -# Examples: -## This setup specifies all modules in the MODULES setting above. -## No RAID, lvm2, or encrypted root is needed. -# HOOKS=(base) -# -## This setup will autodetect all modules for your system and should -## work as a sane default -# HOOKS=(base udev autodetect modconf block filesystems fsck) -# -## This setup will generate a 'full' image which supports most systems. -## No autodetection is done. -# HOOKS=(base udev modconf block filesystems fsck) -# -## This setup assembles a mdadm array with an encrypted root file system. -## Note: See 'mkinitcpio -H mdadm_udev' for more information on RAID devices. -# HOOKS=(base udev modconf keyboard keymap consolefont block mdadm_udev encrypt filesystems fsck) -# -## This setup loads an lvm2 volume group. -# HOOKS=(base udev modconf block lvm2 filesystems fsck) -# -## This will create a systemd based initramfs which loads an encrypted root filesystem. -# HOOKS=(base systemd autodetect modconf kms keyboard sd-vconsole sd-encrypt block filesystems fsck) -# -## NOTE: If you have /usr on a separate partition, you MUST include the -# usr and fsck hooks. -HOOKS=(base systemd autodetect microcode modconf kms keyboard keymap sd-vconsole block sd-encrypt lvm2 filesystems fsck) - -# COMPRESSION -# Use this to compress the initramfs image. By default, zstd compression -# is used for Linux ≥ 5.9 and gzip compression is used for Linux < 5.9. -# Use 'cat' to create an uncompressed image. -#COMPRESSION="zstd" -#COMPRESSION="gzip" -#COMPRESSION="bzip2" -#COMPRESSION="lzma" -#COMPRESSION="xz" -#COMPRESSION="lzop" -#COMPRESSION="lz4" - -# COMPRESSION_OPTIONS -# Additional options for the compressor -#COMPRESSION_OPTIONS=() - -# MODULES_DECOMPRESS -# Decompress loadable kernel modules and their firmware during initramfs -# creation. Switch (yes/no). -# Enable to allow further decreasing image size when using high compression -# (e.g. xz -9e or zstd --long --ultra -22) at the expense of increased RAM usage -# at early boot. -# Note that any compressed files will be placed in the uncompressed early CPIO -# to avoid double compression. -#MODULES_DECOMPRESS="no" diff --git a/config/etc/pacman.conf b/config/etc/pacman.conf deleted file mode 100644 index 6923e83b9..000000000 --- a/config/etc/pacman.conf +++ /dev/null @@ -1,98 +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 -ILoveCandy - -# 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. - -#[core-testing] -#Include = /etc/pacman.d/mirrorlist - -[core] -Include = /etc/pacman.d/mirrorlist - -#[extra-testing] -#Include = /etc/pacman.d/mirrorlist - -[extra] -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 diff --git a/source/dmenu/scripts/dmenu_power b/source/dmenu/scripts/dmenu_power index 6a43bbc9f..30e939f27 100755 --- a/source/dmenu/scripts/dmenu_power +++ b/source/dmenu/scripts/dmenu_power @@ -25,30 +25,66 @@ pwr3="󰾆 Power Saver" pwr4=" Cancel" pwrs="$pwr1\n$pwr2\n$pwr3\n$pwr4" -##### ## This variable will store the current power profile -##### currentpwr=$(powerprofilesctl get) -##### ## This variable will store the current wallpaper set by feh -##### currentwall=$(tail --lines=1 "$HOME/.fehbg" | awk '{print $4}' | sed "s/'//g") -########## -## main ## -########## +# Check for missing dependencies +check_dependencies() { + if ! [ -x "$(command -v dmenu)" ]; then + echo "Missing dependency: dmenu is required." + exit 1 + fi + + local missing_deps=() + for dep in loginctl powerprofilesctl notify-send; do + if ! command -v "$dep" &> /dev/null; then + missing_deps+=("$dep") + fi + done + + if [ ${#missing_deps[@]} -ne 0 ]; then + echo "Missing dependencies: ${missing_deps[*]}" + exit 1 + fi +} + +## Ask yes/no for action confirmation +confirm_action() { + local prompt="$1" + echo -e " Yes\n No" | dmenu -l 2 -i -p "$prompt" | grep -q " Yes" +} + +#################### +# Main Script Flow # +#################### + +# Check for dependencies +check_dependencies + action=$(echo -e "$options" | dmenu -i -p " Power Options"); # main menu prompt +if [ -z "$action" ]; then + exit 0 +fi + case "$action" in - "$option1") - killall -u "$(whoami)" + "$option1") + if confirm_action " Are you sure you want to logout?"; then + pkill X + fi ;; "$option2") - systemctl reboot + if confirm_action " Are you sure you want to reboot?"; then + loginctl reboot + fi ;; "$option3") - systemctl poweroff - ;; + if confirm_action " Are you sure you want to power off?"; then + loginctl poweroff + fi + ;; "$option4") slock -b "$currentwall" systemctl suspend ;; diff --git a/source/dwm/autostart b/source/dwm/autostart index dfb212da1..73bbd2503 100755 --- a/source/dwm/autostart +++ b/source/dwm/autostart @@ -5,11 +5,35 @@ ## / /_/ / / / /_/ / ,< / __/ Clay Gomera (Drake) ## ## /_____/_/ \__,_/_/|_|\___/ My custom dwm build ## -lxpolkit & -dwmblocks & -sh "$HOME"/.fehbg & -unclutter --hide-on-touch & -herbed & -picom & dbus-update-activation-environment DISPLAY XAUTHORITY & -/usr/bin/emacs --daemon & +pidof pipewire || pipewire & +pidof polkit-gnome-authentication-agent-1 || /usr/libexec/polkit-gnome-authentication-agent-1 & +pidof dwmblocks || dwmblocks & +pidof unclutter || unclutter --hide-on-touch & +pidof tiramisu || herbed & +pidof picom || picom & + +gsettings set org.gnome.desktop.interface cursor-theme 'Simp1e-Gruvbox-Dark' +gsettings set org.gnome.desktop.interface cursor-size 16 +gsettings set org.gnome.desktop.interface icon-theme 'Papirus-Dark' +gsettings set org.gnome.desktop.interface gtk-theme 'Gruvbox-Dark' +gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' +gsettings set org.gnome.desktop.interface font-name 'Inter 10' +gsettings set org.gnome.desktop.interface document-font-name 'Liberation Sans 10' +gsettings set org.gnome.desktop.interface monospace-font-name 'Mononoki Nerd Font 10' +gsettings set org.gnome.desktop.interface font-antialiasing 'rgba' +gsettings set org.gnome.desktop.interface font-hinting 'full' + +sleep 3 + +monitor1=$(cat /sys/class/drm/card0-DP-1/status) +monitor2=$(cat /sys/class/drm/card0-HDMI-A-2/status) + +if [ "$monitor1" = "connected" ] && [ "$monitor2" = "connected" ]; then + xrandr --output eDP1 --mode 1920x1080 --pos 0x0 --rotate normal \ + --output DP1 --primary --mode 1920x1080 --rate 100.00 --pos 1924x0 --rotate normal \ + --output HDMI2 --mode 1280x1024 --rate 75.00 --pos 3844x0 --rotate normal +fi +sh "$HOME"/.fehbg & +gnome-keyring-daemon --start --components=secrets & + diff --git a/source/dwm/config.h b/source/dwm/config.h index b03dbebb1..253bb540f 100644 --- a/source/dwm/config.h +++ b/source/dwm/config.h @@ -49,7 +49,7 @@ const char *spcmd9[] = {"st", "-n", "spflm", "-c", "spflm", "-g", "140x35", "-e const char *spcmd10[] = {"st", "-n", "sprss", "-c", "sprss", "-g", "140x35", "-e", "newsboat", NULL}; const char *spcmd11[] = {"flatpak", "run", "com.bitwarden.desktop", NULL}; const char *spcmd12[] = {"flatpak", "run", "qalculate-gtk", NULL}; -const char *spcmd13[] = {"lxrandr", NULL}; +const char *spcmd13[] = {"arandr", NULL}; static Sp scratchpads[] = { /* NAME CMD */ @@ -70,7 +70,7 @@ static Sp scratchpads[] = { /* Tag Definitions */ static const char *tags[] = { - "", /* nDITOR */ + "", /* EDITOR */ "󰙨", /* TESTING */ "󰖟", /* WEB */ "󰭹", /* CHAT */ @@ -134,6 +134,8 @@ static const Rule rules[] = { {"Cadence", NULL, NULL, 1 << 4, 0, -1}, {"easyeffects", NULL, NULL, 1 << 4, 0, -1}, {"kid3", NULL, NULL, 1 << 4, 0, -1}, + {"flacon", NULL, NULL, 1 << 4, 0, -1}, + {"FFaudioConverter", NULL, NULL, 1 << 4, 0, -1}, /* 6 - Video Tag */ {"kdenlive", NULL, NULL, 1 << 5, 0, -1}, {"Pitivi", NULL, NULL, 1 << 5, 0, -1}, @@ -181,6 +183,7 @@ static const Rule rules[] = { {"xemu", NULL, NULL, 1 << 8, 0, -1}, {"org.ppsspp.PPSSPP", NULL, NULL, 1 << 8, 0, -1}, {"dolphin-emu", NULL, NULL, 1 << 8, 0, -1}, + {"es-de", NULL, NULL, 1 << 8, 0, -1}, /* Scratchpads */ {NULL, "sptrm", NULL, SPTAG(0), 1, -1}, {NULL, "sptop", NULL, SPTAG(1), 1, -1}, @@ -195,6 +198,7 @@ static const Rule rules[] = { {"Bitwarden", NULL, NULL, SPTAG(10), 1, -1}, {"Qalculate-gtk", NULL, NULL, SPTAG(11), 1, -1}, {"Lxrandr", NULL, NULL, SPTAG(12), 1, -1}, + {"Arandr", NULL, NULL, SPTAG(12), 1, -1}, }; /* Layout(s) */ @@ -209,10 +213,10 @@ static const Layout layouts[] = { { "[M]", monocle }, { "|||", centeredmaster }, { ">|>", centeredfloatingmaster }, - { "[@]", spiral }, - { "[\\]", dwindle }, + { "[@]", spiral }, + { "[\\]", dwindle }, { "HHH", grid }, - { NULL, NULL }, + { NULL, NULL }, }; /* Tag control keybindings */ @@ -229,7 +233,7 @@ static const Layout layouts[] = { /* Main commands */ static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; static const char *termcmd[] = { "st", NULL }; -static const char *editor[] = { "emacsclient", "-c", "-a", "emacs", NULL}; +static const char *editor[] = { "st", "-n", "editor", "-c", "editor", "-e", ".local/bin/lvim", NULL }; static const char *browser[] = { "flatpak", "run", "com.brave.Browser", NULL }; static const char *chat[] = { "flatpak", "run", "org.signal.Signal", NULL }; static const char *vm[] = { "virt-manager", NULL }; @@ -318,10 +322,10 @@ static Keychord *keychords[] = { &((Keychord){1, {{MODKEY|Mod1Mask, XK_space}}, togglefloating, {0} }), /* Toggle floating mode on active window */ &((Keychord){1, {{MODKEY, XK_0}}, view, {.ui = ~0 } }), /* View all windows from all tags */ &((Keychord){1, {{MODKEY|ShiftMask, XK_0}}, tag, {.ui = ~0 } }), /* View active window on all tags (similar to sticky, more nuclear) */ - &((Keychord){1, {{MODKEY, XK_comma}}, focusmon, {.i = -1 } }), /* Focus next screen */ - &((Keychord){1, {{MODKEY, XK_period}}, focusmon, {.i = +1 } }), /* Focus previous screen */ - &((Keychord){1, {{MODKEY|ShiftMask, XK_comma}}, tagmon, {.i = -1 } }), /* Move active window to next screen */ - &((Keychord){1, {{MODKEY|ShiftMask, XK_period}}, tagmon, {.i = +1 } }), /* Move active window to previous screen */ + &((Keychord){1, {{MODKEY, XK_comma}}, focusmon, {.i = +1 } }), /* Focus next screen */ + &((Keychord){1, {{MODKEY, XK_period}}, focusmon, {.i = -1 } }), /* Focus previous screen */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_comma}}, tagmon, {.i = +1 } }), /* Move active window to next screen */ + &((Keychord){1, {{MODKEY|ShiftMask, XK_period}}, tagmon, {.i = -1 } }), /* Move active window to previous screen */ &((Keychord){1, {{MODKEY, XK_b}}, togglebar, {0} }), /* Toggle bar */ /* Scratchpads */ diff --git a/source/dwmblocks/scripts/block_wifi b/source/dwmblocks/scripts/block_wifi index 245b97250..75b0c0231 100755 --- a/source/dwmblocks/scripts/block_wifi +++ b/source/dwmblocks/scripts/block_wifi @@ -46,7 +46,7 @@ elif [ "$constate" = "connected" ]; then # if it's connected "3") icon=" 󰤥 " ;; "4") icon=" 󰤨 " ;; esac - echo "$icon$currentwfi " + echo "$icon$currentwfi" else echo " 󰤮 " # just in case if nmcli isn't available or something weird is happening fi diff --git a/source/nsxiv/LICENSE b/source/nsxiv/LICENSE deleted file mode 100644 index d159169d1..000000000 --- a/source/nsxiv/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/source/nsxiv/Makefile b/source/nsxiv/Makefile deleted file mode 100644 index aaf47fddd..000000000 --- a/source/nsxiv/Makefile +++ /dev/null @@ -1,95 +0,0 @@ -.POSIX: - -include config.mk - -inc_fonts_0 = -inc_fonts_1 = -I/usr/include/freetype2 -I$(PREFIX)/include/freetype2 -lib_fonts_0 = -lib_fonts_1 = -lXft -lfontconfig -lib_exif_0 = -lib_exif_1 = -lexif - -nsxiv_cppflags = -D_XOPEN_SOURCE=700 \ - -DHAVE_LIBEXIF=$(HAVE_LIBEXIF) -DHAVE_LIBFONTS=$(HAVE_LIBFONTS) \ - -DHAVE_INOTIFY=$(HAVE_INOTIFY) $(inc_fonts_$(HAVE_LIBFONTS)) \ - $(CPPFLAGS) - -nsxiv_ldlibs = -lImlib2 -lX11 \ - $(lib_exif_$(HAVE_LIBEXIF)) $(lib_fonts_$(HAVE_LIBFONTS)) \ - $(LDLIBS) - -objs = autoreload.o commands.o image.o main.o options.o \ - thumbs.o util.o window.o - -.SUFFIXES: -.SUFFIXES: .c .o - -all: nsxiv - -nsxiv: $(objs) - @echo "LINK $@" - $(CC) $(LDFLAGS) -o $@ $(objs) $(nsxiv_ldlibs) - -.c.o: - @echo "CC $@" - $(CC) $(CFLAGS) $(nsxiv_cppflags) -c -o $@ $< - -$(objs): Makefile config.mk nsxiv.h config.h commands.h -options.o: version.h optparse.h -window.o: icon/data.h utf8.h - -version.h: config.mk .git/index - @echo "GEN $@" - v="$$(git describe 2>/dev/null || true)"; \ - echo "#define VERSION \"$${v:-$(VERSION)}\"" >$@ - -.git/index: - -dump_cppflags: - @echo $(nsxiv_cppflags) - -clean: - rm -f *.o nsxiv version.h - -install: all - @echo "INSTALL bin/nsxiv" - mkdir -p $(DESTDIR)$(PREFIX)/bin - cp nsxiv $(DESTDIR)$(PREFIX)/bin/ - chmod 755 $(DESTDIR)$(PREFIX)/bin/nsxiv - @echo "INSTALL nsxiv.1" - mkdir -p $(DESTDIR)$(MANPREFIX)/man1 - sed "s!EGPREFIX!$(EGPREFIX)!g; s!PREFIX!$(PREFIX)!g; s!VERSION!$(VERSION)!g" \ - etc/nsxiv.1 >$(DESTDIR)$(MANPREFIX)/man1/nsxiv.1 - chmod 644 $(DESTDIR)$(MANPREFIX)/man1/nsxiv.1 - @echo "INSTALL share/nsxiv/" - mkdir -p $(DESTDIR)$(EGPREFIX) - cp etc/examples/* $(DESTDIR)$(EGPREFIX) - chmod 755 $(DESTDIR)$(EGPREFIX)/* - @echo "INSTALL nsxiv.desktop" - mkdir -p $(DESTDIR)$(PREFIX)/share/applications - cp etc/nsxiv.desktop $(DESTDIR)$(PREFIX)/share/applications - @echo "INSTALL icon" - for f in $(ICONS); do \ - dir="$(DESTDIR)$(PREFIX)/share/icons/hicolor/$${f%.png}/apps"; \ - mkdir -p "$$dir"; \ - cp "icon/$$f" "$$dir/nsxiv.png"; \ - chmod 644 "$$dir/nsxiv.png"; \ - done - -uninstall-icon: - @echo "REMOVE icon" - for f in $(ICONS); do \ - dir="$(DESTDIR)$(PREFIX)/share/icons/hicolor/$${f%.png}/apps"; \ - rm -f "$$dir/nsxiv.png"; \ - done - -uninstall: uninstall-icon - @echo "REMOVE bin/nsxiv" - rm -f $(DESTDIR)$(PREFIX)/bin/nsxiv - @echo "REMOVE nsxiv.1" - rm -f $(DESTDIR)$(MANPREFIX)/man1/nsxiv.1 - @echo "REMOVE nsxiv.desktop" - rm -f $(DESTDIR)$(PREFIX)/share/applications/nsxiv.desktop - @echo "REMOVE share/nsxiv/" - rm -rf $(DESTDIR)$(EGPREFIX) - diff --git a/source/nsxiv/README.md b/source/nsxiv/README.md deleted file mode 100644 index e8ce9e91d..000000000 --- a/source/nsxiv/README.md +++ /dev/null @@ -1,222 +0,0 @@ -[![nsxiv](https://codeberg.org/nsxiv/pages/raw/branch/master/img/logo.png)](https://codeberg.org/nsxiv/nsxiv) - -[![Codeberg](https://img.shields.io/badge/Hosted_at-Codeberg-%232185D0?style=flat-square&logo=CodeBerg)](https://codeberg.org/nsxiv/nsxiv) -[![tags](https://img.shields.io/github/v/tag/nsxiv/nsxiv?style=flat-square)](https://codeberg.org/nsxiv/nsxiv/tags) -[![license](https://img.shields.io/badge/license-GPL--2.0%2B-lightgreen?style=flat-square)](https://codeberg.org/nsxiv/nsxiv/src/branch/master/LICENSE) -[![loc](https://img.shields.io/tokei/lines/github/nsxiv/nsxiv?color=red&style=flat-square)](https://codeberg.org/nsxiv/nsxiv) - -**Neo (or New or Not) Simple (or Small or Suckless) X Image Viewer** --------------------------------------------------------------------- - -nsxiv is a fork of the now-unmaintained [sxiv](https://github.com/xyb3rt/sxiv) -with the purpose of being a (mostly) drop-in replacement for sxiv, maintaining its -interface and adding simple, sensible features. nsxiv is free software licensed -under GPL-2.0-or-later and aims to be easy to modify and customize. - -Please file a bug report if something does not work as documented or expected on -[Codeberg] after making sure you are using the latest release. Contributions -are welcome, see [CONTRIBUTING] to get started. - -[Codeberg]: https://codeberg.org/nsxiv/nsxiv/issues/new -[CONTRIBUTING]: etc/CONTRIBUTING.md#contribution-guideline - - -Features --------- - -* Basic image operations like zooming, panning, rotating -* Basic support for animated/multi-frame images (**requires Imlib2 v1.8.0 or above**) -* Thumbnail mode: grid of selectable previews of all images -* Ability to cache thumbnails for fast re-loading -* Automatically refreshing modified images -* Customizable keyboard and mouse mappings via `config.h` -* Scriptability via `key-handler` -* Displaying image information in status bar via `image-info` & `thumb-info` -* Customizable window title via `win-title` - - -Screenshots ------------ - -**Image mode with default colors:** - -![Image](https://codeberg.org/nsxiv/pages/raw/branch/master/img/image.png "Image mode") - -**Thumbnail mode with custom colors:** - -![Thumb](https://codeberg.org/nsxiv/pages/raw/branch/master/img/thumb.png "Thumb mode") - - -Installing via package manager ------------------------------- - - - - - -nsxiv is available on the following distributions/repositories. If you don't see -your distro listed here, either contact your distro's package maintainer or -consider packaging it yourself and adding it to the respective community repo. - - -Dependencies ------------- - -nsxiv requires the following software to be installed: - - * X11 - * Imlib2 (built with X11 support) - -The following dependencies are optional: - - * `inotify`\*: Used for auto-reloading images on change. - Disabled via `HAVE_INOTIFY=0`. - * `libXft`, `freetype2`, `fontconfig`: Used for the status bar. - Disabled via `HAVE_LIBFONTS=0`. - * `libexif`: Used for auto-orientation and exif thumbnails. - Disable via `HAVE_LIBEXIF=0`. - -Please make sure to install the corresponding development packages in case that -you want to build nsxiv on a distribution with separate runtime and development -packages (e.g. \*-dev on Debian). - -\* [inotify][] is a Linux-specific API for monitoring filesystem changes. - It's not natively available on `*BSD` systems but can be enabled via - installing and linking against [libinotify-kqueue][]. - -[inotify]: https://www.man7.org/linux/man-pages/man7/inotify.7.html -[libinotify-kqueue]: https://github.com/libinotify-kqueue/libinotify-kqueue - - -Building --------- - -nsxiv is built using the commands: - - $ make - -You can pass `HAVE_X=0` to `make` to disable an optional dependency. -For example: - - $ make HAVE_LIBEXIF=0 - -will disable `libexif` support. Alternatively they can be disabled via editing -`config.mk`. `OPT_DEP_DEFAULT=0` can be used to disable all optional -dependencies. - -Installing nsxiv: - - # make install - -Installing desktop entry: - - # make install-desktop - -Installing icons: - - # make install-icon - -Installing all of the above: - - # make install-all - -Please note, that these requires root privileges. -By default, nsxiv is installed using the prefix `/usr/local`, so the full path -of the executable will be `/usr/local/bin/nsxiv`, the `.desktop` entry will be -`/usr/local/share/applications/nsxiv.desktop` and the icon path will be -`/usr/local/share/icons/hicolor/{size}/apps/nsxiv.png`. - -You can install nsxiv into a directory of your choice by changing this command to: - - $ make PREFIX="/your/dir" install - -Example scripts are installed using `EGPREFIX` which defaults to -`/usr/local/share/doc/nsxiv/examples`. You can change `EGPREFIX` the same way -you can change `PREFIX` shown above. - -The build-time specific settings of nsxiv can be found in the file *config.h*. -Please check and change them, so that they fit your needs. -If the file *config.h* does not already exist, then you have to create it with -the following command: - - $ make config.h - - -Usage ------ - -Refer to the man-page for the documentation: - - $ man nsxiv - -You may also view the man-page [online](https://nsxiv.codeberg.page/man/). -However, note that the online man-page might not accurately represent your local -copy. - - -F.A.Q. ------- - -* Can I open remote urls with nsxiv?
-Yes, see [nsxiv-url](https://codeberg.org/nsxiv/nsxiv-extra/src/branch/master/scripts/nsxiv-url) - -* Can I open all the images in a directory?
-Yes, see [nsxiv-rifle](https://codeberg.org/nsxiv/nsxiv-extra/src/branch/master/scripts/nsxiv-rifle) - -* Can I set default arguments for nsxiv?
-Yes, see [nsxiv-env](https://codeberg.org/nsxiv/nsxiv-extra/src/branch/master/scripts/nsxiv-env) - -* Can I pipe images into nsxiv?
-Yes, see [nsxiv-pipe](https://codeberg.org/nsxiv/nsxiv-extra/src/branch/master/scripts/nsxiv-pipe) - -You may also wish to see the [known issues](https://codeberg.org/nsxiv/nsxiv/issues/242). - - -Customization -------------- - -The main method of customizing nsxiv is by setting values for the variables in *config.h*, -or by using Xresources as explained in the manual. If these options are not sufficient, -you may implement your own features by following -[this guide](https://codeberg.org/nsxiv/nsxiv-extra/src/branch/master/CUSTOMIZATION.md). - -Due to our limited [project scope](etc/CONTRIBUTING.md#project-scope), certain features or -customization cannot be merged into nsxiv mainline. Following the spirit of suckless -software, we host the [nsxiv-extra](https://codeberg.org/nsxiv/nsxiv-extra) repo where users -are free to submit whatever patches or scripts they wish. - -If you think your custom features can be beneficial for the general user base and is within -our project scope, please submit it as a pull request on this repository, then we *may* -merge it to mainline. - -Description on how to use or submit patches can be found on -nsxiv-extra's [README](https://codeberg.org/nsxiv/nsxiv-extra). - - -Download --------- - -You can [browse](https://codeberg.org/nsxiv/nsxiv) the source code repository -on Codeberg or get a copy using git with the following command: - - $ git clone https://codeberg.org/nsxiv/nsxiv.git - -You can view the changelog [here](etc/CHANGELOG.md) - - -Similar projects ----------------- - -If nsxiv isn't able to fit your needs, check out the image viewer section of -**[suckless rocks](https://suckless.org/rocks)** to find other minimal image -viewers to try out. - -Below are a couple other lesser known projects not listed in suckless rocks. - -* [MAGE](https://codeberg.org/explosion-mental/mage): - A smaller/more-suckless version of sxiv. -* [div](https://codeberg.org/TAAPArthur/div): - Minimal and extensive, aimed at C devs willing to build their own features. -* [mpv-image-viewer](https://github.com/occivink/mpv-image-viewer): - Lua script to turn mpv into an image viewer. Supports thumbnails via - [mpv-gallery-view](https://github.com/occivink/mpv-gallery-view). diff --git a/source/nsxiv/autoreload.c b/source/nsxiv/autoreload.c deleted file mode 100644 index 8b3f6da5b..000000000 --- a/source/nsxiv/autoreload.c +++ /dev/null @@ -1,152 +0,0 @@ -/* Copyright 2017 Max Voit, Bert Muennich - * Copyright 2022-2023 nsxiv contributors - * - * This file is a part of nsxiv. - * - * nsxiv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * nsxiv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with nsxiv. If not, see . - */ - -#include "nsxiv.h" - -#if HAVE_INOTIFY - -#include -#include -#include -#include -#include -#include - -static struct { - char *buf; - size_t len; -} scratch; - -void arl_init(arl_t *arl) -{ - arl->fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); - arl->wd_dir = arl->wd_file = -1; - if (arl->fd == -1) - error(0, 0, "Could not initialize inotify, no automatic image reloading"); -} - -CLEANUP void arl_cleanup(arl_t *arl) -{ - if (arl->fd != -1) - close(arl->fd); - free(scratch.buf); -} - -static void rm_watch(int fd, int *wd) -{ - if (*wd != -1) { - inotify_rm_watch(fd, *wd); - *wd = -1; - } -} - -static void add_watch(int fd, int *wd, const char *path, uint32_t mask) -{ - *wd = inotify_add_watch(fd, path, mask); - if (*wd == -1) - error(0, errno, "inotify: %s", path); -} - -static char *arl_scratch_push(const char *filepath, size_t len) -{ - if (scratch.len < len + 1) { - scratch.len = len + 1; - scratch.buf = erealloc(scratch.buf, scratch.len); - } - scratch.buf[len] = '\0'; - return memcpy(scratch.buf, filepath, len); -} - -void arl_add(arl_t *arl, const char *filepath) -{ - char *base, *dir; - - if (arl->fd == -1) - return; - - rm_watch(arl->fd, &arl->wd_dir); - rm_watch(arl->fd, &arl->wd_file); - add_watch(arl->fd, &arl->wd_file, filepath, IN_CLOSE_WRITE | IN_DELETE_SELF); - - base = strrchr(filepath, '/'); - assert(base != NULL); /* filepath must be result of `realpath(3)` */ - dir = arl_scratch_push(filepath, base - filepath); - add_watch(arl->fd, &arl->wd_dir, dir, IN_CREATE | IN_MOVED_TO); - arl->filename = arl_scratch_push(base + 1, strlen(base + 1)); -} - -bool arl_handle(arl_t *arl) -{ - bool reload = false; - char *ptr; - const struct inotify_event *e; - /* inotify_event aligned buffer */ - static union { - char d[4096]; - struct inotify_event e; - } buf; - - while (true) { - ssize_t len = read(arl->fd, buf.d, sizeof(buf.d)); - - if (len == -1) { - if (errno == EINTR) - continue; - break; - } - for (ptr = buf.d; ptr < buf.d + len; ptr += sizeof(*e) + e->len) { - e = (const struct inotify_event *)ptr; - if (e->wd == arl->wd_file && (e->mask & IN_CLOSE_WRITE)) { - reload = true; - } else if (e->wd == arl->wd_file && (e->mask & IN_DELETE_SELF)) { - rm_watch(arl->fd, &arl->wd_file); - } else if (e->wd == arl->wd_dir && (e->mask & (IN_CREATE | IN_MOVED_TO))) { - if (STREQ(e->name, arl->filename)) - reload = true; - } - } - } - return reload; -} - -#else - -void arl_init(arl_t *arl) -{ - arl->fd = -1; -} - -void arl_cleanup(arl_t *arl) -{ - (void)arl; -} - -void arl_add(arl_t *arl, const char *filepath) -{ - (void)arl; - (void)filepath; -} - -bool arl_handle(arl_t *arl) -{ - (void)arl; - return false; -} - -#endif /* HAVE_INOTIFY */ diff --git a/source/nsxiv/commands.c b/source/nsxiv/commands.c deleted file mode 100644 index 3257b1e41..000000000 --- a/source/nsxiv/commands.c +++ /dev/null @@ -1,479 +0,0 @@ -/* Copyright 2011-2020 Bert Muennich - * Copyright 2021-2023 nsxiv contributors - * - * This file is a part of nsxiv. - * - * nsxiv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * nsxiv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with nsxiv. If not, see . - */ - -#include "nsxiv.h" - -#include "commands.h" - -#include -#include -#include -#include -#include - -extern img_t img; -extern tns_t tns; -extern win_t win; - -static bool navigate_to(arg_t n) -{ - if (n >= 0 && n < filecnt && n != fileidx) { - if (mode == MODE_IMAGE) { - load_image(n); - } else if (mode == MODE_THUMB) { - fileidx = n; - tns.dirty = true; - } - return true; - } - return false; -} - -bool cg_quit(arg_t status) -{ - unsigned int i; - - if (options->to_stdout && markcnt > 0) { - for (i = 0; i < (unsigned int)filecnt; i++) { - if (files[i].flags & FF_MARK) - printf("%s%c", files[i].name, options->using_null ? '\0' : '\n'); - } - } - exit(status); - return None; /* silence tcc warning */ -} - -bool cg_pick_quit(arg_t status) -{ - if (options->to_stdout && markcnt == 0) - printf("%s%c", files[fileidx].name, options->using_null ? '\0' : '\n'); - return cg_quit(status); -} - -bool cg_switch_mode(arg_t _) -{ - if (mode == MODE_IMAGE) { - if (tns.thumbs == NULL) - tns_init(&tns, files, &filecnt, &fileidx, &win); - img_close(&img, false); - reset_timeout(reset_cursor); - if (img.ss.on) { - img.ss.on = false; - reset_timeout(slideshow); - } - tns.dirty = true; - mode = MODE_THUMB; - } else { - load_image(fileidx); - mode = MODE_IMAGE; - } - return true; -} - -bool cg_toggle_fullscreen(arg_t _) -{ - win_toggle_fullscreen(&win); - /* redraw after next ConfigureNotify event */ - set_timeout(redraw, TO_REDRAW_RESIZE, false); - if (mode == MODE_IMAGE) - img.checkpan = img.dirty = true; - else - tns.dirty = true; - return false; -} - -bool cg_toggle_bar(arg_t _) -{ - win_toggle_bar(&win); - if (mode == MODE_IMAGE) - img.checkpan = img.dirty = true; - else - tns.dirty = true; - if (win.bar.h > 0) - open_info(); - else - close_info(); - return true; -} - -bool cg_prefix_external(arg_t _) -{ - handle_key_handler(true); - return false; -} - -bool cg_reload_image(arg_t _) -{ - if (mode == MODE_IMAGE) { - load_image(fileidx); - } else { - win_set_cursor(&win, CURSOR_WATCH); - if (!tns_load(&tns, fileidx, true, false)) { - remove_file(fileidx, false); - tns.dirty = true; - } - } - return true; -} - -bool cg_remove_image(arg_t _) -{ - remove_file(fileidx, true); - if (mode == MODE_IMAGE) - load_image(fileidx); - else - tns.dirty = true; - return true; -} - -bool cg_first(arg_t _) -{ - return navigate_to(0); -} - -bool cg_n_or_last(arg_t _) -{ - int n = prefix != 0 && prefix - 1 < filecnt ? prefix - 1 : filecnt - 1; - return navigate_to(n); -} - -bool cg_scroll_screen(arg_t dir) -{ - if (mode == MODE_IMAGE) - return img_pan(&img, dir, -1); - else - return tns_scroll(&tns, dir, true); -} - -bool cg_zoom(arg_t d) -{ - if (mode == MODE_THUMB) - return tns_zoom(&tns, d); - else - return img_zoom(&img, d); -} - -bool cg_toggle_image_mark(arg_t _) -{ - return mark_image(fileidx, !(files[fileidx].flags & FF_MARK)); -} - -bool cg_reverse_marks(arg_t _) -{ - int i; - - for (i = 0; i < filecnt; i++) { - files[i].flags ^= FF_MARK; - markcnt += files[i].flags & FF_MARK ? 1 : -1; - } - if (mode == MODE_THUMB) - tns.dirty = true; - return true; -} - -bool cg_mark_range(arg_t _) -{ - int d = markidx < fileidx ? 1 : -1, end, i; - bool dirty = false, on = !!(files[markidx].flags & FF_MARK); - - for (i = markidx + d, end = fileidx + d; i != end; i += d) - dirty |= mark_image(i, on); - return dirty; -} - -bool cg_unmark_all(arg_t _) -{ - int i; - - for (i = 0; i < filecnt; i++) - files[i].flags &= ~FF_MARK; - markcnt = 0; - if (mode == MODE_THUMB) - tns.dirty = true; - return true; -} - -bool cg_navigate_marked(arg_t n) -{ - int d, i; - int new = fileidx; - - if (prefix > 0) - n *= prefix; - d = n > 0 ? 1 : -1; - for (i = fileidx + d; n != 0 && i >= 0 && i < filecnt; i += d) { - if (files[i].flags & FF_MARK) { - n -= d; - new = i; - } - } - return navigate_to(new); -} - -static bool change_color_modifier(arg_t d, int *target) -{ - if (!img_change_color_modifier(&img, d * (prefix > 0 ? prefix : 1), target)) - return false; - if (mode == MODE_THUMB) - tns.dirty = true; - return true; -} - -bool cg_change_gamma(arg_t d) -{ - return change_color_modifier(d, &img.gamma); -} - -bool cg_change_brightness(arg_t d) -{ - return change_color_modifier(d, &img.brightness); -} - -bool cg_change_contrast(arg_t d) -{ - return change_color_modifier(d, &img.contrast); -} - -bool ci_navigate(arg_t n) -{ - if (prefix > 0) - n *= prefix; - n += fileidx; - n = MAX(0, MIN(n, filecnt - 1)); - - if (n != fileidx) { - load_image(n); - return true; - } else { - return false; - } -} - -bool ci_cursor_navigate(arg_t _) -{ - return ci_navigate(nav_button() - 1); -} - -bool ci_alternate(arg_t _) -{ - load_image(alternate); - return true; -} - -bool ci_navigate_frame(arg_t d) -{ - if (prefix > 0) - d *= prefix; - return !img.multi.animate && img_frame_navigate(&img, d); -} - -bool ci_toggle_animation(arg_t _) -{ - bool dirty = false; - - if (img.multi.cnt > 0) { - img.multi.animate = !img.multi.animate; - if (img.multi.animate) { - dirty = img_frame_animate(&img); - set_timeout(animate, img.multi.frames[img.multi.sel].delay, true); - } else { - reset_timeout(animate); - } - } - return dirty; -} - -bool ci_scroll(arg_t dir) -{ - return img_pan(&img, dir, prefix); -} - -bool ci_scroll_to_center(arg_t _) -{ - return img_pan_center(&img); -} - -bool ci_scroll_to_edge(arg_t dir) -{ - return img_pan_edge(&img, dir); -} - -bool ci_drag(arg_t drag_mode) -{ - int x, y, ox, oy; - float px, py; - XEvent e; - - if ((int)(img.w * img.zoom) <= (int)win.w && (int)(img.h * img.zoom) <= (int)win.h) - return false; - - win_set_cursor(&win, drag_mode == DRAG_ABSOLUTE ? CURSOR_DRAG_ABSOLUTE : CURSOR_DRAG_RELATIVE); - win_cursor_pos(&win, &x, &y); - ox = x; - oy = y; - - while (true) { - if (drag_mode == DRAG_ABSOLUTE) { - px = MIN(MAX(0.0, x - win.w * 0.1), win.w * 0.8) / - (win.w * 0.8) * (win.w - img.w * img.zoom); - py = MIN(MAX(0.0, y - win.h * 0.1), win.h * 0.8) / - (win.h * 0.8) * (win.h - img.h * img.zoom); - } else { - px = img.x + x - ox; - py = img.y + y - oy; - } - - if (img_pos(&img, px, py)) { - img_render(&img); - win_draw(&win); - } - XMaskEvent(win.env.dpy, - ButtonPressMask | ButtonReleaseMask | PointerMotionMask, &e); - if (e.type == ButtonPress || e.type == ButtonRelease) - break; - while (XCheckTypedEvent(win.env.dpy, MotionNotify, &e)) - ; - ox = x; - oy = y; - x = e.xmotion.x; - y = e.xmotion.y; - } - set_timeout(reset_cursor, TO_CURSOR_HIDE, true); - reset_cursor(); - - return true; -} - -bool ci_set_zoom(arg_t zl) -{ - return img_zoom_to(&img, (prefix ? prefix : zl) / 100.0); -} - -bool ci_fit_to_win(arg_t sm) -{ - return img_fit_win(&img, sm); -} - -bool ci_rotate(arg_t degree) -{ - img_rotate(&img, degree); - return true; -} - -bool ci_flip(arg_t dir) -{ - img_flip(&img, dir); - return true; -} - -bool ci_toggle_antialias(arg_t _) -{ - img_toggle_antialias(&img); - return true; -} - -bool ci_toggle_alpha(arg_t _) -{ - img.alpha_layer = !img.alpha_layer; - img.dirty = true; - return true; -} - -bool ci_slideshow(arg_t _) -{ - if (prefix > 0) { - img.ss.on = true; - img.ss.delay = prefix * 10; - set_timeout(slideshow, img.ss.delay * 100, true); - } else if (img.ss.on) { - img.ss.on = false; - reset_timeout(slideshow); - } else { - img.ss.on = true; - } - return true; -} - -bool ct_move_sel(arg_t dir) -{ - return tns_move_selection(&tns, dir, prefix); -} - -bool ct_reload_all(arg_t _) -{ - tns_free(&tns); - tns_init(&tns, files, &filecnt, &fileidx, &win); - tns.dirty = true; - return true; -} - -bool ct_scroll(arg_t dir) -{ - return tns_scroll(&tns, dir, false); -} - -bool ct_drag_mark_image(arg_t _) -{ - int sel; - - if ((sel = tns_translate(&tns, xbutton_ev->x, xbutton_ev->y)) >= 0) { - XEvent e; - bool on = !(files[sel].flags & FF_MARK); - - while (true) { - if (sel >= 0 && mark_image(sel, on)) - redraw(); - XMaskEvent(win.env.dpy, - ButtonPressMask | ButtonReleaseMask | PointerMotionMask, &e); - if (e.type == ButtonPress || e.type == ButtonRelease) - break; - while (XCheckTypedEvent(win.env.dpy, MotionNotify, &e)) - ; - sel = tns_translate(&tns, e.xbutton.x, e.xbutton.y); - } - } - - return false; -} - -bool ct_select(arg_t _) -{ - int sel; - bool dirty = false; - static Time firstclick; - - if ((sel = tns_translate(&tns, xbutton_ev->x, xbutton_ev->y)) >= 0) { - if (sel != fileidx) { - tns_highlight(&tns, fileidx, false); - tns_highlight(&tns, sel, true); - fileidx = sel; - firstclick = xbutton_ev->time; - dirty = true; - } else if (xbutton_ev->time - firstclick <= TO_DOUBLE_CLICK) { - mode = MODE_IMAGE; - set_timeout(reset_cursor, TO_CURSOR_HIDE, true); - load_image(fileidx); - dirty = true; - } else { - firstclick = xbutton_ev->time; - } - } - - return dirty; -} diff --git a/source/nsxiv/commands.h b/source/nsxiv/commands.h deleted file mode 100644 index 4e694f0e6..000000000 --- a/source/nsxiv/commands.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef COMMANDS_H -#define COMMANDS_H -#include - -/* global */ -bool cg_change_gamma(arg_t); -bool cg_change_brightness(arg_t); -bool cg_change_contrast(arg_t); -bool cg_first(arg_t); -bool cg_mark_range(arg_t); -bool cg_n_or_last(arg_t); -bool cg_navigate_marked(arg_t); -bool cg_prefix_external(arg_t); -bool cg_quit(arg_t); -bool cg_pick_quit(arg_t); -bool cg_reload_image(arg_t); -bool cg_remove_image(arg_t); -bool cg_reverse_marks(arg_t); -bool cg_scroll_screen(arg_t); -bool cg_switch_mode(arg_t); -bool cg_toggle_bar(arg_t); -bool cg_toggle_fullscreen(arg_t); -bool cg_toggle_image_mark(arg_t); -bool cg_unmark_all(arg_t); -bool cg_zoom(arg_t); -/* image mode */ -bool ci_alternate(arg_t); -bool ci_cursor_navigate(arg_t); -bool ci_drag(arg_t); -bool ci_fit_to_win(arg_t); -bool ci_flip(arg_t); -bool ci_navigate(arg_t); -bool ci_navigate_frame(arg_t); -bool ci_rotate(arg_t); -bool ci_scroll(arg_t); -bool ci_scroll_to_center(arg_t); -bool ci_scroll_to_edge(arg_t); -bool ci_set_zoom(arg_t); -bool ci_slideshow(arg_t); -bool ci_toggle_alpha(arg_t); -bool ci_toggle_animation(arg_t); -bool ci_toggle_antialias(arg_t); -/* thumbnails mode */ -bool ct_move_sel(arg_t); -bool ct_reload_all(arg_t); -bool ct_scroll(arg_t); -bool ct_drag_mark_image(arg_t); -bool ct_select(arg_t); - -#ifdef INCLUDE_MAPPINGS_CONFIG -/* global */ -#define g_change_gamma { cg_change_gamma, MODE_ALL } -#define g_change_brightness { cg_change_brightness, MODE_ALL } -#define g_change_contrast { cg_change_contrast, MODE_ALL } -#define g_first { cg_first, MODE_ALL } -#define g_mark_range { cg_mark_range, MODE_ALL } -#define g_n_or_last { cg_n_or_last, MODE_ALL } -#define g_navigate_marked { cg_navigate_marked, MODE_ALL } -#define g_prefix_external { cg_prefix_external, MODE_ALL } -#define g_quit { cg_quit, MODE_ALL } -#define g_pick_quit { cg_pick_quit, MODE_ALL } -#define g_reload_image { cg_reload_image, MODE_ALL } -#define g_remove_image { cg_remove_image, MODE_ALL } -#define g_reverse_marks { cg_reverse_marks, MODE_ALL } -#define g_scroll_screen { cg_scroll_screen, MODE_ALL } -#define g_switch_mode { cg_switch_mode, MODE_ALL } -#define g_toggle_bar { cg_toggle_bar, MODE_ALL } -#define g_toggle_fullscreen { cg_toggle_fullscreen, MODE_ALL } -#define g_toggle_image_mark { cg_toggle_image_mark, MODE_ALL } -#define g_unmark_all { cg_unmark_all, MODE_ALL } -#define g_zoom { cg_zoom, MODE_ALL } - -/* image mode */ -#define i_alternate { ci_alternate, MODE_IMAGE } -#define i_cursor_navigate { ci_cursor_navigate, MODE_IMAGE } -#define i_drag { ci_drag, MODE_IMAGE } -#define i_fit_to_win { ci_fit_to_win, MODE_IMAGE } -#define i_flip { ci_flip, MODE_IMAGE } -#define i_navigate { ci_navigate, MODE_IMAGE } -#define i_navigate_frame { ci_navigate_frame, MODE_IMAGE } -#define i_rotate { ci_rotate, MODE_IMAGE } -#define i_scroll { ci_scroll, MODE_IMAGE } -#define i_scroll_to_center { ci_scroll_to_center, MODE_IMAGE } -#define i_scroll_to_edge { ci_scroll_to_edge, MODE_IMAGE } -#define i_set_zoom { ci_set_zoom, MODE_IMAGE } -#define i_slideshow { ci_slideshow, MODE_IMAGE } -#define i_toggle_alpha { ci_toggle_alpha, MODE_IMAGE } -#define i_toggle_animation { ci_toggle_animation, MODE_IMAGE } -#define i_toggle_antialias { ci_toggle_antialias, MODE_IMAGE } - -/* thumbnails mode */ -#define t_move_sel { ct_move_sel, MODE_THUMB } -#define t_reload_all { ct_reload_all, MODE_THUMB } -#define t_scroll { ct_scroll, MODE_THUMB } -#define t_drag_mark_image { ct_drag_mark_image, MODE_THUMB } -#define t_select { ct_select, MODE_THUMB } - -#endif /* _MAPPINGS_CONFIG */ -#endif /* COMMANDS_H */ diff --git a/source/nsxiv/config.h b/source/nsxiv/config.h deleted file mode 100644 index 6aa8d20f8..000000000 --- a/source/nsxiv/config.h +++ /dev/null @@ -1,214 +0,0 @@ -#ifdef INCLUDE_WINDOW_CONFIG - -/* default window dimensions (overwritten via -g option): */ -static const int WIN_WIDTH = 800; -static const int WIN_HEIGHT = 600; - -/* colors and font can be overwritten via X resource properties. - * See nsxiv(1), X(7) section Resources and xrdb(1) for more information. - * X resource value (NULL == default) */ -static const char *WIN_BG[] = { "Nsxiv.window.background", "#282828" }; -static const char *WIN_FG[] = { "Nsxiv.window.foreground", "#ebdbb2" }; -static const char *MARK_FG[] = { "Nsxiv.mark.foreground", "#ebdbb2" }; -#if HAVE_LIBFONTS -static const char *BAR_BG[] = { "Nsxiv.bar.background", "#282828" }; -static const char *BAR_FG[] = { "Nsxiv.bar.foreground", "#fbf1c7" }; -static const char *BAR_FONT[] = { "Nsxiv.bar.font", "Mononoki Nerd Font-10" }; - -/* if true, statusbar appears on top of the window */ -static const bool TOP_STATUSBAR = true; -#endif /* HAVE_LIBFONTS */ - -#endif -#ifdef INCLUDE_IMAGE_CONFIG - -/* levels (in percent) to use when zooming via '-' and '+': - * (first/last value is used as min/max zoom level) - */ -static const float zoom_levels[] = { - 12.5, 25.0, 50.0, 75.0, - 100.0, 150.0, 200.0, 400.0, 800.0 -}; - -/* default slideshow delay (in sec, overwritten via -S option): */ -static const int SLIDESHOW_DELAY = 5; - -/* color correction: the user-visible ranges [-CC_STEPS, 0] and - * (0, CC_STEPS] are mapped to the ranges [0, 1], and (1, *_MAX]. - * Higher step count will have higher granulairy. - */ -static const int CC_STEPS = 32; -static const double GAMMA_MAX = 10.0; -static const double BRIGHTNESS_MAX = 2.0; -static const double CONTRAST_MAX = 4.0; - -/* command i_scroll pans image 1/PAN_FRACTION of screen width/height */ -static const int PAN_FRACTION = 5; - -/* percentage of memory to use for imlib2's cache size. - * 3 means use 3% of total memory which is about 245MiB on 8GiB machine. - * 0 or less means disable cache. - * 100 means use all available memory (but not above CACHE_SIZE_LIMIT). - * - * NOTE: higher cache size means better image reloading performance, but also - * higher memory usage. - */ -static const int CACHE_SIZE_MEM_PERCENTAGE = 3; /* use 3% of total memory for cache */ -static const int CACHE_SIZE_LIMIT = 256 * 1024 * 1024; /* but not above 256MiB */ -static const int CACHE_SIZE_FALLBACK = 32 * 1024 * 1024; /* fallback to 32MiB if we can't determine total memory */ - -#endif -#ifdef INCLUDE_OPTIONS_CONFIG - -/* if false, pixelate images at zoom level != 100%, - * toggled with 'a' key binding (overwritten via `--anti-alias` option) - */ -static const bool ANTI_ALIAS = true; - -/* if true, use a checkerboard background for alpha layer, - * toggled with 'A' key binding (overwritten via `--alpha-layer` option) - */ -static const bool ALPHA_LAYER = false; - -#endif -#ifdef INCLUDE_THUMBS_CONFIG - -/* thumbnail sizes in pixels (width == height): */ -static const int thumb_sizes[] = { 32, 64, 96, 128, 160 }; - -/* thumbnail size at startup, index into thumb_sizes[]: */ -static const int THUMB_SIZE = 3; - -#endif -#ifdef INCLUDE_MAPPINGS_CONFIG - -/* these modifiers will be used when processing keybindings */ -static const unsigned int USED_MODMASK = ShiftMask | ControlMask | Mod1Mask; - -/* abort the keyhandler */ -static const KeySym KEYHANDLER_ABORT = XK_Escape; - -/* keyboard mappings for image and thumbnail mode: */ -static const keymap_t keys[] = { - /* modifiers key function argument */ - { 0, XK_q, g_quit, 0 }, - { 0, XK_Q, g_pick_quit, 0 }, - { 0, XK_Return, g_switch_mode, None }, - { 0, XK_f, g_toggle_fullscreen, None }, - { 0, XK_b, g_toggle_bar, None }, - { ControlMask, XK_x, g_prefix_external, None }, - { 0, XK_g, g_first, None }, - { 0, XK_G, g_n_or_last, None }, - { 0, XK_r, g_reload_image, None }, - { 0, XK_D, g_remove_image, None }, - { ControlMask, XK_h, g_scroll_screen, DIR_LEFT }, - { ControlMask, XK_Left, g_scroll_screen, DIR_LEFT }, - { ControlMask, XK_j, g_scroll_screen, DIR_DOWN }, - { ControlMask, XK_Down, g_scroll_screen, DIR_DOWN }, - { ControlMask, XK_k, g_scroll_screen, DIR_UP }, - { ControlMask, XK_Up, g_scroll_screen, DIR_UP }, - { ControlMask, XK_l, g_scroll_screen, DIR_RIGHT }, - { ControlMask, XK_Right, g_scroll_screen, DIR_RIGHT }, - { 0, XK_plus, g_zoom, +1 }, - { 0, XK_KP_Add, g_zoom, +1 }, - { 0, XK_minus, g_zoom, -1 }, - { 0, XK_KP_Subtract, g_zoom, -1 }, - { 0, XK_m, g_toggle_image_mark, None }, - { 0, XK_M, g_mark_range, None }, - { ControlMask, XK_m, g_reverse_marks, None }, - { ControlMask, XK_u, g_unmark_all, None }, - { 0, XK_N, g_navigate_marked, +1 }, - { 0, XK_P, g_navigate_marked, -1 }, - { 0, XK_braceleft, g_change_gamma, -1 }, - { 0, XK_braceright, g_change_gamma, +1 }, - { ControlMask, XK_g, g_change_gamma, 0 }, - { ControlMask, XK_bracketright, g_change_brightness, +1 }, - { ControlMask, XK_bracketleft, g_change_brightness, -1 }, - { 0, XK_parenleft, g_change_contrast, -1 }, - { 0, XK_parenright, g_change_contrast, +1 }, - - { 0, XK_h, t_move_sel, DIR_LEFT }, - { 0, XK_Left, t_move_sel, DIR_LEFT }, - { 0, XK_j, t_move_sel, DIR_DOWN }, - { 0, XK_Down, t_move_sel, DIR_DOWN }, - { 0, XK_k, t_move_sel, DIR_UP }, - { 0, XK_Up, t_move_sel, DIR_UP }, - { 0, XK_l, t_move_sel, DIR_RIGHT }, - { 0, XK_Right, t_move_sel, DIR_RIGHT }, - { 0, XK_R, t_reload_all, None }, - - { 0, XK_n, i_navigate, +1 }, - { 0, XK_n, i_scroll_to_edge, DIR_LEFT | DIR_UP }, - { 0, XK_space, i_navigate, +1 }, - { 0, XK_p, i_navigate, -1 }, - { 0, XK_p, i_scroll_to_edge, DIR_LEFT | DIR_UP }, - { 0, XK_BackSpace, i_navigate, -1 }, - { 0, XK_bracketright, i_navigate, +10 }, - { 0, XK_bracketleft, i_navigate, -10 }, - { ControlMask, XK_6, i_alternate, None }, - { ControlMask, XK_n, i_navigate_frame, +1 }, - { ControlMask, XK_p, i_navigate_frame, -1 }, - { ControlMask, XK_space, i_toggle_animation, None }, - { ControlMask, XK_a, i_toggle_animation, None }, - { 0, XK_h, i_scroll, DIR_LEFT }, - { 0, XK_Left, i_scroll, DIR_LEFT }, - { 0, XK_j, i_scroll, DIR_DOWN }, - { 0, XK_Down, i_scroll, DIR_DOWN }, - { 0, XK_k, i_scroll, DIR_UP }, - { 0, XK_Up, i_scroll, DIR_UP }, - { 0, XK_l, i_scroll, DIR_RIGHT }, - { 0, XK_Right, i_scroll, DIR_RIGHT }, - { 0, XK_H, i_scroll_to_edge, DIR_LEFT }, - { 0, XK_J, i_scroll_to_edge, DIR_DOWN }, - { 0, XK_K, i_scroll_to_edge, DIR_UP }, - { 0, XK_L, i_scroll_to_edge, DIR_RIGHT }, - { 0, XK_z, i_scroll_to_center, None }, - { 0, XK_equal, i_set_zoom, 100 }, - { 0, XK_w, i_fit_to_win, SCALE_DOWN }, - { 0, XK_W, i_fit_to_win, SCALE_FIT }, - { 0, XK_F, i_fit_to_win, SCALE_FILL }, - { 0, XK_e, i_fit_to_win, SCALE_WIDTH }, - { 0, XK_E, i_fit_to_win, SCALE_HEIGHT }, - { 0, XK_less, i_rotate, DEGREE_270 }, - { 0, XK_greater, i_rotate, DEGREE_90 }, - { 0, XK_question, i_rotate, DEGREE_180 }, - { 0, XK_bar, i_flip, FLIP_HORIZONTAL }, - { 0, XK_underscore, i_flip, FLIP_VERTICAL }, - { 0, XK_a, i_toggle_antialias, None }, - { 0, XK_A, i_toggle_alpha, None }, - { 0, XK_s, i_slideshow, None }, -}; - -/* mouse button mappings for image mode: */ -static const button_t buttons_img[] = { - /* modifiers button function argument */ - { 0, 1, i_cursor_navigate, None }, - { ControlMask, 1, i_drag, DRAG_RELATIVE }, - { 0, 2, i_drag, DRAG_ABSOLUTE }, - { 0, 3, g_switch_mode, None }, - { 0, 4, g_zoom, +1 }, - { 0, 5, g_zoom, -1 }, -}; - -/* mouse button mappings for thumbnail mode: */ -static const button_t buttons_tns[] = { - /* modifiers button function argument */ - { 0, 1, t_select, None }, - { 0, 3, t_drag_mark_image, None }, - { 0, 4, t_scroll, DIR_UP }, - { 0, 5, t_scroll, DIR_DOWN }, - { ControlMask, 4, g_scroll_screen, DIR_UP }, - { ControlMask, 5, g_scroll_screen, DIR_DOWN }, -}; - -/* true means NAV_WIDTH is relative (33%), false means absolute (33 pixels) */ -static const bool NAV_IS_REL = true; -/* width of navigation area, 0 disables cursor navigation, */ -static const unsigned int NAV_WIDTH = 33; - -/* mouse cursor on left, middle and right part of the window */ -static const cursor_t imgcursor[3] = { - CURSOR_LEFT, CURSOR_ARROW, CURSOR_RIGHT -}; - -#endif diff --git a/source/nsxiv/config.mk b/source/nsxiv/config.mk deleted file mode 100644 index 49a165258..000000000 --- a/source/nsxiv/config.mk +++ /dev/null @@ -1,35 +0,0 @@ -# nsxiv version -VERSION = 32 - -# PREFIX for install -PREFIX = /usr/local -MANPREFIX = $(PREFIX)/share/man -EGPREFIX = $(PREFIX)/share/doc/nsxiv/examples - -# default value for optional dependencies. 1 = enabled, 0 = disabled -OPT_DEP_DEFAULT = 1 - -# autoreload backend: 1 = inotify, 0 = none -HAVE_INOTIFY = $(OPT_DEP_DEFAULT) - -# optional dependencies, see README for more info -HAVE_LIBFONTS = $(OPT_DEP_DEFAULT) -HAVE_LIBEXIF = $(OPT_DEP_DEFAULT) - -# CFLAGS, any additional compiler flags goes here -CFLAGS = -Wall -pedantic -O2 -DNDEBUG -# Uncomment for a debug build using gcc/clang -# CFLAGS = -Wall -pedantic -DDEBUG -g3 -fsanitize=address,undefined -# LDFLAGS = $(CFLAGS) - -# icons that will be installed via `make icon` -ICONS = 16x16.png 32x32.png 48x48.png 64x64.png 128x128.png - -# Uncomment on OpenBSD -# HAVE_INOTIFY = 0 -# lib_fonts_bsd_0 = -# lib_fonts_bsd_1 = -lfreetype -L/usr/X11R6/lib/freetype2 -# inc_fonts_bsd_0 = -# inc_fonts_bsd_1 = -I/usr/X11R6/include/freetype2 -# CPPFLAGS = -I/usr/X11R6/include -I/usr/local/include $(inc_fonts_bsd_$(HAVE_LIBFONTS)) -# LDLIBS = -L/usr/X11R6/lib -L/usr/local/lib $(lib_fonts_bsd_$(HAVE_LIBFONTS)) diff --git a/source/nsxiv/etc/CHANGELOG.md b/source/nsxiv/etc/CHANGELOG.md deleted file mode 100644 index 5af260fa2..000000000 --- a/source/nsxiv/etc/CHANGELOG.md +++ /dev/null @@ -1,499 +0,0 @@ -nsxiv - Changelog ------------------ - -**[git](https://codeberg.org/nsxiv/nsxiv.git)** - -Changes will only be documented on stable releases. If you're on git/master then -there may be more changes. Please use `git log` to view them. - -- - - - -**[v32](https://codeberg.org/nsxiv/nsxiv/archive/v32.tar.gz)** -*(October 01, 2023)* - -* Changes: - - * Removed legacy multi-frame loaders. Animated image support now requires - Imlib2 v1.8.0 or above. - * Move loading/caching messages to right side bar [#446] - * Set a default delay if delay is 0 in a multi-frame image [#445] - * `config.mk`: default to `-O2` [#435] - * `config.mk` no longer explicitly sets `CC` to `c99` [#455] - * Assertions are now opt-in and requires explicitly defining `DEBUG` [#447] - -* Added: - - * Added a pick-quit key-binding [#432] - * Ability to configure Xresources class name in `config.h` [#427] - * `--version` output now also includes compiled-in feature list [#462] - * Document handling of empty X resources values [#428] - * Experimental flag `--bg-cache` to generate thumbnail cache in a background - process [#438] - -* Fixes: - - * Changing brightness/contrast on multi-frame images [#440] - * Brightness keybindings on manpage [#467] - * Various autoreload bugs [#437], [#459], [#460] - * `*-info` scripts not updating when selecting thumbnail with mouse [#477] - * Updated openbsd configuration in `config.mk` [#453] - * Memory leak in `win_draw_bar` [#444] - * Thumbnail leak when removing the last file [#423] - -[#423]: https://codeberg.org/nsxiv/nsxiv/pulls/423 -[#427]: https://codeberg.org/nsxiv/nsxiv/pulls/427 -[#428]: https://codeberg.org/nsxiv/nsxiv/pulls/428 -[#432]: https://codeberg.org/nsxiv/nsxiv/pulls/432 -[#435]: https://codeberg.org/nsxiv/nsxiv/pulls/435 -[#437]: https://codeberg.org/nsxiv/nsxiv/pulls/437 -[#438]: https://codeberg.org/nsxiv/nsxiv/pulls/438 -[#440]: https://codeberg.org/nsxiv/nsxiv/pulls/440 -[#444]: https://codeberg.org/nsxiv/nsxiv/pulls/444 -[#445]: https://codeberg.org/nsxiv/nsxiv/pulls/445 -[#446]: https://codeberg.org/nsxiv/nsxiv/pulls/446 -[#447]: https://codeberg.org/nsxiv/nsxiv/pulls/447 -[#453]: https://codeberg.org/nsxiv/nsxiv/pulls/453 -[#455]: https://codeberg.org/nsxiv/nsxiv/pulls/455 -[#459]: https://codeberg.org/nsxiv/nsxiv/pulls/459 -[#460]: https://codeberg.org/nsxiv/nsxiv/pulls/460 -[#462]: https://codeberg.org/nsxiv/nsxiv/pulls/462 -[#467]: https://codeberg.org/nsxiv/nsxiv/pulls/467 -[#477]: https://codeberg.org/nsxiv/nsxiv/pulls/477 - -- - - - -**[v31](https://codeberg.org/nsxiv/nsxiv/archive/v31.tar.gz)** -*(January 28, 2023)* - -* Changes: - - * Uncritical files moved to `etc/`. [#350] - * Empty Xresource entry will now be ignored. [#340] - * `win-title` will be read in a non-blocking manner. [#314] - -* Added: - - * Support for multi-frame images via `Imlib2`. [#373] - * Support for long-opts. [#332] - * Cli flag `--anti-alias` to enable/disable anti-aliasing. [#361] - * Cli flag `--alpha-layer` to enable/disable checkerboard background. [#408] - * Accept directory via stdin (-i) [#383] - * Support for modifying brightness and contrast [#396] - -* Fixes: - - * Build failure when `_SC_PHYS_PAGES` is not defined. [#334] - * Various statusbar issues. [#353],[#341] - * Crashes due to faulty signal-handler. [#411] - * Potential memory leak in `r_readdir()`. [#319] - * Potentially printing incorrect error message. [#321] - * Wrong slideshow length on animated webp. [#381] - * Document missing Ctrl+6 binding in the manpage. [#347] - -[#314]: https://codeberg.org/nsxiv/nsxiv/pulls/314 -[#319]: https://codeberg.org/nsxiv/nsxiv/pulls/319 -[#321]: https://codeberg.org/nsxiv/nsxiv/pulls/321 -[#332]: https://codeberg.org/nsxiv/nsxiv/pulls/332 -[#334]: https://codeberg.org/nsxiv/nsxiv/pulls/334 -[#340]: https://codeberg.org/nsxiv/nsxiv/pulls/340 -[#341]: https://codeberg.org/nsxiv/nsxiv/pulls/341 -[#347]: https://codeberg.org/nsxiv/nsxiv/pulls/347 -[#350]: https://codeberg.org/nsxiv/nsxiv/pulls/350 -[#353]: https://codeberg.org/nsxiv/nsxiv/pulls/353 -[#361]: https://codeberg.org/nsxiv/nsxiv/pulls/361 -[#373]: https://codeberg.org/nsxiv/nsxiv/pulls/373 -[#381]: https://codeberg.org/nsxiv/nsxiv/pulls/381 -[#383]: https://codeberg.org/nsxiv/nsxiv/pulls/383 -[#396]: https://codeberg.org/nsxiv/nsxiv/pulls/396 -[#408]: https://codeberg.org/nsxiv/nsxiv/pulls/408 -[#411]: https://codeberg.org/nsxiv/nsxiv/pulls/411 - -- - - - -***SPECIAL NOTE***: Due to [this incident](https://codeberg.org/nsxiv/nsxiv-tmp/issues/1) -we have moved development over to [Codeberg](https://codeberg.org/nsxiv/nsxiv). - -A lot of the references *below* may now be 404 on GitHub. Any threads which -survived the wipe have been migrated over to [nsxiv-record]. All of the -references *above* can be found on the new main nsxiv repository on Codeberg. - -[nsxiv-record]: https://codeberg.org/nsxiv/nsxiv-record/ - -- - - - -**[v30](https://codeberg.org/nsxiv/nsxiv/archive/v30.tar.gz)** -*(June 15, 2022)* - -* Changes: - - * Development and main repository moved over to Codeberg. - See the special note above for more info. - * `autoreload_{inotify,nop}.c` merged into a single file, `autoreload.c`. [#263] - * Moved all configuration related macros to `config.mk`. [#264] - * `win-title` is now called only when there's change rather than being called - on each redraw. [#266] - -* Added: - - * Added more mimetypes to the `.desktop` entry. [#260] - * Added `thumb-info` for customizing the statusbar in thumbnail-mode. [#265] - * Added comments for building on OpenBSD. [#264] - -* Fixes: - - * "Too many open file" error due to not closing the win-title script. [#245] - * `-f` now directly starts in fullscreen mode rather than opening a normal - window and then going fullscreen. [#251] - * Broken slideshow on slow systems or fast animations. [#282] - * Memory leak when removing an image in thumbnail mode. [#247] - * Correctly setting `_NET_WM_PID`. [#251] - * Don't override statusbar if info script doesn't exist. [#271] - * Potential misbehavior regarding font. [#250] - -[#245]: https://codeberg.org/nsxiv/nsxiv-record/pulls/245 -[#247]: https://codeberg.org/nsxiv/nsxiv-record/pulls/247 -[#250]: https://codeberg.org/nsxiv/nsxiv-record/pulls/250 -[#251]: https://codeberg.org/nsxiv/nsxiv-record/pulls/251 -[#263]: https://codeberg.org/nsxiv/nsxiv-record/pulls/263 -[#264]: https://codeberg.org/nsxiv/nsxiv-record/pulls/264 -[#260]: https://codeberg.org/nsxiv/nsxiv-record/pulls/260 -[#264]: https://codeberg.org/nsxiv/nsxiv-record/pulls/264 -[#265]: https://codeberg.org/nsxiv/nsxiv-record/pulls/265 -[#266]: https://codeberg.org/nsxiv/nsxiv-record/pulls/266 -[#271]: https://codeberg.org/nsxiv/nsxiv-record/pulls/271 -[#282]: https://codeberg.org/nsxiv/nsxiv-record/pulls/282 - -- - - - -**[v29](https://github.com/nsxiv/nsxiv/archive/v29.tar.gz)** -*(March 03, 2022)* - -* Changes: - - * Window title is now customizable via `win-title`, cli flag `-T` and related - config.h options are removed. See `WINDOW TITLE` section of the manpage for - more info. [#213] - * Imlib2 cache size is now set based on total memory percentage, by default - set to 3%. [#184] - * Removed some non-POSIX extensions in the Makefile. [#225] - -* Added: - - * Ability to customize thumbnail mode mouse-bindings via `config.h`. [#167] - * Option to set statusbar position to top via `config.h`. [#231] - * New keybinding z to scroll to center. [#203] - -* Fixes: - - * Manpage cleanup: avoid confusing wording and document thumbnail mode - mouse-bindings. [#186] - * Wrong jpeg exif orientation with Imlib2 v1.7.5 (and above). [#188] - * Animation slowdown when zoomed in. [#200] - * Reset statusbar after failed keyhandler. [#191] - * Window title not working on certain WMs. [#234] - * Various compiler warnings. [#197] - -[#167]: https://codeberg.org/nsxiv/nsxiv-record/pulls/167 -[#184]: https://codeberg.org/nsxiv/nsxiv-record/pulls/184 -[#186]: https://codeberg.org/nsxiv/nsxiv-record/pulls/186 -[#188]: https://codeberg.org/nsxiv/nsxiv-record/pulls/188 -[#191]: https://codeberg.org/nsxiv/nsxiv-record/pulls/191 -[#197]: https://codeberg.org/nsxiv/nsxiv-record/pulls/197 -[#200]: https://codeberg.org/nsxiv/nsxiv-record/pulls/200 -[#203]: https://codeberg.org/nsxiv/nsxiv-record/pulls/203 -[#213]: https://codeberg.org/nsxiv/nsxiv-record/pulls/213 -[#225]: https://codeberg.org/nsxiv/nsxiv-record/pulls/225 -[#231]: https://codeberg.org/nsxiv/nsxiv-record/pulls/231 -[#234]: https://codeberg.org/nsxiv/nsxiv-record/pulls/234 - -- - - - -**[v28](https://github.com/nsxiv/nsxiv/archive/v28.tar.gz)** -*(December 12, 2021)* - -* Changes: - - * Statusbar made optional via `HAVE_LIBFONTS`. [#95] - * Remove library auto-detection, use `OPT_DEP_DEFAULT` instead. [#71] - * Example scripts will now be installed into `EGPREFIX` - (`$(PREFIX)/share/doc/nsxiv/examples` by default). See README for more - info. [#86] - -* Added: - - * Animated webp support (optional via `HAVE_LIBWEBP`). [#20] - * New mouse-binding Ctrl-Button1 for relative drag. [#117] - * Ability to configure colors and fonts in `config.h`. [#115] - * Ability to configure navigation width area in `config.h`. [#155] - * Ability to customize the set of modifiers used when processing keybindings - in `config.h` via `USED_MODMASK`. [#150] - * Ability to configure Imlib2's cache size for better image (re)loading - performance in `config.h`. [#171] - * Cli flag `-0` for sending null-seperated file-list to standard out (`-o`), - and key-handler and receiving null-seperated file-list via stdin (`-i`). - [#68],[#141],[#164] - * Export environment variable `NSXIV_USING_NULL` to key-handler. [#164] - * Embed new nsxiv icon. [#163] - * `make install-icon` to install icons. [#80],[#96] - * `make install-desktop` to install .desktop entry. [#80],[#96] - * `make install-all` to install everything. [#80],[#96] - * Configurable `KEYHANDLER_ABORT` in `config.h`. [#91],[#172] - * Statusbar message upon key-handler activation. [#98] - * Ability to write custom C functions in `config.h` and use them via - keybindings. [#76] - -* Fixes: - - * Not able to use `KEYHANDLER_ABORT` key (Escape by default) in - regular keybindings. [#91] - * Memory leak related to Xresources. [#134] - * Memory leak in gif loader. [#165] - * Better handle gif colormap and prevent out-of-bound access. [#165] - * Prevent crash when zooming out in very small images. [#178] - * Removed non-POSIX commands and extensions from `Makefile`. [#71] - * Regression where nsxiv wouldn't run on non-TrueColor X server. [#114] - * Wrong comments in `config.h` and description in `manpage`. - [#105],[#106],[#152] - -[#20]: https://codeberg.org/nsxiv/nsxiv-record/pulls/20 -[#68]: https://codeberg.org/nsxiv/nsxiv-record/pulls/68 -[#71]: https://codeberg.org/nsxiv/nsxiv-record/pulls/71 -[#76]: https://codeberg.org/nsxiv/nsxiv-record/pulls/76 -[#80]: https://codeberg.org/nsxiv/nsxiv-record/pulls/80 -[#86]: https://codeberg.org/nsxiv/nsxiv-record/pulls/86 -[#91]: https://codeberg.org/nsxiv/nsxiv-record/pulls/91 -[#95]: https://codeberg.org/nsxiv/nsxiv-record/pulls/95 -[#96]: https://codeberg.org/nsxiv/nsxiv-record/pulls/96 -[#98]: https://codeberg.org/nsxiv/nsxiv-record/pulls/98 -[#105]: https://codeberg.org/nsxiv/nsxiv-record/pulls/105 -[#106]: https://codeberg.org/nsxiv/nsxiv-record/pulls/106 -[#114]: https://codeberg.org/nsxiv/nsxiv-record/pulls/114 -[#115]: https://codeberg.org/nsxiv/nsxiv-record/pulls/115 -[#117]: https://codeberg.org/nsxiv/nsxiv-record/pulls/117 -[#134]: https://codeberg.org/nsxiv/nsxiv-record/pulls/134 -[#141]: https://codeberg.org/nsxiv/nsxiv-record/pulls/141 -[#150]: https://codeberg.org/nsxiv/nsxiv-record/pulls/150 -[#152]: https://codeberg.org/nsxiv/nsxiv-record/pulls/152 -[#155]: https://codeberg.org/nsxiv/nsxiv-record/pulls/155 -[#163]: https://codeberg.org/nsxiv/nsxiv-record/pulls/163 -[#164]: https://codeberg.org/nsxiv/nsxiv-record/pulls/164 -[#165]: https://codeberg.org/nsxiv/nsxiv-record/pulls/165 -[#171]: https://codeberg.org/nsxiv/nsxiv-record/pulls/171 -[#172]: https://codeberg.org/nsxiv/nsxiv-record/pulls/172 -[#178]: https://codeberg.org/nsxiv/nsxiv-record/pulls/178 - -- - - - -**[v27.1](https://github.com/nsxiv/nsxiv/archive/v27.1.tar.gz)** -*(September 16, 2021)* - -* Fixes: - - * Source tarball failing build [#66] - -[#66]: https://codeberg.org/nsxiv/nsxiv-record/pulls/66 - -- - - - -**[v27](https://github.com/nsxiv/nsxiv/archive/v27.tar.gz)** -*(September 16, 2021)* - -* Changes: - - * Re-release under the name nsxiv. - * Xresources `Sxiv.foreground` and `Sxiv.background` changed - to `Nsxiv.window.foreground` and `Nsxiv.window.background`. - * Xresources `Sxiv.font` changed to `Nsxiv.bar.font`. - * Rework the build system. Now by default we'll build with only optional - dependencies that are already installed. [#19] - -* Added: - - * Fill scale mode. [#2] - * Configurable X window title (via `config.h` and the `-T` flag). [#23] - * Support custom bar colors via Xresources. [#19] - * Support custom mark color via Xresources. [#51] - * Toggle animation playback with Ctrl-a. [#33] - * Set `_NET_WM_PID` and `WM_CLIENT_MACHINE` X properties. [#13] - * Set `ICCCM WM manager` hints. [#12] - -* Fixes: - - * Cli flag `-G` not initially setting gamma. [#31] - * Wrong keybinding description in the manpage. [#14] - * .desktop entry not advertising webp support. [#15] - * Prevent crash when embedded into transparent window. [#3] - * Small memory leak. [#57] - * Rare crash when showing some GIFs. [#41] - * Rare event where nsxiv wouldn't close after window being destroyed. [#53] - -[#2]: https://codeberg.org/nsxiv/nsxiv-record/pulls/2 -[#3]: https://codeberg.org/nsxiv/nsxiv-record/pulls/3 -[#12]: https://codeberg.org/nsxiv/nsxiv-record/pulls/12 -[#13]: https://codeberg.org/nsxiv/nsxiv-record/pulls/13 -[#14]: https://codeberg.org/nsxiv/nsxiv-record/pulls/14 -[#15]: https://codeberg.org/nsxiv/nsxiv-record/pulls/15 -[#19]: https://codeberg.org/nsxiv/nsxiv-record/pulls/19 -[#23]: https://codeberg.org/nsxiv/nsxiv-record/pulls/23 -[#31]: https://codeberg.org/nsxiv/nsxiv-record/pulls/31 -[#33]: https://codeberg.org/nsxiv/nsxiv-record/pulls/33 -[#41]: https://codeberg.org/nsxiv/nsxiv-record/pulls/41 -[#51]: https://codeberg.org/nsxiv/nsxiv-record/pulls/51 -[#53]: https://codeberg.org/nsxiv/nsxiv-record/pulls/53 -[#57]: https://codeberg.org/nsxiv/nsxiv-record/pulls/57 - - -sxiv ----- - -**Stable releases** - -**[v26](https://github.com/nsxiv/nsxiv/archive/v26.tar.gz)** -*(January 16, 2020)* - - * Maintenance release - -**[v25](https://github.com/nsxiv/nsxiv/archive/v25.tar.gz)** -*(January 26, 2019)* - - * Support font fallback for missing glyphs - * Fix busy loop when built without inotify - * Use background/foreground colors from X resource database - -**[v24](https://github.com/nsxiv/nsxiv/archive/v24.tar.gz)** -*(October 27, 2017)* - - * Automatically reload the current image whenever it changes - * Support embedding into other X windows with -e (e.g. tabbed) - * New option -p prevents sxiv from creating cache and temporary files - * Simpler mouse mappings, the most basic features are accessible with the - mouse only (navigate, zoom, pan) - -**[v1.3.2](https://github.com/nsxiv/nsxiv/archive/v1.3.2.tar.gz)** -*(December 20, 2015)* - - * external key handler gets file paths on stdin, not as arguments - * Cache out-of-view thumbnails in the background - * Apply gamma correction to thumbnails - -**[v1.3.1](https://github.com/nsxiv/nsxiv/archive/v1.3.1.tar.gz)** -*(November 16, 2014)* - - * Fixed build error, caused by delayed config.h creation - * Fixed segfault when run with -c - -**[v1.3](https://github.com/nsxiv/nsxiv/archive/v1.3.tar.gz)** -*(October 24, 2014)* - - * Extract thumbnails from EXIF tags (requires libexif) - * Zoomable thumbnails, supported sizes defined in config.h - * Fixed build error with giflib version >= 5.1.0 - -**[v1.2](https://github.com/nsxiv/nsxiv/archive/v1.2.tar.gz)** -*(April 24, 2014)* - - * Added external key handler, called on keys prefixed with `Ctrl-x` - * New keybinding `{`/`}` to change gamma (by András Mohari) - * Support for slideshows, enabled with `-S` option & toggled with `s` - * Added application icon (created by 0ion9) - * Checkerboard background for alpha layer - * Option `-o` only prints files marked with `m` key - * Fixed rotation/flipping of multi-frame images (gifs) - -**[v1.1.1](https://github.com/nsxiv/nsxiv/archive/v1.1.1.tar.gz)** -*(June 2, 2013)* - - * Various bug fixes - -**[v1.1](https://github.com/nsxiv/nsxiv/archive/v1.1.tar.gz)** -*(March 30, 2013)* - - * Added status bar on bottom of window with customizable content - * New keyboard shortcuts `\`/`|`: flip image vertically/horizontally - * New keyboard shortcut `Ctrl-6`: go to last/alternate image - * Added own EXIF orientation handling, removed dependency on libexif - * Fixed various bugs - -**[v1.0](https://github.com/nsxiv/nsxiv/archive/v1.0.tar.gz)** -*(October 31, 2011)* - - * Support for multi-frame images & GIF animations - * POSIX compliant (IEEE Std 1003.1-2001) - -**[v0.9](https://github.com/nsxiv/nsxiv/archive/v0.9.tar.gz)** -*(August 17, 2011)* - - * Made key and mouse mappings fully configurable in config.h - * Complete code refactoring - -**[v0.8.2](https://github.com/nsxiv/nsxiv/archive/v0.8.2.tar.gz)** -*(June 29, 2011)* - - * POSIX-compliant Makefile; compiles under NetBSD - -**[v0.8.1](https://github.com/nsxiv/nsxiv/archive/v0.8.1.tar.gz)** -*(May 8, 2011)* - - * Fixed fullscreen under window managers, which are not fully EWMH-compliant - -**[v0.8](https://github.com/nsxiv/nsxiv/archive/v0.8.tar.gz)** -*(April 18, 2011)* - - * Support for thumbnail caching - * Ability to run external commands (e.g. jpegtran, convert) on current image - -**[v0.7](https://github.com/nsxiv/nsxiv/archive/v0.7.tar.gz)** -*(February 26, 2011)* - - * Sort directory entries when using `-r` command line option - * Hide cursor in image mode - * Full functional thumbnail mode, use Return key to switch between image and - thumbnail mode - -**[v0.6](https://github.com/nsxiv/nsxiv/archive/v0.6.tar.gz)** -*(February 16, 2011)* - - * Bug fix: Correctly display filenames with umlauts in window title - * Basic support of thumbnails - -**[v0.5](https://github.com/nsxiv/nsxiv/archive/v0.5.tar.gz)** -*(February 6, 2011)* - - * New command line option: `-r`: open all images in given directories - * New key shortcuts: `w`: resize image to fit into window; `W`: resize window - to fit to image - -**[v0.4](https://github.com/nsxiv/nsxiv/archive/v0.4.tar.gz)** -*(February 1, 2011)* - - * New command line option: `-F`, `-g`: use fixed window dimensions and apply - a given window geometry - * New key shortcut: `r`: reload current image - -**[v0.3.1](https://github.com/nsxiv/nsxiv/archive/v0.3.1.tar.gz)** -*(January 30, 2011)* - - * Bug fix: Do not set setuid bit on executable when using `make install` - * Pan image with mouse while pressing middle mouse button - -**[v0.3](https://github.com/nsxiv/nsxiv/archive/v0.3.tar.gz)** -*(January 29, 2011)* - - * New command line options: `-d`, `-f`, `-p`, `-s`, `-v`, `-w`, `-Z`, `-z` - * More mouse mappings: Go to next/previous image with left/right click, - scroll image with mouse wheel (horizontally if Shift key is pressed), - zoom image with mouse wheel if Ctrl key is pressed - -**[v0.2](https://github.com/nsxiv/nsxiv/archive/v0.2.tar.gz)** -*(January 23, 2011)* - - * Bug fix: Handle window resizes correctly - * New keyboard shortcuts: `g`/`G`: go to first/last image; `[`/`]`: go 10 - images back/forward - * Support for mouse wheel zooming (by Dave Reisner) - * Added fullscreen mode - -**[v0.1](https://github.com/nsxiv/nsxiv/archive/v0.1.tar.gz)** -*(January 21, 2011)* - - * Initial release diff --git a/source/nsxiv/etc/CONTRIBUTING.md b/source/nsxiv/etc/CONTRIBUTING.md deleted file mode 100644 index aa960687c..000000000 --- a/source/nsxiv/etc/CONTRIBUTING.md +++ /dev/null @@ -1,112 +0,0 @@ -Project Scope -------------- - - * Bug fixes and maintenance - * Prioritize extensibility and simplicity - * Do not make the codebase more complex, keep it simple to hack on - * Do not add extra dependency (if we do, add compile time switch to disable it) - * New features may be added if it cannot be achieved (easily) via a shell script, - doesn't break backwards compatibility and doesn't violate any of the above rules. - -Note: Since we aim to be a drop-in replacement for sxiv, we intend to keep all -sxiv's behaviors/features even in cases where removing them would make the -code-base simpler. - -Also note that (n)sxiv uses `imlib2` for loading images. Thus any request or -patches for adding support for new image formats should go into -[imlib2's repo](https://git.enlightenment.org/old/legacy-imlib2) instead. - - -Contribution Guideline ----------------------- - -When contributing, make sure: - - * Your contribution falls under nsxiv's scope and aim - * You follow the existing code style (see the "Code Style" section below) - * You open the pull request from a new branch, not from master - * To avoid using force pushes, especially for bigger patches. Only use them - when there's merge conflicts. - -If your contribution is not suitable for general use, it will not be included in nsxiv. -For changes that are very much up to preference, such as changing values in config.h, -please do not open a pull request unless you have an objective explanation. - -See the [open issues](https://codeberg.org/nsxiv/nsxiv/issues) to find something -to work on. You can also filter the issues via label: - -* [Good first issue](https://codeberg.org/nsxiv/nsxiv/issues?labels=49698): - (Easy) Issues which do not require much if any experience. -* [Up for grabs](https://codeberg.org/nsxiv/nsxiv/issues?labels=49705): - (Intermediate) Issues which are free for anyone who wants to pick it up. - Might require some experience. -* [Help wanted](https://codeberg.org/nsxiv/nsxiv/issues?labels=49699): - (Intermediate/Experienced) Issues where we require some help. - - -Code Style ----------- - -`nsxiv` mostly follows the [suckless code-style][sl], with a few exceptions. -If your editor supports [.editorconfig](../.editorconfig) then you'll already be -off to a good start without needing much manual intervention. Additionally we -provide a [clang-format](./.clang-format) configuration for reference, which you -may use via [`git-clang-format`][cf] to format the changes you've made (please -do not run it globally on the entire code-base since clang-format gets a decent -amount of edge cases wrong). - -[sl]: https://suckless.org/coding_style/ -[cf]: https://clang.llvm.org/docs/ClangFormat.html#git-integration - - -Development workflow for maintainers ------------------------------------- - -If we notice you contributing and/or showing interest in issues/pull requests, -we may invite you to join the nsxiv org as a member. Being a member simply means -you will be able to approve, disapprove and merge pull requests. - -Our workflow regarding pull requests is the following: - - * Code related changes require two approvals, but documentation related - changes (e.g. typo) can be merged with just one. - * If a pull request has a single approval, no objections and has been open - for more than 7 days, then it may be force-merged. - * Always prefer squashing when merging. In the case a PR makes more than one - significant change, use the "don't squash" tag and rebase instead. - * When merging, make sure the commit message is cleaned up properly so that - it reflects the current intention of the PR. - -For releases, the process is the following: - - * Tag the release with a "vN" tag, where N is the version number. Also set - the commit message and tag description for the release commit to "Release - version N". Make sure to use an annotated tag. - * Update `VERSION` macro in `config.mk`. - * Update the changelog (`etc/CHANGELOG.md`): - * Include link to the release tarball and add the release date. - * Document only the changes or fixes between releases. Don't document - changes which never made it into a release. - * Use the "Changes" section to document behavior changes since the last - release, the "Added" section for new features, and the "Fixes" section - for fixed bugs or regressions. - * Include pull request IDs with reference style links. - -Mirroring to GitHub: - -Assuming `origin` is the name of the codeberg remote and `github` is the name -of the github remote; run the following commands to mirror the codeberg repo to -github: - -```console -$ git fetch --prune origin -$ git push --prune github '+refs/remotes/origin/*:refs/heads/*' '+refs/tags/*:refs/tags/*' -``` - -The first command updates the local repo and the second command pushes -everything on `origin` without pushing any of the local branches. - -- - - - -For mundane development related talks which don't warrant their own issue, use -the [general discussion](https://codeberg.org/nsxiv/nsxiv/issues/294) thread. diff --git a/source/nsxiv/etc/examples/image-info b/source/nsxiv/etc/examples/image-info deleted file mode 100755 index 5f0612355..000000000 --- a/source/nsxiv/etc/examples/image-info +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -# Example for $XDG_CONFIG_HOME/nsxiv/exec/image-info -# Called by nsxiv(1) whenever an image gets loaded. -# The output is displayed in nsxiv's status bar. -# Arguments: -# $1: path to image file (as provided by the user) -# $2: image width -# $3: image height -# $4: fully resolved path to the image file - -s=" " # field separator - -exec 2>/dev/null - -filename=$(basename -- "$1") -filesize=$(du -Hh -- "$1" | cut -f 1) -geometry="${2}x${3}" - -echo "${filesize}${s}${geometry}${s}${filename}" - diff --git a/source/nsxiv/etc/examples/key-handler b/source/nsxiv/etc/examples/key-handler deleted file mode 100755 index 3fe741d65..000000000 --- a/source/nsxiv/etc/examples/key-handler +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh - -# Example for $XDG_CONFIG_HOME/nsxiv/exec/key-handler -# Called by nsxiv(1) after the external prefix key (C-x by default) is pressed. -# The next key combo is passed as its first argument. Passed via stdin are the -# images to act upon: all marked images, if in thumbnail mode and at least one -# image has been marked, otherwise the current image. nsxiv(1) will block until -# the handler terminates. It then checks which images have been modified and -# reloads them. - -# By default nsxiv(1) will send one image per-line to stdin, however when using -# -0 the image list will be NULL separated and the environment variable -# "$NSXIV_USING_NULL" will be set to 1. - -# The key combo argument has the following form: "[C-][M-][S-]KEY", -# where C/M/S indicate Ctrl/Meta(Alt)/Shift modifier states and KEY is the X -# keysym as listed in /usr/include/X11/keysymdef.h without the "XK_" prefix. -# If KEY has an uppercase equivalent, S-KEY is resolved into it. For instance, -# K replaces S-k and Scedilla replaces S-scedilla, but S-Delete is sent as-is. - -rotate() { - degree="$1" - tr '\n' '\0' | xargs -0 realpath | sort | uniq | while read file; do - case "$(file -b -i "$file")" in - image/jpeg*) jpegtran -rotate "$degree" -copy all -outfile "$file" "$file" ;; - *) mogrify -rotate "$degree" "$file" ;; - esac - done -} - -case "$1" in -"C-x") xclip -in -filter | tr '\n' ' ' | xclip -in -selection clipboard ;; -"C-c") while read file; do xclip -selection clipboard -target image/png "$file"; done ;; -"C-e") while read file; do urxvt -bg "#444" -fg "#eee" -sl 0 -title "$file" -e sh -c "exiv2 pr -q -pa '$file' | less" & done ;; -"C-g") tr '\n' '\0' | xargs -0 gimp & ;; -"C-r") while read file; do rawtherapee "$file" & done ;; -"C-comma") rotate 270 ;; -"C-period") rotate 90 ;; -"C-slash") rotate 180 ;; -esac - diff --git a/source/nsxiv/etc/examples/thumb-info b/source/nsxiv/etc/examples/thumb-info deleted file mode 100755 index b422f9c1c..000000000 --- a/source/nsxiv/etc/examples/thumb-info +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -# Example for $XDG_CONFIG_HOME/nsxiv/exec/thumb-info -# Called by nsxiv(1) whenever the selected thumbnail changes. -# The output is displayed in nsxiv's status bar. -# Arguments: -# $1: path to image file (as provided by the user) -# $2: empty -# $3: empty -# $4: fully resolved path to the image file - -s=" " # field separator - -exec 2>/dev/null - -filename=$(basename -- "$4") -filesize=$(du -Hh -- "$4" | cut -f 1) - -echo "${filesize}${s}${filename}" - diff --git a/source/nsxiv/etc/examples/win-title b/source/nsxiv/etc/examples/win-title deleted file mode 100755 index 31994ef57..000000000 --- a/source/nsxiv/etc/examples/win-title +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -# Example for $XDG_CONFIG_HOME/nsxiv/exec/win-title -# Called by nsxiv(1) whenever any of the relevant information changes. -# The output is set as nsxiv's window title. -# -# Arguments, "Optional" arguments might be empty: -# $1: resolved absolute path of the current file -# $2: current file number -# $3: total file number -# $4: image width (Optional: Disabled on thumbnails mode) -# $5: image height (Optional: Disabled on thumbnails mode) -# $6: current zoom (Optional: Disabled on thumbnails mode) -# -# The term file is used rather than image as nsxiv does not -# precheck that the input files are valid images. Total file -# count may be different from the actual count of valid images. - -exec 2>/dev/null - -filename="${1##*/}" - -if [ -n "$4" ]; then # image mode - printf "%s" "nsxiv - ${filename} | ${4}x${5} ${6}% [${2}/${3}]" -else - printf "%s" "nsxiv - ${filename} [${2}/${3}]" -fi diff --git a/source/nsxiv/etc/nsxiv.1 b/source/nsxiv/etc/nsxiv.1 deleted file mode 100644 index 5edfecec4..000000000 --- a/source/nsxiv/etc/nsxiv.1 +++ /dev/null @@ -1,592 +0,0 @@ -.TH NSXIV 1 nsxiv\-VERSION -.SH NAME -nsxiv \- Neo Simple X Image Viewer -.SH SYNOPSIS -.B nsxiv -.RB [ \-abcfhiopqrtvZ0 ] -.RB [ \-A -.IR FRAMERATE ] -.RB [ \-e -.IR WID ] -.RB [ \-G -.IR GAMMA ] -.RB [ \-g -.IR GEOMETRY ] -.RB [ \-N -.IR NAME ] -.RB [ \-n -.IR NUM ] -.RB [ \-S -.IR DELAY ] -.RB [ \-s -.IR MODE ] -.RB [ \-z -.IR ZOOM ] -.IR FILE ... -.SH DESCRIPTION -nsxiv is a simple image viewer for X. -.P -It has two modes of operation: image and thumbnail mode. The default is image -mode, in which only the current image is shown. In thumbnail mode a grid of -small previews is displayed, making it easy to choose an image to open. -.P -Please note, that the fullscreen mode requires an EWMH/NetWM-compliant window -manager. -.SH OPTIONS -.TP -.BI "\-A, \-\-framerate " FRAMERATE -Play animations with a constant frame rate set to -.IR FRAMERATE . -.TP -.B "\-a, \-\-animate" -Play animations of multi-frame images. -.TP -.B "\-b, \-\-no\-bar" -Do not show statusbar at the bottom of the window. -.TP -.B "\-c, \-\-clean\-cache" -Remove all orphaned cache files from the thumbnail cache directory and exit. -.TP -.BI "\-e, \-\-embed " WID -Embed nsxiv's window into window whose ID is -.IR WID . -.TP -.B "\-f, \-\-fullscreen" -Start in fullscreen mode. -.TP -.BI "\-G, \-\-gamma " GAMMA -Set image gamma to GAMMA (\-32..32). -.TP -.BI "\-g, \-\-geometry " GEOMETRY -Set window position and size. See section GEOMETRY SPECIFICATIONS of X(7) for -more information on GEOMETRY argument. -.TP -.B "\-h, \-\-help" -Print brief usage information to standard output and exit. -.TP -.B "\-i, \-\-stdin" -Read names of files to open from standard input. Also done if FILE is `-'. -.TP -.BI "\-N, \-\-class " NAME -Set the resource name (WM_CLASS) of nsxiv's X window to NAME. -.TP -.BI "\-n, \-\-start\-at " NUM -Start at picture number NUM. -.TP -.B "\-o, \-\-stdout" -Write list of all marked files to standard output when quitting. In combination -with -.B "\-i, \-\-stdin" -nsxiv can be used as a visual filter/pipe. -.TP -.B "\-p, \-\-private" -Enable private mode, in which nsxiv does not write any cache or temporary files. -.TP -.B "\-q, \-\-quiet" -Be quiet, and disable warnings to standard error stream. -.TP -.B "\-r, \-\-recursive" -Search the given directories recursively for images to view. -.TP -.BI "\-S, \-\-ss\-delay " DELAY -Start in slideshow mode. Set the delay between images to -.I DELAY -seconds. -.I DELAY -may be a floating-point number. -.TP -.BI "\-s, \-\-scale\-mode " MODE -Set scale mode according to MODE character. Supported modes are: [d]own, -[f]it, [F]ill, [w]idth, [h]eight. -.TP -.B "\-t, \-\-thumbnail" -Start in thumbnail mode. -.TP -.B "\-v, \-\-version" -Print version information to standard output and exit. -.TP -.B "\-Z, \-\-zoom\-100" -The same as `\-z 100'. -.TP -.BI "\-z, \-\-zoom " ZOOM -Set zoom level to ZOOM percent. -.TP -.B "\-0, \-\-null" -Use NULL-separator. With this option, output of \-o and file-list sent to the -key-handler and the input of \-i will be separated by a NULL character. -.TP -.BI "\-\-anti\-alias" [=no] -Enables anti-aliasing, when given -.I no -as an argument, disables it instead. -.TP -.BI "\-\-alpha\-layer" [=no] -Enables checkerboard background for alpha layer, when given -.I no -as an argument, disables it instead. -.SH KEYBOARD COMMANDS -.SS General -The following keyboard commands are available in both image and thumbnail modes: -.TP -.BR 0 \- 9 -Prefix the next command with a number (denoted via -.IR count ). -.TP -.B q -Quit nsxiv. -.TP -.B Q -Quit nsxiv, but additionally print the current filename when \-o is active and -no files have been marked. -.TP -.B Return -Switch to thumbnail mode / open selected image in image mode. -.TP -.B f -Toggle fullscreen mode. -.TP -.B b -Toggle visibility of statusbar at the bottom of the window. -.TP -.B Ctrl-x -Send the next key to the external key-handler. See section EXTERNAL KEY HANDLER -for more information. -.TP -.B g -Go to the first image. -.TP -.B G -Go to the last image, or image number -.IR count . -.TP -.B r -Reload image. -.TP -.B D -Remove current image from file list and go to next image. -.TP -.BR Ctrl-h ", " Ctrl-Left -Scroll left one screen width. -.TP -.BR Ctrl-j ", " Ctrl-Down -Scroll down one screen height. -.TP -.BR Ctrl-k ", " Ctrl-Up -Scroll up one screen height. -.TP -.BR Ctrl-l ", " Ctrl-Right -Scroll right one screen width. -.TP -.BR + -Zoom in. -.TP -.B \- -Zoom out. -.TP -.B m -Mark/unmark the current image. -.TP -.B M -Mark/unmark all images starting from the last marked/unmarked image up to the -current one. -.TP -.B Ctrl-m -Reverse all image marks. -.TP -.B Ctrl-u -Remove all image marks. -.TP -.B N -Go -.I count -marked images forward. -.TP -.B P -Go -.I count -marked images backward. -.TP -.B { -Decrease gamma correction by -.I count -steps. -.TP -.B } -Increase gamma correction by -.I count -steps. -.TP -.B Ctrl-g -Reset gamma correction. -.TP -.B Ctrl-[ -Decrease brightness correction by -.I count -steps. -.TP -.B Ctrl-] -Increase brightness correction by -.I count -steps. -.TP -.B ( -Decrease contrast by -.I count -steps. -.TP -.B ) -Increase contrast by -.I count -steps. -.SS Thumbnail mode -The following keyboard commands are only available in thumbnail mode: -.TP -.BR h ", " Left -Move selection left -.I count -times. -.TP -.BR j ", " Down -Move selection down -.I count -times. -.TP -.BR k ", " Up -Move selection up -.I count -times. -.TP -.BR l ", " Right -Move selection right -.I count -times. -.TP -.B R -Reload all thumbnails. -.SS Image mode -The following keyboard commands are only available in image mode: -.TP -Navigate image list: -.TP -.BR n ", " Space -Go -.I count -images forward. -.TP -.BR p ", " Backspace -Go -.I count -images backward. -.TP -.B [ -Go -.I count -* 10 images backward. -.TP -.B ] -Go -.I count -* 10 images forward. -.TP -.B Ctrl-6 -Go to the previously viewed image. -.TP -Handle multi-frame images: -.TP -.B Ctrl-n -Go -.I count -frames of a multi-frame image forward. -.TP -.B Ctrl-p -Go -.I count -frames of a multi-frame image backward. -.TP -.BR Ctrl-a ", " Ctrl-Space -Play/stop animations of multi-frame images. -.TP -Panning: -.TP -.BR h ", " Left -Scroll image 1/5 of window width or -.I count -pixels left. -.TP -.BR j ", " Down -Scroll image 1/5 of window height or -.I count -pixels down. -.TP -.BR k ", " Up -Scroll image 1/5 of window height or -.I count -pixels up. -.TP -.BR l ", " Right -Scroll image 1/5 of window width or -.I count -pixels right. -.TP -.B H -Scroll to left image edge. -.TP -.B J -Scroll to bottom image edge. -.TP -.B K -Scroll to top image edge. -.TP -.B L -Scroll to right image edge. -.TP -.B z -Scroll to image center. -.TP -Zooming: -.TP -.B = -Set zoom level to 100%, or -.IR count %. -.TP -.B w -Set zoom level to 100%, but fit large images into window. -.TP -.B W -Fit image to window. -.TP -.B F -Fill image to window. -.TP -.B e -Fit image to window width. -.TP -.B E -Fit image to window height. -.TP -Rotation: -.TP -.B < -Rotate image counter-clockwise by 90 degrees. -.TP -.B > -Rotate image clockwise by 90 degrees. -.TP -.B ? -Rotate image by 180 degrees. -.TP -Flipping: -.TP -.B | -Flip image horizontally. -.TP -.B _ -Flip image vertically. -.TP -Miscellaneous: -.TP -.B a -Toggle anti-aliasing. -.TP -.B A -Toggle visibility of alpha-channel, i.e. image transparency. -.TP -.B s -Toggle slideshow mode and/or set the delay between images to -.I count -seconds. -.SH MOUSE COMMANDS -.SS Thumbnail mode -The following mouse mappings are available in thumbnail mode: -.TP -.B Button1 -Select the image. Goes into image mode if double\-clicked. -.TP -.B Button3 -Mark/unmark the image. Dragging while holding down Button3 will mark/unmark -multiple images. -.TP -.B Button4 -Scroll up by one row. -.TP -.B Button5 -Scroll down by one row. -.TP -.B Ctrl-Button4 -Scroll up by one page. -.TP -.B Ctrl-Button5 -Scroll down by one page. -.SS Image mode -The following mouse mappings are available in image mode: -.TP -.B Button1 -Go to the next image if the mouse cursor is in the right part of the window or -to the previous image if it is in the left part. -.TP -.B Ctrl-Button1 -Pan the image relative to the mouse cursor. -.TP -.B Button2 -Pan the image according to the mouse cursor position in the window while -keeping this button pressed down. -.TP -.B Button3 -Switch to thumbnail mode. -.TP -.B Button4 -Zoom in. -.TP -.B Button5 -Zoom out. -.SH CONFIGURATION -The following X resources are supported under "Nsxiv" (e.g. -.B Nsxiv.bar.font -): -.TP -.B window.background -Color of the window background -.TP -.B window.foreground -Color of the window foreground -.TP -.B bar.font -Name of Xft bar font -.TP -.B bar.background -Color of the bar background. Defaults to window.background -.TP -.B bar.foreground -Color of the bar foreground. Defaults to window.foreground -.TP -.B mark.foreground -Color of the mark foreground. Defaults to window.foreground -.TP -Please see xrdb(1) on how to change them. -.LP -An X resources entry with an empty value means the default -(defined in config.h) will be used. -.SH WINDOW TITLE -The window title can be replaced with the output of a user-provided script, -which is called by nsxiv whenever any of the relevant information changes. -The path of this script is -.I $XDG_CONFIG_HOME/nsxiv/exec/win-title -and the arguments given to it (where "Optional" arguments might be empty) are: -.IP $1 4 -resolved absolute path of the current file -.IP $2 4 -current file number -.IP $3 4 -total file count -.IP $4 4 -image width (Optional: Disabled on thumbnails mode) -.IP $5 4 -image height (Optional: Disabled on thumbnails mode) -.IP $6 4 -current zoom (Optional: Disabled on thumbnails mode) -.P -The term file is used rather than image as nsxiv does not precheck that the -input files are valid images. Total file count may be different from the actual -count of valid images. -.P -There is also an example script installed together with nsxiv as -.IR EGPREFIX/win-title . -.SH STATUS BAR -The information displayed on the left side of the status bar can be replaced -with the output of user-provided script. -.P -The script that is called by nsxiv whenever an image gets loaded is located at -.I $XDG_CONFIG_HOME/nsxiv/exec/image-info -and the arguments given to it are: -.IP $1 4 -path to image file (as provided by the user) -.IP $2 4 -image width -.IP $3 4 -image height -.IP $4 4 -fully resolved path to the image file -.P -In thumbnail mode, the script that is called is located at -.I $XDG_CONFIG_HOME/nsxiv/exec/thumb-info -and the arguments given to it are: -.IP $1 4 -path to image file (as provided by the user) -.IP $2 4 -empty -.IP $3 4 -empty -.IP $4 4 -fully resolved path to the image file -.P -There are also example scripts installed together with nsxiv as -.IR EGPREFIX/image-info -and -.IR EGPREFIX/thumb-info . -.SH EXTERNAL KEY HANDLER -Additional external keyboard commands can be defined using a handler program -located in -.IR $XDG_CONFIG_HOME/nsxiv/exec/key-handler . -The handler is invoked by pressing -.BR Ctrl-x . -The next key combo is passed as its first argument. Passed via stdin are the -images to act upon: all marked images, if in thumbnail mode and at least one -image has been marked, otherwise the current image. nsxiv(1) will block until -the handler terminates. It then checks which images have been modified and -reloads them. - -By default nsxiv(1) will send one image per-line to stdin, however when using -\-0 the image list will be NULL separated and the environment variable -"$NSXIV_USING_NULL" will be set to 1. - -The key combo argument has the following form: "[C-][M-][S-]KEY", -where C/M/S indicate Ctrl/Meta(Alt)/Shift modifier states and KEY is the X -keysym as listed in /usr/include/X11/keysymdef.h without the "XK_" prefix. -If KEY has an uppercase equivalent, S-KEY is resolved into it. For instance, -K replaces S-k and Scedilla replaces S-scedilla, but S-Delete is sent as-is. - -There is also an example script installed together with nsxiv as -.IR EGPREFIX/key-handler . -.SH THUMBNAIL CACHING -nsxiv stores all thumbnails under -.IR $XDG_CACHE_HOME/nsxiv/ . -.P -Use the command line option -.I \-c -to remove all orphaned cache files. Additionally, run the following command -afterwards inside the cache directory to remove empty subdirectories: -.P -.RS -find . \-depth \-type d \-empty ! \-name '.' \-exec rmdir {} \\; -.RE -.SH ORIGINAL AUTHOR -.EX -Bert Muennich -.EE -.SH CURRENT MAINTAINERS -.EX -NRK -Berke Kocaoğlu -TAAPArthur -eylles -Stein Gunnar Bakkeby -explosion-mental -.EE -.SH CONTRIBUTORS -.EX -For a list of contributors, run `git shortlog -s` in the nsxiv git repository. -.EE -.SH HOMEPAGE -.TP -Website: -.EE -https://nsxiv.codeberg.page/ -.EX -.TP -Source code: -.EE -https://codeberg.org/nsxiv/nsxiv -.EX -.SH SEE ALSO -.BR X (7), -.BR xrdb (1) diff --git a/source/nsxiv/etc/nsxiv.desktop b/source/nsxiv/etc/nsxiv.desktop deleted file mode 100644 index de90437bd..000000000 --- a/source/nsxiv/etc/nsxiv.desktop +++ /dev/null @@ -1,8 +0,0 @@ -[Desktop Entry] -Type=Application -Name=nsxiv -GenericName=Image Viewer -Exec=nsxiv %F -MimeType=image/bmp;image/gif;image/jpeg;image/jpg;image/png;image/tiff;image/x-bmp;image/x-portable-anymap;image/x-portable-bitmap;image/x-portable-graymap;image/x-tga;image/x-xpixmap;image/webp;image/heic;image/svg+xml;application/postscript;image/jp2;image/jxl;image/avif;image/heif; -NoDisplay=true -Icon=nsxiv diff --git a/source/nsxiv/etc/woodpecker/CFLAGS b/source/nsxiv/etc/woodpecker/CFLAGS deleted file mode 100644 index 57d7d161f..000000000 --- a/source/nsxiv/etc/woodpecker/CFLAGS +++ /dev/null @@ -1,13 +0,0 @@ -# vanilla flags --std=c99 -Wall -pedantic -# optimizations: enables extra warnings and deeper analysis thus catches more errors/warnings --O3 -flto -# treat warnings as errors --Werror -# extra flags --Wextra -Wshadow -Wvla -Wpointer-arith --Wundef -Wstrict-overflow=4 -Wwrite-strings -Wunreachable-code --Wbad-function-cast -Wdeclaration-after-statement --Wmissing-prototypes -Wstrict-prototypes -# silence --Wno-unused-parameter -Wno-missing-field-initializers diff --git a/source/nsxiv/etc/woodpecker/analysis.sh b/source/nsxiv/etc/woodpecker/analysis.sh deleted file mode 100755 index 5a227ceb4..000000000 --- a/source/nsxiv/etc/woodpecker/analysis.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -e - -std="c99" -NProc=$(( $(nproc) / 4 )) -if [ -z "$NProc" ] || [ "$NProc" -lt 1 ]; then NProc="1"; fi - -run_cppcheck() { - cppcheck --std="$std" --enable=performance,portability \ - --force --quiet --inline-suppr --error-exitcode=1 \ - --max-ctu-depth=8 -j"$NProc" \ - $(make OPT_DEP_DEFAULT="$1" dump_cppflags) -DDEBUG \ - --suppress=varFuncNullUB --suppress=uninitvar \ - $(git ls-files *.c) -} - -run_tidy() { - checks="$(sed '/^#/d' etc/woodpecker/clang-tidy-checks | paste -d ',' -s)" - git ls-files *.c | xargs -P"$NProc" -I{} clang-tidy --quiet \ - --warnings-as-errors="*" --checks="$checks" {} \ - -- -std="$std" $(make OPT_DEP_DEFAULT="$1" dump_cppflags) -DDEBUG -} - -run_cppcheck "0" & run_cppcheck "1" & run_tidy "0" & run_tidy "1"; -wait diff --git a/source/nsxiv/etc/woodpecker/analysis.yml b/source/nsxiv/etc/woodpecker/analysis.yml deleted file mode 100644 index 4edcd6b76..000000000 --- a/source/nsxiv/etc/woodpecker/analysis.yml +++ /dev/null @@ -1,12 +0,0 @@ -when: - branch: master - -steps: - analysis: - image: alpine - commands: | - apk add --no-cache build-base cppcheck clang-extra-tools git \ - imlib2-dev xorgproto \ - libxft-dev libexif-dev >/dev/null - make config.h version.h - ./etc/woodpecker/analysis.sh diff --git a/source/nsxiv/etc/woodpecker/build.yml b/source/nsxiv/etc/woodpecker/build.yml deleted file mode 100644 index 431f27f12..000000000 --- a/source/nsxiv/etc/woodpecker/build.yml +++ /dev/null @@ -1,31 +0,0 @@ -when: - branch: master - -# NOTE: "stable" tcc is too old and fails at linking. instead fetching a recent known working commit. -steps: - build: - image: alpine - environment: - - TCC_SHA=29ae3ed4d5b83eec43598d6cd7949bccb41c8083 - commands: | - apk add --no-cache \ - imlib2 imlib2-dev xorgproto \ - libxft libxft-dev libexif libexif-dev \ - gcc clang llvm llvm-dev build-base wget ca-certificates bc >/dev/null - wget "https://github.com/TinyCC/tinycc/archive/$TCC_SHA.tar.gz" >/dev/null - tar xzf "$TCC_SHA.tar.gz" >/dev/null - ( cd "tinycc-$TCC_SHA" && ./configure --config-musl && make -s -j"$(nproc)" && make install; ) >/dev/null - CFLAGS="$(sed '/^#/d' etc/woodpecker/CFLAGS | paste -d ' ' -s)" - build () { - for cc in "gcc" "clang"; do - echo "### $cc - $2 build ###" - make clean && make -s -j"$(nproc)" CC="$cc" CFLAGS="$CFLAGS" LDFLAGS="$CFLAGS" OPT_DEP_DEFAULT="$1" - done - } - # try all combination with tcc # - etc/woodpecker/try-all-builds.sh - # full-build with gcc and clang # - build "1" "full" - # ensure minimal-build works without opt deps installed - apk del libxft libxft-dev libexif libexif-dev >/dev/null - build "0" "minimal" diff --git a/source/nsxiv/etc/woodpecker/clang-tidy-checks b/source/nsxiv/etc/woodpecker/clang-tidy-checks deleted file mode 100644 index c326eef2b..000000000 --- a/source/nsxiv/etc/woodpecker/clang-tidy-checks +++ /dev/null @@ -1,16 +0,0 @@ -# checks -clang-analyzer-*,clang-diagnostic-*,bugprone-*,performance-*,modernize-* -misc-*,android-cloexec-*,llvm-include-order --readability-*,readability-duplicate-include,readability-misleading-indentation - -# silence --misc-unused-parameters,-misc-include-cleaner,-misc-no-recursion --bugprone-easily-swappable-parameters,-bugprone-narrowing-conversions,-bugprone-incorrect-roundings --bugprone-implicit-widening-of-multiplication-result,-bugprone-integer-division --android-cloexec-fopen,-android-cloexec-pipe,-cert-err33-c --bugprone-assignment-in-if-condition --bugprone-suspicious-realloc-usage --bugprone-switch-missing-default-case - -# false positive warnings --clang-analyzer-valist.Uninitialized diff --git a/source/nsxiv/etc/woodpecker/spell.yml b/source/nsxiv/etc/woodpecker/spell.yml deleted file mode 100644 index 78ce57f8b..000000000 --- a/source/nsxiv/etc/woodpecker/spell.yml +++ /dev/null @@ -1,13 +0,0 @@ -when: - branch: master - -# NOTE: codespell not available on stable alpine, grab it from pip -steps: - spell-check: - image: alpine:edge - commands: | - apk add --no-cache python3 py3-pip git - python3 -m venv ~/py3-venv - . ~/py3-venv/bin/activate - pip install codespell - git ls-files | sed '/\.png$/d' | xargs codespell diff --git a/source/nsxiv/etc/woodpecker/try-all-builds.sh b/source/nsxiv/etc/woodpecker/try-all-builds.sh deleted file mode 100755 index 89e41f198..000000000 --- a/source/nsxiv/etc/woodpecker/try-all-builds.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -# -# Shell script that checks for all possible build combination with TCC. -# Usage: call the script while in the nsxiv root directory - -set -- $(grep -o '^HAVE_[[:alpha:]]* ' config.mk) -CFLAGS="$(sed '/^#/d' etc/woodpecker/CFLAGS | paste -d ' ' -s)" -z=$(echo "2 ^ $#" | bc) - -print_opt_name() { - shift "$(( $1 + 1 ))" - printf "%s=" "$1" -} - -print_opt_arg() { - bn=$(echo "$1 / (2 ^ $2)" | bc) - printf "%d " $(( bn % 2 )) -} - -n=0 -while [ "$n" -lt "$z" ]; do - i=0 - while [ "$i" -lt "$#" ]; do - print_opt_name "$i" "$@" - print_opt_arg "$n" "$i" - i=$((i + 1)) - done | tee "/dev/stderr" | ( - make clean - if ! xargs make -j"$(nproc)" CC=tcc CFLAGS="$CFLAGS" LDFLAGS="$CFLAGS"; then - echo "[FAILED]" >&2 - exit 1 - else - echo "[SUCCESS]" >&2 - fi - ) - [ "$?" -ne 0 ] && exit "$?" - n=$((n + 1)) -done >/dev/null diff --git a/source/nsxiv/icon/128x128.png b/source/nsxiv/icon/128x128.png deleted file mode 100644 index d45250b80769ef098cf165978d2cea7df50c38d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1066 zcmV+_1l9YAP)z-$aHKMI{wf{72qFLgKmY)M00001 z0007jrbPb#1^K+OOb3Kl{g?rQsD88np;SLkfPktWAwXEwj}0KW>i432EM@8Dqw+V* zTsqEmfc-4LHR)z?j2HmSO0PsBYyc~zmz4+@z(w`5Ed1V<9&cSa-~(X(*p2+x_deb2 za_I*QvN)9(1klgmq58uH?ifY`sAce0{b2w{3}yeGGZSa2{y;8Xf0e#GbwHLwKX0eU z`5#F~N?%fa7u`?|&^PeU;JNONs&9*-7C_#>1A|E24N7l?p)SCHfeQwyy0#-PoxAS};N6GUnv?6e6qFuZ(!#F0UG&xXnxSZ7EaEq_N%Aa0PI zRyJiQ+6H>zJ{>J{j3DPM~cNSpZ<|OLjoh z>Q`<69o&u00a*Kz9bi!X`pe(-HV|SHB&>Zc9bi^{CDT9LCP-NO@*QAYeTBU+2t4r>Y}to>j*fa-G=6yX#~SpIYX08uZ0 k2BR0px_Qq z7sn8d^T{DW28jo_-`%rcyC#F{xsl-n4hha%4SWq!{|+;#Nl#We=vl*8$6>Ef&3%GR zDP_OYltmVt(~~bWI64W3I=_6Ts&?5%zJxc`FE;z#+Cw2-XXbv7%&OWd=)^q1==X+2 zZ)80tW^{^fy0qkgd%?G(v-$gUxtuaCgqrd^pVEF} zK`v2DQv46s{P`y^ectBlZxpI#D6DFHr)U)M@hI~$RRFVdQ I&MBb@0P5yqCjbBd diff --git a/source/nsxiv/icon/32x32.png b/source/nsxiv/icon/32x32.png deleted file mode 100644 index e18ccf8f2d085ddfd2acd32f635f5649207734c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 421 zcmV;W0b2fvP)ExuMo0ouY+JY=%n&*Qrh{~M0fYh3MtB5uoJDB%bYI?O1P78p*?8Az(cT8j$ z4KW?gMzaQC0mP0Ly?{>#Ho-+iORTV(m=yR3k9`+!J~*~02)hM95@-jIWH)(3OrYVi z32q0owW2FrRQksiAeqg_xrSBX?=voB#3J<#t3l^)@B@*d=0`Mf2@wG{nd6TyYGM!B zmERwtiTpex0z^3DV7(j=VTxPDtz$w9AN&B19T01PAwz6F0D2;vQy!}wc%tX*H9H1| zLs)V$G0rEyutPRoSJjZhfC760$pYHB9ZO+{=6uYwM207rND7?6Sm6KwqI>o zWy9Vcno}$VRgiS zIf$TZ=;|P1T!W}w@f23%6@w%J!^Gy%a%j{+gSf>=WNxBU2vO)7>W9$HO9&sD=J^by zgOHtq35a(bW0_MIgtg}0Dq1)N4(5H90+Q3KjfhP{AxdGia}6A1ww?nbqCyA)%s#9r zd&fjwcR<1h6JQN3bO%A)1SDJZ0zMtsgeE0gVnxvUq`*hSyY=GD2geo#5tZ#%et(FT zhlz0wC_L870TGc7(pOioid#oi2myhvs$n>%JcP||@`mgTNHENR;RGZhK}8@b0K*Bw z_<2SI4Uv$Bhzm1FP?29!li@5p5eIh@VF$qjiE?KV6I4vN5)i4OMN&`!6AdKYkXgCn z48G$Oxpf*X-a`{j48ekzbU`<#`Vv6d4#Dvu~ x0~I_&q}M@V@K6|QOoJz~Xc%Rb1k#{OLjcMm#ggivpy2=j002ovPDHLkV1kL<$SeQ= diff --git a/source/nsxiv/icon/64x64.png b/source/nsxiv/icon/64x64.png deleted file mode 100644 index 3ce0d279c81f392404ef1c9dd92bb540678a8593..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 620 zcmV-y0+aoTP)00009a7bBm000XT z000XT0n*)m`~Uy~9!W$&RA}Dq**!}GF&GE%UZ{$TlP(Tg6>(Cjv<@9aoJG{dt)L&k zNxC@d+8&S(o=IrsKdY#GKkR)wg)E@0 z0ZvC}KE3ICW*1t!5Y%o1`)(^3m%5FbKhky)8#ClaPa&b`#&tjEl}i%{%HymDlHu1~8(7NSxZ-j{EHB z`7o*IUe$m4@pX4|N(KPSkVx+V+~Q=A09?~w`?XJ$Y -#include -#include -#include -#include - -/* macros */ -#define ARRLEN(a) (sizeof(a) / sizeof((a)[0])) - -/* globals */ -static unsigned long palette[16] = {0}; -static unsigned int palette_size = 0; -static unsigned int run_column = 0; -static unsigned int icon_sizes[16] = {0}; -static unsigned int icon_sizes_size = 0; - -/* functions */ -static void error(int eval, int err, const char *fmt, ...) -{ - va_list ap; - - fflush(stdout); - va_start(ap, fmt); - if (fmt) - vfprintf(stderr, fmt, ap); - va_end(ap); - if (err) - fprintf(stderr, "%s%s", fmt ? ": " : "", strerror(err)); - fputc('\n', stderr); - - if (eval) - exit(eval); -} - -static unsigned long color_to_ulong(Imlib_Color color) -{ - return (color.alpha << 24 & 0xff000000) | - (color.red << 16 & 0x00ff0000) | - (color.green << 8 & 0x0000ff00) | - (color.blue & 0x000000ff); -} - -static int to_palette(unsigned long color) -{ - unsigned int i; - - for (i = 0; i < palette_size; i++) { - if (palette[i] == color) - return i; - } - if (palette_size + 1 == 16) - error(EXIT_FAILURE, 0, "Error: More than 16 colors in palette"); - palette[palette_size] = color; - - return palette_size++; -} - -static void print_run(int color, unsigned int run_length) -{ - while (run_length > 0) { - int x = run_length / 16 >= 1 ? 16 : run_length; - - printf("0x%02x, ", (x - 1) << 4 | color); - run_length -= x; - if (++run_column % 12 == 0) - printf("\n\t"); - } -} - -static void print_palette(void) -{ - unsigned int i; - - printf("static const unsigned long icon_colors[] = {\n\t"); - for (i = 0; i < palette_size; i++) { - printf("0x%08lx, ", palette[i]); - if (i % 4 == 3) - printf("\n\t"); - } - printf("\n};\n\n"); -} - -static void print_icon_array(void) -{ - unsigned int i; - - printf("static const icon_data_t icons[] = {\n"); - for (i = 0; i < icon_sizes_size; i++) - printf("\tICON_(%d),\n", icon_sizes[i]); - printf("};\n\n"); -} - -static unsigned int print_encoded_image(const char *path) -{ - Imlib_Image image; - Imlib_Color color; - unsigned int width, height, x, y; - unsigned int run_length = 1; - int currentcolor = 0; - int lastcolor = -1; - - image = imlib_load_image(path); - - if (image == NULL) - error(EXIT_FAILURE, 0, "Error loading image: %s", path); - - imlib_context_set_image(image); - - width = imlib_image_get_width(); - height = imlib_image_get_height(); - - if (width != height) - error(EXIT_FAILURE, 0, "Image is not square: %s", path); - - printf("static const unsigned char icon_data_%d[] = {\n\t", width); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - imlib_image_query_pixel(x, y, &color); - currentcolor = to_palette(color_to_ulong(color)); - if (currentcolor != lastcolor) { - if (lastcolor != -1) - print_run(lastcolor, run_length); - run_length = 1; - } else { - run_length++; - } - lastcolor = currentcolor; - } - } - print_run(lastcolor, run_length); - printf("\n};\n\n"); - - imlib_free_image(); - - return width; -} - -int main(int argc, char **argv) -{ - unsigned int img_size = 0; - unsigned int i; - - if (argc < 2) - error(EXIT_FAILURE, 0, "No icons provided"); - else if (argc-1 > ARRLEN(icon_sizes)) - error(EXIT_FAILURE, 0, "Too many icons"); - - for (i = 1; i < argc; i++) { - img_size = print_encoded_image(argv[i]); - run_column = 0; - icon_sizes[icon_sizes_size++] = img_size; - } - print_palette(); - print_icon_array(); - - return EXIT_SUCCESS; -} diff --git a/source/nsxiv/image.c b/source/nsxiv/image.c deleted file mode 100644 index a138f957a..000000000 --- a/source/nsxiv/image.c +++ /dev/null @@ -1,790 +0,0 @@ -/* Copyright 2011-2020 Bert Muennich - * Copyright 2021-2023 nsxiv contributors - * - * This file is a part of nsxiv. - * - * nsxiv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * nsxiv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with nsxiv. If not, see . - */ - -#include "nsxiv.h" -#define INCLUDE_IMAGE_CONFIG -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - -#if HAVE_LIBEXIF -#include -#endif - -#if HAVE_IMLIB2_MULTI_FRAME -enum { DEF_ANIM_DELAY = 75 }; -#endif - -#define ZOOM_MIN (zoom_levels[0] / 100) -#define ZOOM_MAX (zoom_levels[ARRLEN(zoom_levels) - 1] / 100) - -static int calc_cache_size(void) -{ - long cache, pages = -1, page_size = -1; - - if (CACHE_SIZE_MEM_PERCENTAGE <= 0) - return 0; -#ifdef _SC_PHYS_PAGES /* _SC_PHYS_PAGES isn't POSIX */ - pages = sysconf(_SC_PHYS_PAGES); - page_size = sysconf(_SC_PAGE_SIZE); -#endif - if (pages < 0 || page_size < 0) - return CACHE_SIZE_FALLBACK; - cache = (pages / 100) * CACHE_SIZE_MEM_PERCENTAGE; - cache *= page_size; - - return MIN(cache, CACHE_SIZE_LIMIT); -} - -void img_init(img_t *img, win_t *win) -{ - imlib_context_set_display(win->env.dpy); - imlib_context_set_visual(win->env.vis); - imlib_context_set_colormap(win->env.cmap); - imlib_set_cache_size(calc_cache_size()); - - img->im = NULL; - img->win = win; - img->scalemode = options->scalemode; - img->zoom = options->zoom; - img->zoom = MAX(img->zoom, ZOOM_MIN); - img->zoom = MIN(img->zoom, ZOOM_MAX); - img->checkpan = false; - img->dirty = false; - img->anti_alias = options->anti_alias; - img->alpha_layer = options->alpha_layer; - img->autoreload_pending = false; - img->multi.cap = img->multi.cnt = 0; - img->multi.animate = options->animate; - img->multi.framedelay = options->framerate > 0 ? 1000 / options->framerate : 0; - img->multi.length = 0; - - img->cmod = imlib_create_color_modifier(); - imlib_context_set_color_modifier(img->cmod); - img->brightness = 0; - img->contrast = 0; - img_change_color_modifier(img, options->gamma, &img->gamma); - - img->ss.on = options->slideshow > 0; - img->ss.delay = options->slideshow > 0 ? options->slideshow : SLIDESHOW_DELAY * 10u; -} - -#if HAVE_LIBEXIF -void exif_auto_orientate(const fileinfo_t *file) -{ - ExifData *ed; - ExifEntry *entry; - int byte_order, orientation = 0; - - if ((ed = exif_data_new_from_file(file->path)) == NULL) - return; - byte_order = exif_data_get_byte_order(ed); - entry = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION); - if (entry != NULL) - orientation = exif_get_short(entry->data, byte_order); - exif_data_unref(ed); - - switch (orientation) { - case 5: - imlib_image_orientate(1); - /* fall through */ - case 2: - imlib_image_flip_vertical(); - break; - case 3: - imlib_image_orientate(2); - break; - case 7: - imlib_image_orientate(1); - /* fall through */ - case 4: - imlib_image_flip_horizontal(); - break; - case 6: - imlib_image_orientate(1); - break; - case 8: - imlib_image_orientate(3); - break; - } -} -#endif - -#if HAVE_IMLIB2_MULTI_FRAME -static void img_area_clear(int x, int y, int w, int h) -{ - assert(x >= 0 && y >= 0); - assert(w > 0 && h > 0); - imlib_image_set_has_alpha(1); - imlib_context_set_blend(0); - imlib_context_set_color(0, 0, 0, 0); - imlib_image_fill_rectangle(x, y, w, h); -} - -static bool img_load_multiframe(img_t *img, const fileinfo_t *file) -{ - unsigned int n, fcnt; - Imlib_Image blank; - Imlib_Frame_Info finfo; - int px, py, pw, ph, pflag; - multi_img_t *m = &img->multi; - - imlib_context_set_image(img->im); - imlib_image_get_frame_info(&finfo); - if ((fcnt = finfo.frame_count) <= 1 || !(finfo.frame_flags & IMLIB_IMAGE_ANIMATED)) - return false; - img->w = finfo.canvas_w; - img->h = finfo.canvas_h; - - if (fcnt > m->cap) { - m->cap = fcnt; - m->frames = erealloc(m->frames, m->cap * sizeof(*m->frames)); - } - - if ((blank = imlib_create_image(img->w, img->h)) == NULL) { - error(0, 0, "%s: couldn't create image", file->name); - return false; - } - imlib_context_set_image(blank); - img_area_clear(0, 0, img->w, img->h); - - imlib_context_set_dither(0); - imlib_context_set_anti_alias(0); - imlib_context_set_color_modifier(NULL); - imlib_context_set_operation(IMLIB_OP_COPY); - - /* - * Imlib2 gives back a "raw frame", we need to blend it on top of the - * previous frame ourselves if necessary to get the fully decoded frame. - */ - pflag = m->length = m->cnt = m->sel = 0; - px = py = pw = ph = 0; - for (n = 1; n <= fcnt; ++n) { - Imlib_Image frame, canvas; - int sx, sy, sw, sh; - bool has_alpha; - - imlib_context_set_image(m->cnt < 1 ? blank : m->frames[m->cnt - 1].im); - canvas = imlib_clone_image(); - if ((frame = imlib_load_image_frame(file->path, n)) != NULL) { - imlib_context_set_image(frame); - imlib_image_set_changes_on_disk(); /* see img_load() for rationale */ - imlib_image_get_frame_info(&finfo); - } - /* NOTE: the underlying file can end up changing during load. - * so check if frame_count, w, h are all still the same or not. - */ - if (canvas == NULL || frame == NULL || finfo.frame_count != (int)fcnt || - finfo.canvas_w != img->w || finfo.canvas_h != img->h) - { - img_free(frame, false); - img_free(canvas, false); - error(0, 0, "%s: failed to load frame %d", file->name, n); - break; - } - - sx = finfo.frame_x; - sy = finfo.frame_y; - sw = finfo.frame_w; - sh = finfo.frame_h; - has_alpha = imlib_image_has_alpha(); - - imlib_context_set_image(canvas); - /* the dispose flags are explained in Imlib2's header */ - if (pflag & IMLIB_FRAME_DISPOSE_CLEAR) { - img_area_clear(px, py, pw, ph); - } else if (pflag & IMLIB_FRAME_DISPOSE_PREV) { - Imlib_Image p = m->cnt < 2 ? blank : m->frames[m->cnt - 2].im; - assert(m->cnt > 0); - img_area_clear(0, 0, img->w, img->h); - imlib_blend_image_onto_image(p, 1, px, py, pw, ph, px, py, pw, ph); - } - pflag = finfo.frame_flags; - if (pflag & (IMLIB_FRAME_DISPOSE_CLEAR | IMLIB_FRAME_DISPOSE_PREV)) { - /* remember these so we can "dispose" them before blending next frame */ - px = sx; - py = sy; - pw = sw; - ph = sh; - } - assert(imlib_context_get_operation() == IMLIB_OP_COPY); - imlib_image_set_has_alpha(has_alpha); - imlib_context_set_blend(!!(finfo.frame_flags & IMLIB_FRAME_BLEND)); - imlib_blend_image_onto_image(frame, has_alpha, 0, 0, sw, sh, sx, sy, sw, sh); - m->frames[m->cnt].im = canvas; - m->frames[m->cnt].delay = finfo.frame_delay ? finfo.frame_delay : DEF_ANIM_DELAY; - m->length += m->frames[m->cnt].delay; - m->cnt++; - img_free(frame, false); - } - img_free(blank, false); - imlib_context_set_color_modifier(img->cmod); /* restore cmod */ - - if (m->cnt > 1) { - img_free(img->im, false); - img->im = m->frames[0].im; - } else if (m->cnt == 1) { - img_free(m->frames[0].im, false); - m->cnt = 0; - } - imlib_context_set_image(img->im); - return m->cnt > 0; -} -#endif /* HAVE_IMLIB2_MULTI_FRAME */ - -Imlib_Image img_open(const fileinfo_t *file) -{ - struct stat st; - Imlib_Image im = NULL; - - if (access(file->path, R_OK) == 0 && - stat(file->path, &st) == 0 && S_ISREG(st.st_mode) && -#if HAVE_IMLIB2_MULTI_FRAME - (im = imlib_load_image_frame(file->path, 1)) != NULL) -#else - (im = imlib_load_image_immediately(file->path)) != NULL) -#endif - { - imlib_context_set_image(im); - } - /* UPGRADE: Imlib2 v1.10.0: better error reporting with - * imlib_get_error() + imlib_strerror() */ - if (im == NULL && (file->flags & FF_WARN)) - error(0, 0, "%s: Error opening image", file->name); - return im; -} - -bool img_load(img_t *img, const fileinfo_t *file) -{ - const char *fmt; - bool animated = false; - - if ((img->im = img_open(file)) == NULL) - return false; - - /* ensure that the image's timestamp is checked when loading from cache - * to avoid issues like: https://codeberg.org/nsxiv/nsxiv/issues/436 - */ - imlib_image_set_changes_on_disk(); - -/* UPGRADE: Imlib2 v1.7.5: remove these exif related ifdefs */ -/* since v1.7.5, Imlib2 can parse exif orientation from jpeg files. - * this version also happens to be the first one which defines the - * IMLIB2_VERSION macro. - */ -#if HAVE_LIBEXIF && !defined(IMLIB2_VERSION) - exif_auto_orientate(file); -#endif - -#if HAVE_IMLIB2_MULTI_FRAME - animated = img_load_multiframe(img, file); -#endif - - (void)fmt; /* maybe unused */ -#if HAVE_LIBEXIF && defined(IMLIB2_VERSION) - if ((fmt = imlib_image_format()) != NULL) { - if (!STREQ(fmt, "jpeg") && !STREQ(fmt, "jpg")) - exif_auto_orientate(file); - } -#endif - /* for animated images, we want the _canvas_ width/height, which - * img_load_multiframe() sets already. - */ - if (!animated) { - img->w = imlib_image_get_width(); - img->h = imlib_image_get_height(); - } - img->checkpan = true; - img->dirty = true; - - return true; -} - -CLEANUP void img_free(Imlib_Image im, bool decache) -{ - if (im != NULL) { - imlib_context_set_image(im); - decache ? imlib_free_image_and_decache() : imlib_free_image(); - } -} - -CLEANUP void img_close(img_t *img, bool decache) -{ - unsigned int i; - - if (img->multi.cnt > 0) { - for (i = 0; i < img->multi.cnt; i++) - img_free(img->multi.frames[i].im, decache); - /* NOTE: the above only decaches the "composed frames", - * and not the "raw frame" that's associated with the file. - * which leads to issues like: https://codeberg.org/nsxiv/nsxiv/issues/456 - */ -#if HAVE_IMLIB2_MULTI_FRAME - #if IMLIB2_VERSION >= IMLIB2_VERSION_(1, 12, 0) - if (decache) - imlib_image_decache_file(files[fileidx].path); - #else /* UPGRADE: Imlib2 v1.12.0: remove this hack */ - /* HACK: try to reload all the frames and forcefully decache them - * if imlib_image_decache_file() isn't available. - */ - for (i = 0; decache && i < img->multi.cnt; i++) - img_free(imlib_load_image_frame(files[fileidx].path, i + 1), true); - #endif -#endif - img->multi.cnt = 0; - img->im = NULL; - } else if (img->im != NULL) { - img_free(img->im, decache); - img->im = NULL; - } -} - -static void img_check_pan(img_t *img, bool moved) -{ - win_t *win; - float w, h, ox, oy; - - win = img->win; - w = img->w * img->zoom; - h = img->h * img->zoom; - ox = img->x; - oy = img->y; - - if (w < win->w) - img->x = (win->w - w) / 2; - else if (img->x > 0) - img->x = 0; - else if (img->x + w < win->w) - img->x = win->w - w; - if (h < win->h) - img->y = (win->h - h) / 2; - else if (img->y > 0) - img->y = 0; - else if (img->y + h < win->h) - img->y = win->h - h; - - if (!moved && (ox != img->x || oy != img->y)) - img->dirty = true; -} - -static bool img_fit(img_t *img) -{ - float z, zw, zh; - - if (img->scalemode == SCALE_ZOOM) - return false; - - zw = (float)img->win->w / (float)img->w; - zh = (float)img->win->h / (float)img->h; - - switch (img->scalemode) { - case SCALE_FILL: - z = MAX(zw, zh); - break; - case SCALE_WIDTH: - z = zw; - break; - case SCALE_HEIGHT: - z = zh; - break; - default: - z = MIN(zw, zh); - break; - } - z = MIN(z, img->scalemode == SCALE_DOWN ? 1.0 : ZOOM_MAX); - - if (ABS(img->zoom - z) > 1.0 / MAX(img->w, img->h)) { - img->zoom = z; - img->dirty = true; - return true; - } else { - return false; - } -} - -void img_render(img_t *img) -{ - win_t *win; - int sx, sy, sw, sh; - int dx, dy, dw, dh; - Imlib_Image bg; - - win = img->win; - img_fit(img); - - if (img->checkpan) { - img_check_pan(img, false); - img->checkpan = false; - } - - if (!img->dirty) - return; - - /* calculate source and destination offsets: - * - part of image drawn on full window, or - * - full image drawn on part of window - */ - if (img->x <= 0) { - sx = -img->x / img->zoom + 0.5; - sw = win->w / img->zoom; - dx = 0; - dw = win->w; - } else { - sx = 0; - sw = img->w; - dx = img->x; - dw = MAX(img->w * img->zoom, 1); - } - if (img->y <= 0) { - sy = -img->y / img->zoom + 0.5; - sh = win->h / img->zoom; - dy = win->bar.top ? win->bar.h : 0; - dh = win->h; - } else { - sy = 0; - sh = img->h; - dy = img->y + (win->bar.top ? win->bar.h : 0); - dh = MAX(img->h * img->zoom, 1); - } - - win_clear(win); - - imlib_context_set_image(img->im); - imlib_context_set_anti_alias(img->anti_alias); - imlib_context_set_drawable(win->buf.pm); - - /* manual blending, for performance reasons. - * see https://phab.enlightenment.org/T8969#156167 for more details. - */ - if (imlib_image_has_alpha()) { - if ((bg = imlib_create_image(dw, dh)) == NULL) { - error(0, ENOMEM, "Failed to create image"); - goto fallback; - } - imlib_context_set_image(bg); - imlib_image_set_has_alpha(0); - - if (img->alpha_layer) { - int i, c, r; - uint32_t col[2] = { 0xFF666666, 0xFF999999 }; - uint32_t *data = imlib_image_get_data(); - - for (r = 0; r < dh; r++) { - i = r * dw; - if (r == 0 || r == 8) { - for (c = 0; c < dw; c++) - data[i++] = col[!(c & 8) ^ !r]; - } else { - memcpy(&data[i], &data[(r & 8) * dw], dw * sizeof(data[0])); - } - } - imlib_image_put_back_data(data); - } else { - XColor c = win->win_bg; - imlib_context_set_color(c.red >> 8, c.green >> 8, c.blue >> 8, 0xFF); - imlib_image_fill_rectangle(0, 0, dw, dh); - } - imlib_context_set_blend(1); - imlib_context_set_operation(IMLIB_OP_COPY); - imlib_blend_image_onto_image(img->im, 0, sx, sy, sw, sh, 0, 0, dw, dh); - imlib_context_set_color_modifier(NULL); - imlib_render_image_on_drawable(dx, dy); - imlib_free_image(); - imlib_context_set_color_modifier(img->cmod); - } else { -fallback: - imlib_render_image_part_on_drawable_at_size(sx, sy, sw, sh, dx, dy, dw, dh); - } - img->dirty = false; -} - -bool img_fit_win(img_t *img, scalemode_t sm) -{ - float oz; - - oz = img->zoom; - img->scalemode = sm; - - if (img_fit(img)) { - img->x = img->win->w / 2 - (img->win->w / 2 - img->x) * img->zoom / oz; - img->y = img->win->h / 2 - (img->win->h / 2 - img->y) * img->zoom / oz; - img->checkpan = true; - return true; - } else { - return false; - } -} - -bool img_zoom_to(img_t *img, float z) -{ - int x, y; - if (ZOOM_MIN <= z && z <= ZOOM_MAX) { - win_cursor_pos(img->win, &x, &y); - if (x < 0 || (unsigned int)x >= img->win->w || - y < 0 || (unsigned int)y >= img->win->h) - { - x = img->win->w / 2; - y = img->win->h / 2; - } - img->x = x - (x - img->x) * z / img->zoom; - img->y = y - (y - img->y) * z / img->zoom; - img->zoom = z; - img->scalemode = SCALE_ZOOM; - img->dirty = img->checkpan = true; - return true; - } else { - return false; - } -} - -bool img_zoom(img_t *img, int d) -{ - int i = d > 0 ? 0 : (int)ARRLEN(zoom_levels) - 1; - while (i >= 0 && i < (int)ARRLEN(zoom_levels) && - (d > 0 ? zoom_levels[i] / 100 <= img->zoom : zoom_levels[i] / 100 >= img->zoom)) - { - i += d; - } - i = MIN(MAX(i, 0), (int)ARRLEN(zoom_levels) - 1); - return img_zoom_to(img, zoom_levels[i] / 100); -} - -bool img_pos(img_t *img, float x, float y) -{ - float ox, oy; - - ox = img->x; - oy = img->y; - - img->x = x; - img->y = y; - - img_check_pan(img, true); - - if (ox != img->x || oy != img->y) { - img->dirty = true; - return true; - } else { - return false; - } -} - -static bool img_move(img_t *img, float dx, float dy) -{ - return img_pos(img, img->x + dx, img->y + dy); -} - -bool img_pan(img_t *img, direction_t dir, int d) -{ - /* d < 0: screen-wise - * d = 0: 1/PAN_FRACTION of screen - * d > 0: num of pixels - */ - float x, y; - - if (d > 0) { - x = y = MAX(1, (float)d * img->zoom); - } else { - x = img->win->w / (d < 0 ? 1 : PAN_FRACTION); - y = img->win->h / (d < 0 ? 1 : PAN_FRACTION); - } - - switch (dir) { - case DIR_LEFT: - return img_move(img, x, 0.0); - case DIR_RIGHT: - return img_move(img, -x, 0.0); - case DIR_UP: - return img_move(img, 0.0, y); - case DIR_DOWN: - return img_move(img, 0.0, -y); - } - return false; -} - -bool img_pan_center(img_t *img) -{ - float x, y; - x = (img->win->w - img->w * img->zoom) / 2.0; - y = (img->win->h - img->h * img->zoom) / 2.0; - return img_pos(img, x, y); -} - -bool img_pan_edge(img_t *img, direction_t dir) -{ - float ox, oy; - - ox = img->x; - oy = img->y; - - if (dir & DIR_LEFT) - img->x = 0; - if (dir & DIR_RIGHT) - img->x = img->win->w - img->w * img->zoom; - if (dir & DIR_UP) - img->y = 0; - if (dir & DIR_DOWN) - img->y = img->win->h - img->h * img->zoom; - - img_check_pan(img, true); - - if (ox != img->x || oy != img->y) { - img->dirty = true; - return true; - } else { - return false; - } -} - -void img_rotate(img_t *img, degree_t d) -{ - unsigned int i, tmp; - float ox, oy; - - imlib_context_set_image(img->im); - imlib_image_orientate(d); - - for (i = 0; i < img->multi.cnt; i++) { - if (i != img->multi.sel) { - imlib_context_set_image(img->multi.frames[i].im); - imlib_image_orientate(d); - } - } - if (d == DEGREE_90 || d == DEGREE_270) { - ox = d == DEGREE_90 ? img->x : img->win->w - img->x - img->w * img->zoom; - oy = d == DEGREE_270 ? img->y : img->win->h - img->y - img->h * img->zoom; - - img->x = oy + (img->win->w - img->win->h) / 2; - img->y = ox + (img->win->h - img->win->w) / 2; - - tmp = img->w; - img->w = img->h; - img->h = tmp; - img->checkpan = true; - } - img->dirty = true; -} - -void img_flip(img_t *img, flipdir_t d) -{ - unsigned int i; - void (*imlib_flip_op[3])(void) = { - imlib_image_flip_horizontal, - imlib_image_flip_vertical, - imlib_image_flip_diagonal - }; - - d = (d & (FLIP_HORIZONTAL | FLIP_VERTICAL)) - 1; - - if (d < 0 || d >= ARRLEN(imlib_flip_op)) - return; - - imlib_context_set_image(img->im); - imlib_flip_op[d](); - - for (i = 0; i < img->multi.cnt; i++) { - if (i != img->multi.sel) { - imlib_context_set_image(img->multi.frames[i].im); - imlib_flip_op[d](); - } - } - img->dirty = true; -} - -void img_toggle_antialias(img_t *img) -{ - img->anti_alias = !img->anti_alias; - imlib_context_set_image(img->im); - imlib_context_set_anti_alias(img->anti_alias); - img->dirty = true; -} - -static double steps_to_range(int d, double max, double offset) -{ - return offset + d * ((d <= 0 ? 1.0 : (max - 1.0)) / CC_STEPS); -} - -void img_update_color_modifiers(img_t *img) -{ - assert(imlib_context_get_color_modifier() == img->cmod); - imlib_reset_color_modifier(); - - if (img->gamma != 0) - imlib_modify_color_modifier_gamma(steps_to_range(img->gamma, GAMMA_MAX, 1.0)); - if (img->brightness != 0) - imlib_modify_color_modifier_brightness(steps_to_range(img->brightness, BRIGHTNESS_MAX, 0.0)); - if (img->contrast != 0) - imlib_modify_color_modifier_contrast(steps_to_range(img->contrast, CONTRAST_MAX, 1.0)); - - img->dirty = true; -} - -bool img_change_color_modifier(img_t *img, int d, int *target) -{ - int value = d == 0 ? 0 : MIN(MAX(*target + d, -CC_STEPS), CC_STEPS); - - if (*target == value) - return false; - - *target = value; - img_update_color_modifiers(img); - return true; -} - -static bool img_frame_goto(img_t *img, int n) -{ - if (n < 0 || (unsigned int)n >= img->multi.cnt || (unsigned int)n == img->multi.sel) - return false; - - img->multi.sel = n; - img->im = img->multi.frames[n].im; - - imlib_context_set_image(img->im); - img->w = imlib_image_get_width(); - img->h = imlib_image_get_height(); - img->checkpan = true; - img->dirty = true; - - return true; -} - -bool img_frame_navigate(img_t *img, int d) -{ - if (img->multi.cnt == 0 || d == 0) - return false; - - d += img->multi.sel; - d = MAX(0, MIN(d, (int)img->multi.cnt - 1)); - - return img_frame_goto(img, d); -} - -bool img_frame_animate(img_t *img) -{ - if (img->multi.cnt > 0) - return img_frame_goto(img, (img->multi.sel + 1) % img->multi.cnt); - else - return false; -} diff --git a/source/nsxiv/main.c b/source/nsxiv/main.c deleted file mode 100644 index 0e1e15cfb..000000000 --- a/source/nsxiv/main.c +++ /dev/null @@ -1,1000 +0,0 @@ -/* Copyright 2011-2020 Bert Muennich - * Copyright 2021-2023 nsxiv contributors - * - * This file is a part of nsxiv. - * - * nsxiv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * nsxiv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with nsxiv. If not, see . - */ - -#include "nsxiv.h" -#define INCLUDE_MAPPINGS_CONFIG -#include "commands.h" -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define MODMASK(mask) (USED_MODMASK & (mask)) -#define BAR_SEP " " - -#define TV_DIFF(t1,t2) (((t1)->tv_sec - (t2)->tv_sec ) * 1000 + \ - ((t1)->tv_usec - (t2)->tv_usec) / 1000) -#define TV_ADD_MSEC(tv, t) \ - do { \ - (tv)->tv_sec += (t) / 1000; \ - (tv)->tv_usec += (t) % 1000 * 1000; \ - } while (0) - -typedef struct { - int err; - char *cmd; -} extcmd_t; - -/* these are not declared in nsxiv.h, as it causes too many -Wshadow warnings */ -arl_t arl; -img_t img; -tns_t tns; -win_t win; - -appmode_t mode; -fileinfo_t *files; -int filecnt, fileidx; -int alternate; -int markcnt; -int markidx; -int prefix; -const XButtonEvent *xbutton_ev; - -static void autoreload(void); - -static bool extprefix; -static bool resized = false; - -static struct { - extcmd_t f, ft; - int fd; - pid_t pid; -} info, wintitle; - -static struct { - extcmd_t f; - bool warned; -} keyhandler; - -static struct { - timeout_f handler; - struct timeval when; - bool active; -} timeouts[] = { - { autoreload }, - { redraw }, - { reset_cursor }, - { slideshow }, - { animate }, - { clear_resize }, -}; - -/* - * function implementations - */ - -static void cleanup(void) -{ - img_close(&img, false); - arl_cleanup(&arl); - tns_free(&tns); - win_close(&win); -} - -static bool xgetline(char **lineptr, size_t *n) -{ - ssize_t len = getdelim(lineptr, n, options->using_null ? '\0' : '\n', stdin); - if (!options->using_null && len > 0 && (*lineptr)[len - 1] == '\n') - (*lineptr)[len - 1] = '\0'; - return len > 0; -} - -static int fncmp(const void *a, const void *b) -{ - return strcoll(((fileinfo_t *)a)->name, ((fileinfo_t *)b)->name); -} - -static void check_add_file(const char *filename, bool given) -{ - char *path; - - if (*filename == '\0') - return; - - if (access(filename, R_OK) < 0 || - (path = realpath(filename, NULL)) == NULL) - { - if (given) - error(0, errno, "%s", filename); - return; - } - - if (fileidx == filecnt) { - filecnt *= 2; - files = erealloc(files, filecnt * sizeof(*files)); - memset(&files[filecnt / 2], 0, filecnt / 2 * sizeof(*files)); - } - - files[fileidx].name = estrdup(filename); - files[fileidx].path = path; - if (given) - files[fileidx].flags |= FF_WARN; - fileidx++; -} - -static void add_entry(const char *entry_name) -{ - int start; - char *filename; - struct stat fstats; - r_dir_t dir; - - if (stat(entry_name, &fstats) < 0) { - error(0, errno, "%s", entry_name); - return; - } - if (!S_ISDIR(fstats.st_mode)) { - check_add_file(entry_name, true); - } else { - if (r_opendir(&dir, entry_name, options->recursive) < 0) { - error(0, errno, "%s", entry_name); - return; - } - start = fileidx; - while ((filename = r_readdir(&dir, true)) != NULL) { - check_add_file(filename, false); - free(filename); - } - r_closedir(&dir); - if (fileidx - start > 1) - qsort(files + start, fileidx - start, sizeof(*files), fncmp); - } -} - -void remove_file(int n, bool manual) -{ - if (n < 0 || n >= filecnt) - return; - - if (filecnt == 1) { - if (!manual) - fprintf(stderr, "%s: no more files to display, aborting\n", progname); - exit(manual ? EXIT_SUCCESS : EXIT_FAILURE); - } - if (files[n].flags & FF_MARK) - markcnt--; - - if (files[n].path != files[n].name) - free((void *)files[n].path); - free((void *)files[n].name); - if (tns.thumbs != NULL) - tns_unload(&tns, n); - - if (n + 1 < filecnt) { - if (tns.thumbs != NULL) { - memmove(tns.thumbs + n, tns.thumbs + n + 1, - (filecnt - n - 1) * sizeof(*tns.thumbs)); - memset(tns.thumbs + filecnt - 1, 0, sizeof(*tns.thumbs)); - } - memmove(files + n, files + n + 1, (filecnt - n - 1) * sizeof(*files)); - } - filecnt--; - if (fileidx > n || fileidx == filecnt) - fileidx--; - if (alternate > n || alternate == filecnt) - alternate--; - if (markidx > n || markidx == filecnt) - markidx--; -} - -void set_timeout(timeout_f handler, int time, bool overwrite) -{ - unsigned int i; - - for (i = 0; i < ARRLEN(timeouts); i++) { - if (timeouts[i].handler == handler) { - if (!timeouts[i].active || overwrite) { - gettimeofday(&timeouts[i].when, 0); - TV_ADD_MSEC(&timeouts[i].when, time); - timeouts[i].active = true; - } - return; - } - } -} - -void reset_timeout(timeout_f handler) -{ - unsigned int i; - - for (i = 0; i < ARRLEN(timeouts); i++) { - if (timeouts[i].handler == handler) { - timeouts[i].active = false; - return; - } - } -} - -static bool check_timeouts(int *t) -{ - int i = 0, tdiff, tmin = -1; - struct timeval now; - - while (i < (int)ARRLEN(timeouts)) { - if (timeouts[i].active) { - gettimeofday(&now, 0); - tdiff = TV_DIFF(&timeouts[i].when, &now); - if (tdiff <= 0) { - timeouts[i].active = false; - if (timeouts[i].handler != NULL) - timeouts[i].handler(); - i = tmin = -1; - } else if (tmin < 0 || tdiff < tmin) { - tmin = tdiff; - } - } - i++; - } - if (tmin > 0 && t != NULL) - *t = tmin; - return tmin > 0; -} - -static void autoreload(void) -{ - if (img.autoreload_pending) { - img_close(&img, true); - /* load_image() sets autoreload_pending to false */ - load_image(fileidx); - redraw(); - } else { - assert(!"unreachable"); - } -} - -static void kill_close(pid_t pid, int *fd) -{ - if (fd != NULL && *fd != -1) { - kill(pid, SIGTERM); - close(*fd); - *fd = -1; - } -} - -static void close_title(void) -{ - kill_close(wintitle.pid, &wintitle.fd); -} - -static void read_title(void) -{ - ssize_t n; - char buf[512]; - - if ((n = read(wintitle.fd, buf, sizeof(buf) - 1)) > 0) { - buf[n] = '\0'; - win_set_title(&win, buf, n); - } - close_title(); -} - -static void open_title(void) -{ - char *argv[8]; - char w[12] = "", h[12] = "", z[12] = "", fidx[12], fcnt[12]; - - if (wintitle.f.err) - return; - - close_title(); - if (mode == MODE_IMAGE) { - snprintf(w, ARRLEN(w), "%d", img.w); - snprintf(h, ARRLEN(h), "%d", img.h); - snprintf(z, ARRLEN(z), "%d", (int)(img.zoom * 100)); - } - snprintf(fidx, ARRLEN(fidx), "%d", fileidx + 1); - snprintf(fcnt, ARRLEN(fcnt), "%d", filecnt); - construct_argv(argv, ARRLEN(argv), wintitle.f.cmd, files[fileidx].path, - fidx, fcnt, w, h, z, NULL); - if ((wintitle.pid = spawn(&wintitle.fd, NULL, argv)) > 0) - fcntl(wintitle.fd, F_SETFL, O_NONBLOCK); -} - -void close_info(void) -{ - kill_close(info.pid, &info.fd); -} - -void open_info(void) -{ - char *argv[6], w[12] = "", h[12] = ""; - char *cmd = mode == MODE_IMAGE ? info.f.cmd : info.ft.cmd; - bool ferr = mode == MODE_IMAGE ? info.f.err : info.ft.err; - - if (ferr || info.fd >= 0 || win.bar.h == 0) - return; - win.bar.l.buf[0] = '\0'; - if (mode == MODE_IMAGE) { - snprintf(w, sizeof(w), "%d", img.w); - snprintf(h, sizeof(h), "%d", img.h); - } - construct_argv(argv, ARRLEN(argv), cmd, files[fileidx].name, w, h, - files[fileidx].path, NULL); - if ((info.pid = spawn(&info.fd, NULL, argv)) > 0) - fcntl(info.fd, F_SETFL, O_NONBLOCK); -} - -static void read_info(void) -{ - ssize_t i, n; - - if ((n = read(info.fd, win.bar.l.buf, win.bar.l.size - 1)) > 0) { - win.bar.l.buf[n] = '\0'; - for (i = 0; i < n; ++i) { - if (win.bar.l.buf[i] == '\n') - win.bar.l.buf[i] = ' '; - } - win_draw(&win); - } - close_info(); -} - -void load_image(int new) -{ - bool prev = new < fileidx; - static int current; - - if (new < 0 || new >= filecnt) - return; - - if (win.xwin != None) - win_set_cursor(&win, CURSOR_WATCH); - reset_timeout(autoreload); - reset_timeout(slideshow); - - if (new != current) { - alternate = current; - img.autoreload_pending = false; - } - - img_close(&img, false); - while (!img_load(&img, &files[new])) { - remove_file(new, false); - if (new >= filecnt) - new = filecnt - 1; - else if (new > 0 && prev) - new -= 1; - } - files[new].flags &= ~FF_WARN; - fileidx = current = new; - - arl_add(&arl, files[fileidx].path); - - if (img.multi.cnt > 0 && img.multi.animate) - set_timeout(animate, img.multi.frames[img.multi.sel].delay, true); - else - reset_timeout(animate); -} - -bool mark_image(int n, bool on) -{ - markidx = n; - if (!!(files[n].flags & FF_MARK) != on) { - files[n].flags ^= FF_MARK; - markcnt += on ? 1 : -1; - if (mode == MODE_THUMB) - tns_mark(&tns, n, on); - return true; - } - return false; -} - -static void bar_put(win_bar_t *bar, const char *fmt, ...) -{ - size_t len = bar->size - (bar->p - bar->buf), n; - va_list ap; - - va_start(ap, fmt); - n = vsnprintf(bar->p, len, fmt, ap); - bar->p += MIN(len, n); - va_end(ap); -} - -static void update_info(void) -{ - unsigned int i, fn, fw; - const char *mark; - win_bar_t *l = &win.bar.l, *r = &win.bar.r; - - static struct { - const char *filepath; - int fileidx; - float zoom; - appmode_t mode; - } prev; - - if (prev.fileidx != fileidx || prev.mode != mode || - (prev.filepath == NULL || !STREQ(prev.filepath, files[fileidx].path))) - { - close_info(); - open_info(); - open_title(); - } else if (mode == MODE_IMAGE && prev.zoom != img.zoom) { - open_title(); - } - - /* update bar contents */ - if (win.bar.h == 0 || extprefix) - return; - - free((char *)prev.filepath); - prev.filepath = estrdup(files[fileidx].path); - prev.fileidx = fileidx; - prev.zoom = img.zoom; - prev.mode = mode; - - for (fw = 0, i = filecnt; i > 0; fw++, i /= 10) - ; - mark = files[fileidx].flags & FF_MARK ? "* " : ""; - l->p = l->buf; - r->p = r->buf; - if (mode == MODE_THUMB) { - if (tns.loadnext < tns.end) - bar_put(r, "Loading... %0*d | ", fw, tns.loadnext + 1); - else if (tns.initnext < filecnt) - bar_put(r, "Caching... %0*d | ", fw, tns.initnext + 1); - bar_put(r, "%s%0*d/%d", mark, fw, fileidx + 1, filecnt); - if (info.ft.err) - strncpy(l->buf, files[fileidx].name, l->size); - } else { - bar_put(r, "%s", mark); - if (img.ss.on) { - if (img.ss.delay % 10 != 0) - bar_put(r, "%2.1fs" BAR_SEP, (float)img.ss.delay / 10); - else - bar_put(r, "%ds" BAR_SEP, img.ss.delay / 10); - } - if (img.gamma) - bar_put(r, "G%+d" BAR_SEP, img.gamma); - if (img.brightness) - bar_put(r, "B%+d" BAR_SEP, img.brightness); - if (img.contrast) - bar_put(r, "C%+d" BAR_SEP, img.contrast); - bar_put(r, "%3d%%" BAR_SEP, (int)(img.zoom * 100.0)); - if (img.multi.cnt > 0) { - for (fn = 0, i = img.multi.cnt; i > 0; fn++, i /= 10) - ; - bar_put(r, "%0*d/%d" BAR_SEP, fn, img.multi.sel + 1, img.multi.cnt); - } - bar_put(r, "%0*d/%d", fw, fileidx + 1, filecnt); - if (info.f.err) - strncpy(l->buf, files[fileidx].name, l->size); - } -} - -int nav_button(void) -{ - int x, y, nw; - - if (NAV_WIDTH == 0) - return 1; - - win_cursor_pos(&win, &x, &y); - nw = NAV_IS_REL ? win.w * NAV_WIDTH / 100 : NAV_WIDTH; - nw = MIN(nw, ((int)win.w + 1) / 2); - - if (x < nw) - return 0; - else if (x < (int)win.w - nw) - return 1; - else - return 2; -} - -void redraw(void) -{ - int t; - - if (mode == MODE_IMAGE) { - img_render(&img); - if (img.ss.on) { - t = img.ss.delay * 100; - if (img.multi.cnt > 0 && img.multi.animate) - t = MAX(t, img.multi.length); - set_timeout(slideshow, t, false); - } - } else { - tns_render(&tns); - } - update_info(); - win_draw(&win); - reset_timeout(redraw); - reset_cursor(); -} - -void reset_cursor(void) -{ - int c; - unsigned int i; - cursor_t cursor = CURSOR_NONE; - - if (mode == MODE_IMAGE) { - for (i = 0; i < ARRLEN(timeouts); i++) { - if (timeouts[i].handler == reset_cursor) { - if (timeouts[i].active) { - c = nav_button(); - c = MAX(fileidx > 0 ? 0 : 1, c); - c = MIN(fileidx + 1 < filecnt ? 2 : 1, c); - cursor = imgcursor[c]; - } - break; - } - } - } else { - if (tns.loadnext < tns.end || tns.initnext < filecnt) - cursor = CURSOR_WATCH; - else - cursor = CURSOR_ARROW; - } - win_set_cursor(&win, cursor); -} - -void animate(void) -{ - if (img_frame_animate(&img)) { - set_timeout(animate, img.multi.frames[img.multi.sel].delay, true); - redraw(); - } -} - -void slideshow(void) -{ - load_image(fileidx + 1 < filecnt ? fileidx + 1 : 0); - redraw(); -} - -void clear_resize(void) -{ - resized = false; -} - -static Bool is_input_ev(Display *dpy, XEvent *ev, XPointer arg) -{ - return ev->type == ButtonPress || ev->type == KeyPress; -} - -void handle_key_handler(bool init) -{ - extprefix = init; - if (win.bar.h == 0) - return; - if (init) { - snprintf(win.bar.r.buf, win.bar.r.size, - "Getting key handler input (%s to abort)...", - XKeysymToString(KEYHANDLER_ABORT)); - } else { /* abort */ - update_info(); - } - win_draw(&win); -} - -static bool run_key_handler(const char *key, unsigned int mask) -{ - FILE *pfs; - bool marked = mode == MODE_THUMB && markcnt > 0; - bool changed = false; - pid_t pid; - int writefd, f, i; - int fcnt = marked ? markcnt : 1; - char kstr[32]; - struct stat *oldst, st; - XEvent dump; - char *argv[3]; - - if (keyhandler.f.err) { - if (!keyhandler.warned) { - error(0, keyhandler.f.err, "%s", keyhandler.f.cmd); - keyhandler.warned = true; - } - return false; - } - if (key == NULL) - return false; - - strncpy(win.bar.r.buf, "Running key handler...", win.bar.r.size); - win_draw(&win); - win_set_cursor(&win, CURSOR_WATCH); - setenv("NSXIV_USING_NULL", options->using_null ? "1" : "0", 1); - - snprintf(kstr, sizeof(kstr), "%s%s%s%s", - mask & ControlMask ? "C-" : "", - mask & Mod1Mask ? "M-" : "", - mask & ShiftMask ? "S-" : "", key); - construct_argv(argv, ARRLEN(argv), keyhandler.f.cmd, kstr, NULL); - if ((pid = spawn(NULL, &writefd, argv)) < 0) - return false; - if ((pfs = fdopen(writefd, "w")) == NULL) { - error(0, errno, "open pipe"); - close(writefd); - return false; - } - - oldst = emalloc(fcnt * sizeof(*oldst)); - for (f = i = 0; f < fcnt; i++) { - if ((marked && (files[i].flags & FF_MARK)) || (!marked && i == fileidx)) { - stat(files[i].path, &oldst[f]); - fprintf(pfs, "%s%c", files[i].name, options->using_null ? '\0' : '\n'); - f++; - } - } - fclose(pfs); - while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) - ; - - for (f = i = 0; f < fcnt; i++) { - if ((marked && (files[i].flags & FF_MARK)) || (!marked && i == fileidx)) { - if (stat(files[i].path, &st) != 0 || - memcmp(&oldst[f].st_mtime, &st.st_mtime, sizeof(st.st_mtime)) != 0) - { - if (tns.thumbs != NULL) { - tns_unload(&tns, i); - tns.loadnext = MIN(tns.loadnext, i); - } - changed = true; - } - f++; - } - } - /* drop user input events that occurred while running the key handler */ - while (XCheckIfEvent(win.env.dpy, &dump, is_input_ev, NULL)) - ; - - if (mode == MODE_IMAGE && changed) { - img_close(&img, true); - load_image(fileidx); - } else { - update_info(); - } - free(oldst); - reset_cursor(); - return true; -} - -static bool process_bindings(const keymap_t *bindings, unsigned int len, KeySym ksym_or_button, - unsigned int state, unsigned int implicit_mod) -{ - unsigned int i; - bool dirty = false; - - for (i = 0; i < len; i++) { - if (bindings[i].ksym_or_button == ksym_or_button && - MODMASK(bindings[i].mask | implicit_mod) == MODMASK(state) && - bindings[i].cmd.func != NULL && - (bindings[i].cmd.mode == MODE_ALL || bindings[i].cmd.mode == mode)) - { - if (bindings[i].cmd.func(bindings[i].arg)) - dirty = true; - } - } - return dirty; -} - -static void on_keypress(XKeyEvent *kev) -{ - unsigned int sh = 0; - KeySym ksym, shksym; - char dummy, key; - bool dirty = false; - - XLookupString(kev, &key, 1, &ksym, NULL); - - if (kev->state & ShiftMask) { - kev->state &= ~ShiftMask; - XLookupString(kev, &dummy, 1, &shksym, NULL); - kev->state |= ShiftMask; - if (ksym != shksym) - sh = ShiftMask; - } - if (IsModifierKey(ksym)) - return; - if (extprefix && ksym == KEYHANDLER_ABORT && MODMASK(kev->state) == 0) { - handle_key_handler(false); - } else if (extprefix) { - if ((dirty = run_key_handler(XKeysymToString(ksym), kev->state & ~sh))) - extprefix = false; - else - handle_key_handler(false); - } else if (key >= '0' && key <= '9') { - /* number prefix for commands */ - prefix = prefix * 10 + (int)(key - '0'); - return; - } else { - dirty = process_bindings(keys, ARRLEN(keys), ksym, kev->state, sh); - } - if (dirty) - redraw(); - prefix = 0; -} - -static void on_buttonpress(const XButtonEvent *bev) -{ - bool dirty = false; - - if (mode == MODE_IMAGE) { - set_timeout(reset_cursor, TO_CURSOR_HIDE, true); - reset_cursor(); - dirty = process_bindings(buttons_img, ARRLEN(buttons_img), bev->button, bev->state, 0); - } else { /* thumbnail mode */ - dirty = process_bindings(buttons_tns, ARRLEN(buttons_tns), bev->button, bev->state, 0); - } - if (dirty) - redraw(); - prefix = 0; -} - -static void run(void) -{ - enum { FD_X, FD_INFO, FD_TITLE, FD_ARL, FD_CNT }; - struct pollfd pfd[FD_CNT]; - int timeout = 0; - bool discard, init_thumb, load_thumb, to_set; - XEvent ev, nextev; - - xbutton_ev = &ev.xbutton; - while (true) { - to_set = check_timeouts(&timeout); - init_thumb = mode == MODE_THUMB && tns.initnext < filecnt; - load_thumb = mode == MODE_THUMB && tns.loadnext < tns.end; - - if ((init_thumb || load_thumb || to_set || info.fd != -1 || arl.fd != -1) && - XPending(win.env.dpy) == 0) - { - if (load_thumb) { - set_timeout(redraw, TO_REDRAW_THUMBS, false); - if (!tns_load(&tns, tns.loadnext, false, false)) { - remove_file(tns.loadnext, false); - tns.dirty = true; - } - if (tns.loadnext >= tns.end) { - open_info(); - redraw(); - } - } else if (init_thumb) { - set_timeout(redraw, TO_REDRAW_THUMBS, false); - if (!tns_load(&tns, tns.initnext, false, true)) - remove_file(tns.initnext, false); - } else { - pfd[FD_X].fd = ConnectionNumber(win.env.dpy); - pfd[FD_INFO].fd = info.fd; - pfd[FD_TITLE].fd = wintitle.fd; - pfd[FD_ARL].fd = arl.fd; - pfd[FD_X].events = pfd[FD_INFO].events = pfd[FD_TITLE].events = pfd[FD_ARL].events = POLLIN; - - if (poll(pfd, ARRLEN(pfd), to_set ? timeout : -1) < 0) - continue; - if (pfd[FD_INFO].revents & POLLIN) - read_info(); - if (pfd[FD_TITLE].revents & POLLIN) - read_title(); - if ((pfd[FD_ARL].revents & POLLIN) && arl_handle(&arl)) { - img.autoreload_pending = true; - set_timeout(autoreload, TO_AUTORELOAD, true); - } - } - continue; - } - - do { - XNextEvent(win.env.dpy, &ev); - discard = false; - if (XEventsQueued(win.env.dpy, QueuedAlready) > 0) { - XPeekEvent(win.env.dpy, &nextev); - switch (ev.type) { - case ConfigureNotify: - case MotionNotify: - discard = ev.type == nextev.type; - break; - case KeyPress: - discard = (nextev.type == KeyPress || nextev.type == KeyRelease) && - ev.xkey.keycode == nextev.xkey.keycode; - break; - } - } - } while (discard); - - switch (ev.type) { /* handle events */ - case ButtonPress: - on_buttonpress(&ev.xbutton); - break; - case ClientMessage: - if ((Atom)ev.xclient.data.l[0] == atoms[ATOM_WM_DELETE_WINDOW]) - cg_quit(EXIT_SUCCESS); - break; - case DestroyNotify: - cg_quit(EXIT_FAILURE); - break; - case ConfigureNotify: - if (win_configure(&win, &ev.xconfigure)) { - if (mode == MODE_IMAGE) { - img.dirty = true; - img.checkpan = true; - } else { - tns.dirty = true; - } - if (!resized) { - redraw(); - set_timeout(clear_resize, TO_REDRAW_RESIZE, false); - resized = true; - } else { - set_timeout(redraw, TO_REDRAW_RESIZE, false); - } - } - break; - case KeyPress: - on_keypress(&ev.xkey); - break; - case MotionNotify: - if (mode == MODE_IMAGE) { - set_timeout(reset_cursor, TO_CURSOR_HIDE, true); - reset_cursor(); - } - break; - } - } -} - -static void setup_signal(int sig, void (*handler)(int sig), int flags) -{ - struct sigaction sa; - - sa.sa_handler = handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = flags; - if (sigaction(sig, &sa, NULL) < 0) - error(EXIT_FAILURE, errno, "signal %d", sig); -} - -int main(int argc, char *argv[]) -{ - int i; - size_t n; - const char *homedir, *dsuffix = ""; - - setup_signal(SIGCHLD, SIG_DFL, SA_RESTART | SA_NOCLDSTOP | SA_NOCLDWAIT); - setup_signal(SIGPIPE, SIG_IGN, 0); - - setlocale(LC_COLLATE, ""); - - parse_options(argc, argv); - - if (options->clean_cache) { - tns_init(&tns, NULL, NULL, NULL, NULL); - tns_clean_cache(); - exit(EXIT_SUCCESS); - } - - if (options->filecnt == 0 && !options->from_stdin) { - print_usage(); - exit(EXIT_FAILURE); - } - - if (options->recursive || options->from_stdin) - filecnt = 1024; - else - filecnt = options->filecnt; - - files = ecalloc(filecnt, sizeof(*files)); - fileidx = 0; - - if (options->from_stdin) { - char *filename = NULL; - n = 0; - while (xgetline(&filename, &n)) - add_entry(filename); - free(filename); - } - - for (i = 0; i < options->filecnt; i++) - add_entry(options->filenames[i]); - - if (fileidx == 0) - error(EXIT_FAILURE, 0, "No valid image file given, aborting"); - - filecnt = fileidx; - fileidx = options->startnum < filecnt ? options->startnum : 0; - - if (options->background_cache && !options->private_mode) { - pid_t ppid = getpid(); /* to check if parent is still alive or not */ - switch (fork()) { - case 0: - tns_init(&tns, files, &filecnt, &fileidx, NULL); - while (filecnt > 0 && getppid() == ppid) { - tns_load(&tns, filecnt - 1, false, true); - remove_file(filecnt - 1, true); - } - exit(0); - break; - case -1: - error(0, errno, "fork failed"); - break; - } - } - - win_init(&win); - img_init(&img, &win); - arl_init(&arl); - - if ((homedir = getenv("XDG_CONFIG_HOME")) == NULL || homedir[0] == '\0') { - homedir = getenv("HOME"); - dsuffix = "/.config"; - } - if (homedir != NULL) { - extcmd_t *cmd[] = { &info.f, &info.ft, &keyhandler.f, &wintitle.f }; - const char *name[] = { "image-info", "thumb-info", "key-handler", "win-title" }; - const char *s = "/nsxiv/exec/"; - - for (i = 0; i < (int)ARRLEN(cmd); i++) { - n = strlen(homedir) + strlen(dsuffix) + strlen(s) + strlen(name[i]) + 1; - cmd[i]->cmd = emalloc(n); - snprintf(cmd[i]->cmd, n, "%s%s%s%s", homedir, dsuffix, s, name[i]); - if (access(cmd[i]->cmd, X_OK) != 0) - cmd[i]->err = errno; - } - } else { - error(0, 0, "Exec directory not found"); - } - wintitle.fd = info.fd = -1; - - if (options->thumb_mode) { - mode = MODE_THUMB; - tns_init(&tns, files, &filecnt, &fileidx, &win); - while (!tns_load(&tns, fileidx, false, false)) - remove_file(fileidx, false); - } else { - mode = MODE_IMAGE; - tns.thumbs = NULL; - load_image(fileidx); - } - win_open(&win); - win_set_cursor(&win, CURSOR_WATCH); - - atexit(cleanup); - - set_timeout(redraw, 25, false); - - run(); - - return 0; -} diff --git a/source/nsxiv/nsxiv.h b/source/nsxiv/nsxiv.h deleted file mode 100644 index 3ffc11344..000000000 --- a/source/nsxiv/nsxiv.h +++ /dev/null @@ -1,473 +0,0 @@ -/* Copyright 2011-2020 Bert Muennich - * Copyright 2021-2023 nsxiv contributors - * - * This file is a part of nsxiv. - * - * nsxiv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * nsxiv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with nsxiv. If not, see . - */ - -#ifndef NSXIV_H -#define NSXIV_H - -#if !defined(DEBUG) && !defined(NDEBUG) - #define NDEBUG -#endif - -#include -#include - -#include -#include - -/* - * Annotation for functions called in cleanup(). - * These functions are not allowed to call error(!0, ...) or exit(). - */ -#define CLEANUP - -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#define ABS(a) ((a) > 0 ? (a) : -(a)) - -#define ARRLEN(a) (sizeof(a) / sizeof((a)[0])) -#define STREQ(s1,s2) (strcmp((s1), (s2)) == 0) - -typedef enum { - MODE_ALL, - MODE_IMAGE, - MODE_THUMB -} appmode_t; - -typedef enum { - DIR_LEFT = 1, - DIR_RIGHT = 2, - DIR_UP = 4, - DIR_DOWN = 8 -} direction_t; - -typedef enum { - DEGREE_90 = 1, - DEGREE_180 = 2, - DEGREE_270 = 3 -} degree_t; - -typedef enum { - FLIP_HORIZONTAL = 1, - FLIP_VERTICAL = 2 -} flipdir_t; - -typedef enum { - SCALE_DOWN, - SCALE_FIT, - SCALE_FILL, - SCALE_WIDTH, - SCALE_HEIGHT, - SCALE_ZOOM -} scalemode_t; - -typedef enum { - DRAG_RELATIVE, - DRAG_ABSOLUTE -} dragmode_t; - -typedef enum { - CURSOR_ARROW, - CURSOR_DRAG_ABSOLUTE, - CURSOR_DRAG_RELATIVE, - CURSOR_WATCH, - CURSOR_LEFT, - CURSOR_RIGHT, - CURSOR_NONE, - - CURSOR_COUNT -} cursor_t; - -typedef enum { - FF_WARN = 1, - FF_MARK = 2, - FF_TN_INIT = 4 -} fileflags_t; - -typedef struct { - const char *name; /* as given by user */ - const char *path; /* always absolute */ - fileflags_t flags; -} fileinfo_t; - -/* timeouts in milliseconds: */ -enum { - TO_AUTORELOAD = 128, - TO_REDRAW_RESIZE = 75, - TO_REDRAW_THUMBS = 200, - TO_CURSOR_HIDE = 1200, - TO_DOUBLE_CLICK = 300 -}; - -typedef void (*timeout_f)(void); - -typedef struct arl arl_t; -typedef struct img img_t; -typedef struct opt opt_t; -typedef struct tns tns_t; -typedef struct win win_t; - - -/* autoreload.c */ - -struct arl { - int fd; - int wd_dir; - int wd_file; - const char *filename; -}; - -void arl_init(arl_t*); -void arl_cleanup(arl_t*); -void arl_add(arl_t*, const char* /* result of realpath(3) */); -bool arl_handle(arl_t*); - - -/* commands.c */ - -typedef int arg_t; -typedef bool (*cmd_f)(arg_t); - -typedef struct { - cmd_f func; - appmode_t mode; -} cmd_t; - -typedef struct { - unsigned int mask; - KeySym ksym_or_button; - cmd_t cmd; - arg_t arg; -} keymap_t; - -typedef keymap_t button_t; - - -/* image.c */ - -#ifdef IMLIB2_VERSION /* UPGRADE: Imlib2 v1.8.0: remove all HAVE_IMLIB2_MULTI_FRAME ifdefs */ - #if IMLIB2_VERSION >= IMLIB2_VERSION_(1, 8, 0) - #define HAVE_IMLIB2_MULTI_FRAME 1 - #endif -#endif -#ifndef HAVE_IMLIB2_MULTI_FRAME - #define HAVE_IMLIB2_MULTI_FRAME 0 -#endif - -typedef struct { - Imlib_Image im; - unsigned int delay; -} img_frame_t; - -typedef struct { - img_frame_t *frames; - unsigned int cap; - unsigned int cnt; - unsigned int sel; - bool animate; - unsigned int framedelay; - int length; -} multi_img_t; - -struct img { - Imlib_Image im; - int w; - int h; - - win_t *win; - float x; - float y; - - Imlib_Color_Modifier cmod; - int gamma; - int brightness; - int contrast; - - scalemode_t scalemode; - float zoom; - - bool checkpan; - bool dirty; - bool anti_alias; - bool alpha_layer; - bool autoreload_pending; - - struct { - bool on; - int delay; - } ss; - - multi_img_t multi; -}; - -void img_init(img_t*, win_t*); -bool img_load(img_t*, const fileinfo_t*); -CLEANUP void img_free(Imlib_Image, bool); -CLEANUP void img_close(img_t*, bool); -void img_render(img_t*); -bool img_fit_win(img_t*, scalemode_t); -bool img_zoom(img_t*, int); -bool img_zoom_to(img_t*, float); -bool img_pos(img_t*, float, float); -bool img_pan(img_t*, direction_t, int); -bool img_pan_center(img_t*); -bool img_pan_edge(img_t*, direction_t); -void img_rotate(img_t*, degree_t); -void img_flip(img_t*, flipdir_t); -void img_toggle_antialias(img_t*); -void img_update_color_modifiers(img_t*); -bool img_change_color_modifier(img_t*, int, int*); -bool img_frame_navigate(img_t*, int); -bool img_frame_animate(img_t*); -Imlib_Image img_open(const fileinfo_t*); -#if HAVE_LIBEXIF -void exif_auto_orientate(const fileinfo_t*); -#endif - - -/* options.c */ - -struct opt { - /* file list: */ - char **filenames; - bool from_stdin; - bool to_stdout; - bool using_null; - bool recursive; - int filecnt; - int startnum; - - /* image: */ - scalemode_t scalemode; - float zoom; - bool animate; - bool anti_alias; - bool alpha_layer; - int gamma; - unsigned int slideshow; - int framerate; - - /* window: */ - bool fullscreen; - bool hide_bar; - Window embed; /* unsigned long */ - char *geometry; - char *res_name; - - /* misc flags: */ - bool quiet; - bool thumb_mode; - bool clean_cache; - bool private_mode; - bool background_cache; -}; - -extern const opt_t *options; - -void print_usage(void); -void parse_options(int, char**); - - -/* thumbs.c */ - -typedef struct { - Imlib_Image im; - int w; - int h; - int x; - int y; -} thumb_t; - -struct tns { - fileinfo_t *files; - thumb_t *thumbs; - const int *cnt; - int *sel; - int initnext; - int loadnext; - int first, end; - int r_first, r_end; - - win_t *win; - int x; - int y; - int cols; - int rows; - int zl; - int bw; - int dim; - - bool dirty; -}; - -void tns_clean_cache(void); -void tns_init(tns_t*, fileinfo_t*, const int*, int*, win_t*); -CLEANUP void tns_free(tns_t*); -bool tns_load(tns_t*, int, bool, bool); -void tns_unload(tns_t*, int); -void tns_render(tns_t*); -void tns_mark(tns_t*, int, bool); -void tns_highlight(tns_t*, int, bool); -bool tns_move_selection(tns_t*, direction_t, int); -bool tns_scroll(tns_t*, direction_t, bool); -bool tns_zoom(tns_t*, int); -int tns_translate(tns_t*, int, int); - - -/* util.c */ - -#include - -typedef struct { - DIR *dir; - char *name; - int d; - bool recursive; - - char **stack; - int stcap; - int stlen; -} r_dir_t; - -extern const char *progname; - -void* emalloc(size_t); -void* ecalloc(size_t, size_t); -void* erealloc(void*, size_t); -char* estrdup(const char*); -void error(int, int, const char*, ...); -int r_opendir(r_dir_t*, const char*, bool); -int r_closedir(r_dir_t*); -char* r_readdir(r_dir_t*, bool); -int r_mkdir(char*); -void construct_argv(char**, unsigned int, ...); -pid_t spawn(int*, int*, char *const []); - - -/* window.c */ - -#include -#if HAVE_LIBFONTS -#include -#endif - -enum { - ATOM_WM_DELETE_WINDOW, - ATOM__NET_WM_NAME, - ATOM__NET_WM_ICON_NAME, - ATOM__NET_WM_ICON, - ATOM__NET_WM_STATE, - ATOM__NET_WM_PID, - ATOM__NET_WM_STATE_FULLSCREEN, - ATOM_UTF8_STRING, - ATOM_WM_NAME, - ATOM_WM_ICON_NAME, - ATOM_COUNT -}; - -typedef struct { - Display *dpy; - int scr; - int scrw, scrh; - Visual *vis; - Colormap cmap; - int depth; -} win_env_t; - -typedef struct { - size_t size; - char *p; - char *buf; -} win_bar_t; - -struct win { - Window xwin; - win_env_t env; - - XColor win_bg; - XColor win_fg; - XColor mrk_fg; -#if HAVE_LIBFONTS - XftColor bar_bg; - XftColor bar_fg; -#endif - - int x; - int y; - unsigned int w; - unsigned int h; /* = win height - bar height */ - unsigned int bw; - - struct { - unsigned int w; - unsigned int h; - Pixmap pm; - } buf; - - struct { - unsigned int h; - bool top; - win_bar_t l; - win_bar_t r; - } bar; -}; - -extern Atom atoms[ATOM_COUNT]; - -void win_init(win_t*); -void win_open(win_t*); -CLEANUP void win_close(win_t*); -bool win_configure(win_t*, XConfigureEvent*); -void win_toggle_fullscreen(win_t*); -void win_toggle_bar(win_t*); -void win_clear(win_t*); -void win_draw(win_t*); -void win_draw_rect(win_t*, int, int, int, int, bool, int, unsigned long); -void win_set_title(win_t*, const char*, size_t); -void win_set_cursor(win_t*, cursor_t); -void win_cursor_pos(win_t*, int*, int*); - -/* main.c */ - -/* timeout handler functions: */ -void redraw(void); -void reset_cursor(void); -void animate(void); -void slideshow(void); -void clear_resize(void); - -void remove_file(int, bool); -void set_timeout(timeout_f, int, bool); -void reset_timeout(timeout_f); -void close_info(void); -void open_info(void); -void load_image(int); -bool mark_image(int, bool); -int nav_button(void); -void handle_key_handler(bool); - -extern appmode_t mode; -extern const XButtonEvent *xbutton_ev; -extern fileinfo_t *files; -extern int filecnt, fileidx; -extern int alternate; -extern int markcnt; -extern int markidx; -extern int prefix; - -#endif /* NSXIV_H */ diff --git a/source/nsxiv/options.c b/source/nsxiv/options.c deleted file mode 100644 index 4ae2ea595..000000000 --- a/source/nsxiv/options.c +++ /dev/null @@ -1,280 +0,0 @@ -/* Copyright 2011-2020 Bert Muennich - * Copyright 2021-2023 nsxiv contributors - * - * This file is a part of nsxiv. - * - * nsxiv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * nsxiv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with nsxiv. If not, see . - */ - -#include "nsxiv.h" -#include "version.h" -#define INCLUDE_OPTIONS_CONFIG -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#define OPTPARSE_IMPLEMENTATION -#define OPTPARSE_API static -#pragma GCC diagnostic push /* also works on clang */ -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wunused-function" -#include "optparse.h" -#pragma GCC diagnostic pop - -const opt_t *options; - -void print_usage(void) -{ - printf("usage: %s [-abcfhiopqrtvZ0] [-A FRAMERATE] [-e WID] [-G GAMMA] " - "[-g GEOMETRY] [-N NAME] [-n NUM] [-S DELAY] [-s MODE] " - "[-z ZOOM] FILES...\n", - progname); -} - -static void print_version(void) -{ - printf("%s %s\n", progname, VERSION); - fputs("features: " -#if HAVE_INOTIFY - "+inotify " -#endif -#if HAVE_LIBFONTS - "+statusbar " -#endif -#if HAVE_LIBEXIF - "+exif " -#endif -#if HAVE_IMLIB2_MULTI_FRAME - "+multiframe " -#endif - "\n", stdout); -} - -void parse_options(int argc, char **argv) -{ - enum { - /* ensure these can't be represented in a single byte in order - * to avoid conflicts with short opts - */ - OPT_START = UCHAR_MAX, - OPT_AA, - OPT_AL, - OPT_BG - }; - static const struct optparse_long longopts[] = { - { "framerate", 'A', OPTPARSE_REQUIRED }, - { "animate", 'a', OPTPARSE_NONE }, - { "no-bar", 'b', OPTPARSE_NONE }, - { "clean-cache", 'c', OPTPARSE_NONE }, - { "embed", 'e', OPTPARSE_REQUIRED }, - { "fullscreen", 'f', OPTPARSE_NONE }, - { "gamma", 'G', OPTPARSE_REQUIRED }, - { "geometry", 'g', OPTPARSE_REQUIRED }, - { "help", 'h', OPTPARSE_NONE }, - { "stdin", 'i', OPTPARSE_NONE }, - { "class", 'N', OPTPARSE_REQUIRED }, - { "start-at", 'n', OPTPARSE_REQUIRED }, - { "stdout", 'o', OPTPARSE_NONE }, - { "private", 'p', OPTPARSE_NONE }, - { "quiet", 'q', OPTPARSE_NONE }, - { "recursive", 'r', OPTPARSE_NONE }, - { "ss-delay", 'S', OPTPARSE_REQUIRED }, - { "scale-mode", 's', OPTPARSE_REQUIRED }, - { "thumbnail", 't', OPTPARSE_NONE }, - { "version", 'v', OPTPARSE_NONE }, - { "zoom-100", 'Z', OPTPARSE_NONE }, - { "zoom", 'z', OPTPARSE_REQUIRED }, - { "null", '0', OPTPARSE_NONE }, - { "anti-alias", OPT_AA, OPTPARSE_OPTIONAL }, - { "alpha-layer", OPT_AL, OPTPARSE_OPTIONAL }, - /* TODO: document this when it's stable */ - { "bg-cache", OPT_BG, OPTPARSE_OPTIONAL }, - { 0 }, /* end */ - }; - - long n, opt; - float f; - char *end, *s; - struct optparse op; - const char scalemodes[] = "dfFwh"; /* must be sorted according to scalemode_t */ - static opt_t _options; - - options = &_options; - _options.from_stdin = false; - _options.to_stdout = false; - _options.using_null = false; - _options.recursive = false; - _options.startnum = 0; - - _options.scalemode = SCALE_DOWN; - _options.zoom = 1.0; - _options.anti_alias = ANTI_ALIAS; - _options.alpha_layer = ALPHA_LAYER; - _options.animate = false; - _options.gamma = 0; - _options.slideshow = 0; - _options.framerate = 0; - - _options.fullscreen = false; - _options.embed = 0; - _options.hide_bar = false; - _options.geometry = NULL; - _options.res_name = NULL; - - _options.quiet = false; - _options.thumb_mode = false; - _options.clean_cache = false; - _options.private_mode = false; - _options.background_cache = false; - - if (argc > 0) { - s = strrchr(argv[0], '/'); - progname = s != NULL && s[1] != '\0' ? s + 1 : argv[0]; - } - - optparse_init(&op, argv); - while ((opt = optparse_long(&op, longopts, NULL)) != -1) { - for (n = 0; n < (int)ARRLEN(longopts); ++n) { /* clang-tidy finds some non-sensical branch and thinks optarg == NULL is possible */ - if (opt == longopts[n].shortname && longopts[n].argtype == OPTPARSE_REQUIRED) - assert(op.optarg != NULL); - } - switch (opt) { - case '?': - fprintf(stderr, "%s\n", op.errmsg); - print_usage(); - exit(EXIT_FAILURE); - case 'A': - n = strtol(op.optarg, &end, 0); - if (*end != '\0' || n <= 0 || n > INT_MAX) - error(EXIT_FAILURE, 0, "Invalid framerate: %s", op.optarg); - _options.framerate = n; - /* fall through */ - case 'a': - _options.animate = true; - break; - case 'b': - _options.hide_bar = true; - break; - case 'c': - _options.clean_cache = true; - break; - case 'e': - n = strtol(op.optarg, &end, 0); - if (*end != '\0') - error(EXIT_FAILURE, 0, "Invalid window id: %s", op.optarg); - _options.embed = n; - break; - case 'f': - _options.fullscreen = true; - break; - case 'G': - n = strtol(op.optarg, &end, 0); - if (*end != '\0' || n < INT_MIN || n > INT_MAX) - error(EXIT_FAILURE, 0, "Invalid gamma: %s", op.optarg); - _options.gamma = n; - break; - case 'g': - _options.geometry = op.optarg; - break; - case 'h': - print_usage(); - exit(EXIT_SUCCESS); - case 'i': - _options.from_stdin = true; - break; - case 'n': - n = strtol(op.optarg, &end, 0); - if (*end != '\0' || n <= 0 || n > INT_MAX) - error(EXIT_FAILURE, 0, "Invalid starting number: %s", op.optarg); - _options.startnum = n - 1; - break; - case 'N': - _options.res_name = op.optarg; - break; - case 'o': - _options.to_stdout = true; - break; - case 'p': - _options.private_mode = true; - break; - case 'q': - _options.quiet = true; - break; - case 'r': - _options.recursive = true; - break; - case 'S': - f = strtof(op.optarg, &end) * 10.0f; - if (*end != '\0' || f <= 0 || f >= (float)UINT_MAX) - error(EXIT_FAILURE, 0, "Invalid slideshow delay: %s", op.optarg); - _options.slideshow = (unsigned int)f; - break; - case 's': - s = strchr(scalemodes, op.optarg[0]); - if (s == NULL || *s == '\0' || strlen(op.optarg) != 1) - error(EXIT_FAILURE, 0, "Invalid scale mode: %s", op.optarg); - _options.scalemode = s - scalemodes; - break; - case 't': - _options.thumb_mode = true; - break; - case 'v': - print_version(); - exit(EXIT_SUCCESS); - case 'Z': - _options.scalemode = SCALE_ZOOM; - _options.zoom = 1.0f; - break; - case 'z': - n = strtol(op.optarg, &end, 0); - if (*end != '\0' || n <= 0) - error(EXIT_FAILURE, 0, "Invalid zoom level: %s", op.optarg); - _options.scalemode = SCALE_ZOOM; - _options.zoom = (float)n / 100.0f; - break; - case '0': - _options.using_null = true; - break; - case OPT_AA: - if (op.optarg != NULL && !STREQ(op.optarg, "no")) - error(EXIT_FAILURE, 0, "Invalid argument for option --anti-alias: %s", op.optarg); - _options.anti_alias = op.optarg == NULL; - break; - case OPT_AL: - if (op.optarg != NULL && !STREQ(op.optarg, "no")) - error(EXIT_FAILURE, 0, "Invalid argument for option --alpha-layer: %s", op.optarg); - _options.alpha_layer = op.optarg == NULL; - break; - case OPT_BG: - if (op.optarg != NULL && !STREQ(op.optarg, "no")) - error(EXIT_FAILURE, 0, "Invalid argument for option --bg-cache: %s", op.optarg); - _options.background_cache = op.optarg == NULL; - break; - } - } - - _options.filenames = argv + op.optind; - _options.filecnt = argc - op.optind; - - if (_options.filecnt == 1 && STREQ(_options.filenames[0], "-")) { - _options.filenames++; - _options.filecnt--; - _options.from_stdin = true; - } -} diff --git a/source/nsxiv/optparse.h b/source/nsxiv/optparse.h deleted file mode 100644 index 8d6c0a947..000000000 --- a/source/nsxiv/optparse.h +++ /dev/null @@ -1,403 +0,0 @@ -/* Optparse --- portable, reentrant, embeddable, getopt-like option parser - * - * This is free and unencumbered software released into the public domain. - * - * To get the implementation, define OPTPARSE_IMPLEMENTATION. - * Optionally define OPTPARSE_API to control the API's visibility - * and/or linkage (static, __attribute__, __declspec). - * - * The POSIX getopt() option parser has three fatal flaws. These flaws - * are solved by Optparse. - * - * 1) Parser state is stored entirely in global variables, some of - * which are static and inaccessible. This means only one thread can - * use getopt(). It also means it's not possible to recursively parse - * nested sub-arguments while in the middle of argument parsing. - * Optparse fixes this by storing all state on a local struct. - * - * 2) The POSIX standard provides no way to properly reset the parser. - * This means for portable code that getopt() is only good for one - * run, over one argv with one option string. It also means subcommand - * options cannot be processed with getopt(). Most implementations - * provide a method to reset the parser, but it's not portable. - * Optparse provides an optparse_arg() function for stepping over - * subcommands and continuing parsing of options with another option - * string. The Optparse struct itself can be passed around to - * subcommand handlers for additional subcommand option parsing. A - * full reset can be achieved by with an additional optparse_init(). - * - * 3) Error messages are printed to stderr. This can be disabled with - * opterr, but the messages themselves are still inaccessible. - * Optparse solves this by writing an error message in its errmsg - * field. The downside to Optparse is that this error message will - * always be in English rather than the current locale. - * - * Optparse should be familiar with anyone accustomed to getopt(), and - * it could be a nearly drop-in replacement. The option string is the - * same and the fields have the same names as the getopt() global - * variables (optarg, optind, optopt). - * - * Optparse also supports GNU-style long options with optparse_long(). - * The interface is slightly different and simpler than getopt_long(). - * - * By default, argv is permuted as it is parsed, moving non-option - * arguments to the end. This can be disabled by setting the `permute` - * field to 0 after initialization. - */ -#ifndef OPTPARSE_H -#define OPTPARSE_H - -#ifndef OPTPARSE_API -# define OPTPARSE_API -#endif - -struct optparse { - char **argv; - int permute; - int optind; - int optopt; - char *optarg; - char errmsg[64]; - int subopt; -}; - -enum optparse_argtype { - OPTPARSE_NONE, - OPTPARSE_REQUIRED, - OPTPARSE_OPTIONAL -}; - -struct optparse_long { - const char *longname; - int shortname; - enum optparse_argtype argtype; -}; - -/** - * Initializes the parser state. - */ -OPTPARSE_API -void optparse_init(struct optparse *options, char **argv); - -/** - * Read the next option in the argv array. - * @param optstring a getopt()-formatted option string. - * @return the next option character, -1 for done, or '?' for error - * - * Just like getopt(), a character followed by no colons means no - * argument. One colon means the option has a required argument. Two - * colons means the option takes an optional argument. - */ -OPTPARSE_API -int optparse(struct optparse *options, const char *optstring); - -/** - * Handles GNU-style long options in addition to getopt() options. - * This works a lot like GNU's getopt_long(). The last option in - * longopts must be all zeros, marking the end of the array. The - * longindex argument may be NULL. - */ -OPTPARSE_API -int optparse_long(struct optparse *options, - const struct optparse_long *longopts, - int *longindex); - -/** - * Used for stepping over non-option arguments. - * @return the next non-option argument, or NULL for no more arguments - * - * Argument parsing can continue with optparse() after using this - * function. That would be used to parse the options for the - * subcommand returned by optparse_arg(). This function allows you to - * ignore the value of optind. - */ -OPTPARSE_API -char *optparse_arg(struct optparse *options); - -/* Implementation */ -#ifdef OPTPARSE_IMPLEMENTATION - -#define OPTPARSE_MSG_INVALID "invalid option" -#define OPTPARSE_MSG_MISSING "option requires an argument" -#define OPTPARSE_MSG_TOOMANY "option takes no arguments" - -static int -optparse_error(struct optparse *options, const char *msg, const char *data) -{ - unsigned p = 0; - const char *sep = " -- '"; - while (*msg) - options->errmsg[p++] = *msg++; - while (*sep) - options->errmsg[p++] = *sep++; - while (p < sizeof(options->errmsg) - 2 && *data) - options->errmsg[p++] = *data++; - options->errmsg[p++] = '\''; - options->errmsg[p++] = '\0'; - return '?'; -} - -OPTPARSE_API -void -optparse_init(struct optparse *options, char **argv) -{ - options->argv = argv; - options->permute = 1; - options->optind = argv[0] != 0; - options->subopt = 0; - options->optarg = 0; - options->errmsg[0] = '\0'; -} - -static int -optparse_is_dashdash(const char *arg) -{ - return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0'; -} - -static int -optparse_is_shortopt(const char *arg) -{ - return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0'; -} - -static int -optparse_is_longopt(const char *arg) -{ - return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0'; -} - -static void -optparse_permute(struct optparse *options, int index) -{ - char *nonoption = options->argv[index]; - int i; - for (i = index; i < options->optind - 1; i++) - options->argv[i] = options->argv[i + 1]; - options->argv[options->optind - 1] = nonoption; -} - -static int -optparse_argtype(const char *optstring, char c) -{ - int count = OPTPARSE_NONE; - if (c == ':') - return -1; - for (; *optstring && c != *optstring; optstring++); - if (!*optstring) - return -1; - if (optstring[1] == ':') - count += optstring[2] == ':' ? 2 : 1; - return count; -} - -OPTPARSE_API -int -optparse(struct optparse *options, const char *optstring) -{ - int type; - char *next; - char *option = options->argv[options->optind]; - options->errmsg[0] = '\0'; - options->optopt = 0; - options->optarg = 0; - if (option == 0) { - return -1; - } else if (optparse_is_dashdash(option)) { - options->optind++; /* consume "--" */ - return -1; - } else if (!optparse_is_shortopt(option)) { - if (options->permute) { - int index = options->optind++; - int r = optparse(options, optstring); - optparse_permute(options, index); - options->optind--; - return r; - } else { - return -1; - } - } - option += options->subopt + 1; - options->optopt = option[0]; - type = optparse_argtype(optstring, option[0]); - next = options->argv[options->optind + 1]; - switch (type) { - case -1: { - char str[2] = {0, 0}; - str[0] = option[0]; - options->optind++; - return optparse_error(options, OPTPARSE_MSG_INVALID, str); - } - case OPTPARSE_NONE: - if (option[1]) { - options->subopt++; - } else { - options->subopt = 0; - options->optind++; - } - return option[0]; - case OPTPARSE_REQUIRED: - options->subopt = 0; - options->optind++; - if (option[1]) { - options->optarg = option + 1; - } else if (next != 0) { - options->optarg = next; - options->optind++; - } else { - char str[2] = {0, 0}; - str[0] = option[0]; - options->optarg = 0; - return optparse_error(options, OPTPARSE_MSG_MISSING, str); - } - return option[0]; - case OPTPARSE_OPTIONAL: - options->subopt = 0; - options->optind++; - if (option[1]) - options->optarg = option + 1; - else - options->optarg = 0; - return option[0]; - } - return 0; -} - -OPTPARSE_API -char * -optparse_arg(struct optparse *options) -{ - char *option = options->argv[options->optind]; - options->subopt = 0; - if (option != 0) - options->optind++; - return option; -} - -static int -optparse_longopts_end(const struct optparse_long *longopts, int i) -{ - return !longopts[i].longname && !longopts[i].shortname; -} - -static void -optparse_from_long(const struct optparse_long *longopts, char *optstring) -{ - char *p = optstring; - int i; - for (i = 0; !optparse_longopts_end(longopts, i); i++) { - if (longopts[i].shortname && longopts[i].shortname < 127) { - int a; - *p++ = longopts[i].shortname; - for (a = 0; a < (int)longopts[i].argtype; a++) - *p++ = ':'; - } - } - *p = '\0'; -} - -/* Unlike strcmp(), handles options containing "=". */ -static int -optparse_longopts_match(const char *longname, const char *option) -{ - const char *a = option, *n = longname; - if (longname == 0) - return 0; - for (; *a && *n && *a != '='; a++, n++) - if (*a != *n) - return 0; - return *n == '\0' && (*a == '\0' || *a == '='); -} - -/* Return the part after "=", or NULL. */ -static char * -optparse_longopts_arg(char *option) -{ - for (; *option && *option != '='; option++); - if (*option == '=') - return option + 1; - else - return 0; -} - -static int -optparse_long_fallback(struct optparse *options, - const struct optparse_long *longopts, - int *longindex) -{ - int result; - char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */ - optparse_from_long(longopts, optstring); - result = optparse(options, optstring); - if (longindex != 0) { - *longindex = -1; - if (result != -1) { - int i; - for (i = 0; !optparse_longopts_end(longopts, i); i++) - if (longopts[i].shortname == options->optopt) - *longindex = i; - } - } - return result; -} - -OPTPARSE_API -int -optparse_long(struct optparse *options, - const struct optparse_long *longopts, - int *longindex) -{ - int i; - char *option = options->argv[options->optind]; - if (option == 0) { - return -1; - } else if (optparse_is_dashdash(option)) { - options->optind++; /* consume "--" */ - return -1; - } else if (optparse_is_shortopt(option)) { - return optparse_long_fallback(options, longopts, longindex); - } else if (!optparse_is_longopt(option)) { - if (options->permute) { - int index = options->optind++; - int r = optparse_long(options, longopts, longindex); - optparse_permute(options, index); - options->optind--; - return r; - } else { - return -1; - } - } - - /* Parse as long option. */ - options->errmsg[0] = '\0'; - options->optopt = 0; - options->optarg = 0; - option += 2; /* skip "--" */ - options->optind++; - for (i = 0; !optparse_longopts_end(longopts, i); i++) { - const char *name = longopts[i].longname; - if (optparse_longopts_match(name, option)) { - char *arg; - if (longindex) - *longindex = i; - options->optopt = longopts[i].shortname; - arg = optparse_longopts_arg(option); - if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) { - return optparse_error(options, OPTPARSE_MSG_TOOMANY, name); - } if (arg != 0) { - options->optarg = arg; - } else if (longopts[i].argtype == OPTPARSE_REQUIRED) { - options->optarg = options->argv[options->optind]; - if (options->optarg == 0) - return optparse_error(options, OPTPARSE_MSG_MISSING, name); - else - options->optind++; - } - return options->optopt; - } - } - return optparse_error(options, OPTPARSE_MSG_INVALID, option); -} - -#endif /* OPTPARSE_IMPLEMENTATION */ -#endif /* OPTPARSE_H */ diff --git a/source/nsxiv/thumbs.c b/source/nsxiv/thumbs.c deleted file mode 100644 index c7b9dc6f3..000000000 --- a/source/nsxiv/thumbs.c +++ /dev/null @@ -1,592 +0,0 @@ -/* Copyright 2011-2020 Bert Muennich - * Copyright 2021-2023 nsxiv contributors - * - * This file is a part of nsxiv. - * - * nsxiv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * nsxiv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with nsxiv. If not, see . - */ - -#include "nsxiv.h" -#define INCLUDE_THUMBS_CONFIG -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#if HAVE_LIBEXIF -#include -#endif - -static char *cache_dir; -static char *cache_tmpfile, *cache_tmpfile_base; -static const char TMP_NAME[] = "/nsxiv-XXXXXX"; - -static char *tns_cache_filepath(const char *filepath) -{ - size_t len; - char *cfile = NULL; - - assert(*filepath == '/' && "filepath should be result of realpath(3)"); - - if (strncmp(filepath, cache_dir, strlen(cache_dir)) != 0) { - /* don't cache images inside the cache directory! */ - len = strlen(cache_dir) + strlen(filepath) + 2; - cfile = emalloc(len); - snprintf(cfile, len, "%s/%s", cache_dir, filepath + 1); - } - return cfile; -} - -static Imlib_Image tns_cache_load(const char *filepath, bool *outdated) -{ - char *cfile; - struct stat cstats, fstats; - Imlib_Image im = NULL; - - if (stat(filepath, &fstats) < 0) - return NULL; - - if ((cfile = tns_cache_filepath(filepath)) != NULL) { - if (stat(cfile, &cstats) == 0) { - if (cstats.st_mtime == fstats.st_mtime) - im = imlib_load_image(cfile); - else - *outdated = true; - } - free(cfile); - } - return im; -} - -static void tns_cache_write(Imlib_Image im, const char *filepath, bool force) -{ - char *cfile, *dirend; - int tmpfd; - struct stat cstats, fstats; - struct utimbuf times; - Imlib_Load_Error err; - - if (options->private_mode) - return; - - if (stat(filepath, &fstats) < 0) - return; - - if ((cfile = tns_cache_filepath(filepath)) != NULL) { - if (force || stat(cfile, &cstats) < 0 || - cstats.st_mtime != fstats.st_mtime) - { - if ((dirend = strrchr(cfile, '/')) != NULL) { - *dirend = '\0'; - if (r_mkdir(cfile) < 0) - goto end; - *dirend = '/'; - } - imlib_context_set_image(im); - if (imlib_image_has_alpha()) { - imlib_image_set_format("png"); - } else { - imlib_image_set_format("jpg"); - imlib_image_attach_data_value("quality", NULL, 90, NULL); - } - memcpy(cache_tmpfile_base, TMP_NAME, sizeof(TMP_NAME)); - if ((tmpfd = mkstemp(cache_tmpfile)) < 0) - goto end; - close(tmpfd); - /* UPGRADE: Imlib2 v1.11.0: use imlib_save_image_fd() */ - imlib_save_image_with_error_return(cache_tmpfile, &err); - times.actime = fstats.st_atime; - times.modtime = fstats.st_mtime; - utime(cache_tmpfile, ×); - if (err || rename(cache_tmpfile, cfile) < 0) - unlink(cache_tmpfile); - } -end: - free(cfile); - } -} - -void tns_clean_cache(void) -{ - int dirlen; - char *cfile, *filename; - r_dir_t dir; - - if (r_opendir(&dir, cache_dir, true) < 0) { - error(0, errno, "%s", cache_dir); - return; - } - - dirlen = strlen(cache_dir); - - while ((cfile = r_readdir(&dir, false)) != NULL) { - filename = cfile + dirlen; - if (access(filename, F_OK) < 0) { - if (unlink(cfile) < 0) - error(0, errno, "%s", cfile); - } - free(cfile); - } - r_closedir(&dir); -} - -void tns_init(tns_t *tns, fileinfo_t *tns_files, const int *cnt, int *sel, win_t *win) -{ - int len; - const char *homedir, *dsuffix = ""; - - if (cnt != NULL && *cnt > 0) - tns->thumbs = ecalloc(*cnt, sizeof(*tns->thumbs)); - else - tns->thumbs = NULL; - tns->files = tns_files; - tns->cnt = cnt; - tns->initnext = tns->loadnext = 0; - tns->first = tns->end = tns->r_first = tns->r_end = 0; - tns->sel = sel; - tns->win = win; - tns->dirty = false; - - tns->zl = THUMB_SIZE; - tns_zoom(tns, 0); - - if ((homedir = getenv("XDG_CACHE_HOME")) == NULL || homedir[0] == '\0') { - homedir = getenv("HOME"); - dsuffix = "/.cache"; - } - if (homedir != NULL) { - const char *s = "/nsxiv"; - free(cache_dir); - len = strlen(homedir) + strlen(dsuffix) + strlen(s) + 1; - cache_dir = emalloc(len); - snprintf(cache_dir, len, "%s%s%s", homedir, dsuffix, s); - cache_tmpfile = emalloc(len + sizeof(TMP_NAME)); - memcpy(cache_tmpfile, cache_dir, len - 1); - cache_tmpfile_base = cache_tmpfile + len - 1; - } else { - error(0, 0, "Cache directory not found"); - } -} - -CLEANUP void tns_free(tns_t *tns) -{ - int i; - - if (tns->thumbs != NULL) { - for (i = 0; i < *tns->cnt; i++) - img_free(tns->thumbs[i].im, false); - free(tns->thumbs); - tns->thumbs = NULL; - } - - free(cache_dir); - cache_dir = NULL; - free(cache_tmpfile); - cache_tmpfile = cache_tmpfile_base = NULL; -} - -static Imlib_Image tns_scale_down(Imlib_Image im, int dim) -{ - int w, h; - float z, zw, zh; - - imlib_context_set_image(im); - w = imlib_image_get_width(); - h = imlib_image_get_height(); - zw = (float)dim / (float)w; - zh = (float)dim / (float)h; - z = MIN(zw, zh); - z = MIN(z, 1.0); - - if (z < 1.0) { - imlib_context_set_anti_alias(1); - im = imlib_create_cropped_scaled_image(0, 0, w, h, - MAX(z * w, 1), MAX(z * h, 1)); - if (im == NULL) - error(EXIT_FAILURE, ENOMEM, NULL); - imlib_free_image_and_decache(); - } - return im; -} - -bool tns_load(tns_t *tns, int n, bool force, bool cache_only) -{ - int maxwh = thumb_sizes[ARRLEN(thumb_sizes) - 1]; - bool cache_hit = false; - char *cfile; - thumb_t *t; - fileinfo_t *file; - Imlib_Image im = NULL; - - if (n < 0 || n >= *tns->cnt) - return false; - file = &tns->files[n]; - if (file->name == NULL || file->path == NULL) - return false; - - t = &tns->thumbs[n]; - img_free(t->im, false); - t->im = NULL; - - if (!force) { - if ((im = tns_cache_load(file->path, &force)) != NULL) { - imlib_context_set_image(im); - if (imlib_image_get_width() < maxwh && - imlib_image_get_height() < maxwh) - { - if ((cfile = tns_cache_filepath(file->path)) != NULL) { - unlink(cfile); - free(cfile); - } - imlib_free_image_and_decache(); - im = NULL; - } else { - cache_hit = true; - } -#if HAVE_LIBEXIF - } else if (!force && !options->private_mode) { - int pw = 0, ph = 0, w, h, x = 0, y = 0; - bool err; - float zw, zh; - ExifData *ed; - ExifEntry *entry; - ExifContent *ifd; - ExifByteOrder byte_order; - int tmpfd; - char tmppath[] = "/tmp/nsxiv-XXXXXX"; - Imlib_Image tmpim; - - /* UPGRADE: Imlib2 v1.10.0: avoid tempfile and use imlib_load_image_mem() */ - if ((ed = exif_data_new_from_file(file->path)) != NULL) { - if (ed->data != NULL && ed->size > 0 && - (tmpfd = mkstemp(tmppath)) >= 0) - { - err = write(tmpfd, ed->data, ed->size) != ed->size; - close(tmpfd); - - if (!err && (tmpim = imlib_load_image(tmppath)) != NULL) { - byte_order = exif_data_get_byte_order(ed); - ifd = ed->ifd[EXIF_IFD_EXIF]; - entry = exif_content_get_entry(ifd, EXIF_TAG_PIXEL_X_DIMENSION); - if (entry != NULL) - pw = exif_get_long(entry->data, byte_order); - entry = exif_content_get_entry(ifd, EXIF_TAG_PIXEL_Y_DIMENSION); - if (entry != NULL) - ph = exif_get_long(entry->data, byte_order); - - imlib_context_set_image(tmpim); - w = imlib_image_get_width(); - h = imlib_image_get_height(); - - if (pw > w && ph > h && (pw - ph >= 0) == (w - h >= 0)) { - zw = (float)pw / (float)w; - zh = (float)ph / (float)h; - if (zw < zh) { - pw /= zh; - x = (w - pw) / 2; - w = pw; - } else if (zw > zh) { - ph /= zw; - y = (h - ph) / 2; - h = ph; - } - } - if (w >= maxwh || h >= maxwh) { - if ((im = imlib_create_cropped_image(x, y, w, h)) == NULL) - error(0, 0, "%s: error generating thumbnail", file->name); - } - imlib_free_image_and_decache(); - } - unlink(tmppath); - } - exif_data_unref(ed); - } -#endif /* HAVE_LIBEXIF */ - } - } - - if (im == NULL) { - if ((im = img_open(file)) == NULL) - return false; - } - imlib_context_set_image(im); - - if (!cache_hit) { -#if HAVE_LIBEXIF - exif_auto_orientate(file); -#endif - im = tns_scale_down(im, maxwh); - imlib_context_set_image(im); - if (imlib_image_get_width() == maxwh || imlib_image_get_height() == maxwh) - tns_cache_write(im, file->path, true); - } - - if (cache_only) { - imlib_free_image_and_decache(); - } else { - t->im = tns_scale_down(im, thumb_sizes[tns->zl]); - imlib_context_set_image(t->im); - t->w = imlib_image_get_width(); - t->h = imlib_image_get_height(); - tns->dirty = true; - } - file->flags |= FF_TN_INIT; - - if (n == tns->initnext) { - while (++tns->initnext < *tns->cnt && ((++file)->flags & FF_TN_INIT)) - ; - } - if (n == tns->loadnext && !cache_only) { - while (++tns->loadnext < tns->end && (++t)->im != NULL) - ; - } - - return true; -} - -void tns_unload(tns_t *tns, int n) -{ - thumb_t *t; - - assert(n >= 0 && n < *tns->cnt); - t = &tns->thumbs[n]; - - img_free(t->im, false); - t->im = NULL; -} - -static void tns_check_view(tns_t *tns, bool scrolled) -{ - int r; - - assert(tns != NULL); - tns->first -= tns->first % tns->cols; - r = *tns->sel % tns->cols; - - if (scrolled) { - /* move selection into visible area */ - if (*tns->sel >= tns->first + tns->cols * tns->rows) - *tns->sel = tns->first + r + tns->cols * (tns->rows - 1); - else if (*tns->sel < tns->first) - *tns->sel = tns->first + r; - } else { - /* scroll to selection */ - if (tns->first + tns->cols * tns->rows <= *tns->sel) { - tns->first = *tns->sel - r - tns->cols * (tns->rows - 1); - tns->dirty = true; - } else if (tns->first > *tns->sel) { - tns->first = *tns->sel - r; - tns->dirty = true; - } - } -} - -void tns_render(tns_t *tns) -{ - thumb_t *t; - win_t *win; - int i, cnt, r, x, y; - - if (!tns->dirty) - return; - - win = tns->win; - win_clear(win); - imlib_context_set_drawable(win->buf.pm); - - tns->cols = MAX(1, win->w / tns->dim); - tns->rows = MAX(1, win->h / tns->dim); - - if (*tns->cnt < tns->cols * tns->rows) { - tns->first = 0; - cnt = *tns->cnt; - } else { - tns_check_view(tns, false); - cnt = tns->cols * tns->rows; - if ((r = tns->first + cnt - *tns->cnt) >= tns->cols) - tns->first -= r - r % tns->cols; - if (r > 0) - cnt -= r % tns->cols; - } - r = cnt % tns->cols ? 1 : 0; - tns->x = x = (win->w - MIN(cnt, tns->cols) * tns->dim) / 2 + tns->bw + 3; - tns->y = y = (win->h - (cnt / tns->cols + r) * tns->dim) / 2 + tns->bw + 3 + - (win->bar.top ? win->bar.h : 0); - tns->loadnext = *tns->cnt; - tns->end = tns->first + cnt; - - for (i = tns->r_first; i < tns->r_end; i++) { - if ((i < tns->first || i >= tns->end) && tns->thumbs[i].im != NULL) - tns_unload(tns, i); - } - tns->r_first = tns->first; - tns->r_end = tns->end; - - for (i = tns->first; i < tns->end; i++) { - t = &tns->thumbs[i]; - if (t->im != NULL) { - t->x = x + (thumb_sizes[tns->zl] - t->w) / 2; - t->y = y + (thumb_sizes[tns->zl] - t->h) / 2; - imlib_context_set_image(t->im); - imlib_render_image_on_drawable_at_size(t->x, t->y, t->w, t->h); - if (tns->files[i].flags & FF_MARK) - tns_mark(tns, i, true); - } else { - tns->loadnext = MIN(tns->loadnext, i); - } - if ((i + 1) % tns->cols == 0) { - x = tns->x; - y += tns->dim; - } else { - x += tns->dim; - } - } - tns->dirty = false; - tns_highlight(tns, *tns->sel, true); -} - -void tns_mark(tns_t *tns, int n, bool mark) -{ - if (n >= 0 && n < *tns->cnt && tns->thumbs[n].im != NULL) { - win_t *win = tns->win; - thumb_t *t = &tns->thumbs[n]; - unsigned long col = win->win_bg.pixel; - int x = t->x + t->w, y = t->y + t->h; - - win_draw_rect(win, x - 1, y + 1, 1, tns->bw, true, 1, col); - win_draw_rect(win, x + 1, y - 1, tns->bw, 1, true, 1, col); - - if (mark) - col = win->mrk_fg.pixel; - - win_draw_rect(win, x, y, tns->bw + 2, tns->bw + 2, true, 1, col); - - if (!mark && n == *tns->sel) - tns_highlight(tns, n, true); - } -} - -void tns_highlight(tns_t *tns, int n, bool hl) -{ - if (n >= 0 && n < *tns->cnt && tns->thumbs[n].im != NULL) { - win_t *win = tns->win; - thumb_t *t = &tns->thumbs[n]; - unsigned long col = hl ? win->win_fg.pixel : win->win_bg.pixel; - int oxy = (tns->bw + 1) / 2 + 1, owh = tns->bw + 2; - - win_draw_rect(win, t->x - oxy, t->y - oxy, t->w + owh, t->h + owh, - false, tns->bw, col); - - if (tns->files[n].flags & FF_MARK) - tns_mark(tns, n, true); - } -} - -bool tns_move_selection(tns_t *tns, direction_t dir, int cnt) -{ - int old, max; - - old = *tns->sel; - cnt = cnt > 1 ? cnt : 1; - - switch (dir) { - case DIR_UP: - *tns->sel = MAX(*tns->sel - cnt * tns->cols, *tns->sel % tns->cols); - break; - case DIR_DOWN: - max = tns->cols * ((*tns->cnt - 1) / tns->cols) + - MIN((*tns->cnt - 1) % tns->cols, *tns->sel % tns->cols); - *tns->sel = MIN(*tns->sel + cnt * tns->cols, max); - break; - case DIR_LEFT: - *tns->sel = MAX(*tns->sel - cnt, 0); - break; - case DIR_RIGHT: - *tns->sel = MIN(*tns->sel + cnt, *tns->cnt - 1); - break; - } - - if (*tns->sel != old) { - tns_highlight(tns, old, false); - tns_check_view(tns, false); - if (!tns->dirty) - tns_highlight(tns, *tns->sel, true); - } - return *tns->sel != old; -} - -bool tns_scroll(tns_t *tns, direction_t dir, bool screen) -{ - int d, max, old; - - old = tns->first; - d = tns->cols * (screen ? tns->rows : 1); - - if (dir == DIR_DOWN) { - max = *tns->cnt - tns->cols * tns->rows; - if (*tns->cnt % tns->cols != 0) - max += tns->cols - *tns->cnt % tns->cols; - tns->first = MIN(tns->first + d, max); - } else if (dir == DIR_UP) { - tns->first = MAX(tns->first - d, 0); - } - - if (tns->first != old) { - tns_check_view(tns, true); - tns->dirty = true; - } - return tns->first != old; -} - -bool tns_zoom(tns_t *tns, int d) -{ - int i, oldzl; - - oldzl = tns->zl; - tns->zl += -(d < 0) + (d > 0); - tns->zl = MAX(tns->zl, 0); - tns->zl = MIN(tns->zl, (int)ARRLEN(thumb_sizes) - 1); - - tns->bw = ((thumb_sizes[tns->zl] - 1) >> 5) + 1; - tns->bw = MIN(tns->bw, 4); - tns->dim = thumb_sizes[tns->zl] + 2 * tns->bw + 6; - - if (tns->zl != oldzl) { - for (i = 0; i < *tns->cnt; i++) - tns_unload(tns, i); - tns->dirty = true; - } - return tns->zl != oldzl; -} - -int tns_translate(tns_t *tns, int x, int y) -{ - int n; - - if (x < tns->x || y < tns->y) - return -1; - - n = tns->first + (y - tns->y) / tns->dim * tns->cols + - (x - tns->x) / tns->dim; - if (n >= *tns->cnt) - n = -1; - - return n; -} diff --git a/source/nsxiv/utf8.h b/source/nsxiv/utf8.h deleted file mode 100644 index a9be2d434..000000000 --- a/source/nsxiv/utf8.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Branchless UTF-8 decoder - * - * This is free and unencumbered software released into the public domain. - */ -#ifndef UTF8_H -#define UTF8_H - -#include - -/* Decode the next character, C, from BUF, reporting errors in E. - * - * Since this is a branchless decoder, four bytes will be read from the - * buffer regardless of the actual length of the next character. This - * means the buffer _must_ have at least three bytes of zero padding - * following the end of the data stream. - * - * Errors are reported in E, which will be non-zero if the parsed - * character was somehow invalid: invalid byte sequence, non-canonical - * encoding, or a surrogate half. - * - * The function returns a pointer to the next character. When an error - * occurs, this pointer will be a guess that depends on the particular - * error, but it will always advance at least one byte. - */ -static void * -utf8_decode(void *buf, uint32_t *c, int *e) -{ - static const char lengths[] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 - }; - static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; - static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; - static const int shiftc[] = {0, 18, 12, 6, 0}; - static const int shifte[] = {0, 6, 4, 2, 0}; - - unsigned char *s = buf; - int len = lengths[s[0] >> 3]; - - /* Compute the pointer to the next character early so that the next - * iteration can start working on the next character. Neither Clang - * nor GCC figure out this reordering on their own. - */ - unsigned char *next = s + len + !len; - - /* Assume a four-byte character and load four bytes. Unused bits are - * shifted out. - */ - *c = (uint32_t)(s[0] & masks[len]) << 18; - *c |= (uint32_t)(s[1] & 0x3f) << 12; - *c |= (uint32_t)(s[2] & 0x3f) << 6; - *c |= (uint32_t)(s[3] & 0x3f) << 0; - *c >>= shiftc[len]; - - /* Accumulate the various error conditions. */ - *e = (*c < mins[len]) << 6; /* non-canonical encoding */ - *e |= ((*c >> 11) == 0x1b) << 7; /* surrogate half? */ - *e |= (*c > 0x10FFFF) << 8; /* out of range? */ - *e |= (s[1] & 0xc0) >> 2; - *e |= (s[2] & 0xc0) >> 4; - *e |= (s[3] ) >> 6; - *e ^= 0x2a; /* top two bits of each tail byte correct? */ - *e >>= shifte[len]; - - return next; -} - -#endif diff --git a/source/nsxiv/util.c b/source/nsxiv/util.c deleted file mode 100644 index c7fb2103e..000000000 --- a/source/nsxiv/util.c +++ /dev/null @@ -1,294 +0,0 @@ -/* Copyright 2011-2020 Bert Muennich - * Copyright 2021-2023 nsxiv contributors - * - * This file is a part of nsxiv. - * - * nsxiv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * nsxiv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with nsxiv. If not, see . - */ - -#include "nsxiv.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern char **environ; -const char *progname = "nsxiv"; - -void *emalloc(size_t size) -{ - void *ptr; - - ptr = malloc(size); - if (ptr == NULL) - error(EXIT_FAILURE, errno, NULL); - return ptr; -} - -void *ecalloc(size_t nmemb, size_t size) -{ - void *ptr; - - ptr = calloc(nmemb, size); - if (ptr == NULL) - error(EXIT_FAILURE, errno, NULL); - return ptr; -} - -void *erealloc(void *ptr, size_t size) -{ - ptr = realloc(ptr, size); - if (ptr == NULL) - error(EXIT_FAILURE, errno, NULL); - return ptr; -} - -char *estrdup(const char *s) -{ - size_t n = strlen(s) + 1; - return memcpy(emalloc(n), s, n); -} - -void error(int eval, int err, const char *fmt, ...) -{ - va_list ap; - - if (eval == 0 && options->quiet) - return; - - fflush(stdout); - fprintf(stderr, "%s: ", progname); - va_start(ap, fmt); - if (fmt != NULL) - vfprintf(stderr, fmt, ap); - va_end(ap); - if (err != 0) - fprintf(stderr, "%s%s", fmt != NULL ? ": " : "", strerror(err)); - fputc('\n', stderr); - - if (eval != 0) - exit(eval); -} - -int r_opendir(r_dir_t *rdir, const char *dirname, bool recursive) -{ - if (*dirname == '\0') - return -1; - - if ((rdir->dir = opendir(dirname)) == NULL) { - rdir->name = NULL; - rdir->stack = NULL; - return -1; - } - - rdir->stcap = 512; - rdir->stack = emalloc(rdir->stcap * sizeof(*rdir->stack)); - rdir->stlen = 0; - - rdir->name = (char *)dirname; - rdir->d = 0; - rdir->recursive = recursive; - - return 0; -} - -int r_closedir(r_dir_t *rdir) -{ - int ret = 0; - - if (rdir->stack != NULL) { - while (rdir->stlen > 0) - free(rdir->stack[--rdir->stlen]); - free(rdir->stack); - rdir->stack = NULL; - } - - if (rdir->dir != NULL) { - if ((ret = closedir(rdir->dir)) == 0) - rdir->dir = NULL; - } - - if (rdir->d != 0) { - free(rdir->name); - rdir->name = NULL; - } - - return ret; -} - -char *r_readdir(r_dir_t *rdir, bool skip_dotfiles) -{ - size_t len; - char *filename; - struct dirent *dentry; - struct stat fstats; - - while (true) { - if (rdir->dir != NULL && (dentry = readdir(rdir->dir)) != NULL) { - if (dentry->d_name[0] == '.') { - if (skip_dotfiles) - continue; - if (dentry->d_name[1] == '\0') - continue; - if (dentry->d_name[1] == '.' && dentry->d_name[2] == '\0') - continue; - } - - len = strlen(rdir->name) + strlen(dentry->d_name) + 2; - filename = emalloc(len); - snprintf(filename, len, "%s%s%s", rdir->name, - rdir->name[strlen(rdir->name) - 1] == '/' ? "" : "/", - dentry->d_name); - - if (stat(filename, &fstats) < 0) { - free(filename); - continue; - } - if (S_ISDIR(fstats.st_mode)) { - /* put subdirectory on the stack */ - if (rdir->stlen == rdir->stcap) { - rdir->stcap *= 2; - rdir->stack = erealloc(rdir->stack, - rdir->stcap * sizeof(*rdir->stack)); - } - rdir->stack[rdir->stlen++] = filename; - continue; - } - return filename; - } - - if (rdir->recursive && rdir->stlen > 0) { - /* open next subdirectory */ - assert(rdir->dir != NULL); - closedir(rdir->dir); - if (rdir->d != 0) - free(rdir->name); - rdir->name = rdir->stack[--rdir->stlen]; - rdir->d = 1; - if ((rdir->dir = opendir(rdir->name)) == NULL) - error(0, errno, "%s", rdir->name); - continue; - } - /* no more entries */ - break; - } - return NULL; -} - -int r_mkdir(char *path) -{ - int rc = 0; - char c, *s = path; - struct stat st; - - while (*s != '\0' && rc == 0) { - if (*s == '/') { - s++; - continue; - } - for (; *s != '\0' && *s != '/'; s++) - ; - c = *s; - *s = '\0'; - if (mkdir(path, 0755) == -1) { - if (errno != EEXIST || stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) { - error(0, errno, "%s", path); - rc = -1; - } - } - *s = c; - } - return rc; -} - -void construct_argv(char **argv, unsigned int len, ...) -{ - unsigned int i; - va_list args; - - va_start(args, len); - for (i = 0; i < len; ++i) - argv[i] = va_arg(args, char *); - va_end(args); - assert(argv[len - 1] == NULL && "argv should be NULL terminated"); -} - -static int mkspawn_pipe(posix_spawn_file_actions_t *fa, const char *cmd, int *pfd, int dupidx) -{ - int err; - if (pipe(pfd) < 0) { - error(0, errno, "pipe: %s", cmd); - return -1; - } - err = posix_spawn_file_actions_adddup2(fa, pfd[dupidx], dupidx); - err = err ? err : posix_spawn_file_actions_addclose(fa, pfd[0]); - err = err ? err : posix_spawn_file_actions_addclose(fa, pfd[1]); - if (err) { - error(0, err, "posix_spawn_file_actions: %s", cmd); - close(pfd[0]); - close(pfd[1]); - } - return err ? -1 : 0; -} - -pid_t spawn(int *readfd, int *writefd, char *const argv[]) -{ - pid_t pid = -1; - const char *cmd; - int err, pfd_read[2], pfd_write[2]; - posix_spawn_file_actions_t fa; - - assert(argv != NULL && argv[0] != NULL); - cmd = argv[0]; - - if ((err = posix_spawn_file_actions_init(&fa)) != 0) { - error(0, err, "spawn: %s", cmd); - return pid; - } - - if (readfd != NULL && mkspawn_pipe(&fa, cmd, pfd_read, 1) < 0) - goto err_destroy_fa; - if (writefd != NULL && mkspawn_pipe(&fa, cmd, pfd_write, 0) < 0) - goto err_close_readfd; - - if ((err = posix_spawnp(&pid, cmd, &fa, NULL, argv, environ)) != 0) { - error(0, err, "spawn: %s", cmd); - } else { - if (readfd != NULL) - *readfd = pfd_read[0]; - if (writefd != NULL) - *writefd = pfd_write[1]; - } - - if (writefd != NULL) { - close(pfd_write[0]); - if (pid < 0) - close(pfd_write[1]); - } -err_close_readfd: - if (readfd != NULL) { - if (pid < 0) - close(pfd_read[0]); - close(pfd_read[1]); - } -err_destroy_fa: - posix_spawn_file_actions_destroy(&fa); - return pid; -} diff --git a/source/nsxiv/window.c b/source/nsxiv/window.c deleted file mode 100644 index 387d78926..000000000 --- a/source/nsxiv/window.c +++ /dev/null @@ -1,547 +0,0 @@ -/* Copyright 2011-2020 Bert Muennich - * Copyright 2021-2023 nsxiv contributors - * - * This file is a part of nsxiv. - * - * nsxiv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * nsxiv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with nsxiv. If not, see . - */ - -#include "nsxiv.h" -#define INCLUDE_WINDOW_CONFIG -#include "config.h" -#include "icon/data.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#if HAVE_LIBFONTS -#include "utf8.h" -#define UTF8_PADDING 4 /* utf8_decode requires 4 bytes of zero padding */ -#define TEXTWIDTH(win, text, len) \ - win_draw_text(win, NULL, NULL, 0, 0, text, len, 0) -#endif - -#define RES_CLASS "Nsxiv" -#define INIT_ATOM_(atom) \ - atoms[ATOM_##atom] = XInternAtom(e->dpy, #atom, False); - -enum { - H_TEXT_PAD = 5, - V_TEXT_PAD = 1 -}; - -Atom atoms[ATOM_COUNT]; - -static GC gc; -static int barheight; -static struct { - int name; - Cursor icon; -} cursors[CURSOR_COUNT] = { - { XC_left_ptr }, - { XC_dotbox }, - { XC_fleur }, - { XC_watch }, - { XC_sb_left_arrow }, - { XC_sb_right_arrow } -}; - -#if HAVE_LIBFONTS -static XftFont *font; -static double fontsize; -#endif - -#if HAVE_LIBFONTS -static void win_init_font(const win_env_t *e, const char *fontstr) -{ - int fontheight = 0; - if ((font = XftFontOpenName(e->dpy, e->scr, fontstr)) == NULL) - error(EXIT_FAILURE, 0, "Error loading font '%s'", fontstr); - fontheight = font->ascent + font->descent; - FcPatternGetDouble(font->pattern, FC_SIZE, 0, &fontsize); - barheight = fontheight + 2 * V_TEXT_PAD; -} - -static void xft_alloc_color(const win_env_t *e, const char *name, XftColor *col) -{ - if (!XftColorAllocName(e->dpy, e->vis, e->cmap, name, col)) - error(EXIT_FAILURE, 0, "Error allocating color '%s'", name); -} -#endif /* HAVE_LIBFONTS */ - -static void win_alloc_color(const win_env_t *e, const char *name, XColor *col) -{ - XColor screen; - if (!XAllocNamedColor(e->dpy, e->cmap, name, &screen, col)) - error(EXIT_FAILURE, 0, "Error allocating color '%s'", name); -} - -static const char *win_res(XrmDatabase db, const char *name, const char *def) -{ - char *type; - XrmValue ret; - - if (db != NULL && - XrmGetResource(db, name, name, &type, &ret) && - STREQ(type, "String") && *ret.addr != '\0') - { - return ret.addr; - } else { - return def; - } -} - -void win_init(win_t *win) -{ - win_env_t *e; - const char *win_bg, *win_fg, *mrk_fg; - char *res_man; - XrmDatabase db; -#if HAVE_LIBFONTS - const char *bar_fg, *bar_bg, *f; - - static char lbuf[512 + UTF8_PADDING], rbuf[64 + UTF8_PADDING]; -#endif - - memset(win, 0, sizeof(*win)); - - e = &win->env; - if ((e->dpy = XOpenDisplay(NULL)) == NULL) - error(EXIT_FAILURE, 0, "Error opening X display"); - - e->scr = DefaultScreen(e->dpy); - e->scrw = DisplayWidth(e->dpy, e->scr); - e->scrh = DisplayHeight(e->dpy, e->scr); - e->depth = DefaultDepth(e->dpy, e->scr); - e->vis = DefaultVisual(e->dpy, e->scr); - e->cmap = DefaultColormap(e->dpy, e->scr); - - if (setlocale(LC_CTYPE, "") == NULL || XSupportsLocale() == 0) - error(0, 0, "No locale support"); - - XrmInitialize(); - res_man = XResourceManagerString(e->dpy); - db = res_man == NULL ? NULL : XrmGetStringDatabase(res_man); - - win_bg = win_res(db, WIN_BG[0], WIN_BG[1] ? WIN_BG[1] : "white"); - win_fg = win_res(db, WIN_FG[0], WIN_FG[1] ? WIN_FG[1] : "black"); - mrk_fg = win_res(db, MARK_FG[0], MARK_FG[1] ? MARK_FG[1] : win_fg); - win_alloc_color(e, win_bg, &win->win_bg); - win_alloc_color(e, win_fg, &win->win_fg); - win_alloc_color(e, mrk_fg, &win->mrk_fg); - -#if HAVE_LIBFONTS - bar_bg = win_res(db, BAR_BG[0], BAR_BG[1] ? BAR_BG[1] : win_bg); - bar_fg = win_res(db, BAR_FG[0], BAR_FG[1] ? BAR_FG[1] : win_fg); - xft_alloc_color(e, bar_bg, &win->bar_bg); - xft_alloc_color(e, bar_fg, &win->bar_fg); - - f = win_res(db, BAR_FONT[0], BAR_FONT[1] ? BAR_FONT[1] : "monospace-8"); - win_init_font(e, f); - - win->bar.l.buf = lbuf; - win->bar.r.buf = rbuf; - win->bar.l.size = sizeof(lbuf) - UTF8_PADDING; - win->bar.r.size = sizeof(rbuf) - UTF8_PADDING; - win->bar.h = options->hide_bar ? 0 : barheight; - win->bar.top = TOP_STATUSBAR; -#endif /* HAVE_LIBFONTS */ - - XrmDestroyDatabase(db); - INIT_ATOM_(WM_DELETE_WINDOW); - INIT_ATOM_(_NET_WM_NAME); - INIT_ATOM_(_NET_WM_ICON_NAME); - INIT_ATOM_(_NET_WM_ICON); - INIT_ATOM_(_NET_WM_STATE); - INIT_ATOM_(_NET_WM_PID); - INIT_ATOM_(_NET_WM_STATE_FULLSCREEN); - INIT_ATOM_(UTF8_STRING); - INIT_ATOM_(WM_NAME); - INIT_ATOM_(WM_ICON_NAME); -} - -void win_open(win_t *win) -{ - int c, i, j, n; - Window parent; - win_env_t *e; - XClassHint classhint; - unsigned long *icon_data; - XColor col; - Cursor *cnone = &cursors[CURSOR_NONE].icon; - char none_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - Pixmap none; - int gmask; - XSizeHints sizehints; - XWMHints hints; - long pid; - char hostname[256]; - XSetWindowAttributes attrs; - char res_class[] = RES_CLASS; - char res_name[] = "nsxiv"; - - e = &win->env; - parent = options->embed ? options->embed : RootWindow(e->dpy, e->scr); - - sizehints.flags = PWinGravity; - sizehints.win_gravity = NorthWestGravity; - - /* determine window offsets, width & height */ - if (options->geometry == NULL) - gmask = 0; - else - gmask = XParseGeometry(options->geometry, &win->x, &win->y, - &win->w, &win->h); - if (gmask & WidthValue) - sizehints.flags |= USSize; - else - win->w = WIN_WIDTH; - if (gmask & HeightValue) - sizehints.flags |= USSize; - else - win->h = WIN_HEIGHT; - if (gmask & XValue) { - if (gmask & XNegative) { - win->x += e->scrw - win->w; - sizehints.win_gravity = NorthEastGravity; - } - sizehints.flags |= USPosition; - } else { - win->x = 0; - } - if (gmask & YValue) { - if (gmask & YNegative) { - win->y += e->scrh - win->h; - sizehints.win_gravity = sizehints.win_gravity == NorthEastGravity ? - SouthEastGravity : SouthWestGravity; - } - sizehints.flags |= USPosition; - } else { - win->y = 0; - } - - attrs.colormap = e->cmap; - attrs.border_pixel = 0; - - win->xwin = XCreateWindow(e->dpy, parent, win->x, win->y, win->w, win->h, 0, - e->depth, InputOutput, e->vis, - CWColormap | CWBorderPixel, &attrs); - if (win->xwin == None) - error(EXIT_FAILURE, 0, "Error creating X window"); - - /* set the _NET_WM_PID */ - pid = getpid(); - XChangeProperty(e->dpy, win->xwin, atoms[ATOM__NET_WM_PID], XA_CARDINAL, - 32, PropModeReplace, (unsigned char *)&pid, 1); - if (gethostname(hostname, ARRLEN(hostname)) == 0) { - XTextProperty tp; - tp.value = (unsigned char *)hostname; - tp.nitems = strnlen(hostname, ARRLEN(hostname)); - tp.encoding = XA_STRING; - tp.format = 8; - XSetWMClientMachine(e->dpy, win->xwin, &tp); - } - - XSelectInput(e->dpy, win->xwin, - ButtonReleaseMask | ButtonPressMask | KeyPressMask | - PointerMotionMask | StructureNotifyMask); - - for (i = 0; i < (int)ARRLEN(cursors); i++) { - if (i != CURSOR_NONE) - cursors[i].icon = XCreateFontCursor(e->dpy, cursors[i].name); - } - if (XAllocNamedColor(e->dpy, e->cmap, "black", &col, &col) == 0) - error(EXIT_FAILURE, 0, "Error allocating color 'black'"); - - none = XCreateBitmapFromData(e->dpy, win->xwin, none_data, 8, 8); - *cnone = XCreatePixmapCursor(e->dpy, none, none, &col, &col, 0, 0); - - gc = XCreateGC(e->dpy, win->xwin, 0, None); - - n = icons[ARRLEN(icons) - 1].size; - icon_data = emalloc((n * n + 2) * sizeof(*icon_data)); - - for (i = 0; i < (int)ARRLEN(icons); i++) { - n = 0; - icon_data[n++] = icons[i].size; - icon_data[n++] = icons[i].size; - - for (j = 0; j < (int)icons[i].cnt; j++) { - for (c = icons[i].data[j] >> 4; c >= 0; c--) - icon_data[n++] = icon_colors[icons[i].data[j] & 0x0F]; - } - XChangeProperty(e->dpy, win->xwin, atoms[ATOM__NET_WM_ICON], XA_CARDINAL, 32, - i == 0 ? PropModeReplace : PropModeAppend, - (unsigned char *)icon_data, n); - } - free(icon_data); - - win_set_title(win, res_name, strlen(res_name)); - classhint.res_class = res_class; - classhint.res_name = options->res_name != NULL ? options->res_name : res_name; - XSetClassHint(e->dpy, win->xwin, &classhint); - - XSetWMProtocols(e->dpy, win->xwin, &atoms[ATOM_WM_DELETE_WINDOW], 1); - - sizehints.width = win->w; - sizehints.height = win->h; - sizehints.x = win->x; - sizehints.y = win->y; - XSetWMNormalHints(win->env.dpy, win->xwin, &sizehints); - - hints.flags = InputHint | StateHint; - hints.input = 1; - hints.initial_state = NormalState; - XSetWMHints(win->env.dpy, win->xwin, &hints); - - if (options->fullscreen) { - XChangeProperty(e->dpy, win->xwin, atoms[ATOM__NET_WM_STATE], - XA_ATOM, 32, PropModeReplace, - (unsigned char *)&atoms[ATOM__NET_WM_STATE_FULLSCREEN], 1); - } - - win->h -= win->bar.h; - - win->buf.w = e->scrw; - win->buf.h = e->scrh; - win->buf.pm = XCreatePixmap(e->dpy, win->xwin, win->buf.w, win->buf.h, e->depth); - - XSetForeground(e->dpy, gc, win->win_bg.pixel); - XFillRectangle(e->dpy, win->buf.pm, gc, 0, 0, win->buf.w, win->buf.h); - XSetWindowBackgroundPixmap(e->dpy, win->xwin, win->buf.pm); - XMapWindow(e->dpy, win->xwin); - XFlush(e->dpy); -} - -CLEANUP void win_close(win_t *win) -{ - unsigned int i; - - for (i = 0; i < ARRLEN(cursors); i++) - XFreeCursor(win->env.dpy, cursors[i].icon); - - XFreeGC(win->env.dpy, gc); -#if HAVE_LIBFONTS - XftFontClose(win->env.dpy, font); -#endif - XDestroyWindow(win->env.dpy, win->xwin); - XCloseDisplay(win->env.dpy); -} - -bool win_configure(win_t *win, XConfigureEvent *c) -{ - bool changed; - - changed = win->w != (unsigned int)c->width || win->h + win->bar.h != (unsigned int)c->height; - - win->x = c->x; - win->y = c->y; - win->w = c->width; - win->h = c->height - win->bar.h; - win->bw = c->border_width; - - return changed; -} - -void win_toggle_fullscreen(win_t *win) -{ - XEvent ev; - XClientMessageEvent *cm; - - memset(&ev, 0, sizeof(ev)); - ev.type = ClientMessage; - - cm = &ev.xclient; - cm->window = win->xwin; - cm->message_type = atoms[ATOM__NET_WM_STATE]; - cm->format = 32; - cm->data.l[0] = 2; /* toggle */ - cm->data.l[1] = atoms[ATOM__NET_WM_STATE_FULLSCREEN]; - - XSendEvent(win->env.dpy, DefaultRootWindow(win->env.dpy), False, - SubstructureNotifyMask | SubstructureRedirectMask, &ev); -} - -void win_toggle_bar(win_t *win) -{ - if (win->bar.h != 0) { - win->h += win->bar.h; - win->bar.h = 0; - } else { - win->bar.h = barheight; - win->h -= win->bar.h; - } -} - -void win_clear(win_t *win) -{ - win_env_t *e = &win->env; - - if (win->w > win->buf.w || win->h + win->bar.h > win->buf.h) { - XFreePixmap(e->dpy, win->buf.pm); - win->buf.w = MAX(win->buf.w, win->w); - win->buf.h = MAX(win->buf.h, win->h + win->bar.h); - win->buf.pm = XCreatePixmap(e->dpy, win->xwin, - win->buf.w, win->buf.h, e->depth); - } - XSetForeground(e->dpy, gc, win->win_bg.pixel); - XFillRectangle(e->dpy, win->buf.pm, gc, 0, 0, win->buf.w, win->buf.h); -} - -#if HAVE_LIBFONTS -static int win_draw_text(win_t *win, XftDraw *d, const XftColor *color, - int x, int y, char *text, int len, int w) -{ - int err, tw = 0, warned = 0; - char *t, *next; - uint32_t rune; - XftFont *f; - FcCharSet *fccharset; - XGlyphInfo ext; - - for (t = text; t - text < len; t = next) { - err = 0; - next = utf8_decode(t, &rune, &err); - if (err) { - if (!warned) - error(0, 0, "error decoding utf8 status-bar text"); - warned = 1; - continue; - } - if (XftCharExists(win->env.dpy, font, rune)) { - f = font; - } else { /* fallback font */ - fccharset = FcCharSetCreate(); - FcCharSetAddChar(fccharset, rune); - f = XftFontOpen(win->env.dpy, win->env.scr, FC_CHARSET, FcTypeCharSet, - fccharset, FC_SCALABLE, FcTypeBool, FcTrue, - FC_SIZE, FcTypeDouble, fontsize, NULL); - FcCharSetDestroy(fccharset); - } - XftTextExtentsUtf8(win->env.dpy, f, (XftChar8 *)t, next - t, &ext); - tw += ext.xOff; - if (tw <= w) { - XftDrawStringUtf8(d, color, f, x, y, (XftChar8 *)t, next - t); - x += ext.xOff; - } - if (f != font) - XftFontClose(win->env.dpy, f); - } - return tw; -} - -static void win_draw_bar(win_t *win) -{ - int len, x, y, w, tw; - win_env_t *e; - win_bar_t *l, *r; - XftDraw *d; - - e = &win->env; - l = &win->bar.l; - r = &win->bar.r; - assert(l->buf != NULL && r->buf != NULL); - y = (win->bar.top ? 0 : win->h) + font->ascent + V_TEXT_PAD; - w = win->w - 2 * H_TEXT_PAD; - d = XftDrawCreate(e->dpy, win->buf.pm, e->vis, e->cmap); - - XSetForeground(e->dpy, gc, win->bar_bg.pixel); - XFillRectangle(e->dpy, win->buf.pm, gc, 0, win->bar.top ? 0 : win->h, win->w, win->bar.h); - - XSetForeground(e->dpy, gc, win->win_bg.pixel); - XSetBackground(e->dpy, gc, win->bar_bg.pixel); - - if ((len = strlen(r->buf)) > 0) { - if ((tw = TEXTWIDTH(win, r->buf, len)) > w) { - XftDrawDestroy(d); - return; - } - x = win->w - tw - H_TEXT_PAD; - w -= tw; - win_draw_text(win, d, &win->bar_fg, x, y, r->buf, len, tw); - } - if ((len = strlen(l->buf)) > 0) { - x = H_TEXT_PAD; - w -= 2 * H_TEXT_PAD; /* gap between left and right parts */ - win_draw_text(win, d, &win->bar_fg, x, y, l->buf, len, w); - } - XftDrawDestroy(d); -} -#else -static void win_draw_bar(win_t *win) -{ - (void)win; -} -#endif /* HAVE_LIBFONTS */ - -void win_draw(win_t *win) -{ - if (win->bar.h > 0) - win_draw_bar(win); - - XSetWindowBackgroundPixmap(win->env.dpy, win->xwin, win->buf.pm); - XClearWindow(win->env.dpy, win->xwin); - XFlush(win->env.dpy); -} - -void win_draw_rect(win_t *win, int x, int y, int w, int h, bool fill, int lw, - unsigned long col) -{ - XGCValues gcval; - - gcval.line_width = lw; - gcval.foreground = col; - XChangeGC(win->env.dpy, gc, GCForeground | GCLineWidth, &gcval); - - if (fill) - XFillRectangle(win->env.dpy, win->buf.pm, gc, x, y, w, h); - else - XDrawRectangle(win->env.dpy, win->buf.pm, gc, x, y, w, h); -} - -void win_set_title(win_t *win, const char *title, size_t len) -{ - int i, targets[] = { ATOM_WM_NAME, ATOM_WM_ICON_NAME, ATOM__NET_WM_NAME, ATOM__NET_WM_ICON_NAME }; - - for (i = 0; i < (int)ARRLEN(targets); ++i) { - XChangeProperty(win->env.dpy, win->xwin, atoms[targets[i]], - atoms[ATOM_UTF8_STRING], 8, PropModeReplace, - (unsigned char *)title, len); - } -} - -void win_set_cursor(win_t *win, cursor_t cursor) -{ - if (cursor >= 0 && cursor < ARRLEN(cursors)) { - XDefineCursor(win->env.dpy, win->xwin, cursors[cursor].icon); - XFlush(win->env.dpy); - } -} - -void win_cursor_pos(win_t *win, int *x, int *y) -{ - int i; - unsigned int ui; - Window w; - - if (!XQueryPointer(win->env.dpy, win->xwin, &w, &w, &i, &i, x, y, &ui)) - *x = *y = 0; -} diff --git a/source/st/st b/source/st/st new file mode 100755 index 0000000000000000000000000000000000000000..05490b7fe143d73cdf546c52c996ec50b1a5a8e0 GIT binary patch literal 133464 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