From 556978c7d042bb8dbc9f96b2012ee3bb458952d0 Mon Sep 17 00:00:00 2001 From: Clay Gomera Date: Mon, 19 Dec 2022 20:15:21 -0400 Subject: [PATCH] switched to dwm bruh --- {user/.config => extras}/awesome/apps.lua | 0 .../.config => extras}/awesome/autostart.lua | 0 {user/.config => extras}/awesome/helpers.lua | 0 .../awesome/keymaps/keyboard.lua | 0 .../awesome/keymaps/mouse.lua | 0 .../modules/custom-layouts/centerwork.lua | 0 {user/.config => extras}/awesome/rc.lua | 0 {user/.config => extras}/awesome/ui/bar.lua | 0 .../awesome/ui/icons/centerworkw.png | Bin .../awesome/ui/icons/cornerne.png | Bin .../awesome/ui/icons/cornernew.png | Bin .../awesome/ui/icons/cornernw.png | Bin .../awesome/ui/icons/cornernww.png | Bin .../awesome/ui/icons/cornerse.png | Bin .../awesome/ui/icons/cornersew.png | Bin .../awesome/ui/icons/cornersw.png | Bin .../awesome/ui/icons/cornersww.png | Bin .../awesome/ui/icons/dwindle.png | Bin .../awesome/ui/icons/dwindlew.png | Bin .../awesome/ui/icons/fairh.png | Bin .../awesome/ui/icons/fairhw.png | Bin .../awesome/ui/icons/fairv.png | Bin .../awesome/ui/icons/fairvw.png | Bin .../awesome/ui/icons/floating.png | Bin .../awesome/ui/icons/floatingw.png | Bin .../awesome/ui/icons/fullscreen.png | Bin .../awesome/ui/icons/fullscreenw.png | Bin .../awesome/ui/icons/magnifier.png | Bin .../awesome/ui/icons/magnifierw.png | Bin .../awesome/ui/icons/max.png | Bin .../awesome/ui/icons/maxw.png | Bin .../awesome/ui/icons/spiral.png | Bin .../awesome/ui/icons/spiralw.png | Bin .../awesome/ui/icons/tile.png | Bin .../awesome/ui/icons/tilebottom.png | Bin .../awesome/ui/icons/tilebottomw.png | Bin .../awesome/ui/icons/tileleft.png | Bin .../awesome/ui/icons/tileleftw.png | Bin .../awesome/ui/icons/tiletop.png | Bin .../awesome/ui/icons/tiletopw.png | Bin .../awesome/ui/icons/tilew.png | Bin .../.config => extras}/awesome/ui/layouts.lua | 0 {user/.config => extras}/awesome/ui/notif.lua | 0 {user/.config => extras}/awesome/ui/rules.lua | 0 {user/.config => extras}/awesome/ui/theme.lua | 0 .../awesome/ui/widgets/battery | 0 .../awesome/ui/widgets/brightness | 0 .../awesome/ui/widgets/layout | 0 .../awesome/ui/widgets/volume | 0 .../awesome/ui/widgets/wifi | 0 {user/.config => extras}/dunst/critical.png | Bin {user/.config => extras}/dunst/dunstrc | 0 {user/.config => extras}/dunst/normal.png | Bin {user/.moc => extras/moc}/config | 0 {user/.moc => extras/moc}/themes/black_theme | 0 .../.moc => extras/moc}/themes/darkdot_theme | 0 .../.moc => extras/moc}/themes/example_theme | 0 {user/.moc => extras/moc}/themes/green_theme | 0 {user/.moc => extras/moc}/themes/moca_theme | 0 .../.moc => extras/moc}/themes/nightly_theme | 0 {user/.moc => extras/moc}/themes/red_theme | 0 .../moc}/themes/transparent-background | 0 .../moc}/themes/yellow_red_theme | 0 .../.config => extras}/musikcube/hotkeys.json | 0 {user/.config => extras}/rofi/config.rasi | 0 .../.config => extras}/rofi/scripts/rofi_edit | 0 .../rofi/scripts/rofi_emoji | 0 .../rofi/scripts/rofi_power | 0 .../rofi/scripts/rofi_scrot | 0 .../.config => extras}/rofi/scripts/rofi_wall | 0 .../.config => extras}/rofi/scripts/rofi_wifi | 0 .../rofi/themes/tokyonight.rasi | 0 {user/.config => extras}/starship.toml | 0 user/.bash_profile | 4 - user/.config/gtk-3.0/settings.ini | 10 +- user/.config/suckless/dmenu/Makefile | 64 + user/.config/suckless/dmenu/arg.h | 49 + user/.config/suckless/dmenu/config.def.h | 27 + user/.config/suckless/dmenu/config.mk | 32 + user/.config/suckless/dmenu/dmenu.1 | 197 ++ user/.config/suckless/dmenu/dmenu.c | 1073 ++++++++ user/.config/suckless/dmenu/dmenu_path | 13 + user/.config/suckless/dmenu/dmenu_run | 2 + user/.config/suckless/dmenu/drw.c | 450 ++++ user/.config/suckless/dmenu/drw.h | 58 + .../dmenu/patches/dmenu-fuzzymatch-4.9.diff | 163 ++ .../dmenu-highlight-20201211-fcdc159.diff | 97 + .../dmenu/patches/dmenu-mousesupport-5.2.diff | 144 + .../dmenu-mousesupport-motion-5.2.diff | 53 + .../dmenu/patches/dmenu-password-5.0.diff | 103 + .../.config/suckless/dmenu/scripts/dmenu_blue | 317 +++ .../.config/suckless/dmenu/scripts/dmenu_drun | 12 + .../.config/suckless/dmenu/scripts/dmenu_edit | 26 + .../suckless/dmenu/scripts/dmenu_emoji | 1839 +++++++++++++ .../suckless/dmenu/scripts/dmenu_power | 56 + .../suckless/dmenu/scripts/dmenu_scrot | 63 + .../.config/suckless/dmenu/scripts/dmenu_wall | 44 + .../.config/suckless/dmenu/scripts/dmenu_wifi | 86 + user/.config/suckless/dmenu/stest.1 | 90 + user/.config/suckless/dmenu/stest.c | 109 + user/.config/suckless/dmenu/util.c | 36 + user/.config/suckless/dmenu/util.h | 8 + user/.config/suckless/dwm/Makefile | 51 + user/.config/suckless/dwm/autostart | 12 + user/.config/suckless/dwm/config.def.h | 291 ++ user/.config/suckless/dwm/config.mk | 39 + user/.config/suckless/dwm/dependencies.txt | 24 + user/.config/suckless/dwm/drw.c | 451 ++++ user/.config/suckless/dwm/drw.h | 58 + user/.config/suckless/dwm/dwm.1 | 209 ++ user/.config/suckless/dwm/dwm.c | 2330 +++++++++++++++++ user/.config/suckless/dwm/dwm.png | Bin 0 -> 373 bytes .../dwm-alwayscenter-20200625-f04cac6.diff | 12 + .../dwm/patches/dwm-attachbottom-6.3.diff | 54 + .../dwm-autostart-20161205-bb3bd6f.diff | 39 + .../patches/dwm-pertag-20200914-61bb8b2.diff | 177 ++ .../patches/dwm-restartsig-20180523-6.2.diff | 139 + .../dwm-scratchpads-20200414-728d397b.diff | 199 ++ .../dwm/patches/dwm-statuspadding-6.3.diff | 62 + .../suckless/dwm/patches/dwm-warp-6.2.diff | 58 + user/.config/suckless/dwm/tcl.c | 74 + user/.config/suckless/dwm/transient.c | 42 + user/.config/suckless/dwm/util.c | 36 + user/.config/suckless/dwm/util.h | 8 + user/.config/suckless/dwmblocks/Makefile | 14 + user/.config/suckless/dwmblocks/blocks.def.h | 22 + user/.config/suckless/dwmblocks/dwmblocks.c | 175 ++ .../suckless/dwmblocks/scripts/block_battery | 13 + .../dwmblocks/scripts/block_brightness | 4 + .../suckless/dwmblocks/scripts/block_clock | 4 + .../suckless/dwmblocks/scripts/block_cpu | 3 + .../suckless/dwmblocks/scripts/block_layout | 3 + .../suckless/dwmblocks/scripts/block_memory | 3 + .../suckless/dwmblocks/scripts/block_volume | 5 + .../suckless/dwmblocks/scripts/block_wifi | 11 + user/.config/suckless/herbe/.gitignore | 3 + user/.config/suckless/herbe/LICENSE | 21 + user/.config/suckless/herbe/Makefile | 25 + user/.config/suckless/herbe/README.md | 139 + user/.config/suckless/herbe/config.def.h | 19 + user/.config/suckless/herbe/herbe.c | 233 ++ user/.config/suckless/herbe/notify-send | 81 + user/.config/suckless/herbe/patches/19.diff | 100 + user/.config/suckless/slock/Makefile | 61 + user/.config/suckless/slock/arg.h | 65 + user/.config/suckless/slock/config.def.h | 28 + user/.config/suckless/slock/config.mk | 32 + user/.config/suckless/slock/explicit_bzero.c | 19 + user/.config/suckless/slock/lockscreen.png | Bin 0 -> 15760 bytes ...ock-background-image-20220318-1c5a538.diff | 149 ++ .../slock-colormessage-20200210-35633d4.diff | 284 ++ .../slock-pam_auth-20190207-35633d4.diff | 154 ++ user/.config/suckless/slock/slock.1 | 46 + user/.config/suckless/slock/slock.c | 635 +++++ user/.config/suckless/slock/util.h | 2 + user/.gtkrc-2.0 | 10 +- user/.icons/default/index.theme | 2 +- user/.xinitrc | 4 - 158 files changed, 11938 insertions(+), 21 deletions(-) rename {user/.config => extras}/awesome/apps.lua (100%) rename {user/.config => extras}/awesome/autostart.lua (100%) rename {user/.config => extras}/awesome/helpers.lua (100%) rename {user/.config => extras}/awesome/keymaps/keyboard.lua (100%) rename {user/.config => extras}/awesome/keymaps/mouse.lua (100%) rename {user/.config => extras}/awesome/modules/custom-layouts/centerwork.lua (100%) rename {user/.config => extras}/awesome/rc.lua (100%) rename {user/.config => extras}/awesome/ui/bar.lua (100%) rename {user/.config => extras}/awesome/ui/icons/centerworkw.png (100%) rename {user/.config => extras}/awesome/ui/icons/cornerne.png (100%) rename {user/.config => extras}/awesome/ui/icons/cornernew.png (100%) rename {user/.config => extras}/awesome/ui/icons/cornernw.png (100%) rename {user/.config => extras}/awesome/ui/icons/cornernww.png (100%) rename {user/.config => extras}/awesome/ui/icons/cornerse.png (100%) rename {user/.config => extras}/awesome/ui/icons/cornersew.png (100%) rename {user/.config => extras}/awesome/ui/icons/cornersw.png (100%) rename {user/.config => extras}/awesome/ui/icons/cornersww.png (100%) rename {user/.config => extras}/awesome/ui/icons/dwindle.png (100%) rename {user/.config => extras}/awesome/ui/icons/dwindlew.png (100%) rename {user/.config => extras}/awesome/ui/icons/fairh.png (100%) rename {user/.config => extras}/awesome/ui/icons/fairhw.png (100%) rename {user/.config => extras}/awesome/ui/icons/fairv.png (100%) rename {user/.config => extras}/awesome/ui/icons/fairvw.png (100%) rename {user/.config => extras}/awesome/ui/icons/floating.png (100%) rename {user/.config => extras}/awesome/ui/icons/floatingw.png (100%) rename {user/.config => extras}/awesome/ui/icons/fullscreen.png (100%) rename {user/.config => extras}/awesome/ui/icons/fullscreenw.png (100%) rename {user/.config => extras}/awesome/ui/icons/magnifier.png (100%) rename {user/.config => extras}/awesome/ui/icons/magnifierw.png (100%) rename {user/.config => extras}/awesome/ui/icons/max.png (100%) rename {user/.config => extras}/awesome/ui/icons/maxw.png (100%) rename {user/.config => extras}/awesome/ui/icons/spiral.png (100%) rename {user/.config => extras}/awesome/ui/icons/spiralw.png (100%) rename {user/.config => extras}/awesome/ui/icons/tile.png (100%) rename {user/.config => extras}/awesome/ui/icons/tilebottom.png (100%) rename {user/.config => extras}/awesome/ui/icons/tilebottomw.png (100%) rename {user/.config => extras}/awesome/ui/icons/tileleft.png (100%) rename {user/.config => extras}/awesome/ui/icons/tileleftw.png (100%) rename {user/.config => extras}/awesome/ui/icons/tiletop.png (100%) rename {user/.config => extras}/awesome/ui/icons/tiletopw.png (100%) rename {user/.config => extras}/awesome/ui/icons/tilew.png (100%) rename {user/.config => extras}/awesome/ui/layouts.lua (100%) rename {user/.config => extras}/awesome/ui/notif.lua (100%) rename {user/.config => extras}/awesome/ui/rules.lua (100%) rename {user/.config => extras}/awesome/ui/theme.lua (100%) rename {user/.config => extras}/awesome/ui/widgets/battery (100%) rename {user/.config => extras}/awesome/ui/widgets/brightness (100%) rename {user/.config => extras}/awesome/ui/widgets/layout (100%) rename {user/.config => extras}/awesome/ui/widgets/volume (100%) rename {user/.config => extras}/awesome/ui/widgets/wifi (100%) rename {user/.config => extras}/dunst/critical.png (100%) rename {user/.config => extras}/dunst/dunstrc (100%) rename {user/.config => extras}/dunst/normal.png (100%) rename {user/.moc => extras/moc}/config (100%) rename {user/.moc => extras/moc}/themes/black_theme (100%) rename {user/.moc => extras/moc}/themes/darkdot_theme (100%) rename {user/.moc => extras/moc}/themes/example_theme (100%) rename {user/.moc => extras/moc}/themes/green_theme (100%) rename {user/.moc => extras/moc}/themes/moca_theme (100%) rename {user/.moc => extras/moc}/themes/nightly_theme (100%) rename {user/.moc => extras/moc}/themes/red_theme (100%) rename {user/.moc => extras/moc}/themes/transparent-background (100%) rename {user/.moc => extras/moc}/themes/yellow_red_theme (100%) rename {user/.config => extras}/musikcube/hotkeys.json (100%) rename {user/.config => extras}/rofi/config.rasi (100%) rename {user/.config => extras}/rofi/scripts/rofi_edit (100%) rename {user/.config => extras}/rofi/scripts/rofi_emoji (100%) rename {user/.config => extras}/rofi/scripts/rofi_power (100%) rename {user/.config => extras}/rofi/scripts/rofi_scrot (100%) rename {user/.config => extras}/rofi/scripts/rofi_wall (100%) rename {user/.config => extras}/rofi/scripts/rofi_wifi (100%) rename {user/.local/share => extras}/rofi/themes/tokyonight.rasi (100%) rename {user/.config => extras}/starship.toml (100%) create mode 100644 user/.config/suckless/dmenu/Makefile create mode 100644 user/.config/suckless/dmenu/arg.h create mode 100644 user/.config/suckless/dmenu/config.def.h create mode 100644 user/.config/suckless/dmenu/config.mk create mode 100644 user/.config/suckless/dmenu/dmenu.1 create mode 100644 user/.config/suckless/dmenu/dmenu.c create mode 100755 user/.config/suckless/dmenu/dmenu_path create mode 100755 user/.config/suckless/dmenu/dmenu_run create mode 100644 user/.config/suckless/dmenu/drw.c create mode 100644 user/.config/suckless/dmenu/drw.h create mode 100644 user/.config/suckless/dmenu/patches/dmenu-fuzzymatch-4.9.diff create mode 100644 user/.config/suckless/dmenu/patches/dmenu-highlight-20201211-fcdc159.diff create mode 100644 user/.config/suckless/dmenu/patches/dmenu-mousesupport-5.2.diff create mode 100644 user/.config/suckless/dmenu/patches/dmenu-mousesupport-motion-5.2.diff create mode 100644 user/.config/suckless/dmenu/patches/dmenu-password-5.0.diff create mode 100755 user/.config/suckless/dmenu/scripts/dmenu_blue create mode 100755 user/.config/suckless/dmenu/scripts/dmenu_drun create mode 100755 user/.config/suckless/dmenu/scripts/dmenu_edit create mode 100755 user/.config/suckless/dmenu/scripts/dmenu_emoji create mode 100755 user/.config/suckless/dmenu/scripts/dmenu_power create mode 100755 user/.config/suckless/dmenu/scripts/dmenu_scrot create mode 100755 user/.config/suckless/dmenu/scripts/dmenu_wall create mode 100755 user/.config/suckless/dmenu/scripts/dmenu_wifi create mode 100644 user/.config/suckless/dmenu/stest.1 create mode 100644 user/.config/suckless/dmenu/stest.c create mode 100644 user/.config/suckless/dmenu/util.c create mode 100644 user/.config/suckless/dmenu/util.h create mode 100644 user/.config/suckless/dwm/Makefile create mode 100755 user/.config/suckless/dwm/autostart create mode 100644 user/.config/suckless/dwm/config.def.h create mode 100644 user/.config/suckless/dwm/config.mk create mode 100644 user/.config/suckless/dwm/dependencies.txt create mode 100644 user/.config/suckless/dwm/drw.c create mode 100644 user/.config/suckless/dwm/drw.h create mode 100644 user/.config/suckless/dwm/dwm.1 create mode 100644 user/.config/suckless/dwm/dwm.c create mode 100644 user/.config/suckless/dwm/dwm.png create mode 100644 user/.config/suckless/dwm/patches/dwm-alwayscenter-20200625-f04cac6.diff create mode 100644 user/.config/suckless/dwm/patches/dwm-attachbottom-6.3.diff create mode 100644 user/.config/suckless/dwm/patches/dwm-autostart-20161205-bb3bd6f.diff create mode 100644 user/.config/suckless/dwm/patches/dwm-pertag-20200914-61bb8b2.diff create mode 100644 user/.config/suckless/dwm/patches/dwm-restartsig-20180523-6.2.diff create mode 100644 user/.config/suckless/dwm/patches/dwm-scratchpads-20200414-728d397b.diff create mode 100644 user/.config/suckless/dwm/patches/dwm-statuspadding-6.3.diff create mode 100644 user/.config/suckless/dwm/patches/dwm-warp-6.2.diff create mode 100644 user/.config/suckless/dwm/tcl.c create mode 100644 user/.config/suckless/dwm/transient.c create mode 100644 user/.config/suckless/dwm/util.c create mode 100644 user/.config/suckless/dwm/util.h create mode 100644 user/.config/suckless/dwmblocks/Makefile create mode 100644 user/.config/suckless/dwmblocks/blocks.def.h create mode 100644 user/.config/suckless/dwmblocks/dwmblocks.c create mode 100755 user/.config/suckless/dwmblocks/scripts/block_battery create mode 100755 user/.config/suckless/dwmblocks/scripts/block_brightness create mode 100755 user/.config/suckless/dwmblocks/scripts/block_clock create mode 100755 user/.config/suckless/dwmblocks/scripts/block_cpu create mode 100755 user/.config/suckless/dwmblocks/scripts/block_layout create mode 100755 user/.config/suckless/dwmblocks/scripts/block_memory create mode 100755 user/.config/suckless/dwmblocks/scripts/block_volume create mode 100755 user/.config/suckless/dwmblocks/scripts/block_wifi create mode 100644 user/.config/suckless/herbe/.gitignore create mode 100644 user/.config/suckless/herbe/LICENSE create mode 100644 user/.config/suckless/herbe/Makefile create mode 100644 user/.config/suckless/herbe/README.md create mode 100644 user/.config/suckless/herbe/config.def.h create mode 100644 user/.config/suckless/herbe/herbe.c create mode 100755 user/.config/suckless/herbe/notify-send create mode 100644 user/.config/suckless/herbe/patches/19.diff create mode 100644 user/.config/suckless/slock/Makefile create mode 100644 user/.config/suckless/slock/arg.h create mode 100644 user/.config/suckless/slock/config.def.h create mode 100644 user/.config/suckless/slock/config.mk create mode 100644 user/.config/suckless/slock/explicit_bzero.c create mode 100644 user/.config/suckless/slock/lockscreen.png create mode 100644 user/.config/suckless/slock/patchs/slock-background-image-20220318-1c5a538.diff create mode 100644 user/.config/suckless/slock/patchs/slock-colormessage-20200210-35633d4.diff create mode 100644 user/.config/suckless/slock/patchs/slock-pam_auth-20190207-35633d4.diff create mode 100644 user/.config/suckless/slock/slock.1 create mode 100644 user/.config/suckless/slock/slock.c create mode 100644 user/.config/suckless/slock/util.h diff --git a/user/.config/awesome/apps.lua b/extras/awesome/apps.lua similarity index 100% rename from user/.config/awesome/apps.lua rename to extras/awesome/apps.lua diff --git a/user/.config/awesome/autostart.lua b/extras/awesome/autostart.lua similarity index 100% rename from user/.config/awesome/autostart.lua rename to extras/awesome/autostart.lua diff --git a/user/.config/awesome/helpers.lua b/extras/awesome/helpers.lua similarity index 100% rename from user/.config/awesome/helpers.lua rename to extras/awesome/helpers.lua diff --git a/user/.config/awesome/keymaps/keyboard.lua b/extras/awesome/keymaps/keyboard.lua similarity index 100% rename from user/.config/awesome/keymaps/keyboard.lua rename to extras/awesome/keymaps/keyboard.lua diff --git a/user/.config/awesome/keymaps/mouse.lua b/extras/awesome/keymaps/mouse.lua similarity index 100% rename from user/.config/awesome/keymaps/mouse.lua rename to extras/awesome/keymaps/mouse.lua diff --git a/user/.config/awesome/modules/custom-layouts/centerwork.lua b/extras/awesome/modules/custom-layouts/centerwork.lua similarity index 100% rename from user/.config/awesome/modules/custom-layouts/centerwork.lua rename to extras/awesome/modules/custom-layouts/centerwork.lua diff --git a/user/.config/awesome/rc.lua b/extras/awesome/rc.lua similarity index 100% rename from user/.config/awesome/rc.lua rename to extras/awesome/rc.lua diff --git a/user/.config/awesome/ui/bar.lua b/extras/awesome/ui/bar.lua similarity index 100% rename from user/.config/awesome/ui/bar.lua rename to extras/awesome/ui/bar.lua diff --git a/user/.config/awesome/ui/icons/centerworkw.png b/extras/awesome/ui/icons/centerworkw.png similarity index 100% rename from user/.config/awesome/ui/icons/centerworkw.png rename to extras/awesome/ui/icons/centerworkw.png diff --git a/user/.config/awesome/ui/icons/cornerne.png b/extras/awesome/ui/icons/cornerne.png similarity index 100% rename from user/.config/awesome/ui/icons/cornerne.png rename to extras/awesome/ui/icons/cornerne.png diff --git a/user/.config/awesome/ui/icons/cornernew.png b/extras/awesome/ui/icons/cornernew.png similarity index 100% rename from user/.config/awesome/ui/icons/cornernew.png rename to extras/awesome/ui/icons/cornernew.png diff --git a/user/.config/awesome/ui/icons/cornernw.png b/extras/awesome/ui/icons/cornernw.png similarity index 100% rename from user/.config/awesome/ui/icons/cornernw.png rename to extras/awesome/ui/icons/cornernw.png diff --git a/user/.config/awesome/ui/icons/cornernww.png b/extras/awesome/ui/icons/cornernww.png similarity index 100% rename from user/.config/awesome/ui/icons/cornernww.png rename to extras/awesome/ui/icons/cornernww.png diff --git a/user/.config/awesome/ui/icons/cornerse.png b/extras/awesome/ui/icons/cornerse.png similarity index 100% rename from user/.config/awesome/ui/icons/cornerse.png rename to extras/awesome/ui/icons/cornerse.png diff --git a/user/.config/awesome/ui/icons/cornersew.png b/extras/awesome/ui/icons/cornersew.png similarity index 100% rename from user/.config/awesome/ui/icons/cornersew.png rename to extras/awesome/ui/icons/cornersew.png diff --git a/user/.config/awesome/ui/icons/cornersw.png b/extras/awesome/ui/icons/cornersw.png similarity index 100% rename from user/.config/awesome/ui/icons/cornersw.png rename to extras/awesome/ui/icons/cornersw.png diff --git a/user/.config/awesome/ui/icons/cornersww.png b/extras/awesome/ui/icons/cornersww.png similarity index 100% rename from user/.config/awesome/ui/icons/cornersww.png rename to extras/awesome/ui/icons/cornersww.png diff --git a/user/.config/awesome/ui/icons/dwindle.png b/extras/awesome/ui/icons/dwindle.png similarity index 100% rename from user/.config/awesome/ui/icons/dwindle.png rename to extras/awesome/ui/icons/dwindle.png diff --git a/user/.config/awesome/ui/icons/dwindlew.png b/extras/awesome/ui/icons/dwindlew.png similarity index 100% rename from user/.config/awesome/ui/icons/dwindlew.png rename to extras/awesome/ui/icons/dwindlew.png diff --git a/user/.config/awesome/ui/icons/fairh.png b/extras/awesome/ui/icons/fairh.png similarity index 100% rename from user/.config/awesome/ui/icons/fairh.png rename to extras/awesome/ui/icons/fairh.png diff --git a/user/.config/awesome/ui/icons/fairhw.png b/extras/awesome/ui/icons/fairhw.png similarity index 100% rename from user/.config/awesome/ui/icons/fairhw.png rename to extras/awesome/ui/icons/fairhw.png diff --git a/user/.config/awesome/ui/icons/fairv.png b/extras/awesome/ui/icons/fairv.png similarity index 100% rename from user/.config/awesome/ui/icons/fairv.png rename to extras/awesome/ui/icons/fairv.png diff --git a/user/.config/awesome/ui/icons/fairvw.png b/extras/awesome/ui/icons/fairvw.png similarity index 100% rename from user/.config/awesome/ui/icons/fairvw.png rename to extras/awesome/ui/icons/fairvw.png diff --git a/user/.config/awesome/ui/icons/floating.png b/extras/awesome/ui/icons/floating.png similarity index 100% rename from user/.config/awesome/ui/icons/floating.png rename to extras/awesome/ui/icons/floating.png diff --git a/user/.config/awesome/ui/icons/floatingw.png b/extras/awesome/ui/icons/floatingw.png similarity index 100% rename from user/.config/awesome/ui/icons/floatingw.png rename to extras/awesome/ui/icons/floatingw.png diff --git a/user/.config/awesome/ui/icons/fullscreen.png b/extras/awesome/ui/icons/fullscreen.png similarity index 100% rename from user/.config/awesome/ui/icons/fullscreen.png rename to extras/awesome/ui/icons/fullscreen.png diff --git a/user/.config/awesome/ui/icons/fullscreenw.png b/extras/awesome/ui/icons/fullscreenw.png similarity index 100% rename from user/.config/awesome/ui/icons/fullscreenw.png rename to extras/awesome/ui/icons/fullscreenw.png diff --git a/user/.config/awesome/ui/icons/magnifier.png b/extras/awesome/ui/icons/magnifier.png similarity index 100% rename from user/.config/awesome/ui/icons/magnifier.png rename to extras/awesome/ui/icons/magnifier.png diff --git a/user/.config/awesome/ui/icons/magnifierw.png b/extras/awesome/ui/icons/magnifierw.png similarity index 100% rename from user/.config/awesome/ui/icons/magnifierw.png rename to extras/awesome/ui/icons/magnifierw.png diff --git a/user/.config/awesome/ui/icons/max.png b/extras/awesome/ui/icons/max.png similarity index 100% rename from user/.config/awesome/ui/icons/max.png rename to extras/awesome/ui/icons/max.png diff --git a/user/.config/awesome/ui/icons/maxw.png b/extras/awesome/ui/icons/maxw.png similarity index 100% rename from user/.config/awesome/ui/icons/maxw.png rename to extras/awesome/ui/icons/maxw.png diff --git a/user/.config/awesome/ui/icons/spiral.png b/extras/awesome/ui/icons/spiral.png similarity index 100% rename from user/.config/awesome/ui/icons/spiral.png rename to extras/awesome/ui/icons/spiral.png diff --git a/user/.config/awesome/ui/icons/spiralw.png b/extras/awesome/ui/icons/spiralw.png similarity index 100% rename from user/.config/awesome/ui/icons/spiralw.png rename to extras/awesome/ui/icons/spiralw.png diff --git a/user/.config/awesome/ui/icons/tile.png b/extras/awesome/ui/icons/tile.png similarity index 100% rename from user/.config/awesome/ui/icons/tile.png rename to extras/awesome/ui/icons/tile.png diff --git a/user/.config/awesome/ui/icons/tilebottom.png b/extras/awesome/ui/icons/tilebottom.png similarity index 100% rename from user/.config/awesome/ui/icons/tilebottom.png rename to extras/awesome/ui/icons/tilebottom.png diff --git a/user/.config/awesome/ui/icons/tilebottomw.png b/extras/awesome/ui/icons/tilebottomw.png similarity index 100% rename from user/.config/awesome/ui/icons/tilebottomw.png rename to extras/awesome/ui/icons/tilebottomw.png diff --git a/user/.config/awesome/ui/icons/tileleft.png b/extras/awesome/ui/icons/tileleft.png similarity index 100% rename from user/.config/awesome/ui/icons/tileleft.png rename to extras/awesome/ui/icons/tileleft.png diff --git a/user/.config/awesome/ui/icons/tileleftw.png b/extras/awesome/ui/icons/tileleftw.png similarity index 100% rename from user/.config/awesome/ui/icons/tileleftw.png rename to extras/awesome/ui/icons/tileleftw.png diff --git a/user/.config/awesome/ui/icons/tiletop.png b/extras/awesome/ui/icons/tiletop.png similarity index 100% rename from user/.config/awesome/ui/icons/tiletop.png rename to extras/awesome/ui/icons/tiletop.png diff --git a/user/.config/awesome/ui/icons/tiletopw.png b/extras/awesome/ui/icons/tiletopw.png similarity index 100% rename from user/.config/awesome/ui/icons/tiletopw.png rename to extras/awesome/ui/icons/tiletopw.png diff --git a/user/.config/awesome/ui/icons/tilew.png b/extras/awesome/ui/icons/tilew.png similarity index 100% rename from user/.config/awesome/ui/icons/tilew.png rename to extras/awesome/ui/icons/tilew.png diff --git a/user/.config/awesome/ui/layouts.lua b/extras/awesome/ui/layouts.lua similarity index 100% rename from user/.config/awesome/ui/layouts.lua rename to extras/awesome/ui/layouts.lua diff --git a/user/.config/awesome/ui/notif.lua b/extras/awesome/ui/notif.lua similarity index 100% rename from user/.config/awesome/ui/notif.lua rename to extras/awesome/ui/notif.lua diff --git a/user/.config/awesome/ui/rules.lua b/extras/awesome/ui/rules.lua similarity index 100% rename from user/.config/awesome/ui/rules.lua rename to extras/awesome/ui/rules.lua diff --git a/user/.config/awesome/ui/theme.lua b/extras/awesome/ui/theme.lua similarity index 100% rename from user/.config/awesome/ui/theme.lua rename to extras/awesome/ui/theme.lua diff --git a/user/.config/awesome/ui/widgets/battery b/extras/awesome/ui/widgets/battery similarity index 100% rename from user/.config/awesome/ui/widgets/battery rename to extras/awesome/ui/widgets/battery diff --git a/user/.config/awesome/ui/widgets/brightness b/extras/awesome/ui/widgets/brightness similarity index 100% rename from user/.config/awesome/ui/widgets/brightness rename to extras/awesome/ui/widgets/brightness diff --git a/user/.config/awesome/ui/widgets/layout b/extras/awesome/ui/widgets/layout similarity index 100% rename from user/.config/awesome/ui/widgets/layout rename to extras/awesome/ui/widgets/layout diff --git a/user/.config/awesome/ui/widgets/volume b/extras/awesome/ui/widgets/volume similarity index 100% rename from user/.config/awesome/ui/widgets/volume rename to extras/awesome/ui/widgets/volume diff --git a/user/.config/awesome/ui/widgets/wifi b/extras/awesome/ui/widgets/wifi similarity index 100% rename from user/.config/awesome/ui/widgets/wifi rename to extras/awesome/ui/widgets/wifi diff --git a/user/.config/dunst/critical.png b/extras/dunst/critical.png similarity index 100% rename from user/.config/dunst/critical.png rename to extras/dunst/critical.png diff --git a/user/.config/dunst/dunstrc b/extras/dunst/dunstrc similarity index 100% rename from user/.config/dunst/dunstrc rename to extras/dunst/dunstrc diff --git a/user/.config/dunst/normal.png b/extras/dunst/normal.png similarity index 100% rename from user/.config/dunst/normal.png rename to extras/dunst/normal.png diff --git a/user/.moc/config b/extras/moc/config similarity index 100% rename from user/.moc/config rename to extras/moc/config diff --git a/user/.moc/themes/black_theme b/extras/moc/themes/black_theme similarity index 100% rename from user/.moc/themes/black_theme rename to extras/moc/themes/black_theme diff --git a/user/.moc/themes/darkdot_theme b/extras/moc/themes/darkdot_theme similarity index 100% rename from user/.moc/themes/darkdot_theme rename to extras/moc/themes/darkdot_theme diff --git a/user/.moc/themes/example_theme b/extras/moc/themes/example_theme similarity index 100% rename from user/.moc/themes/example_theme rename to extras/moc/themes/example_theme diff --git a/user/.moc/themes/green_theme b/extras/moc/themes/green_theme similarity index 100% rename from user/.moc/themes/green_theme rename to extras/moc/themes/green_theme diff --git a/user/.moc/themes/moca_theme b/extras/moc/themes/moca_theme similarity index 100% rename from user/.moc/themes/moca_theme rename to extras/moc/themes/moca_theme diff --git a/user/.moc/themes/nightly_theme b/extras/moc/themes/nightly_theme similarity index 100% rename from user/.moc/themes/nightly_theme rename to extras/moc/themes/nightly_theme diff --git a/user/.moc/themes/red_theme b/extras/moc/themes/red_theme similarity index 100% rename from user/.moc/themes/red_theme rename to extras/moc/themes/red_theme diff --git a/user/.moc/themes/transparent-background b/extras/moc/themes/transparent-background similarity index 100% rename from user/.moc/themes/transparent-background rename to extras/moc/themes/transparent-background diff --git a/user/.moc/themes/yellow_red_theme b/extras/moc/themes/yellow_red_theme similarity index 100% rename from user/.moc/themes/yellow_red_theme rename to extras/moc/themes/yellow_red_theme diff --git a/user/.config/musikcube/hotkeys.json b/extras/musikcube/hotkeys.json similarity index 100% rename from user/.config/musikcube/hotkeys.json rename to extras/musikcube/hotkeys.json diff --git a/user/.config/rofi/config.rasi b/extras/rofi/config.rasi similarity index 100% rename from user/.config/rofi/config.rasi rename to extras/rofi/config.rasi diff --git a/user/.config/rofi/scripts/rofi_edit b/extras/rofi/scripts/rofi_edit similarity index 100% rename from user/.config/rofi/scripts/rofi_edit rename to extras/rofi/scripts/rofi_edit diff --git a/user/.config/rofi/scripts/rofi_emoji b/extras/rofi/scripts/rofi_emoji similarity index 100% rename from user/.config/rofi/scripts/rofi_emoji rename to extras/rofi/scripts/rofi_emoji diff --git a/user/.config/rofi/scripts/rofi_power b/extras/rofi/scripts/rofi_power similarity index 100% rename from user/.config/rofi/scripts/rofi_power rename to extras/rofi/scripts/rofi_power diff --git a/user/.config/rofi/scripts/rofi_scrot b/extras/rofi/scripts/rofi_scrot similarity index 100% rename from user/.config/rofi/scripts/rofi_scrot rename to extras/rofi/scripts/rofi_scrot diff --git a/user/.config/rofi/scripts/rofi_wall b/extras/rofi/scripts/rofi_wall similarity index 100% rename from user/.config/rofi/scripts/rofi_wall rename to extras/rofi/scripts/rofi_wall diff --git a/user/.config/rofi/scripts/rofi_wifi b/extras/rofi/scripts/rofi_wifi similarity index 100% rename from user/.config/rofi/scripts/rofi_wifi rename to extras/rofi/scripts/rofi_wifi diff --git a/user/.local/share/rofi/themes/tokyonight.rasi b/extras/rofi/themes/tokyonight.rasi similarity index 100% rename from user/.local/share/rofi/themes/tokyonight.rasi rename to extras/rofi/themes/tokyonight.rasi diff --git a/user/.config/starship.toml b/extras/starship.toml similarity index 100% rename from user/.config/starship.toml rename to extras/starship.toml diff --git a/user/.bash_profile b/user/.bash_profile index ca7913dcf..df877e96a 100644 --- a/user/.bash_profile +++ b/user/.bash_profile @@ -16,10 +16,6 @@ fi ### ENVIRONMENT VARIABLES export EDITOR="emacsclient -t -a ''" # $EDITOR use Emacs in terminal export VISUAL="emacsclient -c -a emacs" # $VISUAL use Emacs in GUI mode -export READER="zathura" # Zathura as the pdf viewer -export TERMINAL="alacritty" # Alacritty as the default terminal emulator -export BROWSER="firefox" # Firefox as the default web browser -export WM="awesome" # Awesomewm as the default Window Manager export XDG_DATA_HOME="${XDG_DATA_HOME:="$HOME/.local/share"}" export XDG_CACHE_HOME="${XDG_CACHE_HOME:="$HOME/.cache"}" export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:="$HOME/.config"}" diff --git a/user/.config/gtk-3.0/settings.ini b/user/.config/gtk-3.0/settings.ini index ef3e45735..1f65b8c69 100644 --- a/user/.config/gtk-3.0/settings.ini +++ b/user/.config/gtk-3.0/settings.ini @@ -2,14 +2,14 @@ gtk-theme-name=gruvbox-dark-gtk gtk-icon-theme-name=gruvbox-dark-icons-gtk gtk-font-name=mononoki Nerd Font 10 -gtk-cursor-theme-name=Simp1e-Gruvbox-Dark +gtk-cursor-theme-name=volantes_cursors gtk-cursor-theme-size=0 -gtk-toolbar-style=GTK_TOOLBAR_BOTH_HORIZ +gtk-toolbar-style=GTK_TOOLBAR_BOTH gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR -gtk-button-images=0 -gtk-menu-images=0 +gtk-button-images=1 +gtk-menu-images=1 gtk-enable-event-sounds=1 gtk-enable-input-feedback-sounds=1 gtk-xft-antialias=1 gtk-xft-hinting=1 -gtk-xft-hintstyle=hintmedium +gtk-xft-hintstyle=hintfull diff --git a/user/.config/suckless/dmenu/Makefile b/user/.config/suckless/dmenu/Makefile new file mode 100644 index 000000000..a03a95c30 --- /dev/null +++ b/user/.config/suckless/dmenu/Makefile @@ -0,0 +1,64 @@ +# dmenu - dynamic menu +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dmenu.c stest.c util.c +OBJ = $(SRC:.c=.o) + +all: options dmenu stest + +options: + @echo dmenu build options: + @echo "CFLAGS = $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "CC = $(CC)" + +.c.o: + $(CC) -c $(CFLAGS) $< + +config.h: + cp config.def.h $@ + +$(OBJ): arg.h config.h config.mk drw.h + +dmenu: dmenu.o drw.o util.o + $(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS) + +stest: stest.o + $(CC) -o $@ stest.o $(LDFLAGS) + +clean: + rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz + +dist: clean + mkdir -p dmenu-$(VERSION) + cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\ + drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\ + dmenu-$(VERSION) + tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION) + gzip dmenu-$(VERSION).tar + rm -rf dmenu-$(VERSION) + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run + chmod 755 $(DESTDIR)$(PREFIX)/bin/stest + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\ + $(DESTDIR)$(PREFIX)/bin/dmenu_path\ + $(DESTDIR)$(PREFIX)/bin/dmenu_run\ + $(DESTDIR)$(PREFIX)/bin/stest\ + $(DESTDIR)$(MANPREFIX)/man1/dmenu.1\ + $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +.PHONY: all options clean dist install uninstall diff --git a/user/.config/suckless/dmenu/arg.h b/user/.config/suckless/dmenu/arg.h new file mode 100644 index 000000000..e94e02bba --- /dev/null +++ b/user/.config/suckless/dmenu/arg.h @@ -0,0 +1,49 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/user/.config/suckless/dmenu/config.def.h b/user/.config/suckless/dmenu/config.def.h new file mode 100644 index 000000000..46c398db0 --- /dev/null +++ b/user/.config/suckless/dmenu/config.def.h @@ -0,0 +1,27 @@ +/* See LICENSE file for copyright and license details. */ +/* Default settings; can be overriden by command line. */ + +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +static int fuzzy = 1; /* -F option; if 0, dmenu doesn't use fuzzy matching */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "mononoki Nerd Font Mono:size=10" +}; +static const char *prompt = NULL; /* -p option; prompt to the left of input field */ +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#ebdbb2", "#1d2021" }, + [SchemeSel] = { "#ebdbb2", "#cc241d" }, + [SchemeSelHighlight] = { "#fbf1c7", "#282828" }, + [SchemeNormHighlight] = { "#fbf1c7", "#282828" }, + [SchemeOut] = { "#ebdbb2", "#8ec07c" }, + [SchemeOutHighlight] = { "#fabd2f", "#83a598" }, +}; +/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +static unsigned int lines = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; diff --git a/user/.config/suckless/dmenu/config.mk b/user/.config/suckless/dmenu/config.mk new file mode 100644 index 000000000..fd6ff0560 --- /dev/null +++ b/user/.config/suckless/dmenu/config.mk @@ -0,0 +1,32 @@ +# dmenu version +VERSION = 5.2 + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = $(X11INC)/freetype2 +#MANPREFIX = ${PREFIX}/man + +# includes and libs +INCS = -I$(X11INC) -I$(FREETYPEINC) +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) +CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) +LDFLAGS = $(LIBS) + +# compiler and linker +CC = cc diff --git a/user/.config/suckless/dmenu/dmenu.1 b/user/.config/suckless/dmenu/dmenu.1 new file mode 100644 index 000000000..762f7071c --- /dev/null +++ b/user/.config/suckless/dmenu/dmenu.1 @@ -0,0 +1,197 @@ +.TH DMENU 1 dmenu\-VERSION +.SH NAME +dmenu \- dynamic menu +.SH SYNOPSIS +.B dmenu +.RB [ \-bfivP ] +.RB [ \-l +.IR lines ] +.RB [ \-m +.IR monitor ] +.RB [ \-p +.IR prompt ] +.RB [ \-fn +.IR font ] +.RB [ \-nb +.IR color ] +.RB [ \-nf +.IR color ] +.RB [ \-sb +.IR color ] +.RB [ \-sf +.IR color ] +.RB [ \-w +.IR windowid ] +.P +.BR dmenu_run " ..." +.SH DESCRIPTION +.B dmenu +is a dynamic menu for X, which reads a list of newline\-separated items from +stdin. When the user selects an item and presses Return, their choice is printed +to stdout and dmenu terminates. Entering text will narrow the items to those +matching the tokens in the input. +.P +.B dmenu_run +is a script used by +.IR dwm (1) +which lists programs in the user's $PATH and runs the result in their $SHELL. +.SH OPTIONS +.TP +.B \-b +dmenu appears at the bottom of the screen. +.TP +.B \-f +dmenu grabs the keyboard before reading stdin if not reading from a tty. This +is faster, but will lock up X until stdin reaches end\-of\-file. +.TP +.B \-i +dmenu matches menu items case insensitively. +.TP +.B \-P +dmenu will not directly display the keyboard input, but instead replace it with dots. All data from stdin will be ignored. +.TP +.BI \-l " lines" +dmenu lists items vertically, with the given number of lines. +.TP +.BI \-m " monitor" +dmenu is displayed on the monitor number supplied. Monitor numbers are starting +from 0. +.TP +.BI \-p " prompt" +defines the prompt to be displayed to the left of the input field. +.TP +.BI \-fn " font" +defines the font or font set used. +.TP +.BI \-nb " color" +defines the normal background color. +.IR #RGB , +.IR #RRGGBB , +and X color names are supported. +.TP +.BI \-nf " color" +defines the normal foreground color. +.TP +.BI \-sb " color" +defines the selected background color. +.TP +.BI \-sf " color" +defines the selected foreground color. +.TP +.B \-v +prints version information to stdout, then exits. +.TP +.BI \-w " windowid" +embed into windowid. +.SH USAGE +dmenu is completely controlled by the keyboard. Items are selected using the +arrow keys, page up, page down, home, and end. +.TP +.B Tab +Copy the selected item to the input field. +.TP +.B Return +Confirm selection. Prints the selected item to stdout and exits, returning +success. +.TP +.B Ctrl-Return +Confirm selection. Prints the selected item to stdout and continues. +.TP +.B Shift\-Return +Confirm input. Prints the input text to stdout and exits, returning success. +.TP +.B Escape +Exit without selecting an item, returning failure. +.TP +.B Ctrl-Left +Move cursor to the start of the current word +.TP +.B Ctrl-Right +Move cursor to the end of the current word +.TP +.B C\-a +Home +.TP +.B C\-b +Left +.TP +.B C\-c +Escape +.TP +.B C\-d +Delete +.TP +.B C\-e +End +.TP +.B C\-f +Right +.TP +.B C\-g +Escape +.TP +.B C\-h +Backspace +.TP +.B C\-i +Tab +.TP +.B C\-j +Return +.TP +.B C\-J +Shift-Return +.TP +.B C\-k +Delete line right +.TP +.B C\-m +Return +.TP +.B C\-M +Shift-Return +.TP +.B C\-n +Down +.TP +.B C\-p +Up +.TP +.B C\-u +Delete line left +.TP +.B C\-w +Delete word left +.TP +.B C\-y +Paste from primary X selection +.TP +.B C\-Y +Paste from X clipboard +.TP +.B M\-b +Move cursor to the start of the current word +.TP +.B M\-f +Move cursor to the end of the current word +.TP +.B M\-g +Home +.TP +.B M\-G +End +.TP +.B M\-h +Up +.TP +.B M\-j +Page down +.TP +.B M\-k +Page up +.TP +.B M\-l +Down +.SH SEE ALSO +.IR dwm (1), +.IR stest (1) diff --git a/user/.config/suckless/dmenu/dmenu.c b/user/.config/suckless/dmenu/dmenu.c new file mode 100644 index 000000000..2332a9106 --- /dev/null +++ b/user/.config/suckless/dmenu/dmenu.c @@ -0,0 +1,1073 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef XINERAMA +#include +#endif +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ + * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { SchemeNorm, SchemeSel, SchemeOut, SchemeNormHighlight, SchemeSelHighlight, SchemeOutHighlight, SchemeLast }; /* color schemes */ +struct item { + char *text; + struct item *left, *right; + int out; + double distance; +}; + +static char text[BUFSIZ] = ""; +static char *embed; +static int bh, mw, mh; +static int inputw = 0, promptw, passwd = 0; +static int lrpad; /* sum of left and right padding */ +static size_t cursor; +static struct item *items = NULL; +static struct item *matches, *matchend; +static struct item *prev, *curr, *next, *sel; +static int mon = -1, screen; + +static Atom clip, utf8; +static Display *dpy; +static Window root, parentwin, win; +static XIC xic; + +static Drw *drw; +static Clr *scheme[SchemeLast]; + +#include "config.h" + +static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; +static char *(*fstrstr)(const char *, const char *) = strstr; + +static unsigned int +textw_clamp(const char *str, unsigned int n) +{ + unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad; + return MIN(w, n); +} + +static void +appenditem(struct item *item, struct item **list, struct item **last) +{ + if (*last) + (*last)->right = item; + else + *list = item; + + item->left = *last; + item->right = NULL; + *last = item; +} + +static void +calcoffsets(void) +{ + int i, n; + + if (lines > 0) + n = lines * bh; + else + n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); + /* calculate which items will begin the next page and previous page */ + for (i = 0, next = curr; next; next = next->right) + if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n) + break; + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) + if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n) + break; +} + +static void +cleanup(void) +{ + size_t i; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < SchemeLast; i++) + free(scheme[i]); + for (i = 0; items && items[i].text; ++i) + free(items[i].text); + free(items); + drw_free(drw); + XSync(dpy, False); + XCloseDisplay(dpy); +} + +static char * +cistrstr(const char *h, const char *n) +{ + size_t i; + + if (!n[0]) + return (char *)h; + + for (; *h; ++h) { + for (i = 0; n[i] && tolower((unsigned char)n[i]) == + tolower((unsigned char)h[i]); ++i) + ; + if (n[i] == '\0') + return (char *)h; + } + return NULL; +} + +static void +drawhighlights(struct item *item, int x, int y, int maxw) +{ + char restorechar, tokens[sizeof text], *highlight, *token; + int indentx, highlightlen; + + drw_setscheme(drw, scheme[item == sel ? SchemeSelHighlight : item->out ? SchemeOutHighlight : SchemeNormHighlight]); + strcpy(tokens, text); + for (token = strtok(tokens, " "); token; token = strtok(NULL, " ")) { + highlight = fstrstr(item->text, token); + while (highlight) { + // Move item str end, calc width for highlight indent, & restore + highlightlen = highlight - item->text; + restorechar = *highlight; + item->text[highlightlen] = '\0'; + indentx = TEXTW(item->text); + item->text[highlightlen] = restorechar; + + // Move highlight str end, draw highlight, & restore + restorechar = highlight[strlen(token)]; + highlight[strlen(token)] = '\0'; + if (indentx - (lrpad / 2) - 1 < maxw) + drw_text( + drw, + x + indentx - (lrpad / 2) - 1, + y, + MIN(maxw - indentx, TEXTW(highlight) - lrpad), + bh, 0, highlight, 0 + ); + highlight[strlen(token)] = restorechar; + + if (strlen(highlight) - strlen(token) < strlen(token)) break; + highlight = fstrstr(highlight + strlen(token), token); + } + } +} + +static int +drawitem(struct item *item, int x, int y, int w) +{ + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + else if (item->out) + drw_setscheme(drw, scheme[SchemeOut]); + else + drw_setscheme(drw, scheme[SchemeNorm]); + + int r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); + drawhighlights(item, x, y, w); + return r; +} + +static void +drawmenu(void) +{ + unsigned int curpos; + struct item *item; + int x = 0, y = 0, w; + char *censort; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); + + if (prompt && *prompt) { + drw_setscheme(drw, scheme[SchemeSel]); + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); + } + /* draw input field */ + w = (lines > 0 || !matches) ? mw - x : inputw; + drw_setscheme(drw, scheme[SchemeNorm]); + if (passwd) { + censort = ecalloc(1, sizeof(text)); + memset(censort, '.', strlen(text)); + drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0); + free(censort); + } else drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); + + curpos = TEXTW(text) - TEXTW(&text[cursor]); + if ((curpos += lrpad / 2 - 1) < w) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); + } + + if (lines > 0) { + /* draw vertical list */ + for (item = curr; item != next; item = item->right) + drawitem(item, x, y += bh, mw - x); + } else if (matches) { + /* draw horizontal list */ + x += inputw; + w = TEXTW("<"); + if (curr->left) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); + } + x += w; + for (item = curr; item != next; item = item->right) + x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">"))); + if (next) { + w = TEXTW(">"); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); + } + } + drw_map(drw, win, 0, 0, mw, mh); +} + +static void +grabfocus(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; + Window focuswin; + int i, revertwin; + + for (i = 0; i < 100; ++i) { + XGetInputFocus(dpy, &focuswin, &revertwin); + if (focuswin == win) + return; + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + nanosleep(&ts, NULL); + } + die("cannot grab focus"); +} + +static void +grabkeyboard(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + int i; + + if (embed) + return; + /* try to grab keyboard, we may have to wait for another process to ungrab */ + for (i = 0; i < 1000; i++) { + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, + GrabModeAsync, CurrentTime) == GrabSuccess) + return; + nanosleep(&ts, NULL); + } + die("cannot grab keyboard"); +} + +int +compare_distance(const void *a, const void *b) +{ + struct item *da = *(struct item **) a; + struct item *db = *(struct item **) b; + + if (!db) + return 1; + if (!da) + return -1; + + return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1; +} + +void +fuzzymatch(void) +{ + /* bang - we have so much memory */ + struct item *it; + struct item **fuzzymatches = NULL; + char c; + int number_of_matches = 0, i, pidx, sidx, eidx; + int text_len = strlen(text), itext_len; + + matches = matchend = NULL; + + /* walk through all items */ + for (it = items; it && it->text; it++) { + if (text_len) { + itext_len = strlen(it->text); + pidx = 0; /* pointer */ + sidx = eidx = -1; /* start of match, end of match */ + /* walk through item text */ + for (i = 0; i < itext_len && (c = it->text[i]); i++) { + /* fuzzy match pattern */ + if (!fstrncmp(&text[pidx], &c, 1)) { + if(sidx == -1) + sidx = i; + pidx++; + if (pidx == text_len) { + eidx = i; + break; + } + } + } + /* build list of matches */ + if (eidx != -1) { + /* compute distance */ + /* add penalty if match starts late (log(sidx+2)) + * add penalty for long a match without many matching characters */ + it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len); + /* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */ + appenditem(it, &matches, &matchend); + number_of_matches++; + } + } else { + appenditem(it, &matches, &matchend); + } + } + + if (number_of_matches) { + /* initialize array with matches */ + if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*)))) + die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item*)); + for (i = 0, it = matches; it && i < number_of_matches; i++, it = it->right) { + fuzzymatches[i] = it; + } + /* sort matches according to distance */ + qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance); + /* rebuild list of matches */ + matches = matchend = NULL; + for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it && \ + it->text; i++, it = fuzzymatches[i]) { + appenditem(it, &matches, &matchend); + } + free(fuzzymatches); + } + curr = sel = matches; + calcoffsets(); +} + +static void +match(void) +{ + if (fuzzy) { + fuzzymatch(); + return; + } + static char **tokv = NULL; + static int tokn = 0; + + char buf[sizeof text], *s; + int i, tokc = 0; + size_t len, textsize; + struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; + + strcpy(buf, text); + /* separate input text into tokens to be matched individually */ + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) + if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) + die("cannot realloc %zu bytes:", tokn * sizeof *tokv); + len = tokc ? strlen(tokv[0]) : 0; + + matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; + textsize = strlen(text) + 1; + for (item = items; item && item->text; item++) { + for (i = 0; i < tokc; i++) + if (!fstrstr(item->text, tokv[i])) + break; + if (i != tokc) /* not all tokens match */ + continue; + /* exact matches go first, then prefixes, then substrings */ + if (!tokc || !fstrncmp(text, item->text, textsize)) + appenditem(item, &matches, &matchend); + else if (!fstrncmp(tokv[0], item->text, len)) + appenditem(item, &lprefix, &prefixend); + else + appenditem(item, &lsubstr, &substrend); + } + if (lprefix) { + if (matches) { + matchend->right = lprefix; + lprefix->left = matchend; + } else + matches = lprefix; + matchend = prefixend; + } + if (lsubstr) { + if (matches) { + matchend->right = lsubstr; + lsubstr->left = matchend; + } else + matches = lsubstr; + matchend = substrend; + } + curr = sel = matches; + calcoffsets(); +} + +static void +insert(const char *str, ssize_t n) +{ + if (strlen(text) + n > sizeof text - 1) + return; + /* move existing text out of the way, insert new text, and update cursor */ + memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); + if (n > 0) + memcpy(&text[cursor], str, n); + cursor += n; + match(); +} + +static size_t +nextrune(int inc) +{ + ssize_t n; + + /* return location of next utf8 rune in the given direction (+1 or -1) */ + for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) + ; + return n; +} + +static void +movewordedge(int dir) +{ + if (dir < 0) { /* move cursor to the start of the word*/ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + } else { /* move cursor to the end of the word */ + while (text[cursor] && strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + while (text[cursor] && !strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + } +} + +static void +keypress(XKeyEvent *ev) +{ + char buf[32]; + int len; + KeySym ksym; + Status status; + + len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); + switch (status) { + default: /* XLookupNone, XBufferOverflow */ + return; + case XLookupChars: + goto insert; + case XLookupKeySym: + case XLookupBoth: + break; + } + + if (ev->state & ControlMask) { + switch(ksym) { + case XK_a: ksym = XK_Home; break; + case XK_b: ksym = XK_Left; break; + case XK_c: ksym = XK_Escape; break; + case XK_d: ksym = XK_Delete; break; + case XK_e: ksym = XK_End; break; + case XK_f: ksym = XK_Right; break; + case XK_g: ksym = XK_Escape; break; + case XK_h: ksym = XK_BackSpace; break; + case XK_i: ksym = XK_Tab; break; + case XK_j: /* fallthrough */ + case XK_J: /* fallthrough */ + case XK_m: /* fallthrough */ + case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; + case XK_n: ksym = XK_Down; break; + case XK_p: ksym = XK_Up; break; + + case XK_k: /* delete right */ + text[cursor] = '\0'; + match(); + break; + case XK_u: /* delete left */ + insert(NULL, 0 - cursor); + break; + case XK_w: /* delete word */ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + break; + case XK_y: /* paste selection */ + case XK_Y: + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, + utf8, utf8, win, CurrentTime); + return; + case XK_Left: + case XK_KP_Left: + movewordedge(-1); + goto draw; + case XK_Right: + case XK_KP_Right: + movewordedge(+1); + goto draw; + case XK_Return: + case XK_KP_Enter: + break; + case XK_bracketleft: + cleanup(); + exit(1); + default: + return; + } + } else if (ev->state & Mod1Mask) { + switch(ksym) { + case XK_b: + movewordedge(-1); + goto draw; + case XK_f: + movewordedge(+1); + goto draw; + case XK_g: ksym = XK_Home; break; + case XK_G: ksym = XK_End; break; + case XK_h: ksym = XK_Up; break; + case XK_j: ksym = XK_Next; break; + case XK_k: ksym = XK_Prior; break; + case XK_l: ksym = XK_Down; break; + default: + return; + } + } + + switch(ksym) { + default: +insert: + if (!iscntrl((unsigned char)*buf)) + insert(buf, len); + break; + case XK_Delete: + case XK_KP_Delete: + if (text[cursor] == '\0') + return; + cursor = nextrune(+1); + /* fallthrough */ + case XK_BackSpace: + if (cursor == 0) + return; + insert(NULL, nextrune(-1) - cursor); + break; + case XK_End: + case XK_KP_End: + if (text[cursor] != '\0') { + cursor = strlen(text); + break; + } + if (next) { + /* jump to end of list and position items in reverse */ + curr = matchend; + calcoffsets(); + curr = prev; + calcoffsets(); + while (next && (curr = curr->right)) + calcoffsets(); + } + sel = matchend; + break; + case XK_Escape: + cleanup(); + exit(1); + case XK_Home: + case XK_KP_Home: + if (sel == matches) { + cursor = 0; + break; + } + sel = curr = matches; + calcoffsets(); + break; + case XK_Left: + case XK_KP_Left: + if (cursor > 0 && (!sel || !sel->left || lines > 0)) { + cursor = nextrune(-1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Up: + case XK_KP_Up: + if (sel && sel->left && (sel = sel->left)->right == curr) { + curr = prev; + calcoffsets(); + } + break; + case XK_Next: + case XK_KP_Next: + if (!next) + return; + sel = curr = next; + calcoffsets(); + break; + case XK_Prior: + case XK_KP_Prior: + if (!prev) + return; + sel = curr = prev; + calcoffsets(); + break; + case XK_Return: + case XK_KP_Enter: + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + if (!(ev->state & ControlMask)) { + cleanup(); + exit(0); + } + if (sel) + sel->out = 1; + break; + case XK_Right: + case XK_KP_Right: + if (text[cursor] != '\0') { + cursor = nextrune(+1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Down: + case XK_KP_Down: + if (sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + break; + case XK_Tab: + if (!sel) + return; + cursor = strnlen(sel->text, sizeof text - 1); + memcpy(text, sel->text, cursor); + text[cursor] = '\0'; + match(); + break; + } + +draw: + drawmenu(); +} + +static void +buttonpress(XEvent *e) +{ + struct item *item; + XButtonPressedEvent *ev = &e->xbutton; + int x = 0, y = 0, h = bh, w; + + if (ev->window != win) + return; + + /* right-click: exit */ + if (ev->button == Button3) + exit(1); + + if (prompt && *prompt) + x += promptw; + + /* input field */ + w = (lines > 0 || !matches) ? mw - x : inputw; + + /* left-click on input: clear input, + * NOTE: if there is no left-arrow the space for < is reserved so + * add that to the input width */ + if (ev->button == Button1 && + ((lines <= 0 && ev->x >= 0 && ev->x <= x + w + + ((!prev || !curr->left) ? TEXTW("<") : 0)) || + (lines > 0 && ev->y >= y && ev->y <= y + h))) { + insert(NULL, -cursor); + drawmenu(); + return; + } + /* middle-mouse click: paste selection */ + if (ev->button == Button2) { + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, + utf8, utf8, win, CurrentTime); + drawmenu(); + return; + } + /* scroll up */ + if (ev->button == Button4 && prev) { + sel = curr = prev; + calcoffsets(); + drawmenu(); + return; + } + /* scroll down */ + if (ev->button == Button5 && next) { + sel = curr = next; + calcoffsets(); + drawmenu(); + return; + } + if (ev->button != Button1) + return; + if (ev->state & ~ControlMask) + return; + if (lines > 0) { + /* vertical list: (ctrl)left-click on item */ + w = mw - x; + for (item = curr; item != next; item = item->right) { + y += h; + if (ev->y >= y && ev->y <= (y + h)) { + puts(item->text); + if (!(ev->state & ControlMask)) + exit(0); + sel = item; + if (sel) { + sel->out = 1; + drawmenu(); + } + return; + } + } + } else if (matches) { + /* left-click on left arrow */ + x += inputw; + w = TEXTW("<"); + if (prev && curr->left) { + if (ev->x >= x && ev->x <= x + w) { + sel = curr = prev; + calcoffsets(); + drawmenu(); + return; + } + } + /* horizontal list: (ctrl)left-click on item */ + for (item = curr; item != next; item = item->right) { + x += w; + w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); + if (ev->x >= x && ev->x <= x + w) { + puts(item->text); + if (!(ev->state & ControlMask)) + exit(0); + sel = item; + if (sel) { + sel->out = 1; + drawmenu(); + } + return; + } + } + /* left-click on right arrow */ + w = TEXTW(">"); + x = mw - w; + if (next && ev->x >= x && ev->x <= x + w) { + sel = curr = next; + calcoffsets(); + drawmenu(); + return; + } + } +} + +static void +motionevent(XButtonEvent *ev) +{ + struct item *it; + int xy, ev_xy; + + if (ev->window != win || matches == 0) + return; + + xy = lines > 0 ? bh : inputw + promptw + TEXTW("<"); + ev_xy = lines > 0 ? ev->y : ev->x; + for (it = curr; it && it != next; it = it->right) { + int wh = lines > 0 ? bh : textw_clamp(it->text, mw - xy - TEXTW(">")); + if (ev_xy >= xy && ev_xy < (xy + wh)) { + sel = it; + calcoffsets(); + drawmenu(); + break; + } + xy += wh; + } +} + +static void +paste(void) +{ + char *p, *q; + int di; + unsigned long dl; + Atom da; + + /* we have been given the current selection, now insert it into input */ + if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, + utf8, &da, &di, &dl, &dl, (unsigned char **)&p) + == Success && p) { + insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); + XFree(p); + } + drawmenu(); +} + +static void +readstdin(void) +{ + char *line = NULL; + size_t i, junk, size = 0; + ssize_t len; + if(passwd){ + inputw = lines = 0; + return; + } + + /* read each line from stdin and add it to the item list */ + for (i = 0; (len = getline(&line, &junk, stdin)) != -1; i++, line = NULL) { + if (i + 1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) + die("cannot realloc %zu bytes:", size); + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + items[i].text = line; + items[i].out = 0; + } + if (items) + items[i].text = NULL; + lines = MIN(lines, i); +} + +static void +run(void) +{ + XEvent ev; + + while (!XNextEvent(dpy, &ev)) { + if (XFilterEvent(&ev, win)) + continue; + switch(ev.type) { + case DestroyNotify: + if (ev.xdestroywindow.window != win) + break; + cleanup(); + exit(1); + case ButtonPress: + buttonpress(&ev); + break; + case MotionNotify: + motionevent(&ev.xbutton); + break; + case Expose: + if (ev.xexpose.count == 0) + drw_map(drw, win, 0, 0, mw, mh); + break; + case FocusIn: + /* regrab focus from parent window */ + if (ev.xfocus.window != win) + grabfocus(); + break; + case KeyPress: + keypress(&ev.xkey); + break; + case SelectionNotify: + if (ev.xselection.property == utf8) + paste(); + break; + case VisibilityNotify: + if (ev.xvisibility.state != VisibilityUnobscured) + XRaiseWindow(dpy, win); + break; + } + } +} + +static void +setup(void) +{ + int x, y, i, j; + unsigned int du; + XSetWindowAttributes swa; + XIM xim; + Window w, dw, *dws; + XWindowAttributes wa; + XClassHint ch = {"dmenu", "dmenu"}; +#ifdef XINERAMA + XineramaScreenInfo *info; + Window pw; + int a, di, n, area = 0; +#endif + /* init appearance */ + for (j = 0; j < SchemeLast; j++) + scheme[j] = drw_scm_create(drw, colors[j], 2); + + clip = XInternAtom(dpy, "CLIPBOARD", False); + utf8 = XInternAtom(dpy, "UTF8_STRING", False); + + /* calculate menu geometry */ + bh = drw->fonts->h + 2; + lines = MAX(lines, 0); + mh = (lines + 1) * bh; +#ifdef XINERAMA + i = 0; + if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { + XGetInputFocus(dpy, &w, &di); + if (mon >= 0 && mon < n) + i = mon; + else if (w != root && w != PointerRoot && w != None) { + /* find top-level window containing current input focus */ + do { + if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) + XFree(dws); + } while (w != root && w != pw); + /* find xinerama screen with which the window intersects most */ + if (XGetWindowAttributes(dpy, pw, &wa)) + for (j = 0; j < n; j++) + if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { + area = a; + i = j; + } + } + /* no focused window is on screen, so use pointer location instead */ + if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) + for (i = 0; i < n; i++) + if (INTERSECT(x, y, 1, 1, info[i]) != 0) + break; + + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; + XFree(info); + } else +#endif + { + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; + } + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + inputw = mw / 3; /* input width: ~33% of monitor width */ + match(); + + /* create menu window */ + swa.override_redirect = True; + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | + ButtonPressMask | PointerMotionMask; + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + XSetClassHint(dpy, win, &ch); + + + /* input methods */ + if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed: could not open input device"); + + xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, win, XNFocusWindow, win, NULL); + + XMapRaised(dpy, win); + if (embed) { + XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); + if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { + for (i = 0; i < du && dws[i] != win; ++i) + XSelectInput(dpy, dws[i], FocusChangeMask); + XFree(dws); + } + grabfocus(); + } + drw_resize(drw, mw, mh); + drawmenu(); +} + +static void +usage(void) +{ + die("usage: dmenu [-bfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); +} + +int +main(int argc, char *argv[]) +{ + XWindowAttributes wa; + int i, fast = 0; + + for (i = 1; i < argc; i++) + /* these options take no arguments */ + if (!strcmp(argv[i], "-v")) { /* prints version information */ + puts("dmenu-"VERSION); + exit(0); + } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ + topbar = 0; + else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ + fast = 1; + else if (!strcmp(argv[i], "-F")) /* grabs keyboard before reading stdin */ + fuzzy = 0; + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; + } else if (!strcmp(argv[i], "-P")) /* is the input a password */ + passwd = 1; + else if (i + 1 == argc) + usage(); + /* these options take one argument */ + else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ + lines = atoi(argv[++i]); + else if (!strcmp(argv[i], "-m")) + mon = atoi(argv[++i]); + else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ + prompt = argv[++i]; + else if (!strcmp(argv[i], "-fn")) /* font or font set */ + fonts[0] = argv[++i]; + else if (!strcmp(argv[i], "-nb")) /* normal background color */ + colors[SchemeNorm][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ + colors[SchemeNorm][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-sb")) /* selected background color */ + colors[SchemeSel][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ + colors[SchemeSel][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-w")) /* embedding window id */ + embed = argv[++i]; + else + usage(); + + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("cannot open display"); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + if (!embed || !(parentwin = strtol(embed, NULL, 0))) + parentwin = root; + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + drw = drw_create(dpy, screen, root, wa.width, wa.height); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + +#ifdef __OpenBSD__ + if (pledge("stdio rpath", NULL) == -1) + die("pledge"); +#endif + + if (fast && !isatty(0)) { + grabkeyboard(); + readstdin(); + } else { + readstdin(); + grabkeyboard(); + } + setup(); + run(); + + return 1; /* unreachable */ +} diff --git a/user/.config/suckless/dmenu/dmenu_path b/user/.config/suckless/dmenu/dmenu_path new file mode 100755 index 000000000..3a7cda792 --- /dev/null +++ b/user/.config/suckless/dmenu/dmenu_path @@ -0,0 +1,13 @@ +#!/bin/sh + +cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}" +cache="$cachedir/dmenu_run" + +[ ! -e "$cachedir" ] && mkdir -p "$cachedir" + +IFS=: +if stest -dqr -n "$cache" $PATH; then + stest -flx $PATH | sort -u | tee "$cache" +else + cat "$cache" +fi diff --git a/user/.config/suckless/dmenu/dmenu_run b/user/.config/suckless/dmenu/dmenu_run new file mode 100755 index 000000000..834ede54f --- /dev/null +++ b/user/.config/suckless/dmenu/dmenu_run @@ -0,0 +1,2 @@ +#!/bin/sh +dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & diff --git a/user/.config/suckless/dmenu/drw.c b/user/.config/suckless/dmenu/drw.c new file mode 100644 index 000000000..a58a2b489 --- /dev/null +++ b/user/.config/suckless/dmenu/drw.c @@ -0,0 +1,450 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); + drw->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + int i, ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0, overflow = 0; + /* keep track of a couple codepoints for which we have no match. */ + enum { nomatches_len = 64 }; + static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; + static unsigned int ellipsis_width = 0; + + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) + return 0; + + if (!render) { + w = invert ? invert : ~invert; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + d = XftDrawCreate(drw->dpy, drw->drawable, + DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen)); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + if (!ellipsis_width && render) + ellipsis_width = drw_fontset_getwidth(drw, "..."); + while (1) { + ew = ellipsis_len = utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); + if (ew + ellipsis_width <= w) { + /* keep track where the ellipsis still fits */ + ellipsis_x = x + ew; + ellipsis_w = w - ew; + ellipsis_len = utf8strlen; + } + + if (ew + tmpw > w) { + overflow = 1; + /* called from drw_fontset_getwidth_clamp(): + * it wants the width AFTER the overflow + */ + if (!render) + x += tmpw; + else + utf8strlen = ellipsis_len; + } else if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + ew += tmpw; + } else { + nextfont = curfont; + } + break; + } + } + + if (overflow || !charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); + } + x += ew; + w -= ew; + } + if (render && overflow) + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); + + if (!*text || overflow) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + for (i = 0; i < nomatches_len; ++i) { + /* avoid calling XftFontMatch if we know we won't find a match */ + if (utf8codepoint == nomatches.codepoint[i]) + goto no_match; + } + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; +no_match: + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +unsigned int +drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) +{ + unsigned int tmp = 0; + if (drw && drw->fonts && text && n) + tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); + return MIN(n, tmp); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/user/.config/suckless/dmenu/drw.h b/user/.config/suckless/dmenu/drw.h new file mode 100644 index 000000000..fd7631b2b --- /dev/null +++ b/user/.config/suckless/dmenu/drw.h @@ -0,0 +1,58 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/user/.config/suckless/dmenu/patches/dmenu-fuzzymatch-4.9.diff b/user/.config/suckless/dmenu/patches/dmenu-fuzzymatch-4.9.diff new file mode 100644 index 000000000..9fd206d8b --- /dev/null +++ b/user/.config/suckless/dmenu/patches/dmenu-fuzzymatch-4.9.diff @@ -0,0 +1,163 @@ +From 94353eb52055927d9079f3d9e33da1c954abf386 Mon Sep 17 00:00:00 2001 +From: aleks +Date: Wed, 26 Jun 2019 13:25:10 +0200 +Subject: [PATCH] Add support for fuzzy-matching + +--- + config.def.h | 1 + + config.mk | 2 +- + dmenu.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 91 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index 1edb647..51612b9 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -2,6 +2,7 @@ + /* Default settings; can be overriden by command line. */ + + static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ ++static int fuzzy = 1; /* -F option; if 0, dmenu doesn't use fuzzy matching */ + /* -fn option overrides fonts[0]; default X11 font or font set */ + static const char *fonts[] = { + "monospace:size=10" +diff --git a/config.mk b/config.mk +index 0929b4a..d14309a 100644 +--- a/config.mk ++++ b/config.mk +@@ -20,7 +20,7 @@ FREETYPEINC = /usr/include/freetype2 + + # includes and libs + INCS = -I$(X11INC) -I$(FREETYPEINC) +-LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) ++LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) +diff --git a/dmenu.c b/dmenu.c +index 6b8f51b..96ddc98 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -1,6 +1,7 @@ + /* See LICENSE file for copyright and license details. */ + #include + #include ++#include + #include + #include + #include +@@ -32,6 +33,7 @@ struct item { + char *text; + struct item *left, *right; + int out; ++ double distance; + }; + + static char text[BUFSIZ] = ""; +@@ -210,9 +212,94 @@ grabkeyboard(void) + die("cannot grab keyboard"); + } + ++int ++compare_distance(const void *a, const void *b) ++{ ++ struct item *da = *(struct item **) a; ++ struct item *db = *(struct item **) b; ++ ++ if (!db) ++ return 1; ++ if (!da) ++ return -1; ++ ++ return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1; ++} ++ ++void ++fuzzymatch(void) ++{ ++ /* bang - we have so much memory */ ++ struct item *it; ++ struct item **fuzzymatches = NULL; ++ char c; ++ int number_of_matches = 0, i, pidx, sidx, eidx; ++ int text_len = strlen(text), itext_len; ++ ++ matches = matchend = NULL; ++ ++ /* walk through all items */ ++ for (it = items; it && it->text; it++) { ++ if (text_len) { ++ itext_len = strlen(it->text); ++ pidx = 0; /* pointer */ ++ sidx = eidx = -1; /* start of match, end of match */ ++ /* walk through item text */ ++ for (i = 0; i < itext_len && (c = it->text[i]); i++) { ++ /* fuzzy match pattern */ ++ if (!fstrncmp(&text[pidx], &c, 1)) { ++ if(sidx == -1) ++ sidx = i; ++ pidx++; ++ if (pidx == text_len) { ++ eidx = i; ++ break; ++ } ++ } ++ } ++ /* build list of matches */ ++ if (eidx != -1) { ++ /* compute distance */ ++ /* add penalty if match starts late (log(sidx+2)) ++ * add penalty for long a match without many matching characters */ ++ it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len); ++ /* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */ ++ appenditem(it, &matches, &matchend); ++ number_of_matches++; ++ } ++ } else { ++ appenditem(it, &matches, &matchend); ++ } ++ } ++ ++ if (number_of_matches) { ++ /* initialize array with matches */ ++ if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*)))) ++ die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item*)); ++ for (i = 0, it = matches; it && i < number_of_matches; i++, it = it->right) { ++ fuzzymatches[i] = it; ++ } ++ /* sort matches according to distance */ ++ qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance); ++ /* rebuild list of matches */ ++ matches = matchend = NULL; ++ for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it && \ ++ it->text; i++, it = fuzzymatches[i]) { ++ appenditem(it, &matches, &matchend); ++ } ++ free(fuzzymatches); ++ } ++ curr = sel = matches; ++ calcoffsets(); ++} ++ + static void + match(void) + { ++ if (fuzzy) { ++ fuzzymatch(); ++ return; ++ } + static char **tokv = NULL; + static int tokn = 0; + +@@ -702,6 +789,8 @@ main(int argc, char *argv[]) + topbar = 0; + else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ + fast = 1; ++ else if (!strcmp(argv[i], "-F")) /* grabs keyboard before reading stdin */ ++ fuzzy = 0; + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; +-- +2.22.0 + diff --git a/user/.config/suckless/dmenu/patches/dmenu-highlight-20201211-fcdc159.diff b/user/.config/suckless/dmenu/patches/dmenu-highlight-20201211-fcdc159.diff new file mode 100644 index 000000000..c3ac5c12d --- /dev/null +++ b/user/.config/suckless/dmenu/patches/dmenu-highlight-20201211-fcdc159.diff @@ -0,0 +1,97 @@ +From fcdc1593ed418166f20b7e691a49b1e6eefc116e Mon Sep 17 00:00:00 2001 +From: Nathaniel Evan +Date: Fri, 11 Dec 2020 11:08:12 +0700 +Subject: [PATCH] Highlight matched text in a different color scheme + +--- + config.def.h | 3 +++ + dmenu.c | 44 +++++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 44 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1edb647..79be73a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -11,7 +11,10 @@ static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, ++ [SchemeSelHighlight] = { "#ffc978", "#005577" }, ++ [SchemeNormHighlight] = { "#ffc978", "#222222" }, + [SchemeOut] = { "#000000", "#00ffff" }, ++ [SchemeOutHighlight] = { "#ffc978", "#00ffff" }, + }; + /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ + static unsigned int lines = 0; +diff --git a/dmenu.c b/dmenu.c +index 65f25ce..cce1ad1 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -26,8 +26,7 @@ + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + + /* enums */ +-enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ +- ++enum { SchemeNorm, SchemeSel, SchemeOut, SchemeNormHighlight, SchemeSelHighlight, SchemeOutHighlight, SchemeLast }; /* color schemes */ + struct item { + char *text; + struct item *left, *right; +@@ -113,6 +112,43 @@ cistrstr(const char *s, const char *sub) + return NULL; + } + ++static void ++drawhighlights(struct item *item, int x, int y, int maxw) ++{ ++ char restorechar, tokens[sizeof text], *highlight, *token; ++ int indentx, highlightlen; ++ ++ drw_setscheme(drw, scheme[item == sel ? SchemeSelHighlight : item->out ? SchemeOutHighlight : SchemeNormHighlight]); ++ strcpy(tokens, text); ++ for (token = strtok(tokens, " "); token; token = strtok(NULL, " ")) { ++ highlight = fstrstr(item->text, token); ++ while (highlight) { ++ // Move item str end, calc width for highlight indent, & restore ++ highlightlen = highlight - item->text; ++ restorechar = *highlight; ++ item->text[highlightlen] = '\0'; ++ indentx = TEXTW(item->text); ++ item->text[highlightlen] = restorechar; ++ ++ // Move highlight str end, draw highlight, & restore ++ restorechar = highlight[strlen(token)]; ++ highlight[strlen(token)] = '\0'; ++ if (indentx - (lrpad / 2) - 1 < maxw) ++ drw_text( ++ drw, ++ x + indentx - (lrpad / 2) - 1, ++ y, ++ MIN(maxw - indentx, TEXTW(highlight) - lrpad), ++ bh, 0, highlight, 0 ++ ); ++ highlight[strlen(token)] = restorechar; ++ ++ if (strlen(highlight) - strlen(token) < strlen(token)) break; ++ highlight = fstrstr(highlight + strlen(token), token); ++ } ++ } ++} ++ + static int + drawitem(struct item *item, int x, int y, int w) + { +@@ -123,7 +159,9 @@ drawitem(struct item *item, int x, int y, int w) + else + drw_setscheme(drw, scheme[SchemeNorm]); + +- return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); ++ int r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); ++ drawhighlights(item, x, y, w); ++ return r; + } + + static void +-- +2.29.2 + diff --git a/user/.config/suckless/dmenu/patches/dmenu-mousesupport-5.2.diff b/user/.config/suckless/dmenu/patches/dmenu-mousesupport-5.2.diff new file mode 100644 index 000000000..eaacea423 --- /dev/null +++ b/user/.config/suckless/dmenu/patches/dmenu-mousesupport-5.2.diff @@ -0,0 +1,144 @@ +diff --git a/dmenu.c b/dmenu.c +index 7cf253b..d276a94 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -528,6 +528,119 @@ draw: + drawmenu(); + } + ++static void ++buttonpress(XEvent *e) ++{ ++ struct item *item; ++ XButtonPressedEvent *ev = &e->xbutton; ++ int x = 0, y = 0, h = bh, w; ++ ++ if (ev->window != win) ++ return; ++ ++ /* right-click: exit */ ++ if (ev->button == Button3) ++ exit(1); ++ ++ if (prompt && *prompt) ++ x += promptw; ++ ++ /* input field */ ++ w = (lines > 0 || !matches) ? mw - x : inputw; ++ ++ /* left-click on input: clear input, ++ * NOTE: if there is no left-arrow the space for < is reserved so ++ * add that to the input width */ ++ if (ev->button == Button1 && ++ ((lines <= 0 && ev->x >= 0 && ev->x <= x + w + ++ ((!prev || !curr->left) ? TEXTW("<") : 0)) || ++ (lines > 0 && ev->y >= y && ev->y <= y + h))) { ++ insert(NULL, -cursor); ++ drawmenu(); ++ return; ++ } ++ /* middle-mouse click: paste selection */ ++ if (ev->button == Button2) { ++ XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, ++ utf8, utf8, win, CurrentTime); ++ drawmenu(); ++ return; ++ } ++ /* scroll up */ ++ if (ev->button == Button4 && prev) { ++ sel = curr = prev; ++ calcoffsets(); ++ drawmenu(); ++ return; ++ } ++ /* scroll down */ ++ if (ev->button == Button5 && next) { ++ sel = curr = next; ++ calcoffsets(); ++ drawmenu(); ++ return; ++ } ++ if (ev->button != Button1) ++ return; ++ if (ev->state & ~ControlMask) ++ return; ++ if (lines > 0) { ++ /* vertical list: (ctrl)left-click on item */ ++ w = mw - x; ++ for (item = curr; item != next; item = item->right) { ++ y += h; ++ if (ev->y >= y && ev->y <= (y + h)) { ++ puts(item->text); ++ if (!(ev->state & ControlMask)) ++ exit(0); ++ sel = item; ++ if (sel) { ++ sel->out = 1; ++ drawmenu(); ++ } ++ return; ++ } ++ } ++ } else if (matches) { ++ /* left-click on left arrow */ ++ x += inputw; ++ w = TEXTW("<"); ++ if (prev && curr->left) { ++ if (ev->x >= x && ev->x <= x + w) { ++ sel = curr = prev; ++ calcoffsets(); ++ drawmenu(); ++ return; ++ } ++ } ++ /* horizontal list: (ctrl)left-click on item */ ++ for (item = curr; item != next; item = item->right) { ++ x += w; ++ w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); ++ if (ev->x >= x && ev->x <= x + w) { ++ puts(item->text); ++ if (!(ev->state & ControlMask)) ++ exit(0); ++ sel = item; ++ if (sel) { ++ sel->out = 1; ++ drawmenu(); ++ } ++ return; ++ } ++ } ++ /* left-click on right arrow */ ++ w = TEXTW(">"); ++ x = mw - w; ++ if (next && ev->x >= x && ev->x <= x + w) { ++ sel = curr = next; ++ calcoffsets(); ++ drawmenu(); ++ return; ++ } ++ } ++} ++ + static void + paste(void) + { +@@ -582,6 +695,9 @@ run(void) + break; + cleanup(); + exit(1); ++ case ButtonPress: ++ buttonpress(&ev); ++ break; + case Expose: + if (ev.xexpose.count == 0) + drw_map(drw, win, 0, 0, mw, mh); +@@ -679,7 +795,8 @@ setup(void) + /* create menu window */ + swa.override_redirect = True; + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; +- swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; ++ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | ++ ButtonPressMask; + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); diff --git a/user/.config/suckless/dmenu/patches/dmenu-mousesupport-motion-5.2.diff b/user/.config/suckless/dmenu/patches/dmenu-mousesupport-motion-5.2.diff new file mode 100644 index 000000000..414201c56 --- /dev/null +++ b/user/.config/suckless/dmenu/patches/dmenu-mousesupport-motion-5.2.diff @@ -0,0 +1,53 @@ +diff --git a/dmenu.c b/dmenu.c +index 48d4980..7677401 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -641,6 +641,29 @@ buttonpress(XEvent *e) + } + } + ++static void ++motionevent(XButtonEvent *ev) ++{ ++ struct item *it; ++ int xy, ev_xy; ++ ++ if (ev->window != win || matches == 0) ++ return; ++ ++ xy = lines > 0 ? bh : inputw + promptw + TEXTW("<"); ++ ev_xy = lines > 0 ? ev->y : ev->x; ++ for (it = curr; it && it != next; it = it->right) { ++ int wh = lines > 0 ? bh : textw_clamp(it->text, mw - xy - TEXTW(">")); ++ if (ev_xy >= xy && ev_xy < (xy + wh)) { ++ sel = it; ++ calcoffsets(); ++ drawmenu(); ++ break; ++ } ++ xy += wh; ++ } ++} ++ + static void + paste(void) + { +@@ -702,6 +725,9 @@ run(void) + case ButtonPress: + buttonpress(&ev); + break; ++ case MotionNotify: ++ motionevent(&ev.xbutton); ++ break; + case Expose: + if (ev.xexpose.count == 0) + drw_map(drw, win, 0, 0, mw, mh); +@@ -800,7 +826,7 @@ setup(void) + swa.override_redirect = True; + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | +- ButtonPressMask; ++ ButtonPressMask | PointerMotionMask; + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); diff --git a/user/.config/suckless/dmenu/patches/dmenu-password-5.0.diff b/user/.config/suckless/dmenu/patches/dmenu-password-5.0.diff new file mode 100644 index 000000000..7a187b98e --- /dev/null +++ b/user/.config/suckless/dmenu/patches/dmenu-password-5.0.diff @@ -0,0 +1,103 @@ +From c4de1032bd4c247bc20b6ab92a10a8d778966679 Mon Sep 17 00:00:00 2001 +From: Mehrad Mahmoudian +Date: Tue, 4 May 2021 12:05:09 +0300 +Subject: [PATCH] patched with password patch + +--- + dmenu.1 | 5 ++++- + dmenu.c | 21 +++++++++++++++++---- + 2 files changed, 21 insertions(+), 5 deletions(-) + +diff --git a/dmenu.1 b/dmenu.1 +index 323f93c..762f707 100644 +--- a/dmenu.1 ++++ b/dmenu.1 +@@ -3,7 +3,7 @@ + dmenu \- dynamic menu + .SH SYNOPSIS + .B dmenu +-.RB [ \-bfiv ] ++.RB [ \-bfivP ] + .RB [ \-l + .IR lines ] + .RB [ \-m +@@ -47,6 +47,9 @@ is faster, but will lock up X until stdin reaches end\-of\-file. + .B \-i + dmenu matches menu items case insensitively. + .TP ++.B \-P ++dmenu will not directly display the keyboard input, but instead replace it with dots. All data from stdin will be ignored. ++.TP + .BI \-l " lines" + dmenu lists items vertically, with the given number of lines. + .TP +diff --git a/dmenu.c b/dmenu.c +index 65f25ce..ad8f63b 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -37,7 +37,7 @@ struct item { + static char text[BUFSIZ] = ""; + static char *embed; + static int bh, mw, mh; +-static int inputw = 0, promptw; ++static int inputw = 0, promptw, passwd = 0; + static int lrpad; /* sum of left and right padding */ + static size_t cursor; + static struct item *items = NULL; +@@ -132,6 +132,7 @@ drawmenu(void) + unsigned int curpos; + struct item *item; + int x = 0, y = 0, w; ++ char *censort; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); +@@ -143,7 +144,12 @@ drawmenu(void) + /* draw input field */ + w = (lines > 0 || !matches) ? mw - x : inputw; + drw_setscheme(drw, scheme[SchemeNorm]); +- drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); ++ if (passwd) { ++ censort = ecalloc(1, sizeof(text)); ++ memset(censort, '.', strlen(text)); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0); ++ free(censort); ++ } else drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); + + curpos = TEXTW(text) - TEXTW(&text[cursor]); + if ((curpos += lrpad / 2 - 1) < w) { +@@ -524,6 +530,11 @@ readstdin(void) + char buf[sizeof text], *p; + size_t i, imax = 0, size = 0; + unsigned int tmpmax = 0; ++ if(passwd){ ++ inputw = lines = 0; ++ return; ++ } ++ + + /* read each line from stdin and add it to the item list */ + for (i = 0; fgets(buf, sizeof buf, stdin); i++) { +@@ -689,7 +700,7 @@ setup(void) + static void + usage(void) + { +- fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" ++ fputs("usage: dmenu [-bfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); + exit(1); + } +@@ -712,7 +723,9 @@ main(int argc, char *argv[]) + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; +- } else if (i + 1 == argc) ++ } else if (!strcmp(argv[i], "-P")) /* is the input a password */ ++ passwd = 1; ++ else if (i + 1 == argc) + usage(); + /* these options take one argument */ + else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ +-- +2.31.1 + diff --git a/user/.config/suckless/dmenu/scripts/dmenu_blue b/user/.config/suckless/dmenu/scripts/dmenu_blue new file mode 100755 index 000000000..5b6459605 --- /dev/null +++ b/user/.config/suckless/dmenu/scripts/dmenu_blue @@ -0,0 +1,317 @@ +#!/usr/bin/env bash +# _ _ _ _ _ _ +# __| |_ __ ___ ___ _ __ _ _ | |__ | |_ _ ___| |_ ___ ___ | |_ | |__ +# / _` | '_ ` _ \ / _ \ '_ \| | | |_____| '_ \| | | | |/ _ \ __/ _ \ / _ \| __|| '_ \ +# | (_| | | | | | | __/ | | | |_| |_____| |_) | | |_| | __/ || (_) | (_) | |_ | | | | +# \__,_|_| |_| |_|\___|_| |_|\__,_| |_.__/|_|\__,_|\___|\__\___/ \___/ \__||_| |_| +# +# Author: Nick Clyde (clydedroid) +# dmenu support by: Layerex +# +# A script that generates a dmenu menu that uses bluetoothctl to +# connect to bluetooth devices and display status info. +# +# Inspired by networkmanager-dmenu (https://github.com/firecat53/networkmanager-dmenu) +# Thanks to x70b1 (https://github.com/polybar/polybar-scripts/tree/master/polybar-scripts/system-bluetooth-bluetoothctl) +# +# Depends on: +# Arch repositories: dmenu, bluez-utils (contains bluetoothctl) + +# Constants +divider="---------" +goback="Back" + +# Checks if bluetooth controller is powered on +power_on() { + if bluetoothctl show | grep -F -q "Powered: yes"; then + return 0 + else + return 1 + fi +} + +# Toggles power state +toggle_power() { + if power_on; then + bluetoothctl power off + show_menu + else + if rfkill list bluetooth | grep -F -q 'blocked: yes'; then + rfkill unblock bluetooth && sleep 3 + fi + bluetoothctl power on + show_menu + fi +} + +# Checks if controller is scanning for new devices +scan_on() { + if bluetoothctl show | grep -F -q "Discovering: yes"; then + echo "Scan: on" + return 0 + else + echo "Scan: off" + return 1 + fi +} + +# Toggles scanning state +toggle_scan() { + if scan_on; then + kill "$(pgrep -F -f "bluetoothctl scan on")" + bluetoothctl scan off + show_menu + else + bluetoothctl scan on & + echo "Scanning..." + sleep 5 + show_menu + fi +} + +# Checks if controller is able to pair to devices +pairable_on() { + if bluetoothctl show | grep -F -q "Pairable: yes"; then + echo "Pairable: on" + return 0 + else + echo "Pairable: off" + return 1 + fi +} + +# Toggles pairable state +toggle_pairable() { + if pairable_on; then + bluetoothctl pairable off + show_menu + else + bluetoothctl pairable on + show_menu + fi +} + +# Checks if controller is discoverable by other devices +discoverable_on() { + if bluetoothctl show | grep -F -q "Discoverable: yes"; then + echo "Discoverable: on" + return 0 + else + echo "Discoverable: off" + return 1 + fi +} + +# Toggles discoverable state +toggle_discoverable() { + if discoverable_on; then + bluetoothctl discoverable off + show_menu + else + bluetoothctl discoverable on + show_menu + fi +} + +# Checks if a device is connected +device_connected() { + device_info=$(bluetoothctl info "$1") + if echo "$device_info" | grep -F -q "Connected: yes"; then + return 0 + else + return 1 + fi +} + +# Toggles device connection +toggle_connection() { + if device_connected "$1"; then + bluetoothctl disconnect "$1" + # device_menu "$device" + else + bluetoothctl connect "$1" + # device_menu "$device" + fi +} + +# Checks if a device is paired +device_paired() { + device_info=$(bluetoothctl info "$1") + if echo "$device_info" | grep -F -q "Paired: yes"; then + echo "Paired: yes" + return 0 + else + echo "Paired: no" + return 1 + fi +} + +# Toggles device paired state +toggle_paired() { + if device_paired "$1"; then + bluetoothctl remove "$1" + device_menu "$device" + else + bluetoothctl pair "$1" + device_menu "$device" + fi +} + +# Checks if a device is trusted +device_trusted() { + device_info=$(bluetoothctl info "$1") + if echo "$device_info" | grep -F -q "Trusted: yes"; then + echo "Trusted: yes" + return 0 + else + echo "Trusted: no" + return 1 + fi +} + +# Toggles device connection +toggle_trust() { + if device_trusted "$1"; then + bluetoothctl untrust "$1" + device_menu "$device" + else + bluetoothctl trust "$1" + device_menu "$device" + fi +} + +# Prints a short string with the current bluetooth status +# Useful for status bars like polybar, etc. +print_status() { + if power_on; then + printf '' + + mapfile -t paired_devices < <(bluetoothctl paired-devices | grep -F Device | cut -d ' ' -f 2) + counter=0 + + for device in "${paired_devices[@]}"; do + if device_connected "$device"; then + device_alias="$(bluetoothctl info "$device" | grep -F "Alias" | cut -d ' ' -f 2-)" + + if [ $counter -gt 0 ]; then + printf ", %s" "$device_alias" + else + printf " %s" "$device_alias" + fi + + ((counter++)) + fi + done + printf "\n" + else + echo "" + fi +} + +# A submenu for a specific device that allows connecting, pairing, and trusting +device_menu() { + device=$1 + + # Get device name and mac address + device_name="$(echo "$device" | cut -d ' ' -f 3-)" + mac="$(echo "$device" | cut -d ' ' -f 2)" + + # Build options + if device_connected "$mac"; then + connected="Connected: yes" + else + connected="Connected: no" + fi + paired=$(device_paired "$mac") + trusted=$(device_trusted "$mac") + options="$connected\n$paired\n$trusted\n$divider\n$goback\nExit" + + # Open dmenu menu, read chosen option + chosen="$(echo -e "$options" | run_dmenu "$device_name")" + + # Match chosen option to command + case $chosen in + "" | "$divider") + echo "No option chosen." + ;; + "$connected") + toggle_connection "$mac" + ;; + "$paired") + toggle_paired "$mac" + ;; + "$trusted") + toggle_trust "$mac" + ;; + "$goback") + show_menu + ;; + esac +} + +# Opens a dmenu menu with current bluetooth status and options to connect +show_menu() { + # Get menu options + if power_on; then + power="Power: on" + + # Human-readable names of devices, one per line + # If scan is off, will only list paired devices + devices=$(bluetoothctl devices | grep -F Device | cut -d ' ' -f 3-) + + # Get controller flags + scan=$(scan_on) + pairable=$(pairable_on) + discoverable=$(discoverable_on) + + # Options passed to dmenu + options="$devices\n$divider\n$power\n$scan\n$pairable\n$discoverable\nExit" + else + power="Power: off" + options="$power\nExit" + fi + + # Open dmenu menu, read chosen option + chosen="$(echo -e "$options" | run_dmenu "Bluetooth")" + + # Match chosen option to command + case $chosen in + "" | "$divider") + echo "No option chosen." + ;; + "$power") + toggle_power + ;; + "$scan") + toggle_scan + ;; + "$discoverable") + toggle_discoverable + ;; + "$pairable") + toggle_pairable + ;; + *) + device=$(bluetoothctl devices | grep -F "$chosen") + # Open a submenu if a device is selected + if [[ $device ]]; then device_menu "$device"; fi + ;; + esac +} + +original_args=("$@") + +# dmenu command to pipe into. Extra arguments to dmenu-bluetooth are passed through to dmenu. This +# allows the user to set fonts, sizes, colours, etc. +run_dmenu() { + dmenu "${original_args[@]}" -b -l 5 -i -p "$1" +} + +case "$1" in + --status) + print_status + ;; + *) + show_menu + ;; +esac diff --git a/user/.config/suckless/dmenu/scripts/dmenu_drun b/user/.config/suckless/dmenu/scripts/dmenu_drun new file mode 100755 index 000000000..49d256429 --- /dev/null +++ b/user/.config/suckless/dmenu/scripts/dmenu_drun @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# ***This script was made by Clay Gomera (Drake)*** +# - Description: A simple desktop dmenu script +# - Dependencies: dmenu, j4-dmenu-desktop + +prompt="-p launch:" +colors="-nb \#1d2021 -nf \#fbf1c7 -sb \#cc241d -sf \#fbf1c7" +font="-fn 'mononoki Nerd Font-10'" + +DMENU="dmenu -i -l 10 $font $colors $prompt" +j4-dmenu-desktop --dmenu "$DMENU" --no-generic diff --git a/user/.config/suckless/dmenu/scripts/dmenu_edit b/user/.config/suckless/dmenu/scripts/dmenu_edit new file mode 100755 index 000000000..80c270cc9 --- /dev/null +++ b/user/.config/suckless/dmenu/scripts/dmenu_edit @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# ***This script was made by Clay Gomera (Drake)*** +# - Description: A simple script for file editing in dmenu +# - Dependencies: dmenu (Everything else can be changed) + +# Show list of options +EDITOR="emacsclient -c -a emacs" +cd "$HOME" || exit 0 +file=1 +while [ "$file" ]; do + file=$(fd -LHpd 1 | dmenu -i -l 10 -fn "mononoki Nerd Font-10" -nb \#1d2021 -nf \#fbf1c7 -sb \#cc241d -sf \#fbf1c7 -p "file to edit: $(basename "$(pwd)")") + if [ -e "$file" ]; then + owd=$(pwd) + if [ -d "$file" ]; then + cd "$file" || exit 0 + else [ -f "$file" ] + if [ "$file" ]; then + $EDITOR "$owd/$file" & + exit 0 + else + exit 0 + fi + fi + fi +done diff --git a/user/.config/suckless/dmenu/scripts/dmenu_emoji b/user/.config/suckless/dmenu/scripts/dmenu_emoji new file mode 100755 index 000000000..b494cde55 --- /dev/null +++ b/user/.config/suckless/dmenu/scripts/dmenu_emoji @@ -0,0 +1,1839 @@ +#!/bin/bash +# This files comes from: +# https://github.com/porras/dmenu-emoji +# +# If this file includes emojis below "__DATA__" it is generated. +# This file was generated: 2022-04-21 12:38:03+00:00 + +set -e + +case "$1" in + "list") + data=$(sed '0,/^__DATA__$/d' "$0") + echo "$data" + ;; + "copy") + input=$(tee) + if [ ! -z "$input" ]; then + emoji=${input: -1} + echo -n "$emoji" | xclip -selection c + command -v notify-send > /dev/null && notify-send "$emoji copied!" + fi + ;; + "") + bash $0 list | dmenu -b -i -l 10 -p 'Emoji: ' | bash $0 copy + ;; +esac + +exit + +__DATA__ +grinning face 😀 +grinning face with big eyes 😃 +grinning face with smiling eyes 😄 +beaming face with smiling eyes 😁 +grinning squinting face 😆 +grinning face with sweat 😅 +rolling on the floor laughing 🤣 +face with tears of joy 😂 +slightly smiling face 🙂 +upside-down face 🙃 +winking face 😉 +smiling face with smiling eyes 😊 +smiling face with halo 😇 +smiling face with hearts 🥰 +smiling face with heart-eyes 😍 +star-struck 🤩 +face blowing a kiss 😘 +kissing face 😗 +smiling face ☺ +kissing face with closed eyes 😚 +kissing face with smiling eyes 😙 +smiling face with tear 🥲 +face savoring food 😋 +face with tongue 😛 +winking face with tongue 😜 +zany face 🤪 +squinting face with tongue 😝 +money-mouth face 🤑 +hugging face 🤗 +face with hand over mouth 🤭 +shushing face 🤫 +thinking face 🤔 +zipper-mouth face 🤐 +face with raised eyebrow 🤨 +neutral face 😐 +expressionless face 😑 +face without mouth 😶 +smirking face 😏 +unamused face 😒 +face with rolling eyes 🙄 +grimacing face 😬 +face exhaling 😮‍ +lying face 🤥 +relieved face 😌 +pensive face 😔 +sleepy face 😪 +drooling face 🤤 +sleeping face 😴 +face with medical mask 😷 +face with thermometer 🤒 +face with head-bandage 🤕 +nauseated face 🤢 +face vomiting 🤮 +sneezing face 🤧 +hot face 🥵 +cold face 🥶 +woozy face 🥴 +knocked-out face 😵 +exploding head 🤯 +cowboy hat face 🤠 +partying face 🥳 +disguised face 🥸 +smiling face with sunglasses 😎 +nerd face 🤓 +face with monocle 🧐 +confused face 😕 +worried face 😟 +slightly frowning face 🙁 +face with open mouth 😮 +hushed face 😯 +astonished face 😲 +flushed face 😳 +pleading face 🥺 +frowning face with open mouth 😦 +anguished face 😧 +fearful face 😨 +anxious face with sweat 😰 +sad but relieved face 😥 +crying face 😢 +loudly crying face 😭 +face screaming in fear 😱 +confounded face 😖 +persevering face 😣 +disappointed face 😞 +downcast face with sweat 😓 +weary face 😩 +tired face 😫 +yawning face 🥱 +face with steam from nose 😤 +pouting face 😡 +angry face 😠 +face with symbols on mouth 🤬 +smiling face with horns 😈 +angry face with horns 👿 +skull 💀 +skull and crossbones ☠️ +pile of poo 💩 +clown face 🤡 +ogre 👹 +goblin 👺 +ghost 👻 +alien 👽 +alien monster 👾 +robot 🤖 +grinning cat 😺 +grinning cat with smiling eyes 😸 +cat with tears of joy 😹 +smiling cat with heart-eyes 😻 +cat with wry smile 😼 +kissing cat 😽 +weary cat 🙀 +crying cat 😿 +pouting cat 😾 +see-no-evil monkey 🙈 +hear-no-evil monkey 🙉 +speak-no-evil monkey 🙊 +kiss mark 💋 +love letter 💌 +heart with arrow 💘 +heart with ribbon 💝 +sparkling heart 💖 +growing heart 💗 +beating heart 💓 +revolving hearts 💞 +two hearts 💕 +heart decoration 💟 +heart exclamation ❣️ +broken heart 💔 +heart on fire ❤️‍🔥 +mending heart ❤️‍🩹 +red heart ❤️ +orange heart 🧡 +yellow heart 💛 +green heart 💚 +blue heart 💙 +purple heart 💜 +brown heart 🤎 +black heart 🖤 +white heart 🤍 +hundred points 💯 +anger symbol 💢 +collision 💥 +dizzy 💫 +sweat droplets 💦 +dashing away 💨 +hole 🕳️ +bomb 💣 +speech balloon 💬 +eye in speech bubble 👁️‍🗨️ +left speech bubble 🗨️ +right anger bubble 🗯️ +thought balloon 💭 +zzz 💤 +waving hand 👋 +raised back of hand 🤚 +hand with fingers splayed 🖐️ +raised hand ✋ +vulcan salute 🖖 +OK hand 👌 +pinched fingers 🤌 +pinching hand 🤏 +victory hand ✌️ +crossed fingers 🤞 +love-you gesture 🤟 +sign of the horns 🤘 +call me hand 🤙 +backhand index pointing left 👈 +backhand index pointing right 👉 +backhand index pointing up 👆 +middle finger 🖕 +backhand index pointing down 👇 +index pointing up ☝️ +thumbs up 👍 +thumbs down 👎 +raised fist ✊ +oncoming fist 👊 +left-facing fist 🤛 +right-facing fist 🤜 +clapping hands 👏 +raising hands 🙌 +open hands 👐 +palms up together 🤲 +handshake 🤝 +folded hands 🙏 +writing hand ✍️ +nail polish 💅 +selfie 🤳 +flexed biceps 💪 +mechanical arm 🦾 +mechanical leg 🦿 +leg 🦵 +foot 🦶 +ear 👂 +ear with hearing aid 🦻 +nose 👃 +brain 🧠 +anatomical heart 🫀 +lungs 🫁 +tooth 🦷 +bone 🦴 +eyes 👀 +eye 👁️ +tongue 👅 +mouth 👄 +baby 👶 +child 🧒 +boy 👦 +girl 👧 +person 🧑 +person: blond hair 👱 +man 👨 +person: beard 🧔 +man: beard 🧔‍♂️ +woman: beard 🧔‍♀️ +man: red hair 👨‍🦰 +man: curly hair 👨‍🦱 +man: white hair 👨‍🦳 +man: bald 👨‍🦲 +woman 👩 +woman: red hair 👩‍🦰 +person: red hair 🧑‍🦰 +woman: curly hair 👩‍🦱 +person: curly hair 🧑‍🦱 +woman: white hair 👩‍🦳 +person: white hair 🧑‍🦳 +woman: bald 👩‍🦲 +person: bald 🧑‍🦲 +woman: blond hair 👱‍♀️ +man: blond hair 👱‍♂️ +older person 🧓 +old man 👴 +old woman 👵 +person frowning 🙍 +man frowning 🙍‍♂️ +woman frowning 🙍‍♀️ +person pouting 🙎 +man pouting 🙎‍♂️ +woman pouting 🙎‍♀️ +person gesturing NO 🙅 +man gesturing NO 🙅‍♂️ +woman gesturing NO 🙅‍♀️ +person gesturing OK 🙆 +man gesturing OK 🙆‍♂️ +woman gesturing OK 🙆‍♀️ +person tipping hand 💁 +man tipping hand 💁‍♂️ +woman tipping hand 💁‍♀️ +person raising hand 🙋 +man raising hand 🙋‍♂️ +woman raising hand 🙋‍♀️ +deaf person 🧏 +deaf man 🧏‍♂️ +deaf woman 🧏‍♀️ +person bowing 🙇 +man bowing 🙇‍♂️ +woman bowing 🙇‍♀️ +person facepalming 🤦 +man facepalming 🤦‍♂️ +woman facepalming 🤦‍♀️ +person shrugging 🤷 +man shrugging 🤷‍♂️ +woman shrugging 🤷‍♀️ +health worker 🧑‍⚕️ +man health worker 👨‍⚕️ +woman health worker 👩‍⚕️ +student 🧑‍🎓 +man student 👨‍🎓 +woman student 👩‍🎓 +teacher 🧑‍🏫 +man teacher 👨‍🏫 +woman teacher 👩‍🏫 +judge 🧑‍⚖️ +man judge 👨‍⚖️ +woman judge 👩‍⚖️ +farmer 🧑‍🌾 +man farmer 👨‍🌾 +woman farmer 👩‍🌾 +cook 🧑‍🍳 +man cook 👨‍🍳 +woman cook 👩‍🍳 +mechanic 🧑‍🔧 +man mechanic 👨‍🔧 +woman mechanic 👩‍🔧 +factory worker 🧑‍🏭 +man factory worker 👨‍🏭 +woman factory worker 👩‍🏭 +office worker 🧑‍💼 +man office worker 👨‍💼 +woman office worker 👩‍💼 +scientist 🧑‍🔬 +man scientist 👨‍🔬 +woman scientist 👩‍🔬 +technologist 🧑‍💻 +man technologist 👨‍💻 +woman technologist 👩‍💻 +singer 🧑‍🎤 +man singer 👨‍🎤 +woman singer 👩‍🎤 +artist 🧑‍🎨 +man artist 👨‍🎨 +woman artist 👩‍🎨 +pilot 🧑‍✈️ +man pilot 👨‍✈️ +woman pilot 👩‍✈️ +astronaut 🧑‍🚀 +man astronaut 👨‍🚀 +woman astronaut 👩‍🚀 +firefighter 🧑‍🚒 +man firefighter 👨‍🚒 +woman firefighter 👩‍🚒 +police officer 👮 +man police officer 👮‍♂️ +woman police officer 👮‍♀️ +detective 🕵️ +man detective 🕵️‍♂️ +woman detective 🕵️‍♀️ +guard 💂 +man guard 💂‍♂️ +woman guard 💂‍♀️ +ninja 🥷 +construction worker 👷 +man construction worker 👷‍♂️ +woman construction worker 👷‍♀️ +prince 🤴 +princess 👸 +person wearing turban 👳 +man wearing turban 👳‍♂️ +woman wearing turban 👳‍♀️ +person with skullcap 👲 +woman with headscarf 🧕 +person in tuxedo 🤵 +man in tuxedo 🤵‍♂️ +woman in tuxedo 🤵‍♀️ +person with veil 👰 +man with veil 👰‍♂️ +woman with veil 👰‍♀️ +pregnant woman 🤰 +breast-feeding 🤱 +woman feeding baby 👩‍🍼 +man feeding baby 👨‍🍼 +person feeding baby 🧑‍🍼 +baby angel 👼 +Santa Claus 🎅 +Mrs. Claus 🤶 +mx claus 🧑‍🎄 +superhero 🦸 +man superhero 🦸‍♂️ +woman superhero 🦸‍♀️ +supervillain 🦹 +man supervillain 🦹‍♂️ +woman supervillain 🦹‍♀️ +mage 🧙 +man mage 🧙‍♂️ +woman mage 🧙‍♀️ +fairy 🧚 +man fairy 🧚‍♂️ +woman fairy 🧚‍♀️ +vampire 🧛 +man vampire 🧛‍♂️ +woman vampire 🧛‍♀️ +merperson 🧜 +merman 🧜‍♂️ +mermaid 🧜‍♀️ +elf 🧝 +man elf 🧝‍♂️ +woman elf 🧝‍♀️ +genie 🧞 +man genie 🧞‍♂️ +woman genie 🧞‍♀️ +zombie 🧟 +man zombie 🧟‍♂️ +woman zombie 🧟‍♀️ +person getting massage 💆 +man getting massage 💆‍♂️ +woman getting massage 💆‍♀️ +person getting haircut 💇 +man getting haircut 💇‍♂️ +woman getting haircut 💇‍♀️ +person walking 🚶 +man walking 🚶‍♂️ +woman walking 🚶‍♀️ +person standing 🧍 +man standing 🧍‍♂️ +woman standing 🧍‍♀️ +person kneeling 🧎 +man kneeling 🧎‍♂️ +woman kneeling 🧎‍♀️ +person with white cane 🧑‍🦯 +man with white cane 👨‍🦯 +woman with white cane 👩‍🦯 +person in motorized wheelchair 🧑‍🦼 +man in motorized wheelchair 👨‍🦼 +woman in motorized wheelchair 👩‍🦼 +person in manual wheelchair 🧑‍🦽 +man in manual wheelchair 👨‍🦽 +woman in manual wheelchair 👩‍🦽 +person running 🏃 +man running 🏃‍♂️ +woman running 🏃‍♀️ +woman dancing 💃 +man dancing 🕺 +person in suit levitating 🕴️ +people with bunny ears 👯 +men with bunny ears 👯‍♂️ +women with bunny ears 👯‍♀️ +person in steamy room 🧖 +man in steamy room 🧖‍♂️ +woman in steamy room 🧖‍♀️ +person climbing 🧗 +man climbing 🧗‍♂️ +woman climbing 🧗‍♀️ +person fencing 🤺 +horse racing 🏇 +skier ⛷️ +snowboarder 🏂 +person golfing 🏌️ +man golfing 🏌️‍♂️ +woman golfing 🏌️‍♀️ +person surfing 🏄 +man surfing 🏄‍♂️ +woman surfing 🏄‍♀️ +person rowing boat 🚣 +man rowing boat 🚣‍♂️ +woman rowing boat 🚣‍♀️ +person swimming 🏊 +man swimming 🏊‍♂️ +woman swimming 🏊‍♀️ +person bouncing ball ⛹️ +man bouncing ball ⛹️‍♂️ +woman bouncing ball ⛹️‍♀️ +person lifting weights 🏋️ +man lifting weights 🏋️‍♂️ +woman lifting weights 🏋️‍♀️ +person biking 🚴 +man biking 🚴‍♂️ +woman biking 🚴‍♀️ +person mountain biking 🚵 +man mountain biking 🚵‍♂️ +woman mountain biking 🚵‍♀️ +person cartwheeling 🤸 +man cartwheeling 🤸‍♂️ +woman cartwheeling 🤸‍♀️ +people wrestling 🤼 +men wrestling 🤼‍♂️ +women wrestling 🤼‍♀️ +person playing water polo 🤽 +man playing water polo 🤽‍♂️ +woman playing water polo 🤽‍♀️ +person playing handball 🤾 +man playing handball 🤾‍♂️ +woman playing handball 🤾‍♀️ +person juggling 🤹 +man juggling 🤹‍♂️ +woman juggling 🤹‍♀️ +person in lotus position 🧘 +man in lotus position 🧘‍♂️ +woman in lotus position 🧘‍♀️ +person taking bath 🛀 +person in bed 🛌 +people holding hands 🧑‍🤝‍🧑 +women holding hands 👭 +woman and man holding hands 👫 +men holding hands 👬 +kiss 💏 +kiss: woman man 👩‍❤️‍💋‍👨 +kiss: man man 👨‍❤️‍💋‍👨 +kiss: woman woman 👩‍❤️‍💋‍👩 +couple with heart 💑 +couple with heart: woman man 👩‍❤️‍👨 +couple with heart: man man 👨‍❤️‍👨 +couple with heart: woman woman 👩‍❤️‍👩 +family 👪 +family: man woman boy 👨‍👩‍👦 +family: man woman girl 👨‍👩‍👧 +family: man woman girl boy 👨‍👩‍👧‍👦 +family: man woman boy boy 👨‍👩‍👦‍👦 +family: man woman girl girl 👨‍👩‍👧‍👧 +family: man man boy 👨‍👨‍👦 +family: man man girl 👨‍👨‍👧 +family: man man girl boy 👨‍👨‍👧‍👦 +family: man man boy boy 👨‍👨‍👦‍👦 +family: man man girl girl 👨‍👨‍👧‍👧 +family: woman woman boy 👩‍👩‍👦 +family: woman woman girl 👩‍👩‍👧 +family: woman woman girl boy 👩‍👩‍👧‍👦 +family: woman woman boy boy 👩‍👩‍👦‍👦 +family: woman woman girl girl 👩‍👩‍👧‍👧 +family: man boy 👨‍👦 +family: man boy boy 👨‍👦‍👦 +family: man girl 👨‍👧 +family: man girl boy 👨‍👧‍👦 +family: man girl girl 👨‍👧‍👧 +family: woman boy 👩‍👦 +family: woman boy boy 👩‍👦‍👦 +family: woman girl 👩‍👧 +family: woman girl boy 👩‍👧‍👦 +family: woman girl girl 👩‍👧‍👧 +speaking head 🗣️ +bust in silhouette 👤 +busts in silhouette 👥 +people hugging 🫂 +footprints 👣 +monkey face 🐵 +monkey 🐒 +gorilla 🦍 +orangutan 🦧 +dog face 🐶 +dog 🐕 +guide dog 🦮 +service dog 🐕‍🦺 +poodle 🐩 +wolf 🐺 +fox 🦊 +raccoon 🦝 +cat face 🐱 +cat 🐈 +black cat 🐈‍⬛ +lion 🦁 +tiger face 🐯 +tiger 🐅 +leopard 🐆 +horse face 🐴 +horse 🐎 +unicorn 🦄 +zebra 🦓 +deer 🦌 +bison 🦬 +cow face 🐮 +ox 🐂 +water buffalo 🐃 +cow 🐄 +pig face 🐷 +pig 🐖 +boar 🐗 +pig nose 🐽 +ram 🐏 +ewe 🐑 +goat 🐐 +camel 🐪 +two-hump camel 🐫 +llama 🦙 +giraffe 🦒 +elephant 🐘 +mammoth 🦣 +rhinoceros 🦏 +hippopotamus 🦛 +mouse face 🐭 +mouse 🐁 +rat 🐀 +hamster 🐹 +rabbit face 🐰 +rabbit 🐇 +chipmunk 🐿️ +beaver 🦫 +hedgehog 🦔 +bat 🦇 +bear 🐻 +polar bear 🐻‍❄️ +koala 🐨 +panda 🐼 +sloth 🦥 +otter 🦦 +skunk 🦨 +kangaroo 🦘 +badger 🦡 +paw prints 🐾 +turkey 🦃 +chicken 🐔 +rooster 🐓 +hatching chick 🐣 +baby chick 🐤 +front-facing baby chick 🐥 +bird 🐦 +penguin 🐧 +dove 🕊️ +eagle 🦅 +duck 🦆 +swan 🦢 +owl 🦉 +dodo 🦤 +feather 🪶 +flamingo 🦩 +peacock 🦚 +parrot 🦜 +frog 🐸 +crocodile 🐊 +turtle 🐢 +lizard 🦎 +snake 🐍 +dragon face 🐲 +dragon 🐉 +sauropod 🦕 +T-Rex 🦖 +spouting whale 🐳 +whale 🐋 +dolphin 🐬 +seal 🦭 +fish 🐟 +tropical fish 🐠 +blowfish 🐡 +shark 🦈 +octopus 🐙 +spiral shell 🐚 +snail 🐌 +butterfly 🦋 +bug 🐛 +ant 🐜 +honeybee 🐝 +beetle 🪲 +lady beetle 🐞 +cricket 🦗 +cockroach 🪳 +spider 🕷️ +spider web 🕸️ +scorpion 🦂 +mosquito 🦟 +fly 🪰 +worm 🪱 +microbe 🦠 +bouquet 💐 +cherry blossom 🌸 +white flower 💮 +rosette 🏵️ +rose 🌹 +wilted flower 🥀 +hibiscus 🌺 +sunflower 🌻 +blossom 🌼 +tulip 🌷 +seedling 🌱 +potted plant 🪴 +evergreen tree 🌲 +deciduous tree 🌳 +palm tree 🌴 +cactus 🌵 +sheaf of rice 🌾 +herb 🌿 +shamrock ☘️ +four leaf clover 🍀 +maple leaf 🍁 +fallen leaf 🍂 +leaf fluttering in wind 🍃 +grapes 🍇 +melon 🍈 +watermelon 🍉 +tangerine 🍊 +lemon 🍋 +banana 🍌 +pineapple 🍍 +mango 🥭 +red apple 🍎 +green apple 🍏 +pear 🍐 +peach 🍑 +cherries 🍒 +strawberry 🍓 +blueberries 🫐 +kiwi fruit 🥝 +tomato 🍅 +olive 🫒 +coconut 🥥 +avocado 🥑 +eggplant 🍆 +potato 🥔 +carrot 🥕 +ear of corn 🌽 +hot pepper 🌶️ +bell pepper 🫑 +cucumber 🥒 +leafy green 🥬 +broccoli 🥦 +garlic 🧄 +onion 🧅 +mushroom 🍄 +peanuts 🥜 +chestnut 🌰 +bread 🍞 +croissant 🥐 +baguette bread 🥖 +flatbread 🫓 +pretzel 🥨 +bagel 🥯 +pancakes 🥞 +waffle 🧇 +cheese wedge 🧀 +meat on bone 🍖 +poultry leg 🍗 +cut of meat 🥩 +bacon 🥓 +hamburger 🍔 +french fries 🍟 +pizza 🍕 +hot dog 🌭 +sandwich 🥪 +taco 🌮 +burrito 🌯 +tamale 🫔 +stuffed flatbread 🥙 +falafel 🧆 +egg 🥚 +cooking 🍳 +shallow pan of food 🥘 +pot of food 🍲 +fondue 🫕 +bowl with spoon 🥣 +green salad 🥗 +popcorn 🍿 +butter 🧈 +salt 🧂 +canned food 🥫 +bento box 🍱 +rice cracker 🍘 +rice ball 🍙 +cooked rice 🍚 +curry rice 🍛 +steaming bowl 🍜 +spaghetti 🍝 +roasted sweet potato 🍠 +oden 🍢 +sushi 🍣 +fried shrimp 🍤 +fish cake with swirl 🍥 +moon cake 🥮 +dango 🍡 +dumpling 🥟 +fortune cookie 🥠 +takeout box 🥡 +crab 🦀 +lobster 🦞 +shrimp 🦐 +squid 🦑 +oyster 🦪 +soft ice cream 🍦 +shaved ice 🍧 +ice cream 🍨 +doughnut 🍩 +cookie 🍪 +birthday cake 🎂 +shortcake 🍰 +cupcake 🧁 +pie 🥧 +chocolate bar 🍫 +candy 🍬 +lollipop 🍭 +custard 🍮 +honey pot 🍯 +baby bottle 🍼 +glass of milk 🥛 +hot beverage ☕ +teapot 🫖 +teacup without handle 🍵 +sake 🍶 +bottle with popping cork 🍾 +wine glass 🍷 +cocktail glass 🍸 +tropical drink 🍹 +beer mug 🍺 +clinking beer mugs 🍻 +clinking glasses 🥂 +tumbler glass 🥃 +cup with straw 🥤 +bubble tea 🧋 +beverage box 🧃 +mate 🧉 +ice 🧊 +chopsticks 🥢 +fork and knife with plate 🍽️ +fork and knife 🍴 +spoon 🥄 +kitchen knife 🔪 +amphora 🏺 +globe showing Europe-Africa 🌍 +globe showing Americas 🌎 +globe showing Asia-Australia 🌏 +globe with meridians 🌐 +world map 🗺️ +map of Japan 🗾 +compass 🧭 +snow-capped mountain 🏔️ +mountain ⛰️ +volcano 🌋 +mount fuji 🗻 +camping 🏕️ +beach with umbrella 🏖️ +desert 🏜️ +desert island 🏝️ +national park 🏞️ +stadium 🏟️ +classical building 🏛️ +building construction 🏗️ +brick 🧱 +rock 🪨 +wood 🪵 +hut 🛖 +houses 🏘️ +derelict house 🏚️ +house 🏠 +house with garden 🏡 +office building 🏢 +Japanese post office 🏣 +post office 🏤 +hospital 🏥 +bank 🏦 +hotel 🏨 +love hotel 🏩 +convenience store 🏪 +school 🏫 +department store 🏬 +factory 🏭 +Japanese castle 🏯 +castle 🏰 +wedding 💒 +Tokyo tower 🗼 +Statue of Liberty 🗽 +church ⛪ +mosque 🕌 +hindu temple 🛕 +synagogue 🕍 +shinto shrine ⛩️ +kaaba 🕋 +fountain ⛲ +tent ⛺ +foggy 🌁 +night with stars 🌃 +cityscape 🏙️ +sunrise over mountains 🌄 +sunrise 🌅 +cityscape at dusk 🌆 +sunset 🌇 +bridge at night 🌉 +hot springs ♨️ +carousel horse 🎠 +ferris wheel 🎡 +roller coaster 🎢 +barber pole 💈 +circus tent 🎪 +locomotive 🚂 +railway car 🚃 +high-speed train 🚄 +bullet train 🚅 +train 🚆 +metro 🚇 +light rail 🚈 +station 🚉 +tram 🚊 +monorail 🚝 +mountain railway 🚞 +tram car 🚋 +bus 🚌 +oncoming bus 🚍 +trolleybus 🚎 +minibus 🚐 +ambulance 🚑 +fire engine 🚒 +police car 🚓 +oncoming police car 🚔 +taxi 🚕 +oncoming taxi 🚖 +automobile 🚗 +oncoming automobile 🚘 +sport utility vehicle 🚙 +pickup truck 🛻 +delivery truck 🚚 +articulated lorry 🚛 +tractor 🚜 +racing car 🏎️ +motorcycle 🏍️ +motor scooter 🛵 +manual wheelchair 🦽 +motorized wheelchair 🦼 +auto rickshaw 🛺 +bicycle 🚲 +kick scooter 🛴 +skateboard 🛹 +roller skate 🛼 +bus stop 🚏 +motorway 🛣️ +railway track 🛤️ +oil drum 🛢️ +fuel pump ⛽ +police car light 🚨 +horizontal traffic light 🚥 +vertical traffic light 🚦 +stop sign 🛑 +construction 🚧 +anchor ⚓ +sailboat ⛵ +canoe 🛶 +speedboat 🚤 +passenger ship 🛳️ +ferry ⛴️ +motor boat 🛥️ +ship 🚢 +airplane ✈️ +small airplane 🛩️ +airplane departure 🛫 +airplane arrival 🛬 +parachute 🪂 +seat 💺 +helicopter 🚁 +suspension railway 🚟 +mountain cableway 🚠 +aerial tramway 🚡 +satellite 🛰️ +rocket 🚀 +flying saucer 🛸 +bellhop bell 🛎️ +luggage 🧳 +hourglass done ⌛ +hourglass not done ⏳ +watch ⌚ +alarm clock ⏰ +stopwatch ⏱️ +timer clock ⏲️ +mantelpiece clock 🕰️ +twelve o’clock 🕛 +twelve-thirty 🕧 +one o’clock 🕐 +one-thirty 🕜 +two o’clock 🕑 +two-thirty 🕝 +three o’clock 🕒 +three-thirty 🕞 +four o’clock 🕓 +four-thirty 🕟 +five o’clock 🕔 +five-thirty 🕠 +six o’clock 🕕 +six-thirty 🕡 +seven o’clock 🕖 +seven-thirty 🕢 +eight o’clock 🕗 +eight-thirty 🕣 +nine o’clock 🕘 +nine-thirty 🕤 +ten o’clock 🕙 +ten-thirty 🕥 +eleven o’clock 🕚 +eleven-thirty 🕦 +new moon 🌑 +waxing crescent moon 🌒 +first quarter moon 🌓 +waxing gibbous moon 🌔 +full moon 🌕 +waning gibbous moon 🌖 +last quarter moon 🌗 +waning crescent moon 🌘 +crescent moon 🌙 +new moon face 🌚 +first quarter moon face 🌛 +last quarter moon face 🌜 +thermometer 🌡️ +sun ☀️ +full moon face 🌝 +sun with face 🌞 +ringed planet 🪐 +star ⭐ +glowing star 🌟 +shooting star 🌠 +milky way 🌌 +cloud ☁️ +sun behind cloud ⛅ +cloud with lightning and rain ⛈️ +sun behind small cloud 🌤️ +sun behind large cloud 🌥️ +sun behind rain cloud 🌦️ +cloud with rain 🌧️ +cloud with snow 🌨️ +cloud with lightning 🌩️ +tornado 🌪️ +fog 🌫️ +wind face 🌬️ +cyclone 🌀 +rainbow 🌈 +closed umbrella 🌂 +umbrella ☂️ +umbrella with rain drops ☔ +umbrella on ground ⛱️ +high voltage ⚡ +snowflake ❄️ +snowman ☃️ +snowman without snow ⛄ +comet ☄️ +fire 🔥 +droplet 💧 +water wave 🌊 +jack-o-lantern 🎃 +Christmas tree 🎄 +fireworks 🎆 +sparkler 🎇 +firecracker 🧨 +sparkles ✨ +balloon 🎈 +party popper 🎉 +confetti ball 🎊 +tanabata tree 🎋 +pine decoration 🎍 +Japanese dolls 🎎 +carp streamer 🎏 +wind chime 🎐 +moon viewing ceremony 🎑 +red envelope 🧧 +ribbon 🎀 +wrapped gift 🎁 +reminder ribbon 🎗️ +admission tickets 🎟️ +ticket 🎫 +military medal 🎖️ +trophy 🏆 +sports medal 🏅 +1st place medal 🥇 +2nd place medal 🥈 +3rd place medal 🥉 +soccer ball ⚽ +baseball ⚾ +softball 🥎 +basketball 🏀 +volleyball 🏐 +american football 🏈 +rugby football 🏉 +tennis 🎾 +flying disc 🥏 +bowling 🎳 +cricket game 🏏 +field hockey 🏑 +ice hockey 🏒 +lacrosse 🥍 +ping pong 🏓 +badminton 🏸 +boxing glove 🥊 +martial arts uniform 🥋 +goal net 🥅 +flag in hole ⛳ +ice skate ⛸️ +fishing pole 🎣 +diving mask 🤿 +running shirt 🎽 +skis 🎿 +sled 🛷 +curling stone 🥌 +bullseye 🎯 +yo-yo 🪀 +kite 🪁 +pool 8 ball 🎱 +crystal ball 🔮 +magic wand 🪄 +nazar amulet 🧿 +video game 🎮 +joystick 🕹️ +slot machine 🎰 +game die 🎲 +puzzle piece 🧩 +teddy bear 🧸 +piñata 🪅 +nesting dolls 🪆 +spade suit ♠️ +heart suit ♥️ +diamond suit ♦️ +club suit ♣️ +chess pawn ♟️ +joker 🃏 +mahjong red dragon 🀄 +flower playing cards 🎴 +performing arts 🎭 +framed picture 🖼️ +artist palette 🎨 +thread 🧵 +sewing needle 🪡 +yarn 🧶 +knot 🪢 +glasses 👓 +sunglasses 🕶️ +goggles 🥽 +lab coat 🥼 +safety vest 🦺 +necktie 👔 +t-shirt 👕 +jeans 👖 +scarf 🧣 +gloves 🧤 +coat 🧥 +socks 🧦 +dress 👗 +kimono 👘 +sari 🥻 +one-piece swimsuit 🩱 +briefs 🩲 +shorts 🩳 +bikini 👙 +woman’s clothes 👚 +purse 👛 +handbag 👜 +clutch bag 👝 +shopping bags 🛍️ +backpack 🎒 +thong sandal 🩴 +man’s shoe 👞 +running shoe 👟 +hiking boot 🥾 +flat shoe 🥿 +high-heeled shoe 👠 +woman’s sandal 👡 +ballet shoes 🩰 +woman’s boot 👢 +crown 👑 +woman’s hat 👒 +top hat 🎩 +graduation cap 🎓 +billed cap 🧢 +military helmet 🪖 +rescue worker’s helmet ⛑️ +prayer beads 📿 +lipstick 💄 +ring 💍 +gem stone 💎 +muted speaker 🔇 +speaker low volume 🔈 +speaker medium volume 🔉 +speaker high volume 🔊 +loudspeaker 📢 +megaphone 📣 +postal horn 📯 +bell 🔔 +bell with slash 🔕 +musical score 🎼 +musical note 🎵 +musical notes 🎶 +studio microphone 🎙️ +level slider 🎚️ +control knobs 🎛️ +microphone 🎤 +headphone 🎧 +radio 📻 +saxophone 🎷 +accordion 🪗 +guitar 🎸 +musical keyboard 🎹 +trumpet 🎺 +violin 🎻 +banjo 🪕 +drum 🥁 +long drum 🪘 +mobile phone 📱 +mobile phone with arrow 📲 +telephone ☎️ +telephone receiver 📞 +pager 📟 +fax machine 📠 +battery 🔋 +electric plug 🔌 +laptop 💻 +desktop computer 🖥️ +printer 🖨️ +keyboard ⌨️ +computer mouse 🖱️ +trackball 🖲️ +computer disk 💽 +floppy disk 💾 +optical disk 💿 +dvd 📀 +abacus 🧮 +movie camera 🎥 +film frames 🎞️ +film projector 📽️ +clapper board 🎬 +television 📺 +camera 📷 +camera with flash 📸 +video camera 📹 +videocassette 📼 +magnifying glass tilted left 🔍 +magnifying glass tilted right 🔎 +candle 🕯️ +light bulb 💡 +flashlight 🔦 +red paper lantern 🏮 +diya lamp 🪔 +notebook with decorative cover 📔 +closed book 📕 +open book 📖 +green book 📗 +blue book 📘 +orange book 📙 +books 📚 +notebook 📓 +ledger 📒 +page with curl 📃 +scroll 📜 +page facing up 📄 +newspaper 📰 +rolled-up newspaper 🗞️ +bookmark tabs 📑 +bookmark 🔖 +label 🏷️ +money bag 💰 +coin 🪙 +yen banknote 💴 +dollar banknote 💵 +euro banknote 💶 +pound banknote 💷 +money with wings 💸 +credit card 💳 +receipt 🧾 +chart increasing with yen 💹 +envelope ✉️ +e-mail 📧 +incoming envelope 📨 +envelope with arrow 📩 +outbox tray 📤 +inbox tray 📥 +package 📦 +closed mailbox with raised flag 📫 +closed mailbox with lowered flag 📪 +open mailbox with raised flag 📬 +open mailbox with lowered flag 📭 +postbox 📮 +ballot box with ballot 🗳️ +pencil ✏️ +black nib ✒️ +fountain pen 🖋️ +pen 🖊️ +paintbrush 🖌️ +crayon 🖍️ +memo 📝 +briefcase 💼 +file folder 📁 +open file folder 📂 +card index dividers 🗂️ +calendar 📅 +tear-off calendar 📆 +spiral notepad 🗒️ +spiral calendar 🗓️ +card index 📇 +chart increasing 📈 +chart decreasing 📉 +bar chart 📊 +clipboard 📋 +pushpin 📌 +round pushpin 📍 +paperclip 📎 +linked paperclips 🖇️ +straight ruler 📏 +triangular ruler 📐 +scissors ✂️ +card file box 🗃️ +file cabinet 🗄️ +wastebasket 🗑️ +locked 🔒 +unlocked 🔓 +locked with pen 🔏 +locked with key 🔐 +key 🔑 +old key 🗝️ +hammer 🔨 +axe 🪓 +pick ⛏️ +hammer and pick ⚒️ +hammer and wrench 🛠️ +dagger 🗡️ +crossed swords ⚔️ +water pistol 🔫 +boomerang 🪃 +bow and arrow 🏹 +shield 🛡️ +carpentry saw 🪚 +wrench 🔧 +screwdriver 🪛 +nut and bolt 🔩 +gear ⚙️ +clamp 🗜️ +balance scale ⚖️ +white cane 🦯 +link 🔗 +chains ⛓️ +hook 🪝 +toolbox 🧰 +magnet 🧲 +ladder 🪜 +alembic ⚗️ +test tube 🧪 +petri dish 🧫 +dna 🧬 +microscope 🔬 +telescope 🔭 +satellite antenna 📡 +syringe 💉 +drop of blood 🩸 +pill 💊 +adhesive bandage 🩹 +stethoscope 🩺 +door 🚪 +elevator 🛗 +mirror 🪞 +window 🪟 +bed 🛏️ +couch and lamp 🛋️ +chair 🪑 +toilet 🚽 +plunger 🪠 +shower 🚿 +bathtub 🛁 +mouse trap 🪤 +razor 🪒 +lotion bottle 🧴 +safety pin 🧷 +broom 🧹 +basket 🧺 +roll of paper 🧻 +bucket 🪣 +soap 🧼 +toothbrush 🪥 +sponge 🧽 +fire extinguisher 🧯 +shopping cart 🛒 +cigarette 🚬 +coffin ⚰️ +headstone 🪦 +funeral urn ⚱️ +moai 🗿 +placard 🪧 +ATM sign 🏧 +litter in bin sign 🚮 +potable water 🚰 +wheelchair symbol ♿ +men’s room 🚹 +women’s room 🚺 +restroom 🚻 +baby symbol 🚼 +water closet 🚾 +passport control 🛂 +customs 🛃 +baggage claim 🛄 +left luggage 🛅 +warning ⚠️ +children crossing 🚸 +no entry ⛔ +prohibited 🚫 +no bicycles 🚳 +no smoking 🚭 +no littering 🚯 +non-potable water 🚱 +no pedestrians 🚷 +no mobile phones 📵 +no one under eighteen 🔞 +radioactive ☢️ +biohazard ☣️ +up arrow ⬆️ +up-right arrow ↗️ +right arrow ➡️ +down-right arrow ↘️ +down arrow ⬇️ +down-left arrow ↙️ +left arrow ⬅️ +up-left arrow ↖️ +up-down arrow ↕️ +left-right arrow ↔️ +right arrow curving left ↩️ +left arrow curving right ↪️ +right arrow curving up ⤴️ +right arrow curving down ⤵️ +clockwise vertical arrows 🔃 +counterclockwise arrows button 🔄 +BACK arrow 🔙 +END arrow 🔚 +ON! arrow 🔛 +SOON arrow 🔜 +TOP arrow 🔝 +place of worship 🛐 +atom symbol ⚛️ +om 🕉️ +star of David ✡️ +wheel of dharma ☸️ +yin yang ☯️ +latin cross ✝️ +orthodox cross ☦️ +star and crescent ☪️ +peace symbol ☮️ +menorah 🕎 +dotted six-pointed star 🔯 +Aries ♈ +Taurus ♉ +Gemini ♊ +Cancer ♋ +Leo ♌ +Virgo ♍ +Libra ♎ +Scorpio ♏ +Sagittarius ♐ +Capricorn ♑ +Aquarius ♒ +Pisces ♓ +Ophiuchus ⛎ +shuffle tracks button 🔀 +repeat button 🔁 +repeat single button 🔂 +play button ▶️ +fast-forward button ⏩ +next track button ⏭️ +play or pause button ⏯️ +reverse button ◀️ +fast reverse button ⏪ +last track button ⏮️ +upwards button 🔼 +fast up button ⏫ +downwards button 🔽 +fast down button ⏬ +pause button ⏸️ +stop button ⏹️ +record button ⏺️ +eject button ⏏️ +cinema 🎦 +dim button 🔅 +bright button 🔆 +antenna bars 📶 +vibration mode 📳 +mobile phone off 📴 +female sign ♀️ +male sign ♂️ +transgender symbol ⚧️ +multiply ✖️ +plus ➕ +minus ➖ +divide ➗ +infinity ♾️ +double exclamation mark ‼️ +exclamation question mark ⁉️ +red question mark ❓ +white question mark ❔ +white exclamation mark ❕ +red exclamation mark ❗ +wavy dash 〰️ +currency exchange 💱 +heavy dollar sign 💲 +medical symbol ⚕️ +recycling symbol ♻️ +fleur-de-lis ⚜️ +trident emblem 🔱 +name badge 📛 +Japanese symbol for beginner 🔰 +hollow red circle ⭕ +check mark button ✅ +check box with check ☑️ +check mark ✔️ +cross mark ❌ +cross mark button ❎ +curly loop ➰ +double curly loop ➿ +part alternation mark 〽️ +eight-spoked asterisk ✳️ +eight-pointed star ✴️ +sparkle ❇️ +copyright ©️ +registered ®️ +trade mark ™️ +keycap: # #️⃣ +keycap: * *️⃣ +keycap: 0 0️⃣ +keycap: 1 1️⃣ +keycap: 2 2️⃣ +keycap: 3 3️⃣ +keycap: 4 4️⃣ +keycap: 5 5️⃣ +keycap: 6 6️⃣ +keycap: 7 7️⃣ +keycap: 8 8️⃣ +keycap: 9 9️⃣ +keycap: 10 🔟 +input latin uppercase 🔠 +input latin lowercase 🔡 +input numbers 🔢 +input symbols 🔣 +input latin letters 🔤 +A button (blood type) 🅰️ +AB button (blood type) 🆎 +B button (blood type) 🅱️ +CL button 🆑 +COOL button 🆒 +FREE button 🆓 +information ℹ️ +ID button 🆔 +circled M Ⓜ️ +NEW button 🆕 +NG button 🆖 +O button (blood type) 🅾️ +OK button 🆗 +P button 🅿️ +SOS button 🆘 +UP! button 🆙 +VS button 🆚 +Japanese “here” button 🈁 +Japanese “service charge” button 🈂️ +Japanese “monthly amount” button 🈷️ +Japanese “not free of charge” button 🈶 +Japanese “reserved” button 🈯 +Japanese “bargain” button 🉐 +Japanese “discount” button 🈹 +Japanese “free of charge” button 🈚 +Japanese “prohibited” button 🈲 +Japanese “acceptable” button 🉑 +Japanese “application” button 🈸 +Japanese “passing grade” button 🈴 +Japanese “vacancy” button 🈳 +Japanese “congratulations” button ㊗️ +Japanese “secret” button ㊙️ +Japanese “open for business” button 🈺 +Japanese “no vacancy” button 🈵 +red circle 🔴 +orange circle 🟠 +yellow circle 🟡 +green circle 🟢 +blue circle 🔵 +purple circle 🟣 +brown circle 🟤 +black circle ⚫ +white circle ⚪ +red square 🟥 +orange square 🟧 +yellow square 🟨 +green square 🟩 +blue square 🟦 +purple square 🟪 +brown square 🟫 +black large square ⬛ +white large square ⬜ +black medium square ◼️ +white medium square ◻️ +black medium-small square ◾ +white medium-small square ◽ +black small square ▪️ +white small square ▫️ +large orange diamond 🔶 +large blue diamond 🔷 +small orange diamond 🔸 +small blue diamond 🔹 +red triangle pointed up 🔺 +red triangle pointed down 🔻 +diamond with a dot 💠 +radio button 🔘 +white square button 🔳 +black square button 🔲 +chequered flag 🏁 +triangular flag 🚩 +crossed flags 🎌 +black flag 🏴 +white flag 🏳️ +rainbow flag 🏳️‍🌈 +transgender flag 🏳️‍⚧️ +pirate flag 🏴‍☠️ +flag: Ascension Island 🇦🇨 +flag: Andorra 🇦🇩 +flag: United Arab Emirates 🇦🇪 +flag: Afghanistan 🇦🇫 +flag: Antigua & Barbuda 🇦🇬 +flag: Anguilla 🇦🇮 +flag: Albania 🇦🇱 +flag: Armenia 🇦🇲 +flag: Angola 🇦🇴 +flag: Antarctica 🇦🇶 +flag: Argentina 🇦🇷 +flag: American Samoa 🇦🇸 +flag: Austria 🇦🇹 +flag: Australia 🇦🇺 +flag: Aruba 🇦🇼 +flag: Åland Islands 🇦🇽 +flag: Azerbaijan 🇦🇿 +flag: Bosnia & Herzegovina 🇧🇦 +flag: Barbados 🇧🇧 +flag: Bangladesh 🇧🇩 +flag: Belgium 🇧🇪 +flag: Burkina Faso 🇧🇫 +flag: Bulgaria 🇧🇬 +flag: Bahrain 🇧🇭 +flag: Burundi 🇧🇮 +flag: Benin 🇧🇯 +flag: St. Barthélemy 🇧🇱 +flag: Bermuda 🇧🇲 +flag: Brunei 🇧🇳 +flag: Bolivia 🇧🇴 +flag: Caribbean Netherlands 🇧🇶 +flag: Brazil 🇧🇷 +flag: Bahamas 🇧🇸 +flag: Bhutan 🇧🇹 +flag: Bouvet Island 🇧🇻 +flag: Botswana 🇧🇼 +flag: Belarus 🇧🇾 +flag: Belize 🇧🇿 +flag: Canada 🇨🇦 +flag: Cocos (Keeling) Islands 🇨🇨 +flag: Congo - Kinshasa 🇨🇩 +flag: Central African Republic 🇨🇫 +flag: Congo - Brazzaville 🇨🇬 +flag: Switzerland 🇨🇭 +flag: Côte d’Ivoire 🇨🇮 +flag: Cook Islands 🇨🇰 +flag: Chile 🇨🇱 +flag: Cameroon 🇨🇲 +flag: China 🇨🇳 +flag: Colombia 🇨🇴 +flag: Clipperton Island 🇨🇵 +flag: Costa Rica 🇨🇷 +flag: Cuba 🇨🇺 +flag: Cape Verde 🇨🇻 +flag: Curaçao 🇨🇼 +flag: Christmas Island 🇨🇽 +flag: Cyprus 🇨🇾 +flag: Czechia 🇨🇿 +flag: Germany 🇩🇪 +flag: Diego Garcia 🇩🇬 +flag: Djibouti 🇩🇯 +flag: Denmark 🇩🇰 +flag: Dominica 🇩🇲 +flag: Dominican Republic 🇩🇴 +flag: Algeria 🇩🇿 +flag: Ceuta & Melilla 🇪🇦 +flag: Ecuador 🇪🇨 +flag: Estonia 🇪🇪 +flag: Egypt 🇪🇬 +flag: Western Sahara 🇪🇭 +flag: Eritrea 🇪🇷 +flag: Spain 🇪🇸 +flag: Ethiopia 🇪🇹 +flag: European Union 🇪🇺 +flag: Finland 🇫🇮 +flag: Fiji 🇫🇯 +flag: Falkland Islands 🇫🇰 +flag: Micronesia 🇫🇲 +flag: Faroe Islands 🇫🇴 +flag: France 🇫🇷 +flag: Gabon 🇬🇦 +flag: United Kingdom 🇬🇧 +flag: Grenada 🇬🇩 +flag: Georgia 🇬🇪 +flag: French Guiana 🇬🇫 +flag: Guernsey 🇬🇬 +flag: Ghana 🇬🇭 +flag: Gibraltar 🇬🇮 +flag: Greenland 🇬🇱 +flag: Gambia 🇬🇲 +flag: Guinea 🇬🇳 +flag: Guadeloupe 🇬🇵 +flag: Equatorial Guinea 🇬🇶 +flag: Greece 🇬🇷 +flag: South Georgia & South Sandwich Islands 🇬🇸 +flag: Guatemala 🇬🇹 +flag: Guam 🇬🇺 +flag: Guinea-Bissau 🇬🇼 +flag: Guyana 🇬🇾 +flag: Hong Kong SAR China 🇭🇰 +flag: Heard & McDonald Islands 🇭🇲 +flag: Honduras 🇭🇳 +flag: Croatia 🇭🇷 +flag: Haiti 🇭🇹 +flag: Hungary 🇭🇺 +flag: Canary Islands 🇮🇨 +flag: Indonesia 🇮🇩 +flag: Ireland 🇮🇪 +flag: Israel 🇮🇱 +flag: Isle of Man 🇮🇲 +flag: India 🇮🇳 +flag: British Indian Ocean Territory 🇮🇴 +flag: Iraq 🇮🇶 +flag: Iran 🇮🇷 +flag: Iceland 🇮🇸 +flag: Italy 🇮🇹 +flag: Jersey 🇯🇪 +flag: Jamaica 🇯🇲 +flag: Jordan 🇯🇴 +flag: Japan 🇯🇵 +flag: Kenya 🇰🇪 +flag: Kyrgyzstan 🇰🇬 +flag: Cambodia 🇰🇭 +flag: Kiribati 🇰🇮 +flag: Comoros 🇰🇲 +flag: St. Kitts & Nevis 🇰🇳 +flag: North Korea 🇰🇵 +flag: South Korea 🇰🇷 +flag: Kuwait 🇰🇼 +flag: Cayman Islands 🇰🇾 +flag: Kazakhstan 🇰🇿 +flag: Laos 🇱🇦 +flag: Lebanon 🇱🇧 +flag: St. Lucia 🇱🇨 +flag: Liechtenstein 🇱🇮 +flag: Sri Lanka 🇱🇰 +flag: Liberia 🇱🇷 +flag: Lesotho 🇱🇸 +flag: Lithuania 🇱🇹 +flag: Luxembourg 🇱🇺 +flag: Latvia 🇱🇻 +flag: Libya 🇱🇾 +flag: Morocco 🇲🇦 +flag: Monaco 🇲🇨 +flag: Moldova 🇲🇩 +flag: Montenegro 🇲🇪 +flag: St. Martin 🇲🇫 +flag: Madagascar 🇲🇬 +flag: Marshall Islands 🇲🇭 +flag: North Macedonia 🇲🇰 +flag: Mali 🇲🇱 +flag: Myanmar (Burma) 🇲🇲 +flag: Mongolia 🇲🇳 +flag: Macao SAR China 🇲🇴 +flag: Northern Mariana Islands 🇲🇵 +flag: Martinique 🇲🇶 +flag: Mauritania 🇲🇷 +flag: Montserrat 🇲🇸 +flag: Malta 🇲🇹 +flag: Mauritius 🇲🇺 +flag: Maldives 🇲🇻 +flag: Malawi 🇲🇼 +flag: Mexico 🇲🇽 +flag: Malaysia 🇲🇾 +flag: Mozambique 🇲🇿 +flag: Namibia 🇳🇦 +flag: New Caledonia 🇳🇨 +flag: Niger 🇳🇪 +flag: Norfolk Island 🇳🇫 +flag: Nigeria 🇳🇬 +flag: Nicaragua 🇳🇮 +flag: Netherlands 🇳🇱 +flag: Norway 🇳🇴 +flag: Nepal 🇳🇵 +flag: Nauru 🇳🇷 +flag: Niue 🇳🇺 +flag: New Zealand 🇳🇿 +flag: Oman 🇴🇲 +flag: Panama 🇵🇦 +flag: Peru 🇵🇪 +flag: French Polynesia 🇵🇫 +flag: Papua New Guinea 🇵🇬 +flag: Philippines 🇵🇭 +flag: Pakistan 🇵🇰 +flag: Poland 🇵🇱 +flag: St. Pierre & Miquelon 🇵🇲 +flag: Pitcairn Islands 🇵🇳 +flag: Puerto Rico 🇵🇷 +flag: Palestinian Territories 🇵🇸 +flag: Portugal 🇵🇹 +flag: Palau 🇵🇼 +flag: Paraguay 🇵🇾 +flag: Qatar 🇶🇦 +flag: Réunion 🇷🇪 +flag: Romania 🇷🇴 +flag: Serbia 🇷🇸 +flag: Russia 🇷🇺 +flag: Rwanda 🇷🇼 +flag: Saudi Arabia 🇸🇦 +flag: Solomon Islands 🇸🇧 +flag: Seychelles 🇸🇨 +flag: Sudan 🇸🇩 +flag: Sweden 🇸🇪 +flag: Singapore 🇸🇬 +flag: St. Helena 🇸🇭 +flag: Slovenia 🇸🇮 +flag: Svalbard & Jan Mayen 🇸🇯 +flag: Slovakia 🇸🇰 +flag: Sierra Leone 🇸🇱 +flag: San Marino 🇸🇲 +flag: Senegal 🇸🇳 +flag: Somalia 🇸🇴 +flag: Suriname 🇸🇷 +flag: South Sudan 🇸🇸 +flag: São Tomé & Príncipe 🇸🇹 +flag: El Salvador 🇸🇻 +flag: Sint Maarten 🇸🇽 +flag: Syria 🇸🇾 +flag: Eswatini 🇸🇿 +flag: Tristan da Cunha 🇹🇦 +flag: Turks & Caicos Islands 🇹🇨 +flag: Chad 🇹🇩 +flag: French Southern Territories 🇹🇫 +flag: Togo 🇹🇬 +flag: Thailand 🇹🇭 +flag: Tajikistan 🇹🇯 +flag: Tokelau 🇹🇰 +flag: Timor-Leste 🇹🇱 +flag: Turkmenistan 🇹🇲 +flag: Tunisia 🇹🇳 +flag: Tonga 🇹🇴 +flag: Turkey 🇹🇷 +flag: Trinidad & Tobago 🇹🇹 +flag: Tuvalu 🇹🇻 +flag: Taiwan 🇹🇼 +flag: Tanzania 🇹🇿 +flag: Ukraine 🇺🇦 +flag: Uganda 🇺🇬 +flag: U.S. Outlying Islands 🇺🇲 +flag: United Nations 🇺🇳 +flag: United States 🇺🇸 +flag: Uruguay 🇺🇾 +flag: Uzbekistan 🇺🇿 +flag: Vatican City 🇻🇦 +flag: St. Vincent & Grenadines 🇻🇨 +flag: Venezuela 🇻🇪 +flag: British Virgin Islands 🇻🇬 +flag: U.S. Virgin Islands 🇻🇮 +flag: Vietnam 🇻🇳 +flag: Vanuatu 🇻🇺 +flag: Wallis & Futuna 🇼🇫 +flag: Samoa 🇼🇸 +flag: Kosovo 🇽🇰 +flag: Yemen 🇾🇪 +flag: Mayotte 🇾🇹 +flag: South Africa 🇿🇦 +flag: Zambia 🇿🇲 +flag: Zimbabwe 🇿🇼 +flag: England 🏴󠁧󠁢󠁥󠁮󠁧󠁿 +flag: Scotland 🏴󠁧󠁢󠁳󠁣󠁴󠁿 +flag: Wales 🏴󠁧󠁢󠁷󠁬󠁳󠁿 diff --git a/user/.config/suckless/dmenu/scripts/dmenu_power b/user/.config/suckless/dmenu/scripts/dmenu_power new file mode 100755 index 000000000..0568ca612 --- /dev/null +++ b/user/.config/suckless/dmenu/scripts/dmenu_power @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +# ***This script was made by Clay Gomera (Drake)*** +# - Description: A simple power menu dmenu script +# - Dependencies: dmenu, power-profiles-daemon + +## OPTIONS ## +option1="logout" +option2="reboot" +option3="power off" +option4="suspend" +option5="lock" +option6="change power profile" +option7="cancel" + +## OPTIONS ARRAY ## +options="$option1\n$option2\n$option3\n$option4\n$option5\n$option6\n$option7" + +## POWER PROFILE OPTIONS ## +pwr1="performance" +pwr2="balanced" +pwr3="powersaver" +pwr4="cancel" + +## POWER PROFILES ARRAY ## +pwrs="$pwr1\n$pwr2\n$pwr3\n$pwr4" + +## MAIN ACTION COMMAND ## +action=$(echo -e "$options" | dmenu -i -fn "mononoki Nerd Font-10" -nb \#1d2021 -nf \#fbf1c7 -sb \#cc241d -sf \#fbf1c7 -p "power options:") +case "$action" in + $option1*) + pkill X;; + $option2*) + systemctl reboot;; + $option3*) + systemctl poweroff;; + $option4*) + slock systemctl suspend;; + $option5*) + slock;; + $option6*) + currentpwr=$(powerprofilesctl get) + pwraction=$(echo -e "$pwrs" | dmenu -i -fn "mononoki Nerd Font-10" -nb \#1d2021 -nf \#fbf1c7 -sb \#cc241d -sf \#fbf1c7 -p "current profile is: ${currentpwr} | select profile:") + case "$pwraction" in + $pwr1*) + powerprofilesctl set performance && notify-send "power profile switched to performance";; + $pwr2*) + powerprofilesctl set balanced && notify-send "power profile switched to balanced";; + $pwr3*) + powerprofilesctl set power-saver && notify-send "power profile switched to power saver";; + $pwr4*) + exit 0 + esac;; + $option7*) + exit 0 +esac diff --git a/user/.config/suckless/dmenu/scripts/dmenu_scrot b/user/.config/suckless/dmenu/scripts/dmenu_scrot new file mode 100755 index 000000000..d079d8a8d --- /dev/null +++ b/user/.config/suckless/dmenu/scripts/dmenu_scrot @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# ***This script was made by Clay Gomera (Drake)*** +# - Description: A simple screenshot dmenu script +# - Dependencies: scrot, dmenu, notify-send + +## CREATING SCREENSHOT FOLDER ## +mkdir -p "$HOME/pictures/screenshots" +cd "$HOME/pictures/screenshots" || exit 0 + +## CHOICES ## +cho1="entire screen" +cho2="entire screen with delay" +cho3="focused window" +cho4="select area" +chos="$cho1\n$cho2\n$cho3\n$cho4" + +## DELAY OPTIONS ## +del1="3 sec delay" +del2="5 sec delay" +del3="10 sec delay" +dels="$del1\n$del2\n$del3" + +## DELAY FUNCTION ## +delays() { + del=$(echo -e "$dels" | dmenu -i -fn "mononoki Nerd Font-10" -nb \#1d2021 -nf \#fbf1c7 -sb \#cc241d -sf \#fbf1c7 -p "Select: "); + case $del in + $del1) + scrot -d 3 && notify-send "screenshot saved";; + $del2) + scrot -d 5 && notify-send "screenshot saved";; + $del3) + scrot -d 10 && notify-send "screenshot saved" + esac +} + +## ENTIRE SCREEN FUNCTION ## +screen() { + scrot && notify-send "screenshot saved" +} + +## FOCUSED WINDOW FUNCTION +window() { + scrot -u -b && notify-send "screenshot saved." +} + +## SELECTED AREA FUNCTION ## +area() { + scrot -s && notify-send "screenshot saved." +} + +## MAIN ACTION ## +choice=$(echo -e "$chos" | dmenu -i -fn "mononoki Nerd Font-10" -nb \#1d2021 -nf \#fbf1c7 -sb \#cc241d -sf \#fbf1c7 -p "Select: ") +case $choice in + $cho1*) + screen;; + $cho2*) + delays;; + $cho3*) + window;; + $cho4*) + area +esac diff --git a/user/.config/suckless/dmenu/scripts/dmenu_wall b/user/.config/suckless/dmenu/scripts/dmenu_wall new file mode 100755 index 000000000..41e54f3ba --- /dev/null +++ b/user/.config/suckless/dmenu/scripts/dmenu_wall @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +# ***This script was made by Clay Gomera (Drake)*** +# - Description: A simple wallpaper changer script +# - Dependencies: dmenu, fd, feh + +## MAIN VARIABLES AND COMMANDS ## +walldir="pictures/wallpapers/" # wallpapers folder, change it to yours, make sure that it ends with a / +cd "$walldir" || exit + +## SELECT PICTURE FUNCTION ## +selectpic() { + wallpaper=$(fd -p "$walldir" | dmenu -i -l 10 -fn "mononoki Nerd Font-10" -nb \#1d2021 -nf \#fbf1c7 -sb \#cc241d -sf \#fbf1c7 -p "select a wallpaper:") + if [ "$wallpaper" ]; then + chosenwall=$wallpaper + else + exit 0 + fi +} +selectpic + +## WALLPAPER SETTING OPTIONS ## +option1="fill" +option2="center" +option3="tile" +option4="max" +option5="scale" +options="$option1\n$option2\n$option3\n$option4\n$option5" + +## MAIN ACTION ## +action=$(echo -e "$options" | dmenu -i -l 5 -fn "mononoki Nerd Font-10" -nb \#1d2021 -nf \#fbf1c7 -sb \#cc241d -sf \#fbf1c7 -p "chose the format:") +case "$action" in + $option1*) + feh --bg-fill "$chosenwall";; + $option2*) + feh --bg-center "$chosenwall";; + $option3*) + feh --bg-tile "$chosenwall";; + $option4*) + feh --bg-max "$chosenwall";; + $option5*) + feh --bg-scale "$chosenwall";; +esac +exit 0 diff --git a/user/.config/suckless/dmenu/scripts/dmenu_wifi b/user/.config/suckless/dmenu/scripts/dmenu_wifi new file mode 100755 index 000000000..86d6db8d1 --- /dev/null +++ b/user/.config/suckless/dmenu/scripts/dmenu_wifi @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +# ***This script was made by Clay Gomera (Drake)*** +# - Description: A simple wifi dmenu script +# - Dependencies: dmenu, NetworkManager + +## MAIN OPTIONS ## +option1="turn on wifi" +option2="turn off wifi" +option3="disconnect wifi" +option4="connect wifi" +option5="setup captive portal" +option6="exit" +options="$option1\n$option2\n$option3\n$option4\n$option5\n$option6" + +wlan=$(nmcli dev | grep wifi | sed 's/ \{2,\}/|/g' | cut -d '|' -f1 | head -1) +## TURN OFF WIFI FUNCTION ## +turnoff() { + nmcli radio wifi off + notify-send "WiFi has been turned off" +} + +## TURN ON WIFI FUNCTION ## +turnon() { + nmcli radio wifi on + notify-send "WiFi has been turned on" +} + +## DISCONNECT WIFI FUNCTION ## +disconnect() { + nmcli device disconnect "$wlan" + sleep 1 + constate=$(nmcli dev | grep wifi | sed 's/ \{2,\}/|/g' | cut -d '|' -f3 | head -1) + if [ "$constate" = "disconnected" ]; then + notify-send "WiFi has been disconnected" + fi +} + +## CONNECT FUNCTION ## +connect() { + notify-send "Scannig networks, please wait" + sleep 1 + bssid=$(nmcli device wifi list | sed -n '1!p' | cut -b 9- | dmenu -i -l 10 -fn "mononoki Nerd Font-10" -nb \#1d2021 -nf \#fbf1c7 -sb \#cc241d -sf \#fbf1c7 -p "select wifi:" | cut -d' ' -f1) + } + +## SELECT PASSWORD FUNCTION ## +password() { + pass=$(echo " " | dmenu -P -i -fn "mononoki Nerd Font-10" -nb \#1d2021 -nf \#fbf1c7 -sb \#cc241d -sf \#fbf1c7 -p "enter password:") + } + +## MAIN CONNECTION COMMAND ## +action() { + nmcli device wifi connect "$bssid" password "$pass" || nmcli device wifi connect "$bssid" + } + +## CHECKING IF WIFI IS WORKING +check() { + notify-send "Checking if connection was successful" + sleep 1 + currentwfi=$(nmcli dev | grep wifi | sed 's/ \{2,\}/|/g' | cut -d '|' -f4 | head -1) + if ping -q -c 2 -W 2 google.com >/dev/null; then + notify-send "You are now connected to $currentwfi and internet is working properly" + else + notify-send "Your internet is not working :(" + fi +} + +## MAIN ACTION COMMANDS ## +cases=$(echo -e "$options" | dmenu -i -fn "mononoki Nerd Font-10" -nb \#1d2021 -nf \#fbf1c7 -sb \#cc241d -sf \#fbf1c7 -p "wifi options:" ) +case "$cases" in + $option1*) + turnon;; + $option2*) + turnoff;; + $option3*) + disconnect;; + $option4*) + connect; + password; + action; + check;; + $option5*) + io.elementary.capnet-assist;; + $option6*) + exit 0 +esac diff --git a/user/.config/suckless/dmenu/stest.1 b/user/.config/suckless/dmenu/stest.1 new file mode 100644 index 000000000..2667d8aa7 --- /dev/null +++ b/user/.config/suckless/dmenu/stest.1 @@ -0,0 +1,90 @@ +.TH STEST 1 dmenu\-VERSION +.SH NAME +stest \- filter a list of files by properties +.SH SYNOPSIS +.B stest +.RB [ -abcdefghlpqrsuwx ] +.RB [ -n +.IR file ] +.RB [ -o +.IR file ] +.RI [ file ...] +.SH DESCRIPTION +.B stest +takes a list of files and filters by the files' properties, analogous to +.IR test (1). +Files which pass all tests are printed to stdout. If no files are given, stest +reads files from stdin. +.SH OPTIONS +.TP +.B \-a +Test hidden files. +.TP +.B \-b +Test that files are block specials. +.TP +.B \-c +Test that files are character specials. +.TP +.B \-d +Test that files are directories. +.TP +.B \-e +Test that files exist. +.TP +.B \-f +Test that files are regular files. +.TP +.B \-g +Test that files have their set-group-ID flag set. +.TP +.B \-h +Test that files are symbolic links. +.TP +.B \-l +Test the contents of a directory given as an argument. +.TP +.BI \-n " file" +Test that files are newer than +.IR file . +.TP +.BI \-o " file" +Test that files are older than +.IR file . +.TP +.B \-p +Test that files are named pipes. +.TP +.B \-q +No files are printed, only the exit status is returned. +.TP +.B \-r +Test that files are readable. +.TP +.B \-s +Test that files are not empty. +.TP +.B \-u +Test that files have their set-user-ID flag set. +.TP +.B \-v +Invert the sense of tests, only failing files pass. +.TP +.B \-w +Test that files are writable. +.TP +.B \-x +Test that files are executable. +.SH EXIT STATUS +.TP +.B 0 +At least one file passed all tests. +.TP +.B 1 +No files passed all tests. +.TP +.B 2 +An error occurred. +.SH SEE ALSO +.IR dmenu (1), +.IR test (1) diff --git a/user/.config/suckless/dmenu/stest.c b/user/.config/suckless/dmenu/stest.c new file mode 100644 index 000000000..e27d3a5e5 --- /dev/null +++ b/user/.config/suckless/dmenu/stest.c @@ -0,0 +1,109 @@ +/* See LICENSE file for copyright and license details. */ +#include + +#include +#include +#include +#include +#include +#include + +#include "arg.h" +char *argv0; + +#define FLAG(x) (flag[(x)-'a']) + +static void test(const char *, const char *); +static void usage(void); + +static int match = 0; +static int flag[26]; +static struct stat old, new; + +static void +test(const char *path, const char *name) +{ + struct stat st, ln; + + if ((!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */ + && (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */ + && (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */ + && (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */ + && (!FLAG('e') || access(path, F_OK) == 0) /* exists */ + && (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */ + && (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */ + && (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */ + && (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */ + && (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */ + && (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */ + && (!FLAG('r') || access(path, R_OK) == 0) /* readable */ + && (!FLAG('s') || st.st_size > 0) /* not empty */ + && (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */ + && (!FLAG('w') || access(path, W_OK) == 0) /* writable */ + && (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */ + if (FLAG('q')) + exit(0); + match = 1; + puts(name); + } +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] " + "[-n file] [-o file] [file...]\n", argv0); + exit(2); /* like test(1) return > 1 on error */ +} + +int +main(int argc, char *argv[]) +{ + struct dirent *d; + char path[PATH_MAX], *line = NULL, *file; + size_t linesiz = 0; + ssize_t n; + DIR *dir; + int r; + + ARGBEGIN { + case 'n': /* newer than file */ + case 'o': /* older than file */ + file = EARGF(usage()); + if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old)))) + perror(file); + break; + default: + /* miscellaneous operators */ + if (strchr("abcdefghlpqrsuvwx", ARGC())) + FLAG(ARGC()) = 1; + else + usage(); /* unknown flag */ + } ARGEND; + + if (!argc) { + /* read list from stdin */ + while ((n = getline(&line, &linesiz, stdin)) > 0) { + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + test(line, line); + } + free(line); + } else { + for (; argc; argc--, argv++) { + if (FLAG('l') && (dir = opendir(*argv))) { + /* test directory contents */ + while ((d = readdir(dir))) { + r = snprintf(path, sizeof path, "%s/%s", + *argv, d->d_name); + if (r >= 0 && (size_t)r < sizeof path) + test(path, d->d_name); + } + closedir(dir); + } else { + test(*argv, *argv); + } + } + } + return match ? 0 : 1; +} diff --git a/user/.config/suckless/dmenu/util.c b/user/.config/suckless/dmenu/util.c new file mode 100644 index 000000000..96b82c980 --- /dev/null +++ b/user/.config/suckless/dmenu/util.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "util.h" + +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} diff --git a/user/.config/suckless/dmenu/util.h b/user/.config/suckless/dmenu/util.h new file mode 100644 index 000000000..f633b5173 --- /dev/null +++ b/user/.config/suckless/dmenu/util.h @@ -0,0 +1,8 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/user/.config/suckless/dwm/Makefile b/user/.config/suckless/dwm/Makefile new file mode 100644 index 000000000..77bcbc02c --- /dev/null +++ b/user/.config/suckless/dwm/Makefile @@ -0,0 +1,51 @@ +# dwm - dynamic window manager +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dwm.c util.c +OBJ = ${SRC:.c=.o} + +all: options dwm + +options: + @echo dwm build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + ${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk + +config.h: + cp config.def.h $@ + +dwm: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz + +dist: clean + mkdir -p dwm-${VERSION} + cp -R LICENSE Makefile README config.def.h config.mk\ + dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} + tar -cf dwm-${VERSION}.tar dwm-${VERSION} + gzip dwm-${VERSION}.tar + rm -rf dwm-${VERSION} + +install: all + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f dwm ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/dwm\ + ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +.PHONY: all options clean dist install uninstall diff --git a/user/.config/suckless/dwm/autostart b/user/.config/suckless/dwm/autostart new file mode 100755 index 000000000..42e519ffe --- /dev/null +++ b/user/.config/suckless/dwm/autostart @@ -0,0 +1,12 @@ +#!/bin/sh +## ____ __ ## +## / __ \_________ _/ /_____ ## +## / / / / ___/ __ `/ //_/ _ \ ## +## / /_/ / / / /_/ / ,< / __/ Clay Gomera (Drake) ## +## /_____/_/ \__,_/_/|_|\___/ My custom dwm build ## + +lxpolkit & +dwmblocks & +sh "$HOME"/.fehbg & +unclutter --hide-on-touch & +picom --no-fading-openclose --config "$HOME/.config/picom/picom.conf" & diff --git a/user/.config/suckless/dwm/config.def.h b/user/.config/suckless/dwm/config.def.h new file mode 100644 index 000000000..f9ecb805d --- /dev/null +++ b/user/.config/suckless/dwm/config.def.h @@ -0,0 +1,291 @@ +/* See LICENSE file for copyright and license details. */ +/* appearance */ +static const unsigned int borderpx = 1; /* border pixel of windows */ +static const unsigned int snap = 32; /* snap pixel */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const int horizpadbar = 2; /* horizontal padding for statusbar */ +static const int vertpadbar = 1; /* vertical padding for statusbar */ +static const char *fonts[] = {"mononoki Nerd Font Mono:size=10"}; +static const char dmenufont[] = "mononoki Nerd Font Mono:size=10"; +static const char col_gray1[] = "#1d2021"; +static const char col_gray2[] = "#32302f"; +static const char col_gray3[] = "#d5c4a1"; +static const char col_gray4[] = "#fbf1c7"; +static const char col_cyan[] = "#cc241d"; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +}; + +// SCRATCHPADS +typedef struct { + const char *name; + const void *cmd; +} Sp; +const char *spcmd1[] = {"alacritty", "-t", "sptrm", "--class", "sptrm,sptrm", NULL }; +const char *spcmd2[] = {"alacritty", "-t", "sptop", "--class", "sptop,sptop", "-e", "btop", NULL }; +const char *spcmd3[] = {"alacritty", "-t", "spfli", "--class", "spfli,spfli", "-e", "flix-cli", NULL }; +const char *spcmd4[] = {"alacritty", "-t", "spani", "--class", "spani,spani", "-e", "ani-cli", NULL }; +const char *spcmd5[] = {"alacritty", "-t", "spytf", "--class", "spytf,spytf", "-e", "ytfzf", "-flst", NULL }; +const char *spcmd6[] = {"alacritty", "-t", "spamx", "--class", "spamx,spamx", "-e", "alsamixer", NULL }; +const char *spcmd7[] = {"alacritty", "-t", "sppmx", "--class", "sppmx,sppmx", "-e", "pulsemixer", NULL }; +const char *spcmd8[] = {"alacritty", "-t", "spcht", "--class", "spcht,spcht", "-e", "gomuks", NULL }; +const char *spcmd9[] = {"alacritty", "-t", "spmsc", "--class", "spmsc,spmsc", "-e", "cmus", NULL }; +const char *spcmd10[] = {"alacritty", "-t", "spflm", "--class", "spflm,spflm", "-e", "./.config/vifm/scripts/vifmrun", NULL }; +const char *spcmd11[] = {"alacritty", "-t", "sptot", "--class", "sptot,sptot", "-e", "toot", "tui", NULL }; +const char *spcmd12[] = {"alacritty", "-t", "spytm", "--class", "spytm,spytm", "-e", "ytfzf", "-mlst", NULL }; +static Sp scratchpads[] = { + /* name cmd */ + {"sptrm", spcmd1}, + {"sptop", spcmd2}, + {"spfli", spcmd3}, + {"spani", spcmd4}, + {"spytf", spcmd5}, + {"spamx", spcmd6}, + {"sppmx", spcmd7}, + {"spcht", spcmd8}, + {"spmsc", spcmd9}, + {"spflm", spcmd10}, + {"sptot", spcmd11}, + {"spytm", spcmd12}, +}; + +/* tagging */ +static const char *tags[] = { + "cde", // EDITOR + "tst", // FILE MANAGER + "web", // WEB BROWSER + "aud", // CHAT + "tls", // MUSIC + "vid", // VIDEO + "gfx", // IMAGE/EDIT TOOLS + "off", // OFFICE + "gme" // GAMES +}; + +// RULES +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + // class instance title tags mask isfloating monitor + // no workspace + { "Galculator", NULL, NULL, 0, 1, -1 }, + // code tag + { "Emacs", NULL, NULL, 1, 0, -1 }, + { "Godot", NULL, NULL, 1, 0, -1 }, + { "neovim", NULL, NULL, 1, 0, -1 }, + { "neovide", NULL, NULL, 1, 0, -1 }, + // test tag + { "Virt-manager", NULL, NULL, 1, 0, -1 }, + // web tag + { "librewolf", NULL, NULL, 1 << 2, 0, -1 }, + { "firefox", NULL, NULL, 1 << 2, 0, -1 }, + { "qutebrowser", NULL, NULL, 1 << 2, 0, -1 }, + { "Chromium", NULL, NULL, 1 << 2, 0, -1 }, + { "Brave-browser", NULL, NULL, 1 << 2, 0, -1 }, + { "Bitwarden", NULL, NULL, 1 << 2, 0, -1 }, + // audio tag + { "Audacity", NULL, NULL, 1 << 3, 0, -1 }, + { "Ardour", NULL, NULL, 1 << 3, 0, -1 }, + { "Carla2", NULL, NULL, 1 << 3, 0, -1 }, + { "Carla2-Control", NULL, NULL, 1 << 3, 0, -1 }, + // audio tools tag + { "QjackCtl", NULL, NULL, 1 << 4, 1, -1 }, + { "lsp-plugins", NULL, NULL, 1 << 4, 1, -1 }, + { "qpwgraph", NULL, NULL, 1 << 4, 0, -1 }, + { "Cadence", NULL, NULL, 1 << 4, 0, -1 }, + // video tag + { "kdenlive", NULL, NULL, 1 << 5, 0, -1 }, + { "Blender", NULL, NULL, 1 << 5, 0, -1 }, + { "Natron", NULL, NULL, 1 << 5, 0, -1 }, + { "SimpleScreenRecorder", NULL, NULL, 1 << 5, 0, -1 }, + { "Ghb", NULL, NULL, 1 << 5, 0, -1 }, + { "obs", NULL, NULL, 1 << 5, 0, -1 }, + // graphics/extra tools tag + { "Gimp-2.10", NULL, NULL, 1 << 6, 0, -1 }, + { "krita", NULL, NULL, 1 << 6, 0, -1 }, + { "Inkscape", NULL, NULL, 1 << 6, 0, -1 }, + { "Xournalpp", NULL, NULL, 1 << 6, 0, -1 }, + // office tag + { "DesktopEditors", NULL, NULL, 1 << 7, 0, -1 }, + { "Soffice", "soffice", NULL, 1 << 7, 0, -1 }, + { "libreoffice-startcenter", NULL, NULL, 1 << 7, 0, -1 }, + { "Joplin", NULL, NULL, 1 << 7, 0, -1 }, + // games tag + { "retroarch", NULL, NULL, 1 << 8, 0, -1 }, + { "steam", NULL, NULL, 1 << 8, 0, -1 }, + // scratchpads + { NULL, "sptrm", NULL, SPTAG(0), 1, -1 }, + { NULL, "sptop", NULL, SPTAG(1), 1, -1 }, + { NULL, "spfli", NULL, SPTAG(2), 1, -1 }, + { NULL, "spani", NULL, SPTAG(3), 1, -1 }, + { NULL, "spytf", NULL, SPTAG(4), 1, -1 }, + { NULL, "spamx", NULL, SPTAG(5), 1, -1 }, + { NULL, "sppmx", NULL, SPTAG(6), 1, -1 }, + { NULL, "spcht", NULL, SPTAG(7), 1, -1 }, + { NULL, "spmsc", NULL, SPTAG(8), 1, -1 }, + { NULL, "spflm", NULL, SPTAG(9), 1, -1 }, + { NULL, "sptot", NULL, SPTAG(10), 1, -1 }, + { NULL, "spytm", NULL, SPTAG(11), 1, -1 }, +}; + +// layout(s) +static const float mfact = 0.5; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +#include "tcl.c" +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, + { "|||", tcl }, +}; + +//key definitions +#define MODKEY Mod4Mask +#define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + +// helper for spawning shell commands in the pre dwm-5.0 fashion +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* commands */ +// dmenu +static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; +// terminal +static const char *termcmd[] = { "alacritty", NULL }; + +static const Key keys[] = { + /* modifier key function argument */ + // text editor + { MODKEY, XK_e, spawn, SHCMD("emacsclient -c -a 'emacs'")}, + // web browser + { MODKEY, XK_w, spawn, SHCMD("qutebrowser")}, + // set keyboard layout to es + { MODKEY|ControlMask, XK_e, spawn, SHCMD("setxkbmap -layout es")}, + // set keyboard layout to us + { MODKEY|ControlMask, XK_u, spawn, SHCMD("setxkbmap -layout us")}, + // increase volume + { 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("pamixer -i 5")}, + // decrease volume + { 0, XF86XK_AudioLowerVolume, spawn, SHCMD("pamixer -d 5")}, + // mute volume + { 0, XF86XK_AudioMute, spawn, SHCMD("pamixer -t")}, + // mute microphone + { 0, XF86XK_AudioMicMute, spawn, SHCMD("pamixer --default-source -t")}, + // increase brightness + { 0, XF86XK_MonBrightnessUp, spawn, SHCMD("xbacklight -inc 10")}, + // decrease brightness + { 0, XF86XK_MonBrightnessDown, spawn, SHCMD("xbacklight -dec 10")}, + // decrease brightness + { 0, XF86XK_Display, spawn, SHCMD("arandr")}, + // launcher + { MODKEY|ControlMask, XK_p, spawn, {.v = dmenucmd } }, + // desktop launcher + { MODKEY, XK_p, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_drun") }, + // wifi config + { MODKEY|Mod1Mask, XK_i, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_wifi") }, + // screenshots + { MODKEY|Mod1Mask, XK_s, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_scrot") }, + // screenshots + { MODKEY|Mod1Mask, XK_w, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_wall") }, + // edit + { MODKEY|Mod1Mask, XK_e, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_edit") }, + // terminal + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + // toogle the bar + { MODKEY, XK_b, togglebar, {0} }, + // window focusing + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + // increase and decrease master clients count + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + // increase and decrease master client size + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + // switch master window + { MODKEY|ControlMask, XK_Return, zoom, {0} }, + // switch to latest tag + { MODKEY, XK_Tab, view, {0} }, + // close focused client + { MODKEY, XK_q, killclient, {0} }, + // switch to tiling layout + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + // switch to floating layout + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + // switch to monocle layout + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + // switch to three-column layout + { MODKEY, XK_c, setlayout, {.v = &layouts[3]} }, + // switch to latest layout + { MODKEY, XK_space, setlayout, {0} }, + // toggle floating mode on focused window + { MODKEY|ControlMask, XK_space, togglefloating, {0} }, + // view all opened clients on current tag + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + // focus next or previous screen + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + // move focused client to next or previous screen + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + // scratchpads + { MODKEY|ShiftMask, XK_Return, togglescratch, {.ui = 0 } }, + { MODKEY|ShiftMask, XK_b, togglescratch, {.ui = 1 } }, + { MODKEY|ShiftMask, XK_f, togglescratch, {.ui = 2 } }, + { MODKEY|ShiftMask, XK_a, togglescratch, {.ui = 3 } }, + { MODKEY|ShiftMask, XK_y, togglescratch, {.ui = 4 } }, + { MODKEY|ShiftMask, XK_o, togglescratch, {.ui = 5 } }, + { MODKEY|ShiftMask, XK_p, togglescratch, {.ui = 6 } }, + { MODKEY|ShiftMask, XK_c, togglescratch, {.ui = 7 } }, + { MODKEY|ShiftMask, XK_m, togglescratch, {.ui = 8 } }, + { MODKEY|ShiftMask, XK_v, togglescratch, {.ui = 9 } }, + { MODKEY|ShiftMask, XK_t, togglescratch, {.ui = 10 } }, + { MODKEY|ShiftMask, XK_n, togglescratch, {.ui = 11 } }, + // tag bindings + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) + TAGKEYS( XK_4, 3) + TAGKEYS( XK_5, 4) + TAGKEYS( XK_6, 5) + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + // logout + //{ MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY|ShiftMask, XK_q, spawn, SHCMD("$HOME/.config/suckless/dmenu/scripts/dmenu_power")}, + // restart dwm + { MODKEY|ControlMask, XK_r, quit, {1} }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +// mouse bindings +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + diff --git a/user/.config/suckless/dwm/config.mk b/user/.config/suckless/dwm/config.mk new file mode 100644 index 000000000..ef8acf76b --- /dev/null +++ b/user/.config/suckless/dwm/config.mk @@ -0,0 +1,39 @@ +# dwm version +VERSION = 6.4 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = ${X11INC}/freetype2 +#MANPREFIX = ${PREFIX}/man + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} +CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/user/.config/suckless/dwm/dependencies.txt b/user/.config/suckless/dwm/dependencies.txt new file mode 100644 index 000000000..23a3f5248 --- /dev/null +++ b/user/.config/suckless/dwm/dependencies.txt @@ -0,0 +1,24 @@ +# core dependencies +xorg-server +libx11 +libxft +libxinerama + +# extra dependencies +xbacklight # brightness control +pamixer # volume control +arandr # display config +alacritty # terminal +dmenu # run launcher +dwmblocks # status bar scripts +slock # lockscreen +herbe # notifications +neovim & neovide # text editor + +# scratchpads +ani-cli # watch anime +flix-cli # watch movies +ytfzf # watch/listen to youtube +vifm # file manager +gomuks # matrix client +toot # tui mastodon client diff --git a/user/.config/suckless/dwm/drw.c b/user/.config/suckless/dwm/drw.c new file mode 100644 index 000000000..16e386292 --- /dev/null +++ b/user/.config/suckless/dwm/drw.c @@ -0,0 +1,451 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); + drw->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); + dest->pixel |= 0xff << 24; +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + int i, ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0, overflow = 0; + /* keep track of a couple codepoints for which we have no match. */ + enum { nomatches_len = 64 }; + static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; + static unsigned int ellipsis_width = 0; + + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) + return 0; + + if (!render) { + w = invert ? invert : ~invert; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + d = XftDrawCreate(drw->dpy, drw->drawable, + DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen)); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + if (!ellipsis_width && render) + ellipsis_width = drw_fontset_getwidth(drw, "..."); + while (1) { + ew = ellipsis_len = utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); + if (ew + ellipsis_width <= w) { + /* keep track where the ellipsis still fits */ + ellipsis_x = x + ew; + ellipsis_w = w - ew; + ellipsis_len = utf8strlen; + } + + if (ew + tmpw > w) { + overflow = 1; + /* called from drw_fontset_getwidth_clamp(): + * it wants the width AFTER the overflow + */ + if (!render) + x += tmpw; + else + utf8strlen = ellipsis_len; + } else if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + ew += tmpw; + } else { + nextfont = curfont; + } + break; + } + } + + if (overflow || !charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); + } + x += ew; + w -= ew; + } + if (render && overflow) + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); + + if (!*text || overflow) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + for (i = 0; i < nomatches_len; ++i) { + /* avoid calling XftFontMatch if we know we won't find a match */ + if (utf8codepoint == nomatches.codepoint[i]) + goto no_match; + } + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; +no_match: + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +unsigned int +drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) +{ + unsigned int tmp = 0; + if (drw && drw->fonts && text && n) + tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); + return MIN(n, tmp); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/user/.config/suckless/dwm/drw.h b/user/.config/suckless/dwm/drw.h new file mode 100644 index 000000000..64714316b --- /dev/null +++ b/user/.config/suckless/dwm/drw.h @@ -0,0 +1,58 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/user/.config/suckless/dwm/dwm.1 b/user/.config/suckless/dwm/dwm.1 new file mode 100644 index 000000000..0f3a6bab4 --- /dev/null +++ b/user/.config/suckless/dwm/dwm.1 @@ -0,0 +1,209 @@ +.TH DWM 1 dwm\-VERSION +.SH NAME +dwm \- dynamic window manager +.SH SYNOPSIS +.B dwm +.RB [ \-v ] +.SH DESCRIPTION +dwm is a dynamic window manager for X. It manages windows in tiled, monocle +and floating layouts. Either layout can be applied dynamically, optimising the +environment for the application in use and the task performed. +.P +In tiled layouts windows are managed in a master and stacking area. The master +area on the left contains one window by default, and the stacking area on the +right contains all other windows. The number of master area windows can be +adjusted from zero to an arbitrary number. In monocle layout all windows are +maximised to the screen size. In floating layout windows can be resized and +moved freely. Dialog windows are always managed floating, regardless of the +layout applied. +.P +Windows are grouped by tags. Each window can be tagged with one or multiple +tags. Selecting certain tags displays all windows with these tags. +.P +Each screen contains a small status bar which displays all available tags, the +layout, the title of the focused window, and the text read from the root window +name property, if the screen is focused. A floating window is indicated with an +empty square and a maximised floating window is indicated with a filled square +before the windows title. The selected tags are indicated with a different +color. The tags of the focused window are indicated with a filled square in the +top left corner. The tags which are applied to one or more windows are +indicated with an empty square in the top left corner. +.P +dwm draws a small border around windows to indicate the focus state. +.P +On start, dwm can start additional programs that may be specified in two special +shell scripts (see the FILES section below), autostart_blocking.sh and +autostart.sh. The former is executed first and dwm will wait for its +termination before starting. The latter is executed in the background before +dwm enters its handler loop. +.P +Either of these files may be omitted. +.SH OPTIONS +.TP +.B \-v +prints version information to stderr, then exits. +.SH USAGE +.SS Status bar +.TP +.B X root window name +is read and displayed in the status text area. It can be set with the +.BR xsetroot (1) +command. +.TP +.B Button1 +click on a tag label to display all windows with that tag, click on the layout +label toggles between tiled and floating layout. +.TP +.B Button3 +click on a tag label adds/removes all windows with that tag to/from the view. +.TP +.B Mod1\-Button1 +click on a tag label applies that tag to the focused window. +.TP +.B Mod1\-Button3 +click on a tag label adds/removes that tag to/from the focused window. +.SS Keyboard commands +.TP +.B Mod1\-Shift\-Return +Start +.BR st(1). +.TP +.B Mod1\-p +Spawn +.BR dmenu(1) +for launching other programs. +.TP +.B Mod1\-, +Focus previous screen, if any. +.TP +.B Mod1\-. +Focus next screen, if any. +.TP +.B Mod1\-Shift\-, +Send focused window to previous screen, if any. +.TP +.B Mod1\-Shift\-. +Send focused window to next screen, if any. +.TP +.B Mod1\-b +Toggles bar on and off. +.TP +.B Mod1\-t +Sets tiled layout. +.TP +.B Mod1\-f +Sets floating layout. +.TP +.B Mod1\-m +Sets monocle layout. +.TP +.B Mod1\-space +Toggles between current and previous layout. +.TP +.B Mod1\-j +Focus next window. +.TP +.B Mod1\-k +Focus previous window. +.TP +.B Mod1\-i +Increase number of windows in master area. +.TP +.B Mod1\-d +Decrease number of windows in master area. +.TP +.B Mod1\-l +Increase master area size. +.TP +.B Mod1\-h +Decrease master area size. +.TP +.B Mod1\-Return +Zooms/cycles focused window to/from master area (tiled layouts only). +.TP +.B Mod1\-Shift\-c +Close focused window. +.TP +.B Mod1\-Shift\-space +Toggle focused window between tiled and floating state. +.TP +.B Mod1\-Tab +Toggles to the previously selected tags. +.TP +.B Mod1\-Shift\-[1..n] +Apply nth tag to focused window. +.TP +.B Mod1\-Shift\-0 +Apply all tags to focused window. +.TP +.B Mod1\-Control\-Shift\-[1..n] +Add/remove nth tag to/from focused window. +.TP +.B Mod1\-[1..n] +View all windows with nth tag. +.TP +.B Mod1\-0 +View all windows with any tag. +.TP +.B Mod1\-Control\-[1..n] +Add/remove all windows with nth tag to/from the view. +.TP +.B Mod1\-Shift\-q +Quit dwm. +.TP +.B Mod1\-Control\-Shift\-q +Restart dwm. +.SS Mouse commands +.TP +.B Mod1\-Button1 +Move focused window while dragging. Tiled windows will be toggled to the floating state. +.TP +.B Mod1\-Button2 +Toggles focused window between floating and tiled state. +.TP +.B Mod1\-Button3 +Resize focused window while dragging. Tiled windows will be toggled to the floating state. +.SH FILES +The files containing programs to be started along with dwm are searched for in +the following directories: +.IP "1. $XDG_DATA_HOME/dwm" +.IP "2. $HOME/.local/share/dwm" +.IP "3. $HOME/.dwm" +.P +The first existing directory is scanned for any of the autostart files below. +.TP 15 +autostart.sh +This file is started as a shell background process before dwm enters its handler +loop. +.TP 15 +autostart_blocking.sh +This file is started before any autostart.sh; dwm waits for its termination. +.SH CUSTOMIZATION +dwm is customized by creating a custom config.h and (re)compiling the source +code. This keeps it fast, secure and simple. +.SH SIGNALS +.TP +.B SIGHUP - 1 +Restart the dwm process. +.TP +.B SIGTERM - 15 +Cleanly terminate the dwm process. +.SH SEE ALSO +.BR dmenu (1), +.BR st (1) +.SH ISSUES +Java applications which use the XToolkit/XAWT backend may draw grey windows +only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early +JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds +are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the +environment variable +.BR AWT_TOOLKIT=MToolkit +(to use the older Motif backend instead) or running +.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D +or +.B wmname LG3D +(to pretend that a non-reparenting window manager is running that the +XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable +.BR _JAVA_AWT_WM_NONREPARENTING=1 . +.SH BUGS +Send all bug reports with a patch to hackers@suckless.org. diff --git a/user/.config/suckless/dwm/dwm.c b/user/.config/suckless/dwm/dwm.c new file mode 100644 index 000000000..813311ecf --- /dev/null +++ b/user/.config/suckless/dwm/dwm.c @@ -0,0 +1,2330 @@ +/* See LICENSE file for copyright and license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * The event handlers of dwm are organized in an array which is accessed + * whenever a new event has been fetched. This allows event dispatching + * in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a linked client + * list on each monitor, the focus history is remembered through a stack list + * on each monitor. Each client contains a bit array to indicate the tags of a + * client. + * + * Keys and tagging rules are organized as arrays and defined in config.h. + * + * To understand everything else, start reading main(). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define NUMTAGS (LENGTH(tags) + LENGTH(scratchpads)) +#define TAGMASK ((1 << NUMTAGS) - 1) +#define SPTAG(i) ((1 << LENGTH(tags)) << (i)) +#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << LENGTH(tags)) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { SchemeNorm, SchemeSel }; /* color schemes */ +enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +typedef struct Monitor Monitor; +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + Client *next; + Client *snext; + Monitor *mon; + Window win; +}; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +} Layout; + +typedef struct Pertag Pertag; +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; + const Layout *lt[2]; + Pertag *pertag; +}; + +typedef struct { + const char *class; + const char *instance; + const char *title; + unsigned int tags; + int isfloating; + int monitor; +} Rule; + +/* function declarations */ +static void applyrules(Client *c); +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +static void arrange(Monitor *m); +static void arrangemon(Monitor *m); +static void attach(Client *c); +static void attachbottom(Client *c); +static void attachstack(Client *c); +static void buttonpress(XEvent *e); +static void checkotherwm(void); +static void cleanup(void); +static void cleanupmon(Monitor *mon); +static void clientmessage(XEvent *e); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); +static Monitor *createmon(void); +static void destroynotify(XEvent *e); +static void detach(Client *c); +static void detachstack(Client *c); +static Monitor *dirtomon(int dir); +static void drawbar(Monitor *m); +static void drawbars(void); +static void enternotify(XEvent *e); +static void expose(XEvent *e); +static void focus(Client *c); +static void focusin(XEvent *e); +static void focusmon(const Arg *arg); +static void focusstack(const Arg *arg); +static Atom getatomprop(Client *c, Atom prop); +static int getrootptr(int *x, int *y); +static long getstate(Window w); +static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void grabbuttons(Client *c, int focused); +static void grabkeys(void); +static void incnmaster(const Arg *arg); +static void keypress(XEvent *e); +static void killclient(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void monocle(Monitor *m); +static void motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static Client *nexttiled(Client *c); +static void pop(Client *c); +static void propertynotify(XEvent *e); +static void quit(const Arg *arg); +static Monitor *recttomon(int x, int y, int w, int h); +static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resizeclient(Client *c, int x, int y, int w, int h); +static void resizemouse(const Arg *arg); +static void restack(Monitor *m); +static void run(void); +static void runAutostart(void); +static void scan(void); +static int sendevent(Client *c, Atom proto); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setlayout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void showhide(Client *c); +static void sigchld(int unused); +static void sighup(int unused); +static void sigterm(int unused); +static void spawn(const Arg *arg); +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void tile(Monitor *m); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void togglescratch(const Arg *arg); +static void toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void unfocus(Client *c, int setfocus); +static void unmanage(Client *c, int destroyed); +static void unmapnotify(XEvent *e); +static void updatebarpos(Monitor *m); +static void updatebars(void); +static void updateclientlist(void); +static int updategeom(void); +static void updatenumlockmask(void); +static void updatesizehints(Client *c); +static void updatestatus(void); +static void updatetitle(Client *c); +static void updatewindowtype(Client *c); +static void updatewmhints(Client *c); +static void view(const Arg *arg); +static void warp(const Client *c); +static Client *wintoclient(Window w); +static Monitor *wintomon(Window w); +static int xerror(Display *dpy, XErrorEvent *ee); +static int xerrordummy(Display *dpy, XErrorEvent *ee); +static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void zoom(const Arg *arg); + +/* variables */ +static const char broken[] = "broken"; +static char stext[256]; +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar height */ +static int lrpad; /* sum of left and right padding for text */ +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int numlockmask = 0; +static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static int restart = 0; +static int running = 1; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +struct Pertag { + unsigned int curtag, prevtag; /* current and previous tag */ + int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ + unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ + const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ + int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ +}; + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; + if ((r->tags & SPTAGMASK) && r->isfloating) { + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : (c->mon->tagset[c->mon->seltags] & ~SPTAGMASK); +} + +int +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) +{ + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + if (!c->hintsvalid) + updatesizehints(c); + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +void +arrange(Monitor *m) +{ + if (m) + showhide(m->stack); + else for (m = mons; m; m = m->next) + showhide(m->stack); + if (m) { + arrangemon(m); + restack(m); + } else for (m = mons; m; m = m->next) + arrangemon(m); +} + +void +arrangemon(Monitor *m) +{ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +attachbottom(Client *c) +{ + Client **tc; + c->next = NULL; + for (tc = &c->mon->clients; *tc; tc = &(*tc)->next); + *tc = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +buttonpress(XEvent *e) +{ + unsigned int i, x, click; + Arg arg = {0}; + Client *c; + Monitor *m; + XButtonPressedEvent *ev = &e->xbutton; + + click = ClkRootWin; + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + if (ev->window == selmon->barwin) { + i = x = 0; + do + x += TEXTW(tags[i]); + while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - (int)TEXTW(stext)) + click = ClkStatusText; + else + click = ClkWinTitle; + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } + for (i = 0; i < LENGTH(buttons); i++) + if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); +} + +void +checkotherwm(void) +{ + xerrorxlib = XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); + XSync(dpy, False); + XSetErrorHandler(xerror); + XSync(dpy, False); +} + +void +cleanup(void) +{ + Arg a = {.ui = ~0}; + Layout foo = { "", NULL }; + Monitor *m; + size_t i; + + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) + free(scheme[i]); + free(scheme); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); + XSync(dpy, False); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +} + +void +cleanupmon(Monitor *mon) +{ + Monitor *m; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); + free(mon); +} + +void +clientmessage(XEvent *e) +{ + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || cme->data.l[2] == netatom[NetWMFullscreen]) + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ + || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); + } +} + +void +configure(Client *c) +{ + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->bw; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void +configurenotify(XEvent *e) +{ + Monitor *m; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; + + /* TODO: updategeom handling sucks, needs to be simplified */ + if (ev->window == root) { + dirty = (sw != ev->width || sh != ev->height); + sw = ev->width; + sh = ev->height; + if (updategeom() || dirty) { + drw_resize(drw, sw, bh); + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor * +createmon(void) +{ + Monitor *m; + unsigned int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + m->pertag = ecalloc(1, sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + + for (i = 0; i <= LENGTH(tags); i++) { + m->pertag->nmasters[i] = m->nmaster; + m->pertag->mfacts[i] = m->mfact; + + m->pertag->ltidxs[i][0] = m->lt[0]; + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + + m->pertag->showbars[i] = m->showbar; + } + + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; +} + +void +detachstack(Client *c) +{ + Client **tc, *t; + + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); + *tc = c->snext; + + if (c == c->mon->sel) { + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); + c->mon->sel = t; + } +} + +Monitor * +dirtomon(int dir) +{ + Monitor *m = NULL; + + if (dir > 0) { + if (!(m = selmon->next)) + m = mons; + } else if (selmon == mons) + for (m = mons; m->next; m = m->next); + else + for (m = mons; m->next != selmon; m = m->next); + return m; +} + +void +drawbar(Monitor *m) +{ + int x, w, tw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; + Client *c; + + if (!m->showbar) + return; + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext); + drw_text(drw, m->ww - tw, 0, tw, bh, lrpad / 2, stext, 0); + } + + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, + urg & 1 << i); + x += w; + } + w = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + + if ((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +} + +void +drawbars(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +enternotify(XEvent *e) +{ + Client *c; + Monitor *m; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + } else if (!c || c == selmon->sel) + return; + focus(c); +} + +void +expose(XEvent *e) +{ + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) + drawbar(m); +} + +void +focus(Client *c) +{ + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + drawbars(); +} + +/* there are some broken focus acquiring clients needing extra handling */ +void +focusin(XEvent *e) +{ + XFocusChangeEvent *ev = &e->xfocus; + + if (selmon->sel && ev->window != selmon->sel->win) + setfocus(selmon->sel); +} + +void +focusmon(const Arg *arg) +{ + Monitor *m; + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); + warp(selmon->sel); +} + +void +focusstack(const Arg *arg) +{ + Client *c = NULL, *i; + + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if (!c) + for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i)) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i)) + c = i; + } + if (c) { + focus(c); + restack(selmon); + } +} + +Atom +getatomprop(Client *c, Atom prop) +{ + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + return atom; +} + +int +getrootptr(int *x, int *y) +{ + int di; + unsigned int dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) { + strncpy(text, (char *)name.value, size - 1); + } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].click == ClkClientWin) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +void +grabkeys(void) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + KeyCode code; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < LENGTH(keys); i++) + if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, + True, GrabModeAsync, GrabModeAsync); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); +} + +#ifdef XINERAMA +static int +isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +{ + while (n--) + if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org + && unique[n].width == info->width && unique[n].height == info->height) + return 0; + return 1; +} +#endif /* XINERAMA */ + +void +keypress(XEvent *e) +{ + unsigned int i; + KeySym keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for (i = 0; i < LENGTH(keys); i++) + if (keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); +} + +void +killclient(const Arg *arg) +{ + if (!selmon->sel) + return; + if (!sendevent(selmon->sel, wmatom[WMDelete])) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + } else { + c->mon = selmon; + applyrules(c); + } + + if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) + c->x = c->mon->wx + c->mon->ww - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) + c->y = c->mon->wy + c->mon->wh - HEIGHT(c); + c->x = MAX(c->x, c->mon->wx); + c->y = MAX(c->y, c->mon->wy); + c->bw = borderpx; + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); + c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2; + c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2; + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); + attachbottom(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); + focus(NULL); +} + +void +mappingnotify(XEvent *e) +{ + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +void +maprequest(XEvent *e) +{ + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +void +monocle(Monitor *m) +{ + unsigned int n = 0; + Client *c; + + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +} + +void +motionnotify(XEvent *e) +{ + static Monitor *mon = NULL; + Monitor *m; + XMotionEvent *ev = &e->xmotion; + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + mon = m; +} + +void +movemouse(const Arg *arg) +{ + int x, y, ocx, ocy, nx, ny; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) + nx = selmon->wx + selmon->ww - WIDTH(c); + if (abs(selmon->wy - ny) < snap) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) + togglefloating(NULL); + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, nx, ny, c->w, c->h, 1); + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + return c; +} + +void +pop(Client *c) +{ + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} + +void +propertynotify(XEvent *e) +{ + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; + break; + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + } +} + +void +quit(const Arg *arg) +{ + if(arg->i) restart = 1; + running = 0; +} + +Monitor * +recttomon(int x, int y, int w, int h) +{ + Monitor *m, *r = selmon; + int a, area = 0; + + for (m = mons; m; m = m->next) + if ((a = INTERSECT(x, y, w, h, m)) > area) { + area = a; + r = m; + } + return r; +} + +void +resize(Client *c, int x, int y, int w, int h, int interact) +{ + if (applysizehints(c, &x, &y, &w, &h, interact)) + resizeclient(c, x, y, w, h); +} + +void +resizeclient(Client *c, int x, int y, int w, int h) +{ + XWindowChanges wc; + + c->oldx = c->x; c->x = wc.x = x; + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +resizemouse(const Arg *arg) +{ + int ocx, ocy, nw, nh; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + break; + } + } while (ev.type != ButtonRelease); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +void +restack(Monitor *m) +{ + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && selmon->lt[selmon->sellt] != &layouts[2]) + warp(m->sel); +} + +void +run(void) +{ + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ +} + +void +runAutostart(void) { + system("$HOME/.config/suckless/dwm/autostart"); +} + +void +scan(void) +{ + unsigned int i, num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + if (wins) + XFree(wins); + } +} + +void +sendmon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + unfocus(c, 1); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attachbottom(c); + attachstack(c); + focus(NULL); + arrange(NULL); +} + +void +setclientstate(Client *c, long state) +{ + long data[] = { state, None }; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +int +sendevent(Client *c, Atom proto) +{ + int n; + Atom *protocols; + int exists = 0; + XEvent ev; + + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + if (exists) { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + } + return exists; +} + +void +setfocus(Client *c) +{ + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } + sendevent(c, wmatom[WMTakeFocus]); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; + c->oldstate = c->isfloating; + c->oldbw = c->bw; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; + c->isfloating = c->oldstate; + c->bw = c->oldbw; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; + if (arg && arg->v) + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +/* arg > 1.0 will set mfact absolutely */ +void +setmfact(const Arg *arg) +{ + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + + /* clean up any zombies immediately */ + sigchld(0); + + signal(SIGHUP, sighup); + signal(SIGTERM, sigterm); + + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h + horizpadbar; + bh = drw->fonts->h + vertpadbar; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ + updatebars(); + updatestatus(); + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (unsigned char *) "dwm", 3); + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + /* EWMH support per view */ + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + XDeleteProperty(dpy, root, netatom[NetClientList]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + focus(NULL); +} + +void +seturgent(Client *c, int urg) +{ + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void +showhide(Client *c) +{ + if (!c) + return; + if (ISVISIBLE(c)) { + if ((c->tags & SPTAGMASK) && c->isfloating) { + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } +} + +void +sigchld(int unused) +{ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + while (0 < waitpid(-1, NULL, WNOHANG)); +} + +void +sighup(int unused) +{ + Arg a = {.i = 1}; + quit(&a); +} + +void +sigterm(int unused) +{ + Arg a = {.i = 0}; + quit(&a); +} + +void +spawn(const Arg *arg) +{ + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); + } +} + +void +tag(const Arg *arg) +{ + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); + } +} + +void +tagmon(const Arg *arg) +{ + if (!selmon->sel || !mons->next) + return; + sendmon(selmon->sel, dirtomon(arg->i)); +} + +void +tile(Monitor *m) +{ + unsigned int i, n, h, mw, my, ty; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); + } else { + h = (m->wh - ty) / (n - i); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); + } +} + +void +togglebar(const Arg *arg) +{ + selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +} + +void +togglefloating(const Arg *arg) +{ + if (!selmon->sel) + return; + if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) + resize(selmon->sel, selmon->sel->x, selmon->sel->y, + selmon->sel->w, selmon->sel->h, 0); + arrange(selmon); +} + +void +togglescratch(const Arg *arg) +{ + Client *c; + unsigned int found = 0; + unsigned int scratchtag = SPTAG(arg->ui); + Arg sparg = {.v = scratchpads[arg->ui].cmd}; + + for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next); + if (found) { + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } + if (ISVISIBLE(c)) { + focus(c); + restack(selmon); + } + } else { + selmon->tagset[selmon->seltags] |= scratchtag; + spawn(&sparg); + } +} + +void +toggletag(const Arg *arg) +{ + unsigned int newtags; + + if (!selmon->sel) + return; + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + selmon->sel->tags = newtags; + focus(NULL); + arrange(selmon); + } +} + +void +toggleview(const Arg *arg) +{ + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + int i; + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + + if (newtagset == ~0) { + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = 0; + } + + /* test if the user did not select the same tag */ + if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { + selmon->pertag->prevtag = selmon->pertag->curtag; + for (i = 0; !(newtagset & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + + /* apply settings for this view */ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + + focus(NULL); + arrange(selmon); + } +} + +void +unfocus(Client *c, int setfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } +} + +void +unmanage(Client *c, int destroyed) +{ + Monitor *m = c->mon; + XWindowChanges wc; + + detach(c); + detachstack(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XSelectInput(dpy, c->win, NoEventMask); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + free(c); + focus(NULL); + updateclientlist(); + arrange(m); +} + +void +unmapnotify(XEvent *e) +{ + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + } +} + +void +updatebars(void) +{ + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixmap = ParentRelative, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +} + +void +updatebarpos(Monitor *m) +{ + m->wy = m->my; + m->wh = m->mh; + if (m->showbar) { + m->wh -= bh; + m->by = m->topbar ? m->wy : m->wy + m->wh; + m->wy = m->topbar ? m->wy + bh : m->wy; + } else + m->by = -bh; +} + +void +updateclientlist() +{ + Client *c; + Monitor *m; + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +} + +int +updategeom(void) +{ + int dirty = 0; + +#ifdef XINERAMA + if (XineramaIsActive(dpy)) { + int i, j, n, nn; + Client *c; + Monitor *m; + XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); + XineramaScreenInfo *unique = NULL; + + for (n = 0, m = mons; m; m = m->next, n++); + /* only consider unique geometries as separate screens */ + unique = ecalloc(nn, sizeof(XineramaScreenInfo)); + for (i = 0, j = 0; i < nn; i++) + if (isuniquegeom(unique, j, &info[i])) + memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); + XFree(info); + nn = j; + + /* new monitors if nn > n */ + for (i = n; i < nn; i++) { + for (m = mons; m && m->next; m = m->next); + if (m) + m->next = createmon(); + else + mons = createmon(); + } + for (i = 0, m = mons; i < nn && m; m = m->next, i++) + if (i >= n + || unique[i].x_org != m->mx || unique[i].y_org != m->my + || unique[i].width != m->mw || unique[i].height != m->mh) + { + dirty = 1; + m->num = i; + m->mx = m->wx = unique[i].x_org; + m->my = m->wy = unique[i].y_org; + m->mw = m->ww = unique[i].width; + m->mh = m->wh = unique[i].height; + updatebarpos(m); + } + /* removed monitors if n > nn */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); + while ((c = m->clients)) { + dirty = 1; + m->clients = c->next; + detachstack(c); + c->mon = mons; + attachbottom(c); + attachstack(c); + } + if (m == selmon) + selmon = mons; + cleanupmon(m); + } + free(unique); + } else +#endif /* XINERAMA */ + { /* default monitor setup */ + if (!mons) + mons = createmon(); + if (mons->mw != sw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = sw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void +updatenumlockmask(void) +{ + unsigned int i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + XFreeModifiermap(modmap); +} + +void +updatesizehints(Client *c) +{ + long msize; + XSizeHints size; + + if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) + /* size is uninitialized, ensure that size.flags aren't used */ + size.flags = PSize; + if (size.flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else if (size.flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } else + c->basew = c->baseh = 0; + if (size.flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else + c->incw = c->inch = 0; + if (size.flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else + c->maxw = c->maxh = 0; + if (size.flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else if (size.flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } else + c->minw = c->minh = 0; + if (size.flags & PAspect) { + c->mina = (float)size.min_aspect.y / size.min_aspect.x; + c->maxa = (float)size.max_aspect.x / size.max_aspect.y; + } else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + c->hintsvalid = 1; +} + +void +updatestatus(void) +{ + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); +} + +void +updatetitle(Client *c) +{ + if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); + if (c->name[0] == '\0') /* hack to mark broken clients */ + strcpy(c->name, broken); +} + +void +updatewindowtype(Client *c) +{ + Atom state = getatomprop(c, netatom[NetWMState]); + Atom wtype = getatomprop(c, netatom[NetWMWindowType]); + + if (state == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + if (wtype == netatom[NetWMWindowTypeDialog]) + c->isfloating = 1; +} + +void +updatewmhints(Client *c) +{ + XWMHints *wmh; + + if ((wmh = XGetWMHints(dpy, c->win))) { + if (c == selmon->sel && wmh->flags & XUrgencyHint) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, c->win, wmh); + } else + c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} + +void +view(const Arg *arg) +{ + int i; + unsigned int tmptag; + + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) { + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + selmon->pertag->prevtag = selmon->pertag->curtag; + + if (arg->ui == ~0) + selmon->pertag->curtag = 0; + else { + for (i = 0; !(arg->ui & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + + focus(NULL); + arrange(selmon); +} + +void +warp(const Client *c) +{ + int x, y; + + if (!c) { + XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww/2, selmon->wy + selmon->wh/2); + return; + } + + if (!getrootptr(&x, &y) || + (x > c->x - c->bw && + y > c->y - c->bw && + x < c->x + c->w + c->bw*2 && + y < c->y + c->h + c->bw*2) || + (y > c->mon->by && y < c->mon->by + bh) || + (c->mon->topbar && !y)) + return; + + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2); +} + +Client * +wintoclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (c->win == w) + return c; + return NULL; +} + +Monitor * +wintomon(Window w) +{ + int x, y; + Client *c; + Monitor *m; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + if (w == m->barwin) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +xerrordummy(Display *dpy, XErrorEvent *ee) +{ + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int +xerrorstart(Display *dpy, XErrorEvent *ee) +{ + die("dwm: another window manager is already running"); + return -1; +} + +void +zoom(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) + return; + if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) + return; + pop(c); +} + +int +main(int argc, char *argv[]) +{ + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) + die("usage: dwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + checkotherwm(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + runAutostart(); + run(); + if(restart) execvp(argv[0], argv); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} diff --git a/user/.config/suckless/dwm/dwm.png b/user/.config/suckless/dwm/dwm.png new file mode 100644 index 0000000000000000000000000000000000000000..b1f9ba7e5f4cc7350ee2392ebcea5fcbe00fb49b GIT binary patch literal 373 zcmeAS@N?(olHy`uVBq!ia0vp^2Y@($g9*gC@m3f}u_bxCyDx`7I;J! zGca%iWx0hJ8D`Cq01C2~c>21sUt<^MF=V?Ztt9{yk}YwKC~?lu%}vcKVQ?-=O)N=G zQ7F$W$xsN%NL6t6^bL5QqM8R(c+=CxF{I+w+q;fj4F)_6j>`Z3pZ>_($QEQ&92OXP z%lpEKGwG8$G-U1H{@Y%;mx-mNK|p|siBVAj$Z~Mt-~h6K0!}~{PyozQ07(f5fTdVi zm=-zT`NweeJ#%S&{fequZGmkDDC*%x$$Sa*fAP=$`nJkhx1Y~k<8b2;Hq)FOdV=P$ q&oWzoxz_&nv&n0)xBzV8k*jsxheTIy&cCY600f?{elF{r5}E*x)opSB literal 0 HcmV?d00001 diff --git a/user/.config/suckless/dwm/patches/dwm-alwayscenter-20200625-f04cac6.diff b/user/.config/suckless/dwm/patches/dwm-alwayscenter-20200625-f04cac6.diff new file mode 100644 index 000000000..03ea9ef2a --- /dev/null +++ b/user/.config/suckless/dwm/patches/dwm-alwayscenter-20200625-f04cac6.diff @@ -0,0 +1,12 @@ +diff -up dwm/dwm.c dwmmod/dwm.c +--- dwm/dwm.c 2020-06-25 00:21:30.383692180 -0300 ++++ dwmmod/dwm.c 2020-06-25 00:20:35.643692330 -0300 +@@ -1057,6 +1057,8 @@ manage(Window w, XWindowAttributes *wa) + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); ++ c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2; ++ c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2; + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) diff --git a/user/.config/suckless/dwm/patches/dwm-attachbottom-6.3.diff b/user/.config/suckless/dwm/patches/dwm-attachbottom-6.3.diff new file mode 100644 index 000000000..c3955f9b1 --- /dev/null +++ b/user/.config/suckless/dwm/patches/dwm-attachbottom-6.3.diff @@ -0,0 +1,54 @@ +diff -up dwm-6.3/dwm.c dwm-6.3-attachbottom/dwm.c +--- dwm-6.3/dwm.c 2022-01-07 12:42:18.000000000 +0100 ++++ dwm-6.3-attachbottom/dwm.c 2022-08-17 22:14:41.813809073 +0200 +@@ -147,6 +147,7 @@ static int applysizehints(Client *c, int + static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); ++static void attachbottom(Client *c); + static void attachstack(Client *c); + static void buttonpress(XEvent *e); + static void checkotherwm(void); +@@ -408,6 +409,15 @@ attach(Client *c) + } + + void ++attachbottom(Client *c) ++{ ++ Client **tc; ++ c->next = NULL; ++ for (tc = &c->mon->clients; *tc; tc = &(*tc)->next); ++ *tc = c; ++} ++ ++void + attachstack(Client *c) + { + c->snext = c->mon->stack; +@@ -1066,7 +1076,7 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); ++ attachbottom(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1421,7 +1431,7 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ attachbottom(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1903,7 +1913,7 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ attachbottom(c); + attachstack(c); + } + if (m == selmon) diff --git a/user/.config/suckless/dwm/patches/dwm-autostart-20161205-bb3bd6f.diff b/user/.config/suckless/dwm/patches/dwm-autostart-20161205-bb3bd6f.diff new file mode 100644 index 000000000..6f11eaf28 --- /dev/null +++ b/user/.config/suckless/dwm/patches/dwm-autostart-20161205-bb3bd6f.diff @@ -0,0 +1,39 @@ +commit 5918623c5bd7fda155bf9dc3d33890c4ae1722d0 +Author: Simon Bremer +Date: Thu Dec 22 17:31:07 2016 +0100 + + Applied and fixed autostart patch for previous version; + +diff --git a/dwm.c b/dwm.c +index d27cb67..066ed71 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -194,6 +194,7 @@ static void resizeclient(Client *c, int x, int y, int w, int h); + static void resizemouse(const Arg *arg); + static void restack(Monitor *m); + static void run(void); ++static void runAutostart(void); + static void scan(void); + static int sendevent(Client *c, Atom proto); + static void sendmon(Client *c, Monitor *m); +@@ -1386,6 +1387,12 @@ run(void) + } + + void ++runAutostart(void) { ++ system("cd ~/.dwm; ./autostart_blocking.sh"); ++ system("cd ~/.dwm; ./autostart.sh &"); ++} ++ ++void + scan(void) + { + unsigned int i, num; +@@ -2145,6 +2152,7 @@ main(int argc, char *argv[]) + checkotherwm(); + setup(); + scan(); ++ runAutostart(); + run(); + cleanup(); + XCloseDisplay(dpy); diff --git a/user/.config/suckless/dwm/patches/dwm-pertag-20200914-61bb8b2.diff b/user/.config/suckless/dwm/patches/dwm-pertag-20200914-61bb8b2.diff new file mode 100644 index 000000000..c8d7fbcb3 --- /dev/null +++ b/user/.config/suckless/dwm/patches/dwm-pertag-20200914-61bb8b2.diff @@ -0,0 +1,177 @@ +diff --git a/dwm.c b/dwm.c +index 664c527..ac8e4ec 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -111,6 +111,7 @@ typedef struct { + void (*arrange)(Monitor *); + } Layout; + ++typedef struct Pertag Pertag; + struct Monitor { + char ltsymbol[16]; + float mfact; +@@ -130,6 +131,7 @@ struct Monitor { + Monitor *next; + Window barwin; + const Layout *lt[2]; ++ Pertag *pertag; + }; + + typedef struct { +@@ -272,6 +274,15 @@ static Window root, wmcheckwin; + /* configuration, allows nested code to access above variables */ + #include "config.h" + ++struct Pertag { ++ unsigned int curtag, prevtag; /* current and previous tag */ ++ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ ++ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ ++ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ ++ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ ++ int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ ++}; ++ + /* compile-time check if all tags fit into an unsigned int bit array. */ + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +@@ -632,6 +643,7 @@ Monitor * + createmon(void) + { + Monitor *m; ++ unsigned int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; +@@ -642,6 +654,20 @@ createmon(void) + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ m->pertag = ecalloc(1, sizeof(Pertag)); ++ m->pertag->curtag = m->pertag->prevtag = 1; ++ ++ for (i = 0; i <= LENGTH(tags); i++) { ++ m->pertag->nmasters[i] = m->nmaster; ++ m->pertag->mfacts[i] = m->mfact; ++ ++ m->pertag->ltidxs[i][0] = m->lt[0]; ++ m->pertag->ltidxs[i][1] = m->lt[1]; ++ m->pertag->sellts[i] = m->sellt; ++ ++ m->pertag->showbars[i] = m->showbar; ++ } ++ + return m; + } + +@@ -967,7 +993,7 @@ grabkeys(void) + void + incnmaster(const Arg *arg) + { +- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); + } + +@@ -1502,9 +1528,9 @@ void + setlayout(const Arg *arg) + { + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) +- selmon->sellt ^= 1; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; + if (arg && arg->v) +- selmon->lt[selmon->sellt] = (Layout *)arg->v; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); +@@ -1523,7 +1549,7 @@ setmfact(const Arg *arg) + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; +- selmon->mfact = f; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); + } + +@@ -1702,7 +1728,7 @@ tile(Monitor *m) + void + togglebar(const Arg *arg) + { +- selmon->showbar = !selmon->showbar; ++ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +@@ -1741,9 +1767,33 @@ void + toggleview(const Arg *arg) + { + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); ++ int i; + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; ++ ++ if (newtagset == ~0) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = 0; ++ } ++ ++ /* test if the user did not select the same tag */ ++ if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ for (i = 0; !(newtagset & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } ++ ++ /* apply settings for this view */ ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); ++ + focus(NULL); + arrange(selmon); + } +@@ -2038,11 +2088,37 @@ updatewmhints(Client *c) + void + view(const Arg *arg) + { ++ int i; ++ unsigned int tmptag; ++ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ +- if (arg->ui & TAGMASK) ++ if (arg->ui & TAGMASK) { + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ ++ if (arg->ui == ~0) ++ selmon->pertag->curtag = 0; ++ else { ++ for (i = 0; !(arg->ui & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } ++ } else { ++ tmptag = selmon->pertag->prevtag; ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = tmptag; ++ } ++ ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ ++ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) ++ togglebar(NULL); ++ + focus(NULL); + arrange(selmon); + } diff --git a/user/.config/suckless/dwm/patches/dwm-restartsig-20180523-6.2.diff b/user/.config/suckless/dwm/patches/dwm-restartsig-20180523-6.2.diff new file mode 100644 index 000000000..f1f86808e --- /dev/null +++ b/user/.config/suckless/dwm/patches/dwm-restartsig-20180523-6.2.diff @@ -0,0 +1,139 @@ +From 2991f37f0aaf44b9f9b11e7893ff0af8eb88f649 Mon Sep 17 00:00:00 2001 +From: Christopher Drelich +Date: Wed, 23 May 2018 22:50:38 -0400 +Subject: [PATCH] Modifies quit to handle restarts and adds SIGHUP and SIGTERM + handlers. + +Modified quit() to restart if it receives arg .i = 1 +MOD+CTRL+SHIFT+Q was added to confid.def.h to do just that. + +Signal handlers were handled for SIGHUP and SIGTERM. +If dwm receives these signals it calls quit() with +arg .i = to 1 or 0, respectively. + +To restart dwm: +MOD+CTRL+SHIFT+Q +or +kill -HUP dwmpid + +To quit dwm cleanly: +MOD+SHIFT+Q +or +kill -TERM dwmpid +--- + config.def.h | 1 + + dwm.1 | 10 ++++++++++ + dwm.c | 22 ++++++++++++++++++++++ + 3 files changed, 33 insertions(+) + +diff --git a/config.def.h b/config.def.h +index a9ac303..e559429 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -94,6 +94,7 @@ static Key keys[] = { + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, ++ { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} }, + }; + + /* button definitions */ +diff --git a/dwm.1 b/dwm.1 +index 13b3729..36a331c 100644 +--- a/dwm.1 ++++ b/dwm.1 +@@ -142,6 +142,9 @@ Add/remove all windows with nth tag to/from the view. + .TP + .B Mod1\-Shift\-q + Quit dwm. ++.TP ++.B Mod1\-Control\-Shift\-q ++Restart dwm. + .SS Mouse commands + .TP + .B Mod1\-Button1 +@@ -155,6 +158,13 @@ Resize focused window while dragging. Tiled windows will be toggled to the float + .SH CUSTOMIZATION + dwm is customized by creating a custom config.h and (re)compiling the source + code. This keeps it fast, secure and simple. ++.SH SIGNALS ++.TP ++.B SIGHUP - 1 ++Restart the dwm process. ++.TP ++.B SIGTERM - 15 ++Cleanly terminate the dwm process. + .SH SEE ALSO + .BR dmenu (1), + .BR st (1) +diff --git a/dwm.c b/dwm.c +index bb95e26..286eecd 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -205,6 +205,8 @@ static void setup(void); + static void seturgent(Client *c, int urg); + static void showhide(Client *c); + static void sigchld(int unused); ++static void sighup(int unused); ++static void sigterm(int unused); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); +@@ -260,6 +262,7 @@ static void (*handler[LASTEvent]) (XEvent *) = { + [UnmapNotify] = unmapnotify + }; + static Atom wmatom[WMLast], netatom[NetLast]; ++static int restart = 0; + static int running = 1; + static Cur *cursor[CurLast]; + static Clr **scheme; +@@ -1248,6 +1251,7 @@ propertynotify(XEvent *e) + void + quit(const Arg *arg) + { ++ if(arg->i) restart = 1; + running = 0; + } + +@@ -1536,6 +1540,9 @@ setup(void) + /* clean up any zombies immediately */ + sigchld(0); + ++ signal(SIGHUP, sighup); ++ signal(SIGTERM, sigterm); ++ + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); +@@ -1637,6 +1644,20 @@ sigchld(int unused) + } + + void ++sighup(int unused) ++{ ++ Arg a = {.i = 1}; ++ quit(&a); ++} ++ ++void ++sigterm(int unused) ++{ ++ Arg a = {.i = 0}; ++ quit(&a); ++} ++ ++void + spawn(const Arg *arg) + { + if (arg->v == dmenucmd) +@@ -2139,6 +2160,7 @@ main(int argc, char *argv[]) + setup(); + scan(); + run(); ++ if(restart) execvp(argv[0], argv); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +-- +2.7.4 + diff --git a/user/.config/suckless/dwm/patches/dwm-scratchpads-20200414-728d397b.diff b/user/.config/suckless/dwm/patches/dwm-scratchpads-20200414-728d397b.diff new file mode 100644 index 000000000..d3e90c01a --- /dev/null +++ b/user/.config/suckless/dwm/patches/dwm-scratchpads-20200414-728d397b.diff @@ -0,0 +1,199 @@ +From 728d397b21982af88737277fd9d6939a7b558786 Mon Sep 17 00:00:00 2001 +From: Christian Tenllado +Date: Tue, 14 Apr 2020 23:31:15 +0200 +Subject: [PATCH] Multiple scratchpads + +This patch enables multiple scratchpads, each with one asigned window. +This enables the same scratchpad workflow that you have in i3. + +Scratchpads are implemented as special tags, whose mask does not +apply to new spawned windows. To assign a window to a scratchpad you +have to set up a rule, as you do with regular tags. + +Windows tagged with scratchpad tags can be set floating or not in the +rules array. Most users would probably want them floating (i3 style), +but having them tiled does also perfectly work and might fit better the +DWM approach. In case they are set floating, the patch moves them to the +center of the screen whenever they are shown. The patch can easily be +modified to make this last feature configurable in the rules array (see +the center patch). + +The togglescratch function, borrowed from the previous scratchpad patch +and slightly modified, can be used to spawn a registered scratchpad +process or toggle its view. This function looks for a window tagged with +the selected scratchpad tag. If it is found its view is toggled. If it is +not found the corresponding registered command is spawned. The +config.def.h shows three examples of its use to spawn a terminal in the +first scratchpad tag, a second terminal running ranger on the second +scratchpad tag and the keepassxc application to manage passwords on a +third scratchpad tag. + +If you prefer to spawn your scratchpad applications from the startup +script, you might opt for binding keys to toggleview instead, as +scratchpads are just special tags (you may even extend the TAGKEYS macro +to generalize the key bindings). +--- + config.def.h | 28 ++++++++++++++++++++++++---- + dwm.c | 43 +++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 65 insertions(+), 6 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1c0b587..06265e1 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -18,17 +18,33 @@ static const char *colors[][3] = { + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + }; + ++typedef struct { ++ const char *name; ++ const void *cmd; ++} Sp; ++const char *spcmd1[] = {"st", "-n", "spterm", "-g", "120x34", NULL }; ++const char *spcmd2[] = {"st", "-n", "spfm", "-g", "144x41", "-e", "ranger", NULL }; ++const char *spcmd3[] = {"keepassxc", NULL }; ++static Sp scratchpads[] = { ++ /* name cmd */ ++ {"spterm", spcmd1}, ++ {"spranger", spcmd2}, ++ {"keepassxc", spcmd3}, ++}; ++ + /* tagging */ + static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +- + static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating monitor */ +- { "Gimp", NULL, NULL, 0, 1, -1 }, +- { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, ++ { "Gimp", NULL, NULL, 0, 1, -1 }, ++ { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, ++ { NULL, "spterm", NULL, SPTAG(0), 1, -1 }, ++ { NULL, "spfm", NULL, SPTAG(1), 1, -1 }, ++ { NULL, "keepassxc", NULL, SPTAG(2), 0, -1 }, + }; + + /* layout(s) */ +@@ -59,6 +75,7 @@ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() + static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; + static const char *termcmd[] = { "st", NULL }; + ++ + static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, +@@ -84,6 +101,9 @@ static Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY, XK_y, togglescratch, {.ui = 0 } }, ++ { MODKEY, XK_u, togglescratch, {.ui = 1 } }, ++ { MODKEY, XK_x, togglescratch, {.ui = 2 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +@@ -106,7 +126,7 @@ static Button buttons[] = { + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, +- { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, ++ { ClkClientWin, MODKEY, Button1, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, +diff --git a/dwm.c b/dwm.c +index 4465af1..646aa1a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -54,7 +54,10 @@ + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) +-#define TAGMASK ((1 << LENGTH(tags)) - 1) ++#define NUMTAGS (LENGTH(tags) + LENGTH(scratchpads)) ++#define TAGMASK ((1 << NUMTAGS) - 1) ++#define SPTAG(i) ((1 << LENGTH(tags)) << (i)) ++#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << LENGTH(tags)) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + + /* enums */ +@@ -211,6 +214,7 @@ static void tagmon(const Arg *arg); + static void tile(Monitor *); + static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); ++static void togglescratch(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); +@@ -299,6 +303,11 @@ applyrules(Client *c) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; ++ if ((r->tags & SPTAGMASK) && r->isfloating) { ++ c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); ++ c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); ++ } ++ + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +@@ -308,7 +317,7 @@ applyrules(Client *c) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); +- c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; ++ c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : (c->mon->tagset[c->mon->seltags] & ~SPTAGMASK); + } + + int +@@ -1616,6 +1625,10 @@ showhide(Client *c) + if (!c) + return; + if (ISVISIBLE(c)) { ++ if ((c->tags & SPTAGMASK) && c->isfloating) { ++ c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); ++ c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); ++ } + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) +@@ -1719,6 +1732,32 @@ togglefloating(const Arg *arg) + arrange(selmon); + } + ++void ++togglescratch(const Arg *arg) ++{ ++ Client *c; ++ unsigned int found = 0; ++ unsigned int scratchtag = SPTAG(arg->ui); ++ Arg sparg = {.v = scratchpads[arg->ui].cmd}; ++ ++ for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next); ++ if (found) { ++ unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; ++ if (newtagset) { ++ selmon->tagset[selmon->seltags] = newtagset; ++ focus(NULL); ++ arrange(selmon); ++ } ++ if (ISVISIBLE(c)) { ++ focus(c); ++ restack(selmon); ++ } ++ } else { ++ selmon->tagset[selmon->seltags] |= scratchtag; ++ spawn(&sparg); ++ } ++} ++ + void + toggletag(const Arg *arg) + { +-- +2.20.1 + diff --git a/user/.config/suckless/dwm/patches/dwm-statuspadding-6.3.diff b/user/.config/suckless/dwm/patches/dwm-statuspadding-6.3.diff new file mode 100644 index 000000000..fa6780f6f --- /dev/null +++ b/user/.config/suckless/dwm/patches/dwm-statuspadding-6.3.diff @@ -0,0 +1,62 @@ +From d6dd69c26f4272f87672ae54f69dc0d48650d34b Mon Sep 17 00:00:00 2001 +From: taep96 <64481039+taep96@users.noreply.github.com> +Date: Mon, 7 Feb 2022 19:09:45 +0100 +Subject: [PATCH] Fixed | Replaces magic numbers in statusbar with configurable + variables. + +horizpadbar for horizontal statusbar padding +vertpadbar for vertical statusbar padding + +StatusText now has both left and right padding, +as well as the vertical padding that all of the statusbar shares. + +Other than the addition of left padding to StatusText, appearance +of the statusbar is identical to pre-patch when using the defaults +in config.def.h +--- + config.def.h | 2 ++ + dwm.c | 8 ++++---- + 2 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..6cb845c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,8 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ ++static const int horizpadbar = 2; /* horizontal padding for statusbar */ ++static const int vertpadbar = 0; /* vertical padding for statusbar */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +diff --git a/dwm.c b/dwm.c +index a96f33c..a1b8c95 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -708,8 +708,8 @@ drawbar(Monitor *m) + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); +- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ +- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); ++ tw = TEXTW(stext); ++ drw_text(drw, m->ww - tw, 0, tw, bh, lrpad / 2, stext, 0); + } + + for (c = m->clients; c; c = c->next) { +@@ -1548,8 +1548,8 @@ setup(void) + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); +- lrpad = drw->fonts->h; +- bh = drw->fonts->h + 2; ++ lrpad = drw->fonts->h + horizpadbar; ++ bh = drw->fonts->h + vertpadbar; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); +-- +2.35.1 + diff --git a/user/.config/suckless/dwm/patches/dwm-warp-6.2.diff b/user/.config/suckless/dwm/patches/dwm-warp-6.2.diff new file mode 100644 index 000000000..813c41d6d --- /dev/null +++ b/user/.config/suckless/dwm/patches/dwm-warp-6.2.diff @@ -0,0 +1,58 @@ +diff --git a/dwm.c b/dwm.c +index 4465af1..bf74f60 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -227,6 +227,7 @@ static void updatetitle(Client *c); + static void updatewindowtype(Client *c); + static void updatewmhints(Client *c); + static void view(const Arg *arg); ++static void warp(const Client *c); + static Client *wintoclient(Window w); + static Monitor *wintomon(Window w); + static int xerror(Display *dpy, XErrorEvent *ee); +@@ -827,6 +828,7 @@ focusmon(const Arg *arg) + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); ++ warp(selmon->sel); + } + + void +@@ -1367,6 +1369,8 @@ restack(Monitor *m) + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); ++ if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && selmon->lt[selmon->sellt] != &layouts[2]) ++ warp(m->sel); + } + + void +@@ -2044,6 +2048,28 @@ view(const Arg *arg) + arrange(selmon); + } + ++void ++warp(const Client *c) ++{ ++ int x, y; ++ ++ if (!c) { ++ XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww/2, selmon->wy + selmon->wh/2); ++ return; ++ } ++ ++ if (!getrootptr(&x, &y) || ++ (x > c->x - c->bw && ++ y > c->y - c->bw && ++ x < c->x + c->w + c->bw*2 && ++ y < c->y + c->h + c->bw*2) || ++ (y > c->mon->by && y < c->mon->by + bh) || ++ (c->mon->topbar && !y)) ++ return; ++ ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2); ++} ++ + Client * + wintoclient(Window w) + { diff --git a/user/.config/suckless/dwm/tcl.c b/user/.config/suckless/dwm/tcl.c new file mode 100644 index 000000000..4c94914f7 --- /dev/null +++ b/user/.config/suckless/dwm/tcl.c @@ -0,0 +1,74 @@ +void +tcl(Monitor * m) +{ + int x, y, h, w, mw, sw, bdw; + unsigned int i, n; + Client * c; + + for (n = 0, c = nexttiled(m->clients); c; + c = nexttiled(c->next), n++); + + if (n == 0) + return; + + c = nexttiled(m->clients); + + mw = m->mfact * m->ww; + sw = (m->ww - mw) / 2; + bdw = (2 * c->bw); + resize(c, + n < 3 ? m->wx : m->wx + sw, + m->wy, + n == 1 ? m->ww - bdw : mw - bdw, + m->wh - bdw, + False); + + if (--n == 0) + return; + + w = (m->ww - mw) / ((n > 1) + 1); + c = nexttiled(c->next); + + if (n > 1) + { + x = m->wx + ((n > 1) ? mw + sw : mw); + y = m->wy; + h = m->wh / (n / 2); + + if (h < bh) + h = m->wh; + + for (i = 0; c && i < n / 2; c = nexttiled(c->next), i++) + { + resize(c, + x, + y, + w - bdw, + (i + 1 == n / 2) ? m->wy + m->wh - y - bdw : h - bdw, + False); + + if (h != m->wh) + y = c->y + HEIGHT(c); + } + } + + x = (n + 1 / 2) == 1 ? mw : m->wx; + y = m->wy; + h = m->wh / ((n + 1) / 2); + + if (h < bh) + h = m->wh; + + for (i = 0; c; c = nexttiled(c->next), i++) + { + resize(c, + x, + y, + (i + 1 == (n + 1) / 2) ? w - bdw : w - bdw, + (i + 1 == (n + 1) / 2) ? m->wy + m->wh - y - bdw : h - bdw, + False); + + if (h != m->wh) + y = c->y + HEIGHT(c); + } +} diff --git a/user/.config/suckless/dwm/transient.c b/user/.config/suckless/dwm/transient.c new file mode 100644 index 000000000..040adb5b3 --- /dev/null +++ b/user/.config/suckless/dwm/transient.c @@ -0,0 +1,42 @@ +/* cc transient.c -o transient -lX11 */ + +#include +#include +#include +#include + +int main(void) { + Display *d; + Window r, f, t = None; + XSizeHints h; + XEvent e; + + d = XOpenDisplay(NULL); + if (!d) + exit(1); + r = DefaultRootWindow(d); + + f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); + h.min_width = h.max_width = h.min_height = h.max_height = 400; + h.flags = PMinSize | PMaxSize; + XSetWMNormalHints(d, f, &h); + XStoreName(d, f, "floating"); + XMapWindow(d, f); + + XSelectInput(d, f, ExposureMask); + while (1) { + XNextEvent(d, &e); + + if (t == None) { + sleep(5); + t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); + XSetTransientForHint(d, t, f); + XStoreName(d, t, "transient"); + XMapWindow(d, t); + XSelectInput(d, t, ExposureMask); + } + } + + XCloseDisplay(d); + exit(0); +} diff --git a/user/.config/suckless/dwm/util.c b/user/.config/suckless/dwm/util.c new file mode 100644 index 000000000..96b82c980 --- /dev/null +++ b/user/.config/suckless/dwm/util.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "util.h" + +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} diff --git a/user/.config/suckless/dwm/util.h b/user/.config/suckless/dwm/util.h new file mode 100644 index 000000000..f633b5173 --- /dev/null +++ b/user/.config/suckless/dwm/util.h @@ -0,0 +1,8 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/user/.config/suckless/dwmblocks/Makefile b/user/.config/suckless/dwmblocks/Makefile new file mode 100644 index 000000000..c9911b0eb --- /dev/null +++ b/user/.config/suckless/dwmblocks/Makefile @@ -0,0 +1,14 @@ +PREFIX ?= /usr/local +CC ?= cc + +output: dwmblocks.c blocks.def.h blocks.h + ${CC} `pkg-config --cflags x11 --libs x11` dwmblocks.c -o dwmblocks +blocks.h: + cp -v blocks.def.h $@ +clean: + rm -fv *.o *.gch dwmblocks +install: output + mkdir -pv $(DESTDIR)$(PREFIX)/bin + install -m 0755 dwmblocks $(DESTDIR)$(PREFIX)/bin/dwmblocks +uninstall: + rm -fv $(DESTDIR)$(PREFIX)/bin/dwmblocks diff --git a/user/.config/suckless/dwmblocks/blocks.def.h b/user/.config/suckless/dwmblocks/blocks.def.h new file mode 100644 index 000000000..aac6a3007 --- /dev/null +++ b/user/.config/suckless/dwmblocks/blocks.def.h @@ -0,0 +1,22 @@ +//Modify this file to change what commands output to your statusbar, and recompile using the make command. +static const Block blocks[] = { + /*Command*/ /*Update Interval*/ /*Update Signal*/ +// {"", "$HOME/.config/suckless/dwmblocks/block_cpu", 1, 1}, + +// {"", "$HOME/.config/suckless/dwmblocks/block_memory", 1, 1}, + + {"", "$HOME/.config/suckless/dwmblocks/scripts/block_layout", 1, 0}, + + {"", "$HOME/.config/suckless/dwmblocks/scripts/block_battery", 1, 0}, + + {"", "$HOME/.config/suckless/dwmblocks/scripts/block_brightness", 1, 0}, + + {"", "$HOME/.config/suckless/dwmblocks/scripts/block_volume", 1, 0}, + + {"", "$HOME/.config/suckless/dwmblocks/scripts/block_clock", 1, 0}, + + {"", "$HOME/.config/suckless/dwmblocks/scripts/block_wifi", 1, 0}, +}; + +//sets delimeter between status commands. NULL character ('\0') means no delimeter. +static char delim = '|'; diff --git a/user/.config/suckless/dwmblocks/dwmblocks.c b/user/.config/suckless/dwmblocks/dwmblocks.c new file mode 100644 index 000000000..88bdfb0b0 --- /dev/null +++ b/user/.config/suckless/dwmblocks/dwmblocks.c @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#define LENGTH(X) (sizeof(X) / sizeof (X[0])) +#define CMDLENGTH 50 + +typedef struct { + char* icon; + char* command; + unsigned int interval; + unsigned int signal; +} Block; +void sighandler(int num); +void replace(char *str, char old, char new); +void getcmds(int time); +#ifndef __OpenBSD__ +void getsigcmds(int signal); +void setupsignals(); +void sighandler(int signum); +#endif +int getstatus(char *str, char *last); +void setroot(); +void statusloop(); +void termhandler(int signum); + + +#include "blocks.h" + +static Display *dpy; +static int screen; +static Window root; +static char statusbar[LENGTH(blocks)][CMDLENGTH] = {0}; +static char statusstr[2][256]; +static int statusContinue = 1; +static void (*writestatus) () = setroot; + +void replace(char *str, char old, char new) +{ + int N = strlen(str); + for(int i = 0; i < N; i++) + if(str[i] == old) + str[i] = new; +} + +//opens process *cmd and stores output in *output +void getcmd(const Block *block, char *output) +{ + strcpy(output, block->icon); + char *cmd = block->command; + FILE *cmdf = popen(cmd,"r"); + if (!cmdf) + return; + char c; + int i = strlen(block->icon); + fgets(output+i, CMDLENGTH-i, cmdf); + i = strlen(output); + if (delim != '\0' && --i) + output[i++] = delim; + output[i++] = '\0'; + pclose(cmdf); +} + +void getcmds(int time) +{ + const Block* current; + for(int i = 0; i < LENGTH(blocks); i++) + { + current = blocks + i; + if ((current->interval != 0 && time % current->interval == 0) || time == -1) + getcmd(current,statusbar[i]); + } +} + +#ifndef __OpenBSD__ +void getsigcmds(int signal) +{ + const Block *current; + for (int i = 0; i < LENGTH(blocks); i++) + { + current = blocks + i; + if (current->signal == signal) + getcmd(current,statusbar[i]); + } +} + +void setupsignals() +{ + for(int i = 0; i < LENGTH(blocks); i++) + { + if (blocks[i].signal > 0) + signal(SIGRTMIN+blocks[i].signal, sighandler); + } + +} +#endif + +int getstatus(char *str, char *last) +{ + strcpy(last, str); + str[0] = '\0'; + for(int i = 0; i < LENGTH(blocks); i++) + strcat(str, statusbar[i]); + str[strlen(str)-1] = '\0'; + return strcmp(str, last);//0 if they are the same +} + +void setroot() +{ + if (!getstatus(statusstr[0], statusstr[1]))//Only set root if text has changed. + return; + Display *d = XOpenDisplay(NULL); + if (d) { + dpy = d; + } + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + XStoreName(dpy, root, statusstr[0]); + XCloseDisplay(dpy); +} + +void pstdout() +{ + if (!getstatus(statusstr[0], statusstr[1]))//Only write out if text has changed. + return; + printf("%s\n",statusstr[0]); + fflush(stdout); +} + + +void statusloop() +{ +#ifndef __OpenBSD__ + setupsignals(); +#endif + int i = 0; + getcmds(-1); + while(statusContinue) + { + getcmds(i); + writestatus(); + sleep(1.0); + i++; + } +} + +#ifndef __OpenBSD__ +void sighandler(int signum) +{ + getsigcmds(signum-SIGRTMIN); + writestatus(); +} +#endif + +void termhandler(int signum) +{ + statusContinue = 0; + exit(0); +} + +int main(int argc, char** argv) +{ + for(int i = 0; i < argc; i++) + { + if (!strcmp("-d",argv[i])) + delim = argv[++i][0]; + else if(!strcmp("-p",argv[i])) + writestatus = pstdout; + } + signal(SIGTERM, termhandler); + signal(SIGINT, termhandler); + statusloop(); +} diff --git a/user/.config/suckless/dwmblocks/scripts/block_battery b/user/.config/suckless/dwmblocks/scripts/block_battery new file mode 100755 index 000000000..d0c53e5f3 --- /dev/null +++ b/user/.config/suckless/dwmblocks/scripts/block_battery @@ -0,0 +1,13 @@ +#!/bin/bash +# Loop through all attached batteries and format the info +currntpwr=$(powerprofilesctl get) +if [ "${currntpwr}" = "performance" ]; then + pwr="perf" +elif [ "${currntpwr}" = "balanced" ]; then + pwr="balc" +elif [ "${currntpwr}" = "power-saver" ]; then + pwr="pwrs" +fi +battery="/sys/class/power_supply/BAT0" +capacity=$(cat "$battery"/capacity) +echo -e " bat: $capacity% - $pwr " diff --git a/user/.config/suckless/dwmblocks/scripts/block_brightness b/user/.config/suckless/dwmblocks/scripts/block_brightness new file mode 100755 index 000000000..35404cf8c --- /dev/null +++ b/user/.config/suckless/dwmblocks/scripts/block_brightness @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +brt=$(xbacklight -get) +echo " bri: ${brt%.*}% " diff --git a/user/.config/suckless/dwmblocks/scripts/block_clock b/user/.config/suckless/dwmblocks/scripts/block_clock new file mode 100755 index 000000000..a5d15126c --- /dev/null +++ b/user/.config/suckless/dwmblocks/scripts/block_clock @@ -0,0 +1,4 @@ +#!/bin/bash +date="$(date +%d/%m/%Y)" +clock="$(date +%R:%S)" +echo " dte: $date - $clock " diff --git a/user/.config/suckless/dwmblocks/scripts/block_cpu b/user/.config/suckless/dwmblocks/scripts/block_cpu new file mode 100755 index 000000000..3815c72ad --- /dev/null +++ b/user/.config/suckless/dwmblocks/scripts/block_cpu @@ -0,0 +1,3 @@ +#!/bin/bash +usage=$(top -b -n1 | grep "Cpu(s)" | awk '{print $2 + $4}') +echo -e "CPU: [${usage%.*}%] " diff --git a/user/.config/suckless/dwmblocks/scripts/block_layout b/user/.config/suckless/dwmblocks/scripts/block_layout new file mode 100755 index 000000000..fa722e116 --- /dev/null +++ b/user/.config/suckless/dwmblocks/scripts/block_layout @@ -0,0 +1,3 @@ +#!/bin/bash +layout=$(setxkbmap -query | grep -oP 'layout:\s*\K\w+') +echo "key: $layout " diff --git a/user/.config/suckless/dwmblocks/scripts/block_memory b/user/.config/suckless/dwmblocks/scripts/block_memory new file mode 100755 index 000000000..9c899f6d8 --- /dev/null +++ b/user/.config/suckless/dwmblocks/scripts/block_memory @@ -0,0 +1,3 @@ +#!/bin/bash +mem="$(free -h | awk '/^Mem:/ {print $3 "/" $2}')" +echo -e " RAM: [$mem] " diff --git a/user/.config/suckless/dwmblocks/scripts/block_volume b/user/.config/suckless/dwmblocks/scripts/block_volume new file mode 100755 index 000000000..8b878a943 --- /dev/null +++ b/user/.config/suckless/dwmblocks/scripts/block_volume @@ -0,0 +1,5 @@ +#!/bin/bash +# Prints the current volume or 🔇 if muted. +[ $(pamixer --get-mute) = true ] && echo " VL: [MUT] " && exit +vol="$(pamixer --get-volume)" +echo " vol: $vol% " diff --git a/user/.config/suckless/dwmblocks/scripts/block_wifi b/user/.config/suckless/dwmblocks/scripts/block_wifi new file mode 100755 index 000000000..c4328b459 --- /dev/null +++ b/user/.config/suckless/dwmblocks/scripts/block_wifi @@ -0,0 +1,11 @@ +#!/bin/bash +constate=$(nmcli dev | grep wifi | sed 's/ \{2,\}/|/g' | cut -d '|' -f3 | head -1) +currentwfi=$(nmcli dev | grep wifi | sed 's/ \{2,\}/|/g' | cut -d '|' -f4 | head -1) + +if [ "$constate" = "disconnected" ]; then + echo " wfi: disc" +elif [ "$constate" = "connected" ]; then + echo " wfi: $currentwfi" +else + echo " wfi: disc" +fi diff --git a/user/.config/suckless/herbe/.gitignore b/user/.config/suckless/herbe/.gitignore new file mode 100644 index 000000000..fba990b27 --- /dev/null +++ b/user/.config/suckless/herbe/.gitignore @@ -0,0 +1,3 @@ +herbe +config.h +.ccls-cache diff --git a/user/.config/suckless/herbe/LICENSE b/user/.config/suckless/herbe/LICENSE new file mode 100644 index 000000000..7b85eda1b --- /dev/null +++ b/user/.config/suckless/herbe/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Samuel Dudík + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/user/.config/suckless/herbe/Makefile b/user/.config/suckless/herbe/Makefile new file mode 100644 index 000000000..473523054 --- /dev/null +++ b/user/.config/suckless/herbe/Makefile @@ -0,0 +1,25 @@ +CFLAGS = -Wall -Wextra -pedantic -lX11 -lXft -I/usr/include/freetype2 -pthread + +PREFIX ?= /usr/local +CC ?= cc + +all: herbe + +config.h: config.def.h + cp config.def.h config.h + +herbe: herbe.c config.h + $(CC) herbe.c $(CFLAGS) -o herbe + +install: herbe + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f herbe ${DESTDIR}${PREFIX}/bin + cp -f notify-send ${DESTDIR}${PREFIX}/bin + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/herbe + +clean: + rm -f herbe + +.PHONY: all install uninstall clean diff --git a/user/.config/suckless/herbe/README.md b/user/.config/suckless/herbe/README.md new file mode 100644 index 000000000..25eb20c8c --- /dev/null +++ b/user/.config/suckless/herbe/README.md @@ -0,0 +1,139 @@ +# 🌱 herbe +> Daemon-less notifications without D-Bus. Minimal and lightweight. + +

+ +

+ +## Features +* Under 200 lines of code +* Doesn't run in the background, just displays the notification and exits +* No external dependencies except Xlib and Xft +* Configurable through `config.h` or Xresources ([using this patch](https://github.com/dudik/herbe/pull/11)) +* [Actions support](#actions) +* Extensible through [patches](https://github.com/dudik/herbe/pulls?q=is%3Aopen+is%3Apr+label%3Apatch) + +## Table of contents + +* [Usage](#usage) + * [Patches](#patches) + * [Dismiss a notification](#dismiss-a-notification) + * [Actions](#actions) + * [Newlines](#newlines) + * [Multiple notifications](#multiple-notifications) + * [Notifications don't show up](#notifications-dont-show-up) +* [Installation](#installation) + * [Packages](#packages) + * [Dependencies](#dependencies) + * [Build](#build) +* [Configuration](#configuration) +* [Contribute](#contribute) + +## Usage + +### Patches +[List of available patches](https://github.com/dudik/herbe/pulls?q=is%3Aopen+is%3Apr+label%3Apatch) + +To create a new patch you'll have to open a pull request with your changes. Append `.diff` to the pull request URL to get a downloadable diff file. Don't forget to prefix the title with `patch:` and to apply the `patch` label to it. For inspiration, look at [my Xresources patch](https://github.com/dudik/herbe/pull/11). Thank you. + +_Note: This patching method was heavily inspired by [dylan's sowm](https://github.com/dylanaraps/sowm)._ + +### Dismiss a notification +A notification can be dismissed either by clicking on it with `DISMISS_BUTTON` (set in config.h, defaults to left mouse button) or sending a `SIGUSR1` signal to it: +```shell +$ pkill -SIGUSR1 herbe +``` +Dismissed notifications return exit code 2. + +### Actions +Action is a piece of shell code that runs when a notification gets accepted. Accepting a notification is the same as dismissing it, but you have to use either `ACTION_BUTTON` (defaults to right mouse button) or the `SIGUSR2` signal. +An accepted notification always returns exit code 0. To specify an action: +```shell +$ herbe "Notification body" && echo "This is an action" +``` +Where everything after `&&` is the action and will get executed after the notification gets accepted. + +### Newlines +Every command line argument gets printed on a separate line by default e.g.: +```shell +$ herbe "First line" "Second line" "Third line" ... +``` +You can also use `\n` e.g. in `bash`: +```shell +$ herbe $'First line\nSecond line\nThird line' +``` +But by default `herbe` prints `\n` literally: +```shell +$ herbe "First line\nStill the first line" +``` +Output of other programs will get printed correctly, just make sure to escape it (so you don't end up with every word on a separate line): +```shell +$ herbe "$(ps axch -o cmd:15,%cpu --sort=-%cpu | head)" +``` + +### Multiple notifications +Notifications are put in a queue and shown one after another in order of creation (first in, first out). They don't overlap and each one is shown for its entire duration. + +### Notifications don't show up +Most likely a running notification got terminated forcefully (SIGKILL or any uncaught signal) which caused the semaphore not getting unlocked. First, kill any `herbe` instance that is stuck: +```shell +$ pkill -SIGKILL herbe +``` +Then just call `herbe` without any arguments: +```shell +$ herbe +``` +Notifications should now show up as expected. + +Don't ever send any signals to `herbe` except these: +```shell +# same as pkill -SIGTERM herbe, terminates every running herbe process +$ pkill herbe + +$ pkill -SIGUSR1 herbe +$ pkill -SIGUSR2 herbe +``` +And you should be fine. That's all you really need to interact with `herbe`. + +## Installation +### Packages +[![Packaging status](https://repology.org/badge/vertical-allrepos/herbe.svg)](https://repology.org/project/herbe/versions) + +[OpenBSD patch](https://github.com/dudik/herbe/pull/4) + +[FreeBSD patch](https://github.com/dudik/herbe/pull/16) + +[Wayland port](https://github.com/muevoid/Wayherb) by [muevoid](https://github.com/muevoid) + +**Only the [herbe-git AUR package](https://aur.archlinux.org/packages/herbe-git/) is maintained by me.** + +### Dependencies +* X11 (Xlib) +* Xft + +The names of packages are different depending on which distribution you use. +For example, if you use [Void Linux](https://voidlinux.org/) you will have to install these dependencies: +```shell +sudo xbps-install base-devel libX11-devel libXft-devel +``` + +### Build +```shell +git clone https://github.com/dudik/herbe +cd herbe +sudo make install +``` +`make install` requires root privileges because it copies the resulting binary to `/usr/local/bin`. This makes `herbe` accessible globally. + +You can also use `make clean` to remove the binary from the build folder, `sudo make uninstall` to remove the binary from `/usr/local/bin` or just `make` to build the binary locally. + +## Configuration +herbe is configured at compile-time by editing `config.h`. Every option should be self-explanatory. There is no `height` option because height is determined by font size and text padding. + +[Xresources patch](https://github.com/dudik/herbe/pull/11) + +## Contribute +If you want to report a bug or you have a feature request, feel free to [open an issue](https://github.com/dudik/herbe/issues). + +## Projects with herbe integration +- [qutebrowser](https://qutebrowser.org/) supports showing web notifications via herbe, via the `content.notifications.presenter` setting. diff --git a/user/.config/suckless/herbe/config.def.h b/user/.config/suckless/herbe/config.def.h new file mode 100644 index 000000000..94932d4fe --- /dev/null +++ b/user/.config/suckless/herbe/config.def.h @@ -0,0 +1,19 @@ +static const char *background_color = "#1d2021"; +static const char *border_color = "#cc241d"; +static const char *font_color = "#fbf1c7"; +static const char *font_pattern = "mononoki Nerd Font Mono:size=10"; +static const unsigned line_spacing = 5; +static const unsigned int padding = 15; + +static const unsigned int width = 450; +static const unsigned int border_size = 2; +static const unsigned int pos_x = 30; +static const unsigned int pos_y = 60; + +enum corners { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }; +enum corners corner = TOP_RIGHT; + +static const unsigned int duration = 5; /* in seconds */ + +#define DISMISS_BUTTON Button1 +#define ACTION_BUTTON Button3 diff --git a/user/.config/suckless/herbe/herbe.c b/user/.config/suckless/herbe/herbe.c new file mode 100644 index 000000000..8bfdbc172 --- /dev/null +++ b/user/.config/suckless/herbe/herbe.c @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#define EXIT_ACTION 0 +#define EXIT_FAIL 1 +#define EXIT_DISMISS 2 + +Display *display; +Window window; +int exit_code = EXIT_DISMISS; + +static void die(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(EXIT_FAIL); +} + +int get_max_len(char *string, XftFont *font, int max_text_width) +{ + int eol = strlen(string); + XGlyphInfo info; + XftTextExtentsUtf8(display, font, (FcChar8 *)string, eol, &info); + + if (info.width > max_text_width) + { + eol = max_text_width / font->max_advance_width; + info.width = 0; + + while (info.width < max_text_width) + { + eol++; + XftTextExtentsUtf8(display, font, (FcChar8 *)string, eol, &info); + } + + eol--; + } + + for (int i = 0; i < eol; i++) + if (string[i] == '\n') + { + string[i] = ' '; + return ++i; + } + + if (info.width <= max_text_width) + return eol; + + int temp = eol; + + while (string[eol] != ' ' && eol) + --eol; + + if (eol == 0) + return temp; + else + return ++eol; +} + +void expire(int sig) +{ + XEvent event; + event.type = ButtonPress; + event.xbutton.button = (sig == SIGUSR2) ? (ACTION_BUTTON) : (DISMISS_BUTTON); + XSendEvent(display, window, 0, 0, &event); + XFlush(display); +} + +void read_y_offset(unsigned int **offset, int *id) { + int shm_id = shmget(8432, sizeof(unsigned int), IPC_CREAT | 0660); + if (shm_id == -1) die("shmget failed"); + + *offset = (unsigned int *)shmat(shm_id, 0, 0); + if (*offset == (unsigned int *)-1) die("shmat failed\n"); + *id = shm_id; +} + +void free_y_offset(int id) { + shmctl(id, IPC_RMID, NULL); +} + +int main(int argc, char *argv[]) +{ + if (argc == 1) + die("Usage: %s body", argv[0]); + + struct sigaction act_expire, act_ignore; + + act_expire.sa_handler = expire; + act_expire.sa_flags = SA_RESTART; + sigemptyset(&act_expire.sa_mask); + + act_ignore.sa_handler = SIG_IGN; + act_ignore.sa_flags = 0; + sigemptyset(&act_ignore.sa_mask); + + sigaction(SIGALRM, &act_expire, 0); + sigaction(SIGTERM, &act_expire, 0); + sigaction(SIGINT, &act_expire, 0); + + sigaction(SIGUSR1, &act_ignore, 0); + sigaction(SIGUSR2, &act_ignore, 0); + + if (!(display = XOpenDisplay(0))) + die("Cannot open display"); + + int screen = DefaultScreen(display); + Visual *visual = DefaultVisual(display, screen); + Colormap colormap = DefaultColormap(display, screen); + + int screen_width = DisplayWidth(display, screen); + int screen_height = DisplayHeight(display, screen); + + XSetWindowAttributes attributes; + attributes.override_redirect = True; + XftColor color; + XftColorAllocName(display, visual, colormap, background_color, &color); + attributes.background_pixel = color.pixel; + XftColorAllocName(display, visual, colormap, border_color, &color); + attributes.border_pixel = color.pixel; + + int num_of_lines = 0; + int max_text_width = width - 2 * padding; + int lines_size = 5; + char **lines = malloc(lines_size * sizeof(char *)); + if (!lines) + die("malloc failed"); + + XftFont *font = XftFontOpenName(display, screen, font_pattern); + + for (int i = 1; i < argc; i++) + { + for (unsigned int eol = get_max_len(argv[i], font, max_text_width); eol; argv[i] += eol, num_of_lines++, eol = get_max_len(argv[i], font, max_text_width)) + { + if (lines_size <= num_of_lines) + { + lines = realloc(lines, (lines_size += 5) * sizeof(char *)); + if (!lines) + die("realloc failed"); + } + + lines[num_of_lines] = malloc((eol + 1) * sizeof(char)); + if (!lines[num_of_lines]) + die("malloc failed"); + + strncpy(lines[num_of_lines], argv[i], eol); + lines[num_of_lines][eol] = '\0'; + } + } + + int y_offset_id; + unsigned int *y_offset; + read_y_offset(&y_offset, &y_offset_id); + + unsigned int text_height = font->ascent - font->descent; + unsigned int height = (num_of_lines - 1) * line_spacing + num_of_lines * text_height + 2 * padding; + unsigned int x = pos_x; + unsigned int y = pos_y + *y_offset; + + unsigned int used_y_offset = (*y_offset) += height + padding; + + if (corner == TOP_RIGHT || corner == BOTTOM_RIGHT) + x = screen_width - width - border_size * 2 - x; + + if (corner == BOTTOM_LEFT || corner == BOTTOM_RIGHT) + y = screen_height - height - border_size * 2 - y; + + window = XCreateWindow(display, RootWindow(display, screen), x, y, width, height, border_size, DefaultDepth(display, screen), + CopyFromParent, visual, CWOverrideRedirect | CWBackPixel | CWBorderPixel, &attributes); + + XftDraw *draw = XftDrawCreate(display, window, visual, colormap); + XftColorAllocName(display, visual, colormap, font_color, &color); + + XSelectInput(display, window, ExposureMask | ButtonPress); + XMapWindow(display, window); + + sigaction(SIGUSR1, &act_expire, 0); + sigaction(SIGUSR2, &act_expire, 0); + + if (duration != 0) + alarm(duration); + + for (;;) + { + XEvent event; + XNextEvent(display, &event); + + if (event.type == Expose) + { + XClearWindow(display, window); + for (int i = 0; i < num_of_lines; i++) + XftDrawStringUtf8(draw, &color, font, padding, line_spacing * i + text_height * (i + 1) + padding, + (FcChar8 *)lines[i], strlen(lines[i])); + } + else if (event.type == ButtonPress) + { + if (event.xbutton.button == DISMISS_BUTTON) + break; + else if (event.xbutton.button == ACTION_BUTTON) + { + exit_code = EXIT_ACTION; + break; + } + } + } + + + for (int i = 0; i < num_of_lines; i++) + free(lines[i]); + + if (used_y_offset == *y_offset) free_y_offset(y_offset_id); + free(lines); + XftDrawDestroy(draw); + XftColorFree(display, visual, colormap, &color); + XftFontClose(display, font); + XCloseDisplay(display); + + return exit_code; +} diff --git a/user/.config/suckless/herbe/notify-send b/user/.config/suckless/herbe/notify-send new file mode 100755 index 000000000..3a3872b6d --- /dev/null +++ b/user/.config/suckless/herbe/notify-send @@ -0,0 +1,81 @@ +#!/bin/sh +# Shell script to redirect notify-send calls to herbe. The purpose is to ignore +# options passed to notify-send. +# +# Option parser generated by getoptions +# URL: https://github.com/ko1nksm/getoptions +# LICENSE: Creative Commons Zero v1.0 Universal + +usage() { + printf '%s\n' "${0##*/}: notify-send replacement for herbe" "accepts but ignores all notify-send options." +} + +REST='' +parse() { + OPTIND=$(($#+1)) + while [ $# -gt 0 ] && OPTARG=; do + case $1 in + --?*=*) OPTARG=$1; shift + eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'} + ;; + -[utich]?*) OPTARG=$1; shift + eval 'set -- "${OPTARG%"${OPTARG#??}"}" "${OPTARG#??}"' ${1+'"$@"'} + ;; + -[!-]?*) OPTARG=$1; shift + eval 'set -- "${OPTARG%"${OPTARG#??}"}" "-${OPTARG#??}"' ${1+'"$@"'} + OPTARG= ;; + esac + case $1 in + -u | --urgency) + [ $# -le 1 ] && set -- "$1" required && break + OPTARG=$2 + _=$OPTARG + shift ;; + -t | --expire-time) + [ $# -le 1 ] && set -- "$1" required && break + OPTARG=$2 + _=$OPTARG + shift ;; + -i | --icon) + [ $# -le 1 ] && set -- "$1" required && break + OPTARG=$2 + _=$OPTARG + shift ;; + -c | --category) + [ $# -le 1 ] && set -- "$1" required && break + OPTARG=$2 + _=$OPTARG + shift ;; + -h | --hint) + [ $# -le 1 ] && set -- "$1" required && break + OPTARG=$2 + _=$OPTARG + shift ;; + -? | --help) + usage + exit 0 ;; + --) shift + while [ $# -gt 0 ]; do + REST="${REST} \"\${$(($OPTIND-$#))}\"" + shift + done + break ;; + [-]?*) set -- "$1" unknown && break ;; + *) REST="${REST} \"\${$(($OPTIND-$#))}\"" + esac + shift + done + [ $# -eq 0 ] && return 0 + case $2 in + unknown) echo "unrecognized option '$1'" ;; + noarg) echo "option '$1' doesn't allow an argument" ;; + required) echo "option '$1' requires an argument" ;; + pattern) echo "option '$1' does not match the pattern ($3)" ;; + *) echo "option '$1' validation error: $2" + esac >&2 + exit 1 +} + +parse "$@" +eval set -- "$REST" +herbe "$@" & diff --git a/user/.config/suckless/herbe/patches/19.diff b/user/.config/suckless/herbe/patches/19.diff new file mode 100644 index 000000000..2ece3fbd7 --- /dev/null +++ b/user/.config/suckless/herbe/patches/19.diff @@ -0,0 +1,100 @@ +diff --git a/herbe.c b/herbe.c +index 51d3990..8bfdbc1 100644 +--- a/herbe.c ++++ b/herbe.c +@@ -7,7 +7,8 @@ + #include + #include + #include +-#include ++#include ++#include + + #include "config.h" + +@@ -79,13 +80,23 @@ void expire(int sig) + XFlush(display); + } + ++void read_y_offset(unsigned int **offset, int *id) { ++ int shm_id = shmget(8432, sizeof(unsigned int), IPC_CREAT | 0660); ++ if (shm_id == -1) die("shmget failed"); ++ ++ *offset = (unsigned int *)shmat(shm_id, 0, 0); ++ if (*offset == (unsigned int *)-1) die("shmat failed\n"); ++ *id = shm_id; ++} ++ ++void free_y_offset(int id) { ++ shmctl(id, IPC_RMID, NULL); ++} ++ + int main(int argc, char *argv[]) + { + if (argc == 1) +- { +- sem_unlink("/herbe"); +- die("Usage: %s body", argv[0]); +- } ++ die("Usage: %s body", argv[0]); + + struct sigaction act_expire, act_ignore; + +@@ -151,16 +162,22 @@ int main(int argc, char *argv[]) + } + } + +- unsigned int x = pos_x; +- unsigned int y = pos_y; ++ int y_offset_id; ++ unsigned int *y_offset; ++ read_y_offset(&y_offset, &y_offset_id); ++ + unsigned int text_height = font->ascent - font->descent; + unsigned int height = (num_of_lines - 1) * line_spacing + num_of_lines * text_height + 2 * padding; ++ unsigned int x = pos_x; ++ unsigned int y = pos_y + *y_offset; ++ ++ unsigned int used_y_offset = (*y_offset) += height + padding; + + if (corner == TOP_RIGHT || corner == BOTTOM_RIGHT) +- x = screen_width - width - border_size * 2 - pos_x; ++ x = screen_width - width - border_size * 2 - x; + + if (corner == BOTTOM_LEFT || corner == BOTTOM_RIGHT) +- y = screen_height - height - border_size * 2 - pos_y; ++ y = screen_height - height - border_size * 2 - y; + + window = XCreateWindow(display, RootWindow(display, screen), x, y, width, height, border_size, DefaultDepth(display, screen), + CopyFromParent, visual, CWOverrideRedirect | CWBackPixel | CWBorderPixel, &attributes); +@@ -171,9 +188,6 @@ int main(int argc, char *argv[]) + XSelectInput(display, window, ExposureMask | ButtonPress); + XMapWindow(display, window); + +- sem_t *mutex = sem_open("/herbe", O_CREAT, 0644, 1); +- sem_wait(mutex); +- + sigaction(SIGUSR1, &act_expire, 0); + sigaction(SIGUSR2, &act_expire, 0); + +@@ -204,12 +218,11 @@ int main(int argc, char *argv[]) + } + } + +- sem_post(mutex); +- sem_close(mutex); + + for (int i = 0; i < num_of_lines; i++) + free(lines[i]); + ++ if (used_y_offset == *y_offset) free_y_offset(y_offset_id); + free(lines); + XftDrawDestroy(draw); + XftColorFree(display, visual, colormap, &color); +@@ -217,4 +230,4 @@ int main(int argc, char *argv[]) + XCloseDisplay(display); + + return exit_code; +-} +\ No newline at end of file ++} diff --git a/user/.config/suckless/slock/Makefile b/user/.config/suckless/slock/Makefile new file mode 100644 index 000000000..b20fd4ee6 --- /dev/null +++ b/user/.config/suckless/slock/Makefile @@ -0,0 +1,61 @@ +# slock - simple screen locker +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = slock.c ${COMPATSRC} +OBJ = ${SRC:.c=.o} + +all: options slock + +options: + @echo slock build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + @echo CC $< + @${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk arg.h util.h + +config.h: + @echo creating $@ from config.def.h + @cp config.def.h $@ + +slock: ${OBJ} + @echo CC -o $@ + @${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + @echo cleaning + @rm -f slock ${OBJ} slock-${VERSION}.tar.gz + +dist: clean + @echo creating dist tarball + @mkdir -p slock-${VERSION} + @cp -R LICENSE Makefile README slock.1 config.mk \ + ${SRC} config.def.h arg.h util.h slock-${VERSION} + @tar -cf slock-${VERSION}.tar slock-${VERSION} + @gzip slock-${VERSION}.tar + @rm -rf slock-${VERSION} + +install: all + @echo installing executable file to ${DESTDIR}${PREFIX}/bin + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f slock ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/slock + @chmod u+s ${DESTDIR}${PREFIX}/bin/slock + @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @sed "s/VERSION/${VERSION}/g" ${DESTDIR}${MANPREFIX}/man1/slock.1 + @chmod 644 ${DESTDIR}${MANPREFIX}/man1/slock.1 + +uninstall: + @echo removing executable file from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/slock + @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 + @rm -f ${DESTDIR}${MANPREFIX}/man1/slock.1 + +.PHONY: all options clean dist install uninstall diff --git a/user/.config/suckless/slock/arg.h b/user/.config/suckless/slock/arg.h new file mode 100644 index 000000000..0b23c53a9 --- /dev/null +++ b/user/.config/suckless/slock/arg.h @@ -0,0 +1,65 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define LNGARG() &argv[0][0] + +#endif diff --git a/user/.config/suckless/slock/config.def.h b/user/.config/suckless/slock/config.def.h new file mode 100644 index 000000000..1cd7372ac --- /dev/null +++ b/user/.config/suckless/slock/config.def.h @@ -0,0 +1,28 @@ +/* user and group to drop privileges to */ +static const char *user = "drk"; +static const char *group = "drk"; + +static const char *colorname[NUMCOLS] = { + [INIT] = "#1d2021", /* after initialization */ + [INPUT] = "#076678", /* during input */ + [FAILED] = "#cc241d", /* wrong password */ + [PAM] = "#32302f", /* waiting for PAM */ +}; + +/* treat a cleared input like a wrong password (color) */ +static const int failonclear = 1; + +/* default message */ +static const char * message = "This device is locked\neven if you manage to unlock it\nyou wont know how to use it"; + +/* text color */ +static const char * text_color = "#d4c4a1"; + +/* text size (must be a valid size) */ +static const char * font_name = "mtx"; + +/* Background image path, should be available to the user above */ +static const char* background_image = ".config/suckless/slock/lockscreen.png"; + +/* PAM service that's used for authentication */ +static const char* pam_service = "system-local-login"; diff --git a/user/.config/suckless/slock/config.mk b/user/.config/suckless/slock/config.mk new file mode 100644 index 000000000..050bc5c71 --- /dev/null +++ b/user/.config/suckless/slock/config.mk @@ -0,0 +1,32 @@ +# slock version +VERSION = 1.5 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# includes and libs +INCS = -I. -I/usr/include -I${X11INC} +LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr -lImlib2 -lXinerama -lpam + +# flags +CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -DHAVE_SHADOW_H +CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} +LDFLAGS = -s ${LIBS} +COMPATSRC = explicit_bzero.c + +# On OpenBSD and Darwin remove -lcrypt from LIBS +#LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lXext -lXrandr +# On *BSD remove -DHAVE_SHADOW_H from CPPFLAGS +# On NetBSD add -D_NETBSD_SOURCE to CPPFLAGS +#CPPFLAGS = -DVERSION=\"${VERSION}\" -D_BSD_SOURCE -D_NETBSD_SOURCE +# On OpenBSD set COMPATSRC to empty +#COMPATSRC = + +# compiler and linker +CC = cc diff --git a/user/.config/suckless/slock/explicit_bzero.c b/user/.config/suckless/slock/explicit_bzero.c new file mode 100644 index 000000000..3e33ca85b --- /dev/null +++ b/user/.config/suckless/slock/explicit_bzero.c @@ -0,0 +1,19 @@ +/* $OpenBSD: explicit_bzero.c,v 1.3 2014/06/21 02:34:26 matthew Exp $ */ +/* + * Public domain. + * Written by Matthew Dempsky. + */ + +#include + +__attribute__((weak)) void +__explicit_bzero_hook(void *buf, size_t len) +{ +} + +void +explicit_bzero(void *buf, size_t len) +{ + memset(buf, 0, len); + __explicit_bzero_hook(buf, len); +} diff --git a/user/.config/suckless/slock/lockscreen.png b/user/.config/suckless/slock/lockscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..11143e954778c88b707f12b733ec39d46428011b GIT binary patch literal 15760 zcmd^mcTiJZ*Ka}>X(~u>7EnPzilKv|BA_6G^lAa6BfTX-R34S`2qGd~L`1rD0wjun zbOGsv1nIq#KuEaz;5&EbyYtPRx&Pgnk2y0WXYaH2TEA8I+9xk=8S1jL3b4XpFm}D` zS8l^#%xD;lL74^IfOS-*rNdwv=6Y8ynFbCo;pfaoettlAZ%D-HvV_YqvSc3OI?7@~ z_q02BpQLIJo{Z@IQI% zFDU;#F8?2mi4{0a*p&`#dVt85#o<|J^M6b~O5FhF%&J}jpl)YyG$h!Z^0gk_C_(MRbjy)Sz%`UFNlyF$LR z$IZZ(;Wlp!lYVyG0Uh_~%B59^f_^rC`*%K^c~Blee&(Y- z5gxPm54eYZpGg`JDg(2OHOR2!2*5KPE0Sqg6@v4_nb4RlZU^-DN0VZ>J6aOp)_WHY zIyVlW8dk~9OXyT)Ao$hZb{&-a6(HM(+?f+N5uh*aZ>By+95^9f9UiZ-3?9h)K6G>7 z;KSQ%hIEUH`heP-VDJf&ms|h5B;5aWCiLmFEWCmhsQ2ZmnEdaT(eNf2S*jm*BpA{= ziGWwIL5qJ(ONLo9fyEi)lCSWr$uMCJLckcCrUo3LC!A3fu zYnI{%zW^$%#xdkahvd*OHFg0wQ();Q*hw6W&g~@Nfu)%8FNk<>|D(`aO;s{FFOz)^ zZ)}LV(gBN!dcxe>C$q6&+a4<^exj!d8UXrnzUWiXfTx{$L28Hz3K9QJ=6N0K z_XBFbj(BaY-jT3Q_MBZYjB5(&V1YaUo%MM;$66BAl*P9{rt&fx5Ya|YWG$zT3U2Rs z#`S*!d}{M+q(2#(?`!dFC_s5+b9gO8P*}iXizkVi<2FLmi!-lI)oDQzX}8da_F&@8 zw>FbT4W5^rgS>4S)b`b9$w87g>Fp=!x7BS z*YCZ*mc}khTo^n78SxSec>{NA`P22u8w0%;J2|#Vu|;Dl)#o9Oe!x(oe8l8=;uQ~U z;mWslsuszyw0UFP9S*s6E=m2_#QMYi*~W~m4dRu0RfYxPoMn7p^ax41c$*D!NnYZ@ z%#25wD7nce#KV}sF(xQ?3OKiJ=s_jCXyRRt7 z9Q!cct;vEm3igSU-$SG0Y?< z&FH!=X4{?TA*icORd?5YF!!{3b{Z0;3|6$)Hs0EHVPV2&q|{LaUg;DPI?$8s8N9jy z`5YseaL7!u2(w$I?TX8OnrD<(Gqf>chu`(O%K)G|&Ay7AGO=YUv=^zb7}WY#&4|}w zOOr4^(FRNBhXW~w==X96JQ3DZ`W@eA8@JO})PjQ~i;n<@c%z7mHTktb6&VrP>gVpe+JojoXJo zh`LnfRj*4~Hfpt67-%kN{$R&BY_zG4v%Us_+H+*JVa>FZYLqm3J%FP*LlC*pMPGXW z*aP-eYR%^)JGIE$41uJQ&-B}ewXl0~TYgUuo~(rtR&?Gk+`)EU)wQQEt~cK^MzQ(0 z>O4Ls2%%|7KRSzLS)lQ+1!FarvpH6CoZ*@;qaeq%dB4BzKZZ)^8zRE?LvPG?J$&bW z-TuT$C>OY+)r`CXf*Xr`_@-ujUllVgNeu`3dwU&taH}A3ie|oHw}sE%yeqo80zZpK z2@o5uLqu!gg<}<*-pl1;^A#&6=6C~GIR}}A>^0a|pFk%1f<6>`EU>W4@t3(8DL~p( zOS&L*XmD`Q!a5~6_{MfStwYB&d5-hD>Z=3bXY>eW6b%MD{WyYDg1^%MNjAM8vql4N zy+4@i@{FxOIJ3w z9m0^i4L!YQC#l((OTPE!8aFrR27-%R6FSpm@GdM+Vpydbe&j{Sn<7>!Nh0L|q^5;R zqocvqNUaxCLR;=^L>KusX# zg|6%<*W<(dzUWR3^m;1CK%g^_y~TZdf@^&pcV{moE_tt&j;}Q6PdED4%3{<->IXwB zx^rpyV+a!+rmDG_(;(#;FL-AYW4C#~SR0w$IDgI9uc&!BYfAYg&3AuzZT{XQZ#p7a z17d}XSTRg}y3vRK>_0yDIB#;LXJ#W!Dzgh=W?fhBzu{A_U{JTWFh|}|hLGhVM!q^a zEUe*j#du3MF6mWAXNypI`9!3ly;9_o=kJ4P^CYm&`r&y!EBh4@W_yVwRSZgKbW}Oy zUH4xCx`Mo=LmVj40EyImKU!5R22tl>JD<0Fz;^f2sVIH!jugq0-KrKIS~~^E7$^WT zke~G>-@jjIzrtadH~n{}`)N}@E&hVuEhx^SVc2)G)YPcaj^#)ML)APqct85+eV%O<=N)m&dMEm zC)5`XsNVU?Tov>>3UB0W%nD^Uo1N4fxTvgRP(h5s$5OALA_eHwpmYy%LgR}~MD^Dr zfa_Kv;{4Qox)in&!KyzHM^_K8Za>{kmL7zPfb5Mb&Z?pJrfBm;9T2eAnF8<96Eo@D zMB_kklEU?^iG*)$8c`0uzcHMscgve-Us+r1g_A<>^MI1=jS`M>Pl#H0)p&2gUDDeE zWCB*~>D)OHX z^BX5+W1q7YmqM~|xlE)y@o;Z6R^0d#J1le#X9g9V;_B*X>V@YtyY{QbWk|{&RI1=P z+-uP!z~)(8X_gj?YtiwmKYKU^xVIG9)&!5<)w%RaqLJ+EJxnBhli$Oj*Cy@>hcY~QXOHZbt#%uDS3O0k;qFp{Jq2PSOEOEvfy zuRM#K_Hv!j6Pa{byk8(9OZOY|(%I<*k4suu>qMT8&_JE=gOWCgG|wGXFgfYrph{7D z6_GxIpdyv${7;YLQ`wCDO3T-t!$zQ;M-V9(Nhr=Kn7BpMhM``lk88!~t+WPY# zZZEh^sH^}Oh;^x0doe_y!> z73^Q<1zgxFuT zo|7HdBx5^@P)zD1jiHng{xmU0(Q-r$csiEDx z@RiZw<@8H=AJs6Z${LjSgge~-EQkz~KCn^0c@hz6N4`g~d+xE2Y7~p4j_y>Hl$V!p zZLCW7C9p5yv3(ID_F)q`f7!`ij+E1KL)6raaHKS*J`KR2R-SkCI%`zQ)vbva_bM~+ ziN)3w=ck9J(y&P&k>CLVr;4r>Wm_-ybbQ7>{}ys4|F^a_fp)Djw%NZhn2(* z1W=Mb484NK|L&_i4r28g9Bij2?2uX6YJODCmW(rkQ(@1YvlPp*$17d0gsXrIwsVuu zw@_1dV$L>aMwalFu(91s2!5%zn2KN?EVvFFNLHhQ=2owc+iAdC4!?e{G$D_`J(%6Mlf~HkaL4bt`M+{1o1gr7=|5uw%PT~RPvh^Q~O!M^7+xk z(15BAgIBNppxsZPMsu=#$XmbCjV@Eo{rMqce|IYtOKun|4v7pP9C4U|0`)#_Sytn=)eKyyP7qu<MD$v&aZQ2vDxt9aX{nle5fq=CQyWUQcR8kZ$B9$jjmjmHl%ISLpW^!r=a=Kpva)g$YsLKSX41eDL748yG0xSHu0LJ}9C!i_QbW}3`c4rx;b`-QR9xK9PI={25UKJXG;c$M-|4=_KaAe%lPI z8e)~iwL~3sSgt(KF0N<%O}H=ll>?ZB{smZ!tPbm{-{}ZCLC~_nO|q+w-c>o&>caFF z{2QaVXpLq^MzZ3;d{&2Bd72X8No``dl5`A={ziYs`Rm<9zvyat56nMkK*;pVoY_OT zhf?X8;tj~BKP% z={E|GQz(8CMi=U=nI&G_0dkg{T?D6Tng^p0QFMr5@WmT&$0;J#XOG_trH~wP5$g2a z>*K4vtUL+#ij#KYc8~)6IYcp~hx3GS)?kN5S=}JjHo2~{=I^=>!s~OL?l?%^W(9pH zCR{{mSt^5viabO`MIn&UY)N7hw%;m^i$^a~%g~ z(DlUq(|3Dv_5?3nGxi451bxkf%hS^gjXL;Z_|YPqCw=A=j-bF+JOR>$!yAFKx64xE zX%dKppZB1^_e`;pOl>hh$9GwBgyUPmN!iKx>CMm8qnn7f0-VPKz+mHg6TQA({>WL$ zoZ8_pRZL$3cwunKJ?a}*1ha1*0?EV&v{r}oD;)w=Tau=JLh5@ejn6f<4m!N5r!b-S zFa&ZDgDu~k>UL{%e7|9z9mrlRcNFlYJ&mU#JHHAusBrG&W- z=6-hCFZ|LoOngkYq;5Tob=yt7dD<1ZMT(gA( zJ#lmiMw?MS2m}&m8~NY#z4{n94Fw^GCp|V#HnY$`XX0g$*E=XblE_GmyZpYCM-L5` zkZ5F`oquqFK9uhu#2t&9W@}CTnDMptt=yMgIgs#z&$h~soO3~w@VsVGIcGT?P%^=g~u$6;{1{I~4aHPVC8}y5} zfw9eIdBFA_4jr&|`lg;!Yd0aP@*uowdp-ioX&D?PnrZaHnq?q!7wH*&!mVU|`jFSx zxFxQtX?h7944+-|)Dt63y@`AvNE~Fofri)Umow%Do4kh+OiRyu;dP-~dgUul8fRm# zLag%D`MSm!8gLUuU#EhcBj=K9=8!PS6%%4!0)6Ldf(2r4zzwI{F99JxXA3MTS!J_& zHO*YIehxBb(eK+l_p?l?H#<)r$n5|VN==l)0e_-XV+YojW>7ihS8<vT<|<9*KXgNL=|wqd$$QwyHkoZ(WM9&~K0Vh_}|_4$bHqI3tlD;;2X$?{Br z-O%|&)b&<1)bX}K!lafD=$y+5Y`vKx8@IytUmgf{iWxVh_AG13^FDMqaZP;bTvn`} zO5X`*Z*T&@VtU*EUe-r=T3aZ~qJkt2mTQz5^&u;cDG+~vV0f8Ae&0wO6Ipi}Ubyoc zSmp3(i|)&Ml;o5LA`St7eeHRT>)rmKx%U!cU32Bt4xMl?>a9Vj&Bf_h#+sw)o!SM5-Lz2h(Go=org*KL9O3*Hj{*n$IO9E*P!&# z&V@m@)v+tc0(-38Mon%Mi3!f)(u2OE1Q(IhL+3Gn z1mN@TyZAF%HIxHNO`^olca~vAPNKy3u^BLWI`zQ^)%3NeAn9HQezw^=8-Skq%IVbr zNWFItq`U)lx4w|&+ zJl-I1%|lvr2j5RRsD(2Rl|w@kyu^+k;87=m(%;#ja|{HDL9r0vQqkkGWEF4z%zIFB zY@=O#TkqyUB?UDlJjL5!Sb@QPonMK!hW8`w)IUzU1XK`OGus|Bk$*yzepX@EzK^{M zVM57TJXc!J$X+%C2+bjFz*A0|Z;^Ts<~gB*$PZe~Y!* z|17=w5_BMNf1GxIqB?Jk_m??)_&!zhXb5yP!e51g)%MRHOYL<^N~g0=U3B+!4;#U2 zRSUITdUH`6IJJH%9N&tjFxPZp%wA!+cJD9EO-1y@x*Bw5f~?DCI(&pYDp0mlPWePk zcrp_pOUXmoGgq1AA)=lD3r*_GHB_jKh)tihBTk%{AuXCHYI^sDgOc)%?#z(Kmh{zV z8H^@#TFrS66v`}X?WHTzcQAHE@&YC|*mt;GSMb@=O2cyA@Oo_42G$u%svMYh$?_=X z=?1edJ2P&&N!h;UsBy==coi(!Fiqv!pD7KP`SUO!tX`$nxcDBZpw85H9vIRyfSx=X{D%VxH<&Rf=?(Q6@(GlG9n z^DN!3ei2sRraGZ^9A9c}pRz!QKjB2H(HFkjv~&queEYh9j*0Awd8&)Y1}68-=hw|J zBi;HI!OH;|Z08z^KB%s|O(Pcv2GA<@)6_KTgWnmW>^p>Q!Y+csB7>~jP%yl8>%nff zpTw4@xVb~e*6w`pwS|J3McVMZ`r*ReKvub17NkTj4mDKm==swMjMo43)g-6J8ij-f zT}+0dmTPtf{XyPkEmTMv<-3?V(DD0x%NqPw7;g^V$yf9?iQR@U`b!L_o$1$^aU+PS zxm4M&qif(#>bGtm*q&N4&2Wo6AUhldOe!32q-|9Byr}ounx}-@b_iX{qSGWF6&TL(MYPL|KfG9y8b`G)WIVL#cam z7)qnuT@8CadxeK4o{2EhjM14I}$wmbrDGiy7^rKRgV=$&8bRh16L{pco4O zGg&PS3O&)rtsAb)2C~uJhkMpFLM!!JCg#z0ewOte`!XLj zC&+o-9(2q3Wra0I0cuZvS8h*|zXVI@I&Id!MJ}HkfY(zx?#l8{UHnK6{pI@3M;yf8 zoq>GwbYT${RML3rCEKv7pFZf7v3#V#4+4s-*{X)rFAqZ6WA*j0BZM z6u@qb5VpBD((gajH8x}3XDX8dA$rn(bma5Exgtre0r?aW&ABjzur0AlxF#lqN;Paj zk{+;~MKY?EfQ%V#(^P3!;2XA0-cj#0bKeYVuTCC-RgMHuKjo5qdxYS_mUDfZ#>XAR zjMfS^`QCN9J*ZQj_y1(%Twk%yPu%|VwI3J%o}dX&o#*&c6Ai7cmoHEOBl%dI@h;?)!pZ-m?>aOwTD%#^AeXZS^7+|h6E?^rL&GGX;2o3F~66+hF#0) zXh^=W<>HA;I#zA{vOK7APno|%hq&`wE5Xc-A5?DNLN2mIYaxZLLr2~+yVbOe+|J)$ zzLrLvut42IB}S6n?;j}TvU!l|CKai&>|l#vvI$ErH7U8s#6&Jo3rAS#)U)M*)Wk9v zKR2`6bfwclQ>6G@)%rIKPg?b* z4mupIp^i$`Oub)fC>24c81^;~hT_}2nb**v=~?e+nQ|CxH(7?L44aF6_p{11sxD#I zj}j9w_ez;Lb|b5f=GO0gO7FZwakeJ$^E!xKAyFKiJS2n)`a_#s(UP+pcv;1W9DHv~ z_zbdkYY6xC0ceXblAr5y>?K4j!~W ztL~?Aj@LF*xg)+MVS`v!d+;AhOdDu*)kxF`XI{ukh}MeM=((E>O zmH)d47@L%F&?TApa;6xHgKEpT5JNp;>TrhIY|2g-Gv7gFT;DKZjo+N@K+faP9Te$tpA zXQw|?@!)VKBOl96=VPn-XQqaKk}qc*+6fGZG~tt-s?f;eQUiP0aGb9QoVoN{g;Met zoC2>SbKvgsV!O7Tf%Bu}2}s{JLa0VUo&T)o$9=&?_h!(CBIZZgrW)I!j*~WexGtIf zv+c-PBPX`t3%=+7jkYyH=Dgqm@g}3pT%#b8yXXzGViN_S zc3{LCr8>ebmgx4`DzF8Z0n*7b!XxP)M;*kfV4}Y@45VbSW$0QagRUS4qI&qjydOKC6s9+P_L zD0qjZdfaJ3^eG$~M)FgOufV12u&o-SIs2_Sjr!Yc0V|h}Omq9r)0fa$FKiN5m!H`_ zuRyr$AlYo@U-2{WaS`Dapc*@e%X^K)*A*}Pvg=z~Z%AkbJ16hdttR-ze5J$+T*!DG zxek{2mQa_o!09>w%Cjh#J+m@{VThQSBJY^%KL4zzQj4L@_7X@+3@3j?D5tsG3fJ;0 z*Je(8WNhr}edSFIRRe*@kx{uFL+ugy$M5Kv_J+rMM7zRe!}+PP7?2h%;F_#odCa4I z;hG%F34{W%yhwiHWayXQuQCB_Qn>K6+|YM2xbbKLBI}F`lK^qjrK0^{>{1Q~tQaIP@J2QAm@hK0nL)*k#A@u~#V7zV_6R)UsYJ51<0t+_M z@$9QspR>vFf6Sh*dR(d5;kw%0jHXWsm0g0~6ntV_M_F0DyLzwOCiQ9gV^WQi4u{v{ zTW23Zn!I*?(V~oLtabHd?f3(VUl^+@uEn@KB|w2ij>`<4)4<+1gw~to{Kk$(1=y`! z9)uer2MjEMN}hDny1dR4Z-p+15{VUZT8nmetm?RyA)Ro8a&cgm#dJcG)s39-zeFip zh^nc3v5D8EBSnalwZ`8eSB>gKJ(bm2zLK67o)e+Q9K1B1U2tmWtk(-C2qWIueV6c$uu%8ULSJNOv8M?e?$^gVkYF5+ZBxf=!wQo0$u ztQuRD!*y7}%(`D%eQ0|d68`!Fj2Pg_>xW~S#8$iE01e5p^uSKNuKtk%FWhi@<**It z;OjAv%?A@^RS!{B%CbkyMbqkQJ$G1ECoJ!swq!kz({_N59j-lUE0Gzou+p(uI#8J! z50gwc*a9z3hI^GEcJ|onj$&jf6s0oSSUq1hs>=ZF>C~zS`kx1nY92e>MG|F9tn9zL z>TF6HDp&Rsl3`l;-cnHC2at}`qKupWX~tG` z(Abw6@iNa)*Zo9E&Ca0Hpxn1EZxiy+X3N=2(wrb&Yxj|=rVH0xTDeD7bSd#X zI#`t!AxUmb~U}7&^8WQ0k2G@!{{O zyOWmzmpDSyN9wOlx)1}u^KvYi29prXks+E2?xH}DJC7^w&d%kptTT{yZKSVI_ge0t zP>EB}HVIy>_RLE{x1b7Hvs7`RguR`zRZoq!u4Y~RS_-HhB|cMc{FA?{=xZRyfxa|%tp61CMa0Tyj1}P>{w_k#fEvWeP*USZ(nG}%lo9rD8Z)S+pgvN zJg{eF(NhXApE<4fn6gg84#i)fwiMulU+zu2u=lUHJbJ4Q94HQBdwtEllP+bnz4pe~ z-jMP-MvEsqE=$QmGL_bEupR{RFyE5j4ZUR>MLTv?tWAtMR`kQ0`swLt^~KFftG8X? zU?cNkz@iV0E+xgxzKc>Avu^kz89#TX+&(BsFyN282E?e?Qk46Y90&@D<+s*mj5Rvy z_ll0Qi%TY2k4pR5d1PC@=d6VYnJ+5G!t3p8Df5=AcH0Z-R=VjEj_ZOqrzV8elVxmC z0F$c}z8JDZA{<#QO!hYEX(G6ZB0O(i)v@Ym_LL))lKbSt{(xy$LX0|W;!Ny=-RZ2z zNqsBa=31Ip8`>H!2q8F=b6vA{e*p3S+2)(Y)-qM~Q7(T7yb!^(k57uts#HF_Uq_+J z;>fE_=#+MK>o{ml|5DAI^*-?d&*YHB&_9+=(vMLbxw*_ z&njFKF1}b@QX8`NJZL5&x>`9HLzGS2rN|Z8eVQ@{djF_FUl%ty*h0L8w7_fD7^=zd zg%ocEoeR$KRIv=Z^R*cZQpc&zzRV+dqqyq>Kbroav|{(SH6NGyd4I*0ua^is5MOk+ zP2@D{G(b>6Z6i0F{O>tLHHX(%mh;Ztjii)c0AOXMY8TP2+$b&QB zDGFz$o@w%fy>=k}({1|S*`XwGxTBb6N#sg$EBPU8~iuMa|@Z?Fmwxr%;ksj52E>Cl$&7RHj zco9gB54GsV&R#*sm!s)IkFKv)T5cQF0dIRK`g0Y-2d4>kaw*r|Q!W%mY2@zw26WIE zB@6F7KDgoAh(oJ49(s0MYFIS~RgGzbZHZcKyaQeB@qR&{ayK&I77eFayn#u6TVxz# zpLgG}L;!}xe!#l8C2%8|49XvK=6Mev*8)VH&^fHfh=*N^?S=|eclFWXF#P%NUq+9A z!u2{b1gfTVbf>FR!Lx%f-UouW<4#FAGLU`5Jat`j=I0&XYNXK^&yY}i<>8aI&vjD8W`LE=bVfaM!o)(K8r#KEJQNHup5dyuFHPk+)$wO)eZn(v!93z#WU%qWqM@8i& z=x48w!Hk^Dw^*31t<~j?Hm^yjw93OO>31~YKL`GV8QnG6GHz(p1^YsiC4!fyKSjO> zZWru*ZeygEa#!0X@1rP(=uMB^p*rAelSZZ6_kSx_`Xsc$y40gRo4PJ1Z&}rptaf?t z7ZlB;)`3JPbNAaD==Az+)))< zsNKNmgq0mD>986N&*uu=N^7-{|2r z(08rZRqOJIWUXq2(l7j2wqygb&U??j@~$()Af4^WQHwDZ_8Hp8jli!MZ<_GwZNlvS zSA;>K2sh}I9VrY@;o4cw^x|talezgAHtM}^-P$vY8X^H=!3qq#v z(eK{esm7~6N#fM};V@9pvuJTV!_w4~N1tvkMT#9irPN%^w!6L1V7**=2B*E_tHu0f z_Yt-EzQ|z|`{c#ZZOm7#7`$#t9Af5ciJ}`cjYmctDE{W2+)x=>IAOn+Cd21JyUWrM}s){0?GK|04LY zkatlN97XX?<(gv_Z@-*(WzN#l3YN^c ziBpBF?xu~eYW~fVB}W&9jUZBgYs1I-F7OTs+e5FqUAK^)JsOLXlc(y)-3c#R|I$Ed z{Fc4XH#{(Vxu-Mig(!`-VEUfRUbZe;j782N0{v4_2V-s#=>=`?@pWhYQ7h?mviE~` zF85A=I25Q8mu72WZ>!Sjc6Zz4pRTKF5wrrf#GnBU8i?uA0_xQB@jR9TK9`>7D%eCD zeH}yICxIwX+gNJw=1(=r=;}U|YZh0m;7~y8oL&DmI41~>s5%{2dz-(Nmy>wr$E}{g z8h-PC?f+`w-9KEsX9><`S9Z3ExdB4IN{%tBUl`fBnnk105u{b9`k=|Z7J6s%H_G$N z@5azy%_k6QSYcB_h{9@P6bLlh{td+8Qk}1#5Rt;n1dm9h^y3L+X#GxMscwuQ>2ULw zIUwM&S{i>Yr(WojR_&Vq4AR=|=ge(&?!dKq;aJqY5nupfK3zEY3{&A`jG68v$nSx7{b)ef z_XL@BjrSZMED${tOIAC^Q9efX)dDoa(ztQJ-qhVeT3YBo(pX?LYcUZc_?_b7bwXz> zwIM4$i&}^8=rf9T=8WFx&fcByi1J0`X2sv>2t|GYJM)krQFJOGDvB4qm6GQB*gi+SwErz{9VKr%7o=(_iS~L;FogJ zzSi@2pXj6(XE>8ssiRj7Q?t?zj{{8JV-&E030FM9tPm2HdFOL7quS@^_6#4%)2B`hL{+s9i-^V^~pzi)CK%VL3R|3DZ PhUr~3yi$1C;mQ93(3r<0 literal 0 HcmV?d00001 diff --git a/user/.config/suckless/slock/patchs/slock-background-image-20220318-1c5a538.diff b/user/.config/suckless/slock/patchs/slock-background-image-20220318-1c5a538.diff new file mode 100644 index 000000000..2d4caf453 --- /dev/null +++ b/user/.config/suckless/slock/patchs/slock-background-image-20220318-1c5a538.diff @@ -0,0 +1,149 @@ +From 1c5a5383a1cf3351fe9c80a21cfbc98c5ec4355d Mon Sep 17 00:00:00 2001 +From: Yan Doroshenko +Date: Fri, 18 Mar 2022 12:28:13 +0100 +Subject: [PATCH] Provide a way to set a background image + +--- + config.def.h | 5 ++++- + config.mk | 2 +- + slock.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- + 3 files changed, 52 insertions(+), 5 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 9855e21..eb88b3d 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -1,6 +1,6 @@ + /* user and group to drop privileges to */ + static const char *user = "nobody"; +-static const char *group = "nogroup"; ++static const char *group = "nobody"; + + static const char *colorname[NUMCOLS] = { + [INIT] = "black", /* after initialization */ +@@ -10,3 +10,6 @@ static const char *colorname[NUMCOLS] = { + + /* treat a cleared input like a wrong password (color) */ + static const int failonclear = 1; ++ ++/* Background image path, should be available to the user above */ ++static const char* background_image = ""; +diff --git a/config.mk b/config.mk +index 74429ae..987819e 100644 +--- a/config.mk ++++ b/config.mk +@@ -12,7 +12,7 @@ X11LIB = /usr/X11R6/lib + + # includes and libs + INCS = -I. -I/usr/include -I${X11INC} +-LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr ++LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr -lImlib2 + + # flags + CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -DHAVE_SHADOW_H +diff --git a/slock.c b/slock.c +index 5ae738c..345a279 100644 +--- a/slock.c ++++ b/slock.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + + #include "arg.h" + #include "util.h" +@@ -35,6 +36,7 @@ struct lock { + int screen; + Window root, win; + Pixmap pmap; ++ Pixmap bgmap; + unsigned long colors[NUMCOLS]; + }; + +@@ -46,6 +48,8 @@ struct xrandr { + + #include "config.h" + ++Imlib_Image image; ++ + static void + die(const char *errstr, ...) + { +@@ -190,9 +194,10 @@ readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, + color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT); + if (running && oldc != color) { + for (screen = 0; screen < nscreens; screen++) { +- XSetWindowBackground(dpy, +- locks[screen]->win, +- locks[screen]->colors[color]); ++ if (locks[screen]->bgmap) ++ XSetWindowBackgroundPixmap(dpy, locks[screen]->win, locks[screen]->bgmap); ++ else ++ XSetWindowBackground(dpy, locks[screen]->win, locks[screen]->colors[0]); + XClearWindow(dpy, locks[screen]->win); + } + oldc = color; +@@ -235,6 +240,17 @@ lockscreen(Display *dpy, struct xrandr *rr, int screen) + lock->screen = screen; + lock->root = RootWindow(dpy, lock->screen); + ++ if(image) ++ { ++ lock->bgmap = XCreatePixmap(dpy, lock->root, DisplayWidth(dpy, lock->screen), DisplayHeight(dpy, lock->screen), DefaultDepth(dpy, lock->screen)); ++ imlib_context_set_display(dpy); ++ imlib_context_set_visual(DefaultVisual(dpy, lock->screen)); ++ imlib_context_set_colormap(DefaultColormap(dpy, lock->screen)); ++ imlib_context_set_drawable(lock->bgmap); ++ imlib_render_image_on_drawable(0, 0); ++ imlib_free_image(); ++ } ++ + for (i = 0; i < NUMCOLS; i++) { + XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), + colorname[i], &color, &dummy); +@@ -251,6 +267,8 @@ lockscreen(Display *dpy, struct xrandr *rr, int screen) + CopyFromParent, + DefaultVisual(dpy, lock->screen), + CWOverrideRedirect | CWBackPixel, &wa); ++ if(lock->bgmap) ++ XSetWindowBackgroundPixmap(dpy, lock->win, lock->bgmap); + lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8); + invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap, + &color, &color, 0, 0); +@@ -355,6 +373,32 @@ main(int argc, char **argv) { + if (setuid(duid) < 0) + die("slock: setuid: %s\n", strerror(errno)); + ++ /* Load picture */ ++ Imlib_Image buffer = imlib_load_image(background_image); ++ imlib_context_set_image(buffer); ++ int background_image_width = imlib_image_get_width(); ++ int background_image_height = imlib_image_get_height(); ++ ++ /* Create an image to be rendered */ ++ Screen *scr = ScreenOfDisplay(dpy, DefaultScreen(dpy)); ++ image = imlib_create_image(scr->width, scr->height); ++ imlib_context_set_image(image); ++ ++ /* Fill the image for every X monitor */ ++ XRRMonitorInfo *monitors; ++ int number_of_monitors; ++ monitors = XRRGetMonitors(dpy, RootWindow(dpy, XScreenNumberOfScreen(scr)), True, &number_of_monitors); ++ ++ int i; ++ for (i = 0; i < number_of_monitors; i++) { ++ imlib_blend_image_onto_image(buffer, 0, 0, 0, background_image_width, background_image_height, monitors[i].x, monitors[i].y, monitors[i].width, monitors[i].height); ++ } ++ ++ /* Clean up */ ++ imlib_context_set_image(buffer); ++ imlib_free_image(); ++ imlib_context_set_image(image); ++ + /* check for Xrandr support */ + rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase); + +-- +2.35.1 + diff --git a/user/.config/suckless/slock/patchs/slock-colormessage-20200210-35633d4.diff b/user/.config/suckless/slock/patchs/slock-colormessage-20200210-35633d4.diff new file mode 100644 index 000000000..0ef08474a --- /dev/null +++ b/user/.config/suckless/slock/patchs/slock-colormessage-20200210-35633d4.diff @@ -0,0 +1,284 @@ +From 53ba5a8d3608ca9c9c406b12c51c2bfdfb3e01d3 Mon Sep 17 00:00:00 2001 +From: Guy Shefy +Date: Wed, 10 Feb 2021 00:17:46 +0200 +Subject: [PATCH] Add a message command with 24 bit color support + +--- + config.def.h | 9 +++ + config.mk | 2 +- + slock.1 | 7 +++ + slock.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++-- + 4 files changed, 167 insertions(+), 5 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 9855e21..c2a0ab2 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -10,3 +10,12 @@ static const char *colorname[NUMCOLS] = { + + /* treat a cleared input like a wrong password (color) */ + static const int failonclear = 1; ++ ++/* default message */ ++static const char * message = "Suckless: Software that sucks less."; ++ ++/* text color */ ++static const char * text_color = "#ffffff"; ++ ++/* text size (must be a valid size) */ ++static const char * font_name = "6x10"; +diff --git a/config.mk b/config.mk +index 74429ae..c4ccf66 100644 +--- a/config.mk ++++ b/config.mk +@@ -12,7 +12,7 @@ X11LIB = /usr/X11R6/lib + + # includes and libs + INCS = -I. -I/usr/include -I${X11INC} +-LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr ++LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr -lXinerama + + # flags + CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -DHAVE_SHADOW_H +diff --git a/slock.1 b/slock.1 +index 82cdcd6..946165f 100644 +--- a/slock.1 ++++ b/slock.1 +@@ -6,6 +6,8 @@ + .Sh SYNOPSIS + .Nm + .Op Fl v ++.Op Fl f ++.Op Fl m Ar message + .Op Ar cmd Op Ar arg ... + .Sh DESCRIPTION + .Nm +@@ -16,6 +18,11 @@ is executed after the screen has been locked. + .Bl -tag -width Ds + .It Fl v + Print version information to stdout and exit. ++.It Fl f ++List all valid X fonts and exit. ++.It Fl m Ar message ++Overrides default slock lock message. ++.TP + .El + .Sh SECURITY CONSIDERATIONS + To make sure a locked screen can not be bypassed by switching VTs +diff --git a/slock.c b/slock.c +index 5ae738c..b8b7fe4 100644 +--- a/slock.c ++++ b/slock.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -24,6 +25,9 @@ + + char *argv0; + ++/* global count to prevent repeated error messages */ ++int count_error = 0; ++ + enum { + INIT, + INPUT, +@@ -83,6 +87,132 @@ dontkillme(void) + } + #endif + ++static int ++readescapedint(const char *str, int *i) { ++ int n = 0; ++ if (str[*i]) ++ ++*i; ++ while(str[*i] && str[*i] != ';' && str[*i] != 'm') { ++ n = 10 * n + str[*i] - '0'; ++ ++*i; ++ } ++ return n; ++} ++ ++static void ++writemessage(Display *dpy, Window win, int screen) ++{ ++ int len, line_len, width, height, s_width, s_height, i, k, tab_size, r, g, b, escaped_int, curr_line_len; ++ XGCValues gr_values; ++ XFontStruct *fontinfo; ++ XColor color, dummy; ++ XineramaScreenInfo *xsi; ++ GC gc; ++ fontinfo = XLoadQueryFont(dpy, font_name); ++ ++ if (fontinfo == NULL) { ++ if (count_error == 0) { ++ fprintf(stderr, "slock: Unable to load font \"%s\"\n", font_name); ++ fprintf(stderr, "slock: Try listing fonts with 'slock -f'\n"); ++ count_error++; ++ } ++ return; ++ } ++ ++ tab_size = 8 * XTextWidth(fontinfo, " ", 1); ++ ++ XAllocNamedColor(dpy, DefaultColormap(dpy, screen), ++ text_color, &color, &dummy); ++ ++ gr_values.font = fontinfo->fid; ++ gr_values.foreground = color.pixel; ++ gc=XCreateGC(dpy,win,GCFont+GCForeground, &gr_values); ++ ++ /* To prevent "Uninitialized" warnings. */ ++ xsi = NULL; ++ ++ /* ++ * Start formatting and drawing text ++ */ ++ ++ len = strlen(message); ++ ++ /* Max max line length (cut at '\n') */ ++ line_len = curr_line_len = 0; ++ k = 0; ++ for (i = 0; i < len; i++) { ++ if (message[i] == '\n') { ++ curr_line_len = 0; ++ k++; ++ } else if (message[i] == 0x1b) { ++ while (i < len && message[i] != 'm') { ++ i++; ++ } ++ if (i == len) ++ die("slock: unclosed escape sequence\n"); ++ } else { ++ curr_line_len += XTextWidth(fontinfo, message + i, 1); ++ if (curr_line_len > line_len) ++ line_len = curr_line_len; ++ } ++ } ++ /* If there is only one line */ ++ if (line_len == 0) ++ line_len = len; ++ ++ if (XineramaIsActive(dpy)) { ++ xsi = XineramaQueryScreens(dpy, &i); ++ s_width = xsi[0].width; ++ s_height = xsi[0].height; ++ } else { ++ s_width = DisplayWidth(dpy, screen); ++ s_height = DisplayHeight(dpy, screen); ++ } ++ height = s_height*3/7 - (k*20)/3; ++ width = (s_width - line_len)/2; ++ ++ line_len = 0; ++ /* print the text while parsing 24 bit color ANSI escape codes*/ ++ for (i = k = 0; i < len; i++) { ++ switch (message[i]) { ++ case '\n': ++ line_len = 0; ++ while (message[i + 1] == '\t') { ++ line_len += tab_size; ++ i++; ++ } ++ k++; ++ break; ++ case 0x1b: ++ i++; ++ if (message[i] == '[') { ++ escaped_int = readescapedint(message, &i); ++ if (escaped_int == 39) ++ continue; ++ if (escaped_int != 38) ++ die("slock: unknown escape sequence%d\n", escaped_int); ++ if (readescapedint(message, &i) != 2) ++ die("slock: only 24 bit color supported\n"); ++ r = readescapedint(message, &i) & 0xff; ++ g = readescapedint(message, &i) & 0xff; ++ b = readescapedint(message, &i) & 0xff; ++ XSetForeground(dpy, gc, r << 16 | g << 8 | b); ++ } else ++ die("slock: unknown escape sequence\n"); ++ break; ++ default: ++ XDrawString(dpy, win, gc, width + line_len, height + 20 * k, message + i, 1); ++ line_len += XTextWidth(fontinfo, message + i, 1); ++ } ++ } ++ ++ /* xsi should not be NULL anyway if Xinerama is active, but to be safe */ ++ if (XineramaIsActive(dpy) && xsi != NULL) ++ XFree(xsi); ++} ++ ++ ++ + static const char * + gethash(void) + { +@@ -194,6 +324,7 @@ readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, + locks[screen]->win, + locks[screen]->colors[color]); + XClearWindow(dpy, locks[screen]->win); ++ writemessage(dpy, locks[screen]->win, screen); + } + oldc = color; + } +@@ -300,7 +431,7 @@ lockscreen(Display *dpy, struct xrandr *rr, int screen) + static void + usage(void) + { +- die("usage: slock [-v] [cmd [arg ...]]\n"); ++ die("usage: slock [-v] [-f] [-m message] [cmd [arg ...]]\n"); + } + + int +@@ -313,12 +444,25 @@ main(int argc, char **argv) { + gid_t dgid; + const char *hash; + Display *dpy; +- int s, nlocks, nscreens; ++ int i, s, nlocks, nscreens; ++ int count_fonts; ++ char **font_names; + + ARGBEGIN { + case 'v': + fprintf(stderr, "slock-"VERSION"\n"); + return 0; ++ case 'm': ++ message = EARGF(usage()); ++ break; ++ case 'f': ++ if (!(dpy = XOpenDisplay(NULL))) ++ die("slock: cannot open display\n"); ++ font_names = XListFonts(dpy, "*", 10000 /* list 10000 fonts*/, &count_fonts); ++ for (i=0; iwin, s); + nlocks++; +- else ++ } else { + break; ++ } + } + XSync(dpy, 0); + +-- +2.30.0 + diff --git a/user/.config/suckless/slock/patchs/slock-pam_auth-20190207-35633d4.diff b/user/.config/suckless/slock/patchs/slock-pam_auth-20190207-35633d4.diff new file mode 100644 index 000000000..136f4b57a --- /dev/null +++ b/user/.config/suckless/slock/patchs/slock-pam_auth-20190207-35633d4.diff @@ -0,0 +1,154 @@ +diff --git a/config.def.h b/config.def.h +index 9855e21..19e7f62 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -6,7 +6,11 @@ static const char *colorname[NUMCOLS] = { + [INIT] = "black", /* after initialization */ + [INPUT] = "#005577", /* during input */ + [FAILED] = "#CC3333", /* wrong password */ ++ [PAM] = "#9400D3", /* waiting for PAM */ + }; + + /* treat a cleared input like a wrong password (color) */ + static const int failonclear = 1; ++ ++/* PAM service that's used for authentication */ ++static const char* pam_service = "login"; +diff --git a/config.mk b/config.mk +index 74429ae..6e82074 100644 +--- a/config.mk ++++ b/config.mk +@@ -12,7 +12,7 @@ X11LIB = /usr/X11R6/lib + + # includes and libs + INCS = -I. -I/usr/include -I${X11INC} +-LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr ++LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr -lpam + + # flags + CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -DHAVE_SHADOW_H +diff --git a/slock.c b/slock.c +index 5ae738c..3a8da42 100644 +--- a/slock.c ++++ b/slock.c +@@ -18,16 +18,22 @@ + #include + #include + #include ++#include ++#include + + #include "arg.h" + #include "util.h" + + char *argv0; ++static int pam_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr); ++struct pam_conv pamc = {pam_conv, NULL}; ++char passwd[256]; + + enum { + INIT, + INPUT, + FAILED, ++ PAM, + NUMCOLS + }; + +@@ -57,6 +63,31 @@ die(const char *errstr, ...) + exit(1); + } + ++static int ++pam_conv(int num_msg, const struct pam_message **msg, ++ struct pam_response **resp, void *appdata_ptr) ++{ ++ int retval = PAM_CONV_ERR; ++ for(int i=0; imsg_style == PAM_PROMPT_ECHO_OFF && ++ strncmp(msg[i]->msg, "Password: ", 10) == 0) { ++ struct pam_response *resp_msg = malloc(sizeof(struct pam_response)); ++ if (!resp_msg) ++ die("malloc failed\n"); ++ char *password = malloc(strlen(passwd) + 1); ++ if (!password) ++ die("malloc failed\n"); ++ memset(password, 0, strlen(passwd) + 1); ++ strcpy(password, passwd); ++ resp_msg->resp_retcode = 0; ++ resp_msg->resp = password; ++ resp[i] = resp_msg; ++ retval = PAM_SUCCESS; ++ } ++ } ++ return retval; ++} ++ + #ifdef __linux__ + #include + #include +@@ -121,6 +152,8 @@ gethash(void) + } + #endif /* HAVE_SHADOW_H */ + ++ /* pam, store user name */ ++ hash = pw->pw_name; + return hash; + } + +@@ -129,11 +162,12 @@ readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, + const char *hash) + { + XRRScreenChangeNotifyEvent *rre; +- char buf[32], passwd[256], *inputhash; +- int num, screen, running, failure, oldc; ++ char buf[32]; ++ int num, screen, running, failure, oldc, retval; + unsigned int len, color; + KeySym ksym; + XEvent ev; ++ pam_handle_t *pamh; + + len = 0; + running = 1; +@@ -160,10 +194,26 @@ readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, + case XK_Return: + passwd[len] = '\0'; + errno = 0; +- if (!(inputhash = crypt(passwd, hash))) +- fprintf(stderr, "slock: crypt: %s\n", strerror(errno)); ++ retval = pam_start(pam_service, hash, &pamc, &pamh); ++ color = PAM; ++ for (screen = 0; screen < nscreens; screen++) { ++ XSetWindowBackground(dpy, locks[screen]->win, locks[screen]->colors[color]); ++ XClearWindow(dpy, locks[screen]->win); ++ XRaiseWindow(dpy, locks[screen]->win); ++ } ++ XSync(dpy, False); ++ ++ if (retval == PAM_SUCCESS) ++ retval = pam_authenticate(pamh, 0); ++ if (retval == PAM_SUCCESS) ++ retval = pam_acct_mgmt(pamh, 0); ++ ++ running = 1; ++ if (retval == PAM_SUCCESS) ++ running = 0; + else +- running = !!strcmp(inputhash, hash); ++ fprintf(stderr, "slock: %s\n", pam_strerror(pamh, retval)); ++ pam_end(pamh, retval); + if (running) { + XBell(dpy, 100); + failure = 1; +@@ -339,10 +389,9 @@ main(int argc, char **argv) { + dontkillme(); + #endif + ++ /* the contents of hash are used to transport the current user name */ + hash = gethash(); + errno = 0; +- if (!crypt("", hash)) +- die("slock: crypt: %s\n", strerror(errno)); + + if (!(dpy = XOpenDisplay(NULL))) + die("slock: cannot open display\n"); diff --git a/user/.config/suckless/slock/slock.1 b/user/.config/suckless/slock/slock.1 new file mode 100644 index 000000000..946165fa0 --- /dev/null +++ b/user/.config/suckless/slock/slock.1 @@ -0,0 +1,46 @@ +.Dd 2016-08-23 +.Dt SLOCK 1 +.Sh NAME +.Nm slock +.Nd simple X screen locker +.Sh SYNOPSIS +.Nm +.Op Fl v +.Op Fl f +.Op Fl m Ar message +.Op Ar cmd Op Ar arg ... +.Sh DESCRIPTION +.Nm +is a simple X screen locker. If provided, +.Ar cmd Op Ar arg ... +is executed after the screen has been locked. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl v +Print version information to stdout and exit. +.It Fl f +List all valid X fonts and exit. +.It Fl m Ar message +Overrides default slock lock message. +.TP +.El +.Sh SECURITY CONSIDERATIONS +To make sure a locked screen can not be bypassed by switching VTs +or killing the X server with Ctrl+Alt+Backspace, it is recommended +to disable both in +.Xr xorg.conf 5 +for maximum security: +.Bd -literal -offset left +Section "ServerFlags" + Option "DontVTSwitch" "True" + Option "DontZap" "True" +EndSection +.Ed +.Sh EXAMPLES +$ +.Nm +/usr/sbin/s2ram +.Sh CUSTOMIZATION +.Nm +can be customized by creating a custom config.h from config.def.h and +(re)compiling the source code. This keeps it fast, secure and simple. diff --git a/user/.config/suckless/slock/slock.c b/user/.config/suckless/slock/slock.c new file mode 100644 index 000000000..ab6040b43 --- /dev/null +++ b/user/.config/suckless/slock/slock.c @@ -0,0 +1,635 @@ +/* See LICENSE file for license details. */ +#define _XOPEN_SOURCE 500 +#if HAVE_SHADOW_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arg.h" +#include "util.h" + +char *argv0; + +static int pam_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr); +struct pam_conv pamc = {pam_conv, NULL}; +char passwd[256]; + +/* global count to prevent repeated error messages */ +int count_error = 0; + +enum { + INIT, + INPUT, + FAILED, + PAM, + NUMCOLS +}; + +struct lock { + int screen; + Window root, win; + Pixmap pmap; + Pixmap bgmap; + unsigned long colors[NUMCOLS]; +}; + +struct xrandr { + int active; + int evbase; + int errbase; +}; + +#include "config.h" + +Imlib_Image image; + +static void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +static int +pam_conv(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + int retval = PAM_CONV_ERR; + for(int i=0; imsg_style == PAM_PROMPT_ECHO_OFF && + strncmp(msg[i]->msg, "Password: ", 10) == 0) { + struct pam_response *resp_msg = malloc(sizeof(struct pam_response)); + if (!resp_msg) + die("malloc failed\n"); + char *password = malloc(strlen(passwd) + 1); + if (!password) + die("malloc failed\n"); + memset(password, 0, strlen(passwd) + 1); + strcpy(password, passwd); + resp_msg->resp_retcode = 0; + resp_msg->resp = password; + resp[i] = resp_msg; + retval = PAM_SUCCESS; + } + } + return retval; +} + +#ifdef __linux__ +#include +#include + +static void +dontkillme(void) +{ + FILE *f; + const char oomfile[] = "/proc/self/oom_score_adj"; + + if (!(f = fopen(oomfile, "w"))) { + if (errno == ENOENT) + return; + die("slock: fopen %s: %s\n", oomfile, strerror(errno)); + } + fprintf(f, "%d", OOM_SCORE_ADJ_MIN); + if (fclose(f)) { + if (errno == EACCES) + die("slock: unable to disable OOM killer. " + "Make sure to suid or sgid slock.\n"); + else + die("slock: fclose %s: %s\n", oomfile, strerror(errno)); + } +} +#endif + +static int +readescapedint(const char *str, int *i) { + int n = 0; + if (str[*i]) + ++*i; + while(str[*i] && str[*i] != ';' && str[*i] != 'm') { + n = 10 * n + str[*i] - '0'; + ++*i; + } + return n; +} + +static void +writemessage(Display *dpy, Window win, int screen) +{ + int len, line_len, width, height, s_width, s_height, i, k, tab_size, r, g, b, escaped_int, curr_line_len; + XGCValues gr_values; + XFontStruct *fontinfo; + XColor color, dummy; + XineramaScreenInfo *xsi; + GC gc; + fontinfo = XLoadQueryFont(dpy, font_name); + + if (fontinfo == NULL) { + if (count_error == 0) { + fprintf(stderr, "slock: Unable to load font \"%s\"\n", font_name); + fprintf(stderr, "slock: Try listing fonts with 'slock -f'\n"); + count_error++; + } + return; + } + + tab_size = 8 * XTextWidth(fontinfo, " ", 1); + + XAllocNamedColor(dpy, DefaultColormap(dpy, screen), + text_color, &color, &dummy); + + gr_values.font = fontinfo->fid; + gr_values.foreground = color.pixel; + gc=XCreateGC(dpy,win,GCFont+GCForeground, &gr_values); + + /* To prevent "Uninitialized" warnings. */ + xsi = NULL; + + /* + * Start formatting and drawing text + */ + + len = strlen(message); + + /* Max max line length (cut at '\n') */ + line_len = curr_line_len = 0; + k = 0; + for (i = 0; i < len; i++) { + if (message[i] == '\n') { + curr_line_len = 0; + k++; + } else if (message[i] == 0x1b) { + while (i < len && message[i] != 'm') { + i++; + } + if (i == len) + die("slock: unclosed escape sequence\n"); + } else { + curr_line_len += XTextWidth(fontinfo, message + i, 1); + if (curr_line_len > line_len) + line_len = curr_line_len; + } + } + /* If there is only one line */ + if (line_len == 0) + line_len = len; + + if (XineramaIsActive(dpy)) { + xsi = XineramaQueryScreens(dpy, &i); + s_width = xsi[0].width; + s_height = xsi[0].height; + } else { + s_width = DisplayWidth(dpy, screen); + s_height = DisplayHeight(dpy, screen); + } + height = s_height*3/7 - (k*20)/3; + width = (s_width - line_len)/2; + + line_len = 0; + /* print the text while parsing 24 bit color ANSI escape codes*/ + for (i = k = 0; i < len; i++) { + switch (message[i]) { + case '\n': + line_len = 0; + while (message[i + 1] == '\t') { + line_len += tab_size; + i++; + } + k++; + break; + case 0x1b: + i++; + if (message[i] == '[') { + escaped_int = readescapedint(message, &i); + if (escaped_int == 39) + continue; + if (escaped_int != 38) + die("slock: unknown escape sequence%d\n", escaped_int); + if (readescapedint(message, &i) != 2) + die("slock: only 24 bit color supported\n"); + r = readescapedint(message, &i) & 0xff; + g = readescapedint(message, &i) & 0xff; + b = readescapedint(message, &i) & 0xff; + XSetForeground(dpy, gc, r << 16 | g << 8 | b); + } else + die("slock: unknown escape sequence\n"); + break; + default: + XDrawString(dpy, win, gc, width + line_len, height + 20 * k, message + i, 1); + line_len += XTextWidth(fontinfo, message + i, 1); + } + } + + /* xsi should not be NULL anyway if Xinerama is active, but to be safe */ + if (XineramaIsActive(dpy) && xsi != NULL) + XFree(xsi); +} + + + +static const char * +gethash(void) +{ + const char *hash; + struct passwd *pw; + + /* Check if the current user has a password entry */ + errno = 0; + if (!(pw = getpwuid(getuid()))) { + if (errno) + die("slock: getpwuid: %s\n", strerror(errno)); + else + die("slock: cannot retrieve password entry\n"); + } + hash = pw->pw_passwd; + +#if HAVE_SHADOW_H + if (!strcmp(hash, "x")) { + struct spwd *sp; + if (!(sp = getspnam(pw->pw_name))) + die("slock: getspnam: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); + hash = sp->sp_pwdp; + } +#else + if (!strcmp(hash, "*")) { +#ifdef __OpenBSD__ + if (!(pw = getpwuid_shadow(getuid()))) + die("slock: getpwnam_shadow: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); + hash = pw->pw_passwd; +#else + die("slock: getpwuid: cannot retrieve shadow entry. " + "Make sure to suid or sgid slock.\n"); +#endif /* __OpenBSD__ */ + } +#endif /* HAVE_SHADOW_H */ + + /* pam, store user name */ + hash = pw->pw_name; + return hash; +} + +static void +readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, + const char *hash) +{ + XRRScreenChangeNotifyEvent *rre; + char buf[32]; + int num, screen, running, failure, oldc, retval; + unsigned int len, color; + KeySym ksym; + XEvent ev; + pam_handle_t *pamh; + + len = 0; + running = 1; + failure = 0; + oldc = INIT; + + while (running && !XNextEvent(dpy, &ev)) { + if (ev.type == KeyPress) { + explicit_bzero(&buf, sizeof(buf)); + num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0); + if (IsKeypadKey(ksym)) { + if (ksym == XK_KP_Enter) + ksym = XK_Return; + else if (ksym >= XK_KP_0 && ksym <= XK_KP_9) + ksym = (ksym - XK_KP_0) + XK_0; + } + if (IsFunctionKey(ksym) || + IsKeypadKey(ksym) || + IsMiscFunctionKey(ksym) || + IsPFKey(ksym) || + IsPrivateKeypadKey(ksym)) + continue; + switch (ksym) { + case XK_Return: + passwd[len] = '\0'; + errno = 0; + retval = pam_start(pam_service, hash, &pamc, &pamh); + color = PAM; + for (screen = 0; screen < nscreens; screen++) { + XSetWindowBackground(dpy, locks[screen]->win, locks[screen]->colors[color]); + XClearWindow(dpy, locks[screen]->win); + XRaiseWindow(dpy, locks[screen]->win); + } + XSync(dpy, False); + + if (retval == PAM_SUCCESS) + retval = pam_authenticate(pamh, 0); + if (retval == PAM_SUCCESS) + retval = pam_acct_mgmt(pamh, 0); + + running = 1; + if (retval == PAM_SUCCESS) + running = 0; + else + fprintf(stderr, "slock: %s\n", pam_strerror(pamh, retval)); + pam_end(pamh, retval); + if (running) { + XBell(dpy, 100); + failure = 1; + } + explicit_bzero(&passwd, sizeof(passwd)); + len = 0; + break; + case XK_Escape: + explicit_bzero(&passwd, sizeof(passwd)); + len = 0; + break; + case XK_BackSpace: + if (len) + passwd[--len] = '\0'; + break; + default: + if (num && !iscntrl((int)buf[0]) && + (len + num < sizeof(passwd))) { + memcpy(passwd + len, buf, num); + len += num; + } + break; + } + color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT); + if (running && oldc != color) { + for (screen = 0; screen < nscreens; screen++) { + if (locks[screen]->bgmap) + XSetWindowBackgroundPixmap(dpy, locks[screen]->win, locks[screen]->bgmap); + else + XSetWindowBackground(dpy, locks[screen]->win, locks[screen]->colors[0]); + XClearWindow(dpy, locks[screen]->win); + writemessage(dpy, locks[screen]->win, screen); + } + oldc = color; + } + } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) { + rre = (XRRScreenChangeNotifyEvent*)&ev; + for (screen = 0; screen < nscreens; screen++) { + if (locks[screen]->win == rre->window) { + if (rre->rotation == RR_Rotate_90 || + rre->rotation == RR_Rotate_270) + XResizeWindow(dpy, locks[screen]->win, + rre->height, rre->width); + else + XResizeWindow(dpy, locks[screen]->win, + rre->width, rre->height); + XClearWindow(dpy, locks[screen]->win); + break; + } + } + } else { + for (screen = 0; screen < nscreens; screen++) + XRaiseWindow(dpy, locks[screen]->win); + } + } +} + +static struct lock * +lockscreen(Display *dpy, struct xrandr *rr, int screen) +{ + char curs[] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, ptgrab, kbgrab; + struct lock *lock; + XColor color, dummy; + XSetWindowAttributes wa; + Cursor invisible; + + if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock)))) + return NULL; + + lock->screen = screen; + lock->root = RootWindow(dpy, lock->screen); + + if(image) + { + lock->bgmap = XCreatePixmap(dpy, lock->root, DisplayWidth(dpy, lock->screen), DisplayHeight(dpy, lock->screen), DefaultDepth(dpy, lock->screen)); + imlib_context_set_display(dpy); + imlib_context_set_visual(DefaultVisual(dpy, lock->screen)); + imlib_context_set_colormap(DefaultColormap(dpy, lock->screen)); + imlib_context_set_drawable(lock->bgmap); + imlib_render_image_on_drawable(0, 0); + imlib_free_image(); + } + + for (i = 0; i < NUMCOLS; i++) { + XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen), + colorname[i], &color, &dummy); + lock->colors[i] = color.pixel; + } + + /* init */ + wa.override_redirect = 1; + wa.background_pixel = lock->colors[INIT]; + lock->win = XCreateWindow(dpy, lock->root, 0, 0, + DisplayWidth(dpy, lock->screen), + DisplayHeight(dpy, lock->screen), + 0, DefaultDepth(dpy, lock->screen), + CopyFromParent, + DefaultVisual(dpy, lock->screen), + CWOverrideRedirect | CWBackPixel, &wa); + if(lock->bgmap) + XSetWindowBackgroundPixmap(dpy, lock->win, lock->bgmap); + lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8); + invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap, + &color, &color, 0, 0); + XDefineCursor(dpy, lock->win, invisible); + + /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */ + for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) { + if (ptgrab != GrabSuccess) { + ptgrab = XGrabPointer(dpy, lock->root, False, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask, GrabModeAsync, + GrabModeAsync, None, invisible, CurrentTime); + } + if (kbgrab != GrabSuccess) { + kbgrab = XGrabKeyboard(dpy, lock->root, True, + GrabModeAsync, GrabModeAsync, CurrentTime); + } + + /* input is grabbed: we can lock the screen */ + if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) { + XMapRaised(dpy, lock->win); + if (rr->active) + XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask); + + XSelectInput(dpy, lock->root, SubstructureNotifyMask); + return lock; + } + + /* retry on AlreadyGrabbed but fail on other errors */ + if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) || + (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess)) + break; + + usleep(100000); + } + + /* we couldn't grab all input: fail out */ + if (ptgrab != GrabSuccess) + fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n", + screen); + if (kbgrab != GrabSuccess) + fprintf(stderr, "slock: unable to grab keyboard for screen %d\n", + screen); + return NULL; +} + +static void +usage(void) +{ + die("usage: slock [-v] [-f] [-m message] [cmd [arg ...]]\n"); +} + +int +main(int argc, char **argv) { + struct xrandr rr; + struct lock **locks; + struct passwd *pwd; + struct group *grp; + uid_t duid; + gid_t dgid; + const char *hash; + Display *dpy; + int i, s, nlocks, nscreens; + int count_fonts; + char **font_names; + + ARGBEGIN { + case 'v': + fprintf(stderr, "slock-"VERSION"\n"); + return 0; + case 'm': + message = EARGF(usage()); + break; + case 'f': + if (!(dpy = XOpenDisplay(NULL))) + die("slock: cannot open display\n"); + font_names = XListFonts(dpy, "*", 10000 /* list 10000 fonts*/, &count_fonts); + for (i=0; ipw_uid; + errno = 0; + if (!(grp = getgrnam(group))) + die("slock: getgrnam %s: %s\n", group, + errno ? strerror(errno) : "group entry not found"); + dgid = grp->gr_gid; + +#ifdef __linux__ + dontkillme(); +#endif + + /* the contents of hash are used to transport the current user name */ + hash = gethash(); + errno = 0; + + if (!(dpy = XOpenDisplay(NULL))) + die("slock: cannot open display\n"); + + /* drop privileges */ + if (setgroups(0, NULL) < 0) + die("slock: setgroups: %s\n", strerror(errno)); + if (setgid(dgid) < 0) + die("slock: setgid: %s\n", strerror(errno)); + if (setuid(duid) < 0) + die("slock: setuid: %s\n", strerror(errno)); + + /* Load picture */ + Imlib_Image buffer = imlib_load_image(background_image); + imlib_context_set_image(buffer); + int background_image_width = imlib_image_get_width(); + int background_image_height = imlib_image_get_height(); + + /* Create an image to be rendered */ + Screen *scr = ScreenOfDisplay(dpy, DefaultScreen(dpy)); + image = imlib_create_image(scr->width, scr->height); + imlib_context_set_image(image); + + /* Fill the image for every X monitor */ + XRRMonitorInfo *monitors; + int number_of_monitors; + monitors = XRRGetMonitors(dpy, RootWindow(dpy, XScreenNumberOfScreen(scr)), True, &number_of_monitors); + + int e; + for (e = 0; e < number_of_monitors; e++) { + imlib_blend_image_onto_image(buffer, 0, 0, 0, background_image_width, background_image_height, monitors[e].x, monitors[e].y, monitors[e].width, monitors[e].height); + } + + /* Clean up */ + imlib_context_set_image(buffer); + imlib_free_image(); + imlib_context_set_image(image); + + /* check for Xrandr support */ + rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase); + + /* get number of screens in display "dpy" and blank them */ + nscreens = ScreenCount(dpy); + if (!(locks = calloc(nscreens, sizeof(struct lock *)))) + die("slock: out of memory\n"); + for (nlocks = 0, s = 0; s < nscreens; s++) { + if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL) { + writemessage(dpy, locks[s]->win, s); + nlocks++; + } else { + break; + } + } + XSync(dpy, 0); + + /* did we manage to lock everything? */ + if (nlocks != nscreens) + return 1; + + /* run post-lock command */ + if (argc > 0) { + switch (fork()) { + case -1: + die("slock: fork failed: %s\n", strerror(errno)); + case 0: + if (close(ConnectionNumber(dpy)) < 0) + die("slock: close: %s\n", strerror(errno)); + execvp(argv[0], argv); + fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno)); + _exit(1); + } + } + + /* everything is now blank. Wait for the correct password */ + readpw(dpy, &rr, locks, nscreens, hash); + + return 0; +} diff --git a/user/.config/suckless/slock/util.h b/user/.config/suckless/slock/util.h new file mode 100644 index 000000000..6f748b890 --- /dev/null +++ b/user/.config/suckless/slock/util.h @@ -0,0 +1,2 @@ +#undef explicit_bzero +void explicit_bzero(void *, size_t); diff --git a/user/.gtkrc-2.0 b/user/.gtkrc-2.0 index c551d6fa7..4f9218841 100644 --- a/user/.gtkrc-2.0 +++ b/user/.gtkrc-2.0 @@ -1,11 +1,7 @@ -# DO NOT EDIT! This file will be overwritten by LXAppearance. -# Any customization should be done in ~/.gtkrc-2.0.mine instead. - -include "/home/drk/.gtkrc-2.0.mine" -gtk-theme-name="Tokyonight-Dark-BL" -gtk-icon-theme-name="Papirus-Dark" +gtk-theme-name="gruvbox-dark-gtk" +gtk-icon-theme-name="gruvbox-dark-icons-gtk" gtk-font-name="mononoki Nerd Font 10" -gtk-cursor-theme-name="Simp1e-Tokyo-Night" +gtk-cursor-theme-name="volantes_cursors" gtk-cursor-theme-size=0 gtk-toolbar-style=GTK_TOOLBAR_BOTH gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR diff --git a/user/.icons/default/index.theme b/user/.icons/default/index.theme index 639d6cc3f..701315fa8 100644 --- a/user/.icons/default/index.theme +++ b/user/.icons/default/index.theme @@ -1,4 +1,4 @@ [Icon Theme] Name=Default Comment=Default Cursor Theme -Inherits=Simp1e-Gruvbox-Dark +Inherits=volantes_cursors diff --git a/user/.xinitrc b/user/.xinitrc index 978be5323..ecebd76de 100755 --- a/user/.xinitrc +++ b/user/.xinitrc @@ -33,10 +33,6 @@ fi export MOZ_USE_XINPUT2=1 export EDITOR="emacsclient -t -a ''" # $EDITOR use Emacs in terminal export VISUAL="emacsclient -c -a emacs" # $VISUAL use Emacs in GUI mode -export READER="zathura" # Zathura as the pdf viewer -export TERMINAL="alacritty" # Alacritty as the default terminal emulator -export BROWSER="firefox" # Firefox as the default web browser -export WM="awesome" # Awesomewm as the default Window Manager export XDG_DATA_HOME="${XDG_DATA_HOME:="$HOME/.local/share"}" export XDG_CACHE_HOME="${XDG_CACHE_HOME:="$HOME/.cache"}" export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:="$HOME/.config"}"