From 11cda0379398b75ea5e45337e52649193370b44f Mon Sep 17 00:00:00 2001 From: coast Date: Thu, 5 Jun 2025 09:30:06 +0330 Subject: [PATCH] new rice and stuffs --- .config/dunst/dunstrc | 48 + .config/picom/picom.conf | 117 + .config/picom/picom.conf~ | 107 + .emacs.d/init.el | 8 +- .suckless/dmenu/config.def.h | 34 +- .suckless/dmenu/config.h | 34 +- .suckless/dmenu/dmenu.1 | 10 +- .suckless/dmenu/dmenu.c | 144 +- .suckless/dwm/Makefile | 32 +- .suckless/dwm/README | 48 + .suckless/dwm/attachaside.diff | 93 + .suckless/dwm/config.def.h | 129 + .suckless/dwm/config.h | 481 +-- .suckless/dwm/config.h~ | 160 + .suckless/dwm/config.mk | 40 +- .suckless/dwm/drw.c | 239 +- .suckless/dwm/drw.h | 30 +- .suckless/dwm/drw.o | Bin 10368 -> 11296 bytes .suckless/dwm/dwm | Bin 100176 -> 74736 bytes .suckless/dwm/dwm.1 | 95 +- .suckless/dwm/dwm.c | 1199 ++---- .suckless/dwm/dwm.c.orig | 2278 ++++++++++ .suckless/dwm/dwm.c.rej | 20 + .suckless/dwm/dwm.o | Bin 100416 -> 62464 bytes .suckless/dwm/movestack.c | 48 + .../dwm-attachaside-20160718-56a31dc.diff | 92 + .../patches/dwm-bar-height-spacing-6.3.diff | 25 + .../dwm-barpadding-20211020-a786211.diff | 118 + .suckless/dwm/patches/dwm-fullgaps-6.4.diff | 94 + .../patches/dwm-notitle-20210715-138b405.diff | 81 + .../patches/dwm-restartsig-20180523-6.2.diff | 139 + .../dwm/patches/dwm-statusallmons-6.2.diff | 25 + .suckless/dwm/patches/dwm-warp-6.4.diff | 79 + .suckless/dwm/patches/dwm-xrdb-6.4.diff | 203 + .suckless/dwm/transient.c | 1 - .suckless/dwm/util.c | 35 +- .suckless/dwm/util.h | 12 +- .suckless/dwm/util.o | Bin 2216 -> 2400 bytes .suckless/slstatus/LICENSE | 5 +- .suckless/slstatus/components/battery.c | 8 +- .suckless/slstatus/components/battery.o | Bin 5152 -> 5160 bytes .suckless/slstatus/components/ip.c | 26 - .suckless/slstatus/components/ip.o | Bin 3104 -> 2552 bytes .suckless/slstatus/components/keymap.c | 4 +- .suckless/slstatus/components/keymap.o | Bin 4016 -> 4016 bytes .suckless/slstatus/components/pixVol.sh | 10 + .suckless/slstatus/components/wifi.c | 258 +- .suckless/slstatus/components/wifi.o | Bin 5920 -> 4320 bytes .suckless/slstatus/config.def.h | 17 +- .suckless/slstatus/config.h | 26 +- .suckless/slstatus/config.h~ | 77 + .suckless/slstatus/config.mk | 2 +- .suckless/slstatus/slstatus | Bin 31832 -> 31800 bytes .suckless/slstatus/slstatus.c | 1 - .suckless/slstatus/slstatus.h | 1 - .suckless/slstatus/slstatus.o | Bin 6768 -> 6720 bytes .suckless/st/LEGACY | 17 + .suckless/st/Makefile | 6 +- .suckless/st/README | 34 + .suckless/st/TODO | 28 + .suckless/st/boxdraw.c | 194 + .suckless/st/boxdraw.o | Bin 0 -> 7600 bytes .suckless/st/boxdraw_data.h | 214 + .suckless/st/config.def.h | 517 +++ .suckless/st/config.h | 610 +-- .suckless/st/config.mk | 9 +- .suckless/st/dmenu/LICENSE | 30 + .suckless/st/dmenu/Makefile | 58 + .suckless/st/dmenu/README | 24 + .suckless/st/dmenu/arg.h | 49 + .suckless/st/dmenu/config.def.h | 23 + .suckless/st/dmenu/config.h | 23 + .suckless/st/dmenu/config.mk | 32 + .suckless/st/dmenu/dmenu | Bin 0 -> 42928 bytes .suckless/st/dmenu/dmenu.1 | 194 + .suckless/st/dmenu/dmenu.c | 795 ++++ .suckless/st/dmenu/dmenu.o | Bin 0 -> 31968 bytes .suckless/st/dmenu/dmenu_path | 13 + .suckless/st/dmenu/dmenu_run | 2 + .suckless/st/dmenu/drw.c | 448 ++ .suckless/st/dmenu/drw.h | 58 + .suckless/st/dmenu/drw.o | Bin 0 -> 11296 bytes .suckless/st/dmenu/stest | Bin 0 -> 16408 bytes .suckless/st/dmenu/stest.1 | 90 + .suckless/st/dmenu/stest.c | 109 + .suckless/st/dmenu/stest.o | Bin 0 -> 5240 bytes .suckless/st/dmenu/util.c | 37 + .suckless/st/dmenu/util.h | 9 + .suckless/st/dmenu/util.o | Bin 0 -> 2400 bytes .suckless/st/dwm/LICENSE | 38 + .suckless/st/dwm/Makefile | 67 + .suckless/st/dwm/README | 48 + .suckless/st/dwm/README.md | 30 + .suckless/st/dwm/attachaside.diff | 93 + .suckless/st/dwm/config.def.h | 129 + .suckless/st/dwm/config.h | 405 ++ .suckless/st/dwm/config.h~ | 160 + .suckless/st/dwm/config.mk | 55 + .suckless/st/dwm/configure | 40 + .suckless/st/dwm/drw.c | 451 ++ .suckless/st/dwm/drw.h | 72 + .suckless/st/dwm/drw.o | Bin 0 -> 10368 bytes .suckless/st/dwm/dwm | Bin 0 -> 100176 bytes .suckless/st/dwm/dwm.1 | 187 + .suckless/st/dwm/dwm.c | 2619 +++++++++++ .suckless/st/dwm/dwm.c.orig | 2278 ++++++++++ .suckless/st/dwm/dwm.c.rej | 20 + .suckless/st/dwm/dwm.o | Bin 0 -> 100416 bytes .suckless/st/dwm/dwm.png | Bin 0 -> 373 bytes .suckless/st/dwm/mkconfig/config.mk.freebsd | 59 + .suckless/st/dwm/mkconfig/config.mk.linux | 55 + .suckless/st/dwm/mkconfig/config.mk.openbsd | 57 + .suckless/st/dwm/mkconfig/config.mk.solaris | 58 + .suckless/st/dwm/movestack.c | 48 + .suckless/st/dwm/patch/attachx.c | 20 + .suckless/st/dwm/patch/attachx.h | 2 + .suckless/st/dwm/patch/bar.c | 39 + .suckless/st/dwm/patch/bar.h | 2 + .suckless/st/dwm/patch/bar_alpha.c | 43 + .suckless/st/dwm/patch/bar_alpha.h | 4 + .suckless/st/dwm/patch/bar_indicators.c | 104 + .suckless/st/dwm/patch/bar_indicators.h | 21 + .suckless/st/dwm/patch/bar_ltsymbol.c | 18 + .suckless/st/dwm/patch/bar_ltsymbol.h | 4 + .suckless/st/dwm/patch/bar_status.c | 20 + .suckless/st/dwm/patch/bar_status.h | 4 + .suckless/st/dwm/patch/bar_tagicons.c | 9 + .suckless/st/dwm/patch/bar_tagicons.h | 8 + .suckless/st/dwm/patch/bar_tags.c | 81 + .suckless/st/dwm/patch/bar_tags.h | 4 + .suckless/st/dwm/patch/bar_wintitle.c | 48 + .suckless/st/dwm/patch/bar_wintitle.h | 4 + .suckless/st/dwm/patch/cool_autostart.c | 29 + .suckless/st/dwm/patch/cool_autostart.h | 2 + .../dwm/patch/dwm-alpha-20230401-348f655.diff | Bin 0 -> 1024 bytes .suckless/st/dwm/patch/dwmc | 130 + .suckless/st/dwm/patch/dwmc.c | 85 + .suckless/st/dwm/patch/dwmc.h | 14 + .suckless/st/dwm/patch/fullscreen.c | 15 + .suckless/st/dwm/patch/fullscreen.h | 2 + .suckless/st/dwm/patch/include.c | 27 + .suckless/st/dwm/patch/include.h | 26 + .suckless/st/dwm/patch/ipc/IPCClient.h | 62 + .suckless/st/dwm/patch/ipc/dwm-msg.c | 549 +++ .suckless/st/dwm/patch/ipc/yajl_dumps.h | 66 + .suckless/st/dwm/patch/layout_facts.c | 24 + .suckless/st/dwm/patch/layout_tile.c | 41 + .suckless/st/dwm/patch/layout_tile.h | 2 + .suckless/st/dwm/patch/moveresize.c | 65 + .suckless/st/dwm/patch/moveresize.h | 2 + .suckless/st/dwm/patch/movestack.c | 51 + .suckless/st/dwm/patch/movestack.h | 2 + .suckless/st/dwm/patch/scratchpad.c | 77 + .suckless/st/dwm/patch/scratchpad.h | 9 + .suckless/st/dwm/patch/swallow.c | 212 + .suckless/st/dwm/patch/swallow.h | 8 + .suckless/st/dwm/patch/togglefullscreen.c | 10 + .suckless/st/dwm/patch/togglefullscreen.h | 2 + .suckless/st/dwm/patch/vanitygaps.c | 177 + .suckless/st/dwm/patch/vanitygaps.h | 16 + .suckless/st/dwm/patch/xrdb.c | 73 + .suckless/st/dwm/patch/xrdb.h | 22 + .../dwm-attachaside-20160718-56a31dc.diff | 92 + .../patches/dwm-bar-height-spacing-6.3.diff | 25 + .../dwm-barpadding-20211020-a786211.diff | 118 + .../st/dwm/patches/dwm-fullgaps-6.4.diff | 94 + .../patches/dwm-notitle-20210715-138b405.diff | 81 + .../patches/dwm-restartsig-20180523-6.2.diff | 139 + .../st/dwm/patches/dwm-statusallmons-6.2.diff | 25 + .suckless/st/dwm/patches/dwm-warp-6.4.diff | 79 + .suckless/st/dwm/patches/dwm-xrdb-6.4.diff | 203 + .suckless/st/dwm/readme.dwm.txt | 48 + .suckless/st/dwm/transient.c | 43 + .suckless/st/dwm/util.c | 36 + .suckless/st/dwm/util.h | 19 + .suckless/st/dwm/util.o | Bin 0 -> 2216 bytes .suckless/st/graphics.c | 3812 +++++++++++++++++ .suckless/st/graphics.h | 107 + .suckless/st/graphics.o | Bin 0 -> 82328 bytes .suckless/st/icat-mini.sh | 800 ++++ .suckless/st/khash.h | 627 +++ .suckless/st/kvec.h | 90 + .suckless/st/rowcolumn_diacritics_helpers.c | 391 ++ .suckless/st/rowcolumn_diacritics_helpers.o | Bin 0 -> 16264 bytes .suckless/st/slstatus/LICENSE | 46 + .suckless/st/slstatus/Makefile | 69 + .suckless/st/slstatus/README | 65 + .suckless/st/slstatus/arg.h | 33 + .suckless/st/slstatus/components/battery.c | 247 ++ .suckless/st/slstatus/components/battery.o | Bin 0 -> 5152 bytes .suckless/st/slstatus/components/cat.c | 32 + .suckless/st/slstatus/components/cat.o | Bin 0 -> 2104 bytes .suckless/st/slstatus/components/cpu.c | 157 + .suckless/st/slstatus/components/cpu.o | Bin 0 -> 3112 bytes .suckless/st/slstatus/components/datetime.c | 20 + .suckless/st/slstatus/components/datetime.o | Bin 0 -> 1944 bytes .suckless/st/slstatus/components/disk.c | 59 + .suckless/st/slstatus/components/disk.o | Bin 0 -> 3120 bytes .suckless/st/slstatus/components/entropy.c | 29 + .suckless/st/slstatus/components/entropy.o | Bin 0 -> 1800 bytes .suckless/st/slstatus/components/hostname.c | 17 + .suckless/st/slstatus/components/hostname.o | Bin 0 -> 1656 bytes .suckless/st/slstatus/components/ip.c | 87 + .suckless/st/slstatus/components/ip.o | Bin 0 -> 3104 bytes .../st/slstatus/components/kernel_release.c | 19 + .../st/slstatus/components/kernel_release.o | Bin 0 -> 1832 bytes .../slstatus/components/keyboard_indicators.c | 50 + .../slstatus/components/keyboard_indicators.o | Bin 0 -> 2416 bytes .suckless/st/slstatus/components/keymap.c | 86 + .suckless/st/slstatus/components/keymap.o | Bin 0 -> 4016 bytes .suckless/st/slstatus/components/load_avg.c | 19 + .suckless/st/slstatus/components/load_avg.o | Bin 0 -> 1864 bytes .suckless/st/slstatus/components/netspeeds.c | 129 + .suckless/st/slstatus/components/netspeeds.o | Bin 0 -> 2760 bytes .suckless/st/slstatus/components/num_files.c | 32 + .suckless/st/slstatus/components/num_files.o | Bin 0 -> 2112 bytes .suckless/st/slstatus/components/pixVol.sh | 10 + .suckless/st/slstatus/components/ram.c | 212 + .suckless/st/slstatus/components/ram.o | Bin 0 -> 3072 bytes .../st/slstatus/components/run_command.c | 31 + .../st/slstatus/components/run_command.o | Bin 0 -> 2120 bytes .suckless/st/slstatus/components/swap.c | 274 ++ .suckless/st/slstatus/components/swap.o | Bin 0 -> 3880 bytes .../st/slstatus/components/temperature.c | 73 + .../st/slstatus/components/temperature.o | Bin 0 -> 1720 bytes .suckless/st/slstatus/components/uptime.c | 34 + .suckless/st/slstatus/components/uptime.o | Bin 0 -> 1968 bytes .suckless/st/slstatus/components/user.c | 33 + .suckless/st/slstatus/components/user.o | Bin 0 -> 2192 bytes .suckless/st/slstatus/components/volume.c | 219 + .suckless/st/slstatus/components/volume.o | Bin 0 -> 3584 bytes .suckless/st/slstatus/components/wifi.c | 413 ++ .suckless/st/slstatus/components/wifi.o | Bin 0 -> 5920 bytes .suckless/st/slstatus/config.def.h | 70 + .suckless/st/slstatus/config.h | 81 + .suckless/st/slstatus/config.h~ | 77 + .suckless/st/slstatus/config.mk | 22 + .suckless/st/slstatus/slstatus | Bin 0 -> 31832 bytes .suckless/st/slstatus/slstatus.1 | 47 + .suckless/st/slstatus/slstatus.c | 135 + .suckless/st/slstatus/slstatus.h | 85 + .suckless/st/slstatus/slstatus.o | Bin 0 -> 6768 bytes .suckless/st/slstatus/util.c | 141 + .suckless/st/slstatus/util.h | 16 + .suckless/st/slstatus/util.o | Bin 0 -> 5576 bytes .suckless/st/st | Bin 113712 -> 176672 bytes .suckless/st/st.1 | 2 +- .suckless/st/st.c | 687 ++- .suckless/st/st.h | 102 +- .suckless/st/st.info | 6 + .suckless/st/st.o | Bin 82592 -> 89504 bytes .suckless/st/win.h | 5 +- .suckless/st/x.c | 887 ++-- .suckless/st/x.o | Bin 86736 -> 87680 bytes .zshrc | 7 +- 255 files changed, 28640 insertions(+), 2614 deletions(-) create mode 100644 .config/dunst/dunstrc create mode 100644 .config/picom/picom.conf create mode 100644 .config/picom/picom.conf~ create mode 100644 .suckless/dwm/README create mode 100644 .suckless/dwm/attachaside.diff create mode 100644 .suckless/dwm/config.def.h create mode 100644 .suckless/dwm/config.h~ create mode 100644 .suckless/dwm/dwm.c.orig create mode 100644 .suckless/dwm/dwm.c.rej create mode 100644 .suckless/dwm/movestack.c create mode 100644 .suckless/dwm/patches/dwm-attachaside-20160718-56a31dc.diff create mode 100644 .suckless/dwm/patches/dwm-bar-height-spacing-6.3.diff create mode 100755 .suckless/dwm/patches/dwm-barpadding-20211020-a786211.diff create mode 100755 .suckless/dwm/patches/dwm-fullgaps-6.4.diff create mode 100755 .suckless/dwm/patches/dwm-notitle-20210715-138b405.diff create mode 100755 .suckless/dwm/patches/dwm-restartsig-20180523-6.2.diff create mode 100755 .suckless/dwm/patches/dwm-statusallmons-6.2.diff create mode 100755 .suckless/dwm/patches/dwm-warp-6.4.diff create mode 100755 .suckless/dwm/patches/dwm-xrdb-6.4.diff create mode 100644 .suckless/slstatus/components/pixVol.sh create mode 100644 .suckless/slstatus/config.h~ create mode 100644 .suckless/st/LEGACY create mode 100644 .suckless/st/README create mode 100644 .suckless/st/TODO create mode 100644 .suckless/st/boxdraw.c create mode 100644 .suckless/st/boxdraw.o create mode 100644 .suckless/st/boxdraw_data.h create mode 100644 .suckless/st/config.def.h create mode 100644 .suckless/st/dmenu/LICENSE create mode 100644 .suckless/st/dmenu/Makefile create mode 100644 .suckless/st/dmenu/README create mode 100644 .suckless/st/dmenu/arg.h create mode 100644 .suckless/st/dmenu/config.def.h create mode 100644 .suckless/st/dmenu/config.h create mode 100644 .suckless/st/dmenu/config.mk create mode 100755 .suckless/st/dmenu/dmenu create mode 100644 .suckless/st/dmenu/dmenu.1 create mode 100644 .suckless/st/dmenu/dmenu.c create mode 100644 .suckless/st/dmenu/dmenu.o create mode 100755 .suckless/st/dmenu/dmenu_path create mode 100755 .suckless/st/dmenu/dmenu_run create mode 100644 .suckless/st/dmenu/drw.c create mode 100644 .suckless/st/dmenu/drw.h create mode 100644 .suckless/st/dmenu/drw.o create mode 100755 .suckless/st/dmenu/stest create mode 100644 .suckless/st/dmenu/stest.1 create mode 100644 .suckless/st/dmenu/stest.c create mode 100644 .suckless/st/dmenu/stest.o create mode 100644 .suckless/st/dmenu/util.c create mode 100644 .suckless/st/dmenu/util.h create mode 100644 .suckless/st/dmenu/util.o create mode 100644 .suckless/st/dwm/LICENSE create mode 100644 .suckless/st/dwm/Makefile create mode 100644 .suckless/st/dwm/README create mode 100644 .suckless/st/dwm/README.md create mode 100644 .suckless/st/dwm/attachaside.diff create mode 100644 .suckless/st/dwm/config.def.h create mode 100644 .suckless/st/dwm/config.h create mode 100644 .suckless/st/dwm/config.h~ create mode 100644 .suckless/st/dwm/config.mk create mode 100644 .suckless/st/dwm/configure create mode 100644 .suckless/st/dwm/drw.c create mode 100644 .suckless/st/dwm/drw.h create mode 100644 .suckless/st/dwm/drw.o create mode 100755 .suckless/st/dwm/dwm create mode 100644 .suckless/st/dwm/dwm.1 create mode 100644 .suckless/st/dwm/dwm.c create mode 100644 .suckless/st/dwm/dwm.c.orig create mode 100644 .suckless/st/dwm/dwm.c.rej create mode 100644 .suckless/st/dwm/dwm.o create mode 100644 .suckless/st/dwm/dwm.png create mode 100644 .suckless/st/dwm/mkconfig/config.mk.freebsd create mode 100644 .suckless/st/dwm/mkconfig/config.mk.linux create mode 100644 .suckless/st/dwm/mkconfig/config.mk.openbsd create mode 100644 .suckless/st/dwm/mkconfig/config.mk.solaris create mode 100644 .suckless/st/dwm/movestack.c create mode 100644 .suckless/st/dwm/patch/attachx.c create mode 100644 .suckless/st/dwm/patch/attachx.h create mode 100644 .suckless/st/dwm/patch/bar.c create mode 100644 .suckless/st/dwm/patch/bar.h create mode 100644 .suckless/st/dwm/patch/bar_alpha.c create mode 100644 .suckless/st/dwm/patch/bar_alpha.h create mode 100644 .suckless/st/dwm/patch/bar_indicators.c create mode 100644 .suckless/st/dwm/patch/bar_indicators.h create mode 100644 .suckless/st/dwm/patch/bar_ltsymbol.c create mode 100644 .suckless/st/dwm/patch/bar_ltsymbol.h create mode 100644 .suckless/st/dwm/patch/bar_status.c create mode 100644 .suckless/st/dwm/patch/bar_status.h create mode 100644 .suckless/st/dwm/patch/bar_tagicons.c create mode 100644 .suckless/st/dwm/patch/bar_tagicons.h create mode 100644 .suckless/st/dwm/patch/bar_tags.c create mode 100644 .suckless/st/dwm/patch/bar_tags.h create mode 100644 .suckless/st/dwm/patch/bar_wintitle.c create mode 100644 .suckless/st/dwm/patch/bar_wintitle.h create mode 100644 .suckless/st/dwm/patch/cool_autostart.c create mode 100644 .suckless/st/dwm/patch/cool_autostart.h create mode 100644 .suckless/st/dwm/patch/dwm-alpha-20230401-348f655.diff create mode 100644 .suckless/st/dwm/patch/dwmc create mode 100644 .suckless/st/dwm/patch/dwmc.c create mode 100644 .suckless/st/dwm/patch/dwmc.h create mode 100644 .suckless/st/dwm/patch/fullscreen.c create mode 100644 .suckless/st/dwm/patch/fullscreen.h create mode 100644 .suckless/st/dwm/patch/include.c create mode 100644 .suckless/st/dwm/patch/include.h create mode 100644 .suckless/st/dwm/patch/ipc/IPCClient.h create mode 100644 .suckless/st/dwm/patch/ipc/dwm-msg.c create mode 100644 .suckless/st/dwm/patch/ipc/yajl_dumps.h create mode 100644 .suckless/st/dwm/patch/layout_facts.c create mode 100644 .suckless/st/dwm/patch/layout_tile.c create mode 100644 .suckless/st/dwm/patch/layout_tile.h create mode 100644 .suckless/st/dwm/patch/moveresize.c create mode 100644 .suckless/st/dwm/patch/moveresize.h create mode 100644 .suckless/st/dwm/patch/movestack.c create mode 100644 .suckless/st/dwm/patch/movestack.h create mode 100644 .suckless/st/dwm/patch/scratchpad.c create mode 100644 .suckless/st/dwm/patch/scratchpad.h create mode 100644 .suckless/st/dwm/patch/swallow.c create mode 100644 .suckless/st/dwm/patch/swallow.h create mode 100644 .suckless/st/dwm/patch/togglefullscreen.c create mode 100644 .suckless/st/dwm/patch/togglefullscreen.h create mode 100644 .suckless/st/dwm/patch/vanitygaps.c create mode 100644 .suckless/st/dwm/patch/vanitygaps.h create mode 100644 .suckless/st/dwm/patch/xrdb.c create mode 100644 .suckless/st/dwm/patch/xrdb.h create mode 100644 .suckless/st/dwm/patches/dwm-attachaside-20160718-56a31dc.diff create mode 100644 .suckless/st/dwm/patches/dwm-bar-height-spacing-6.3.diff create mode 100755 .suckless/st/dwm/patches/dwm-barpadding-20211020-a786211.diff create mode 100755 .suckless/st/dwm/patches/dwm-fullgaps-6.4.diff create mode 100755 .suckless/st/dwm/patches/dwm-notitle-20210715-138b405.diff create mode 100755 .suckless/st/dwm/patches/dwm-restartsig-20180523-6.2.diff create mode 100755 .suckless/st/dwm/patches/dwm-statusallmons-6.2.diff create mode 100755 .suckless/st/dwm/patches/dwm-warp-6.4.diff create mode 100755 .suckless/st/dwm/patches/dwm-xrdb-6.4.diff create mode 100644 .suckless/st/dwm/readme.dwm.txt create mode 100644 .suckless/st/dwm/transient.c create mode 100644 .suckless/st/dwm/util.c create mode 100644 .suckless/st/dwm/util.h create mode 100644 .suckless/st/dwm/util.o create mode 100644 .suckless/st/graphics.c create mode 100644 .suckless/st/graphics.h create mode 100644 .suckless/st/graphics.o create mode 100755 .suckless/st/icat-mini.sh create mode 100644 .suckless/st/khash.h create mode 100644 .suckless/st/kvec.h create mode 100644 .suckless/st/rowcolumn_diacritics_helpers.c create mode 100644 .suckless/st/rowcolumn_diacritics_helpers.o create mode 100644 .suckless/st/slstatus/LICENSE create mode 100644 .suckless/st/slstatus/Makefile create mode 100644 .suckless/st/slstatus/README create mode 100644 .suckless/st/slstatus/arg.h create mode 100644 .suckless/st/slstatus/components/battery.c create mode 100644 .suckless/st/slstatus/components/battery.o create mode 100644 .suckless/st/slstatus/components/cat.c create mode 100644 .suckless/st/slstatus/components/cat.o create mode 100644 .suckless/st/slstatus/components/cpu.c create mode 100644 .suckless/st/slstatus/components/cpu.o create mode 100644 .suckless/st/slstatus/components/datetime.c create mode 100644 .suckless/st/slstatus/components/datetime.o create mode 100644 .suckless/st/slstatus/components/disk.c create mode 100644 .suckless/st/slstatus/components/disk.o create mode 100644 .suckless/st/slstatus/components/entropy.c create mode 100644 .suckless/st/slstatus/components/entropy.o create mode 100644 .suckless/st/slstatus/components/hostname.c create mode 100644 .suckless/st/slstatus/components/hostname.o create mode 100644 .suckless/st/slstatus/components/ip.c create mode 100644 .suckless/st/slstatus/components/ip.o create mode 100644 .suckless/st/slstatus/components/kernel_release.c create mode 100644 .suckless/st/slstatus/components/kernel_release.o create mode 100644 .suckless/st/slstatus/components/keyboard_indicators.c create mode 100644 .suckless/st/slstatus/components/keyboard_indicators.o create mode 100644 .suckless/st/slstatus/components/keymap.c create mode 100644 .suckless/st/slstatus/components/keymap.o create mode 100644 .suckless/st/slstatus/components/load_avg.c create mode 100644 .suckless/st/slstatus/components/load_avg.o create mode 100644 .suckless/st/slstatus/components/netspeeds.c create mode 100644 .suckless/st/slstatus/components/netspeeds.o create mode 100644 .suckless/st/slstatus/components/num_files.c create mode 100644 .suckless/st/slstatus/components/num_files.o create mode 100644 .suckless/st/slstatus/components/pixVol.sh create mode 100644 .suckless/st/slstatus/components/ram.c create mode 100644 .suckless/st/slstatus/components/ram.o create mode 100644 .suckless/st/slstatus/components/run_command.c create mode 100644 .suckless/st/slstatus/components/run_command.o create mode 100644 .suckless/st/slstatus/components/swap.c create mode 100644 .suckless/st/slstatus/components/swap.o create mode 100644 .suckless/st/slstatus/components/temperature.c create mode 100644 .suckless/st/slstatus/components/temperature.o create mode 100644 .suckless/st/slstatus/components/uptime.c create mode 100644 .suckless/st/slstatus/components/uptime.o create mode 100644 .suckless/st/slstatus/components/user.c create mode 100644 .suckless/st/slstatus/components/user.o create mode 100644 .suckless/st/slstatus/components/volume.c create mode 100644 .suckless/st/slstatus/components/volume.o create mode 100644 .suckless/st/slstatus/components/wifi.c create mode 100644 .suckless/st/slstatus/components/wifi.o create mode 100644 .suckless/st/slstatus/config.def.h create mode 100644 .suckless/st/slstatus/config.h create mode 100644 .suckless/st/slstatus/config.h~ create mode 100644 .suckless/st/slstatus/config.mk create mode 100755 .suckless/st/slstatus/slstatus create mode 100644 .suckless/st/slstatus/slstatus.1 create mode 100644 .suckless/st/slstatus/slstatus.c create mode 100644 .suckless/st/slstatus/slstatus.h create mode 100644 .suckless/st/slstatus/slstatus.o create mode 100644 .suckless/st/slstatus/util.c create mode 100644 .suckless/st/slstatus/util.h create mode 100644 .suckless/st/slstatus/util.o diff --git a/.config/dunst/dunstrc b/.config/dunst/dunstrc new file mode 100644 index 0000000..9fc6e3d --- /dev/null +++ b/.config/dunst/dunstrc @@ -0,0 +1,48 @@ +[global] +# Basic settings +font = Fira Code 10 +geometry = "300x100-10+55" # 10px from right edge, 55px from top +separator_height = 2 +frame_width = 4 +corner_radius = 8 +grow_direction = up + +[urgency_low] +background = "#222222" # normbgcolor +foreground = "#bbbbbb" # normfgcolor +frame_color = "#444444" # normbordercolor +timeout = 5 +icon_position = left + +[urgency_normal] +background = "#722F37" # selbgcolor wine red yay! +foreground = "#eeeeee" # selfgcolor bright text +frame_color = "#722F37" # selbordercolor same wine red frame +timeout = 8 +icon_position = left + +[urgency_critical] +background = "#7B2D33" # slightly brighter wine red for critical +foreground = "#eeeeee" +frame_color = "#7B2D33" +timeout = 0 # stays until dismissed +icon_position = left + +[frame] +# subtle shadow for modern look +shadow = true +shadow_offset_x = -5 +shadow_offset_y = -5 +shadow_radius = 12 +shadow_opacity = 0.7 +shadow_color = "#000000" + +[format] +# tweak layout to be modern and clean +title = "%s" +body = "%s" + +[mouse] +# allow click to close +close_on_click = true + diff --git a/.config/picom/picom.conf b/.config/picom/picom.conf new file mode 100644 index 0000000..7356882 --- /dev/null +++ b/.config/picom/picom.conf @@ -0,0 +1,117 @@ +# Speedy controlled animations +animations = true; +animation-stiffness-in-tag = 120; +animation-stiffness-tag-change = 90.0; +animation-window-mass = 0.3; +animation-dampening = 5; +animation-clamping = false; + +# Zoom, but FAST +animation-for-open-window = "zoom"; +animation-for-unmap-window = "zoom"; +animation-for-transient-window = "zoom"; +animation-for-prev-tag = "zoom"; +animation-for-next-tag = "zoom"; +animation-for-workspace-switch-in = "zoom"; +animation-for-workspace-switch-out = "zoom"; + +animation-max-size = 20; + +# Fading transitions (FASTER) +enable-fading-prev-tag = true; +enable-fading-next-tag = true; +fading = true; +fade-in-step = 0.07; +fade-out-step = 0.06; +fade-delta = 5; + +# Shadow stuff +shadow = true; +shadow-radius = 12; +shadow-offset-x = -5; +shadow-offset-y = -5; +shadow-opacity = 0.7; +shadow-color = "#000000"; +shadow-exclude = [ + "class_g = 'slop'" +]; + +# Blur (still spicy) +blur-background = true; +blur-background-frame = true; +blur-method = "dual_kawase"; +blur-strength = 6; +blur-kern = "3x3box"; +blur-background-fixed = false; +blur-exclude = [ + "class_g = 'slop'", + "class_g = 'maim'", + "window_type = 'dock'" +]; + +# Corners +corner-radius = 10; +rounded-corners-exclude = [ + "class_g = 'slop'", + "class_g = 'maim'" +]; + +# Opacity rules +opacity-rule = [ + "93:class_g = 'discord'", + "93:class_g = 'spotify'", + "80:class_g = 'st'", + "80:class_g = 'dmenu'", + "85:class_g = 'dwm'", + "100:class_g = 'floorp'", + "100:class_g = 'firefox'", + "100:class_g = 'slop'" +]; + +# Focus and animation exclude +focus-exclude = [ + "class_g = 'slop'", + "class_g = 'maim'" +]; +animation-exclude = [ + "class_g = 'slop'" +]; + +# General stuff +daemon = false; +backend = "glx"; +dithered-present = false; +vsync = true; +mark-wmwin-focused = true; +mark-ovredir-focused = true; +detect-rounded-corners = true; +detect-client-opacity = true; +use-ewmh-active-win = true; +unredir-if-possible = false; +detect-transient = true; +glx-no-stencil = true; +use-damage = true; +xrender-sync-fence = true; +window-shader-fg = "default"; +transparent-clipping = false; +log-level = "warn"; + +# Wintypes (with fast popup animations) +wintypes: { + tooltip = { fade = true; shadow = true; opacity = 0.95; focus = true; animations = true; animation-for-open-window = "zoom"; }; + dock = { shadow = false; clip-shadow-above = true; }; + dnd = { shadow = false; }; + popup_menu = { opacity = 0.95; animations = true; shadow = true; animation-for-open-window = "zoom"; }; + dropdown_menu = { opacity = 0.95; animations = true; shadow = true; animation-for-open-window = "zoom"; }; + unknown = { fade = false; shadow = false; opacity = 1.0; focus = false; full-shadow = false; clip-shadow-above = false; animations = false; }; +}; + +# Global exclusion +blur-exclude = [ + "class_g = 'slop'", + "class_g = 'screenie'", + "window_type = 'utility'", + "window_type = 'notification'", + "_NET_WM_WINDOW_TYPE@:32a *= '_NET_WM_WINDOW_TYPE_DOCK'", + "name = 'slop'" +]; diff --git a/.config/picom/picom.conf~ b/.config/picom/picom.conf~ new file mode 100644 index 0000000..7ab9458 --- /dev/null +++ b/.config/picom/picom.conf~ @@ -0,0 +1,107 @@ +# Animation settings - controlled zoom +animations = true; +animation-stiffness-in-tag = 80; +animation-stiffness-tag-change = 35.0; +animation-window-mass = 0.6; +animation-dampening = 9; +animation-clamping = false; +# Animation styles with controlled zoom effect +animation-for-open-window = "zoom"; # Kept zoom for main windows +animation-for-unmap-window = "zoom"; +animation-for-transient-window = "zoom"; # Changed to squeeze for popups to limit screen space usage +animation-for-prev-tag = "zoom"; +animation-for-next-tag = "zoom"; +# Additional animation control +animation-for-workspace-switch-in = "zoom"; # Control workspace transitions +animation-for-workspace-switch-out = "zoom"; # Control workspace transitions +# Animation size limits (new parameters) +animation-max-size = 50; # Limit maximum animation expansion +# Tag transition fading +enable-fading-prev-tag = true; +enable-fading-next-tag = true; +# Shadow settings +shadow = true; +shadow-radius = 15; +shadow-offset-x = -5; +shadow-offset-y = -5; +shadow-opacity = 0.8; +shadow-color = "#000000" +shadow-exclude = [ + "class_g = 'slop'" +]; +# Fading settings +fading = true; +fade-in-step = 0.025; +fade-out-step = 0.015; +fade-delta = 15; +fade-exclude = [ + "class_g = 'slop'" +]; +# Focus settings +focus-exclude = [ + "class_g = 'slop'", + "class_g = 'maim'" +]; +# Opacity rules +opacity-rule = [ + "93:class_g = 'discord'", + "80:class_g = 'st'", + "80:class_g = 'dmenu'", + "85:class_g = 'dwm'", + "100:class_g = 'floorp'", + "100:class_g = 'slop'" +]; +# Corner settings +corner-radius = 12; +rounded-corners-exclude = [ + "class_g = 'slop'", + "class_g = 'maim'", +]; +# Blur settings +blur-background = true; +blur-background-frame = true; +blur-method = "dual_kawase"; +blur-strength = 6; +blur-kern = "3x3box"; +blur-background-fixed = false; +blur-exclude = [ + "class_g = 'slop'", + "class_g = 'maim'", +]; +# Animation exclude +animation-exclude = [ + "class_g = 'slop'" +]; +# General settings +daemon = false; +backend = "glx"; +dithered-present = false; +vsync = true; +mark-wmwin-focused = true; +mark-ovredir-focused = true; +detect-rounded-corners = true; +detect-client-opacity = true; +use-ewmh-active-win = true; +unredir-if-possible = false; +detect-transient = true; +glx-no-stencil = true; +use-damage = true; +xrender-sync-fence = true; +window-shader-fg = "default"; +transparent-clipping = false; +log-level = "warn"; +# Window type specific settings - special handling for popups +wintypes: { + tooltip = { fade = true; shadow = true; opacity = 0.95; focus = true; animations = true; animation-for-open-window = "squeeze"; }; + dock = { shadow = false; clip-shadow-above = true; }; + dnd = { shadow = false; }; + popup_menu = { opacity = 0.95; animations = true; shadow = true; animation-for-open-window = "squeeze"; }; + dropdown_menu = { opacity = 0.95; animations = true; shadow = true; animation-for-open-window = "squeeze"; }; + # Add slop to wintypes for specific handling + unknown = { fade = false; shadow = false; opacity = 1.0; focus = false; full-shadow = false; clip-shadow-above = false; animations = false; }; +}; + +# Global exclusion for slop +unredir-if-possible-exclude = [ + "class_g = 'slop'" +]; diff --git a/.emacs.d/init.el b/.emacs.d/init.el index 834e552..1ec38f8 100644 --- a/.emacs.d/init.el +++ b/.emacs.d/init.el @@ -96,10 +96,10 @@ (global-set-key (kbd "C-s") 'swiper) (global-set-key (kbd "C-r") 'swiper-backward) -;;(use-package evil -;; :ensure t -;; :config -;; (evil-mode 1)) +(use-package evil + :ensure t + :config + (evil-mode 1)) (use-package swiper) diff --git a/.suckless/dmenu/config.def.h b/.suckless/dmenu/config.def.h index 1edb647..fdee1d1 100644 --- a/.suckless/dmenu/config.def.h +++ b/.suckless/dmenu/config.def.h @@ -1,23 +1,33 @@ /* 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 topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +static int centered = 1; /* -c option; centers dmenu on screen */ +static int min_width = 80; /* minimum width when centered */ +static const float menu_height_ratio = + 2.0f; /* This is the ratio used in the original calculation */ /* -fn option overrides fonts[0]; default X11 font or font set */ -static const char *fonts[] = { - "monospace: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] = { "#bbbbbb", "#222222" }, - [SchemeSel] = { "#eeeeee", "#005577" }, - [SchemeOut] = { "#000000", "#00ffff" }, +static const char *fonts[] = {"monospace:size=10"}; +static const char *prompt = + NULL; /* -p option; prompt to the left of input field */ + +#include "/home/pixel/.cache/wal/colors-wal-dmenu.h" +// static const char *colors[SchemeLast][2] = { +/* fg bg */ +/* [SchemeNorm] = {"#bbbbbb", "#222222"}, + [SchemeSel] = {"#eeeeee", "#005577"}, + [SchemeOut] = {"#000000", "#00ffff"}, }; -/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ -static unsigned int lines = 0; +*/ +/* -l and -g options; controls number of lines and columns in grid if > 0 */ +static unsigned int lines = 9; +static unsigned int columns = 1; /* * Characters not considered part of a word while deleting words * for example: " /?\"&[]" */ static const char worddelimiters[] = " "; + +/* Size of the window border */ +static unsigned int border_width = 3; diff --git a/.suckless/dmenu/config.h b/.suckless/dmenu/config.h index 1edb647..fdee1d1 100644 --- a/.suckless/dmenu/config.h +++ b/.suckless/dmenu/config.h @@ -1,23 +1,33 @@ /* 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 topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +static int centered = 1; /* -c option; centers dmenu on screen */ +static int min_width = 80; /* minimum width when centered */ +static const float menu_height_ratio = + 2.0f; /* This is the ratio used in the original calculation */ /* -fn option overrides fonts[0]; default X11 font or font set */ -static const char *fonts[] = { - "monospace: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] = { "#bbbbbb", "#222222" }, - [SchemeSel] = { "#eeeeee", "#005577" }, - [SchemeOut] = { "#000000", "#00ffff" }, +static const char *fonts[] = {"monospace:size=10"}; +static const char *prompt = + NULL; /* -p option; prompt to the left of input field */ + +#include "/home/pixel/.cache/wal/colors-wal-dmenu.h" +// static const char *colors[SchemeLast][2] = { +/* fg bg */ +/* [SchemeNorm] = {"#bbbbbb", "#222222"}, + [SchemeSel] = {"#eeeeee", "#005577"}, + [SchemeOut] = {"#000000", "#00ffff"}, }; -/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ -static unsigned int lines = 0; +*/ +/* -l and -g options; controls number of lines and columns in grid if > 0 */ +static unsigned int lines = 9; +static unsigned int columns = 1; /* * Characters not considered part of a word while deleting words * for example: " /?\"&[]" */ static const char worddelimiters[] = " "; + +/* Size of the window border */ +static unsigned int border_width = 3; diff --git a/.suckless/dmenu/dmenu.1 b/.suckless/dmenu/dmenu.1 index 323f93c..57ea184 100644 --- a/.suckless/dmenu/dmenu.1 +++ b/.suckless/dmenu/dmenu.1 @@ -4,6 +4,8 @@ dmenu \- dynamic menu .SH SYNOPSIS .B dmenu .RB [ \-bfiv ] +.RB [ \-g +.IR columns ] .RB [ \-l .IR lines ] .RB [ \-m @@ -40,6 +42,9 @@ which lists programs in the user's $PATH and runs the result in their $SHELL. .B \-b dmenu appears at the bottom of the screen. .TP +.B \-c +dmenu appears centered on 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. @@ -47,8 +52,11 @@ is faster, but will lock up X until stdin reaches end\-of\-file. .B \-i dmenu matches menu items case insensitively. .TP +.BI \-g " columns" +dmenu lists items in a grid with the given number of columns. +.TP .BI \-l " lines" -dmenu lists items vertically, with the given number of lines. +dmenu lists items in a grid with the given number of lines. .TP .BI \-m " monitor" dmenu is displayed on the monitor number supplied. Monitor numbers are starting diff --git a/.suckless/dmenu/dmenu.c b/.suckless/dmenu/dmenu.c index fd49549..77f7c77 100644 --- a/.suckless/dmenu/dmenu.c +++ b/.suckless/dmenu/dmenu.c @@ -15,6 +15,7 @@ #include #endif #include +#include #include "drw.h" #include "util.h" @@ -52,6 +53,10 @@ static XIC xic; static Drw *drw; static Clr *scheme[SchemeLast]; +/* Temporary arrays to allow overriding xresources values */ +static char *colortemp[4]; +static char *tempfonts; + #include "config.h" static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; @@ -83,7 +88,7 @@ calcoffsets(void) int i, n; if (lines > 0) - n = lines * bh; + n = lines * columns * bh; else n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); /* calculate which items will begin the next page and previous page */ @@ -95,12 +100,21 @@ calcoffsets(void) break; } +static int +max_textw(void) +{ + int len = 0; + for (struct item *item = items; item && item->text; item++) + len = MAX(TEXTW(item->text), len); + return len; +} + static void cleanup(void) { size_t i; - XUngrabKeyboard(dpy, CurrentTime); + XUngrabKey(dpy, AnyKey, AnyModifier, root); for (i = 0; i < SchemeLast; i++) free(scheme[i]); for (i = 0; items && items[i].text; ++i) @@ -168,9 +182,15 @@ drawmenu(void) } if (lines > 0) { - /* draw vertical list */ - for (item = curr; item != next; item = item->right) - drawitem(item, x, y += bh, mw - x); + /* draw grid */ + int i = 0; + for (item = curr; item != next; item = item->right, i++) + drawitem( + item, + x + ((i / lines) * ((mw - x) / columns)), + y + (((i % lines) + 1) * bh), + (mw - x) / columns + ); } else if (matches) { /* draw horizontal list */ x += inputw; @@ -626,8 +646,13 @@ setup(void) int a, di, n, area = 0; #endif /* init appearance */ - for (j = 0; j < SchemeLast; j++) - scheme[j] = drw_scm_create(drw, colors[j], 2); + for (j = 0; j < SchemeLast; j++) { + scheme[j] = drw_scm_create(drw, (const char**)colors[j], 2); + } + for (j = 0; j < SchemeOut; ++j) { + for (i = 0; i < 2; ++i) + free(colors[j][i]); + } clip = XInternAtom(dpy, "CLIPBOARD", False); utf8 = XInternAtom(dpy, "UTF8_STRING", False); @@ -636,6 +661,7 @@ setup(void) bh = drw->fonts->h + 2; lines = MAX(lines, 0); mh = (lines + 1) * bh; + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; #ifdef XINERAMA i = 0; if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { @@ -662,9 +688,16 @@ setup(void) 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; + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); + x = info[i].x_org + ((info[i].width - mw) / 2); + y = info[i].y_org + ((info[i].height - mh) / menu_height_ratio); + } else { + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; + } + XFree(info); } else #endif @@ -672,9 +705,16 @@ setup(void) 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; + + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); + x = (wa.width - mw) / 2; + y = (wa.height - mh) / 2; + } else { + 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 */ @@ -684,9 +724,11 @@ setup(void) swa.override_redirect = True; swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; - win = XCreateWindow(dpy, root, x, y, mw, mh, 0, + win = XCreateWindow(dpy, root, x, y, mw, mh, border_width, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + if (border_width) + XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); XSetClassHint(dpy, win, &ch); /* input methods */ @@ -718,6 +760,41 @@ usage(void) " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); } +void +readxresources(void) { + XrmInitialize(); + + char* xrm; + if ((xrm = XResourceManagerString(drw->dpy))) { + char *type; + XrmDatabase xdb = XrmGetStringDatabase(xrm); + XrmValue xval; + + if (XrmGetResource(xdb, "dmenu.font", "*", &type, &xval)) + fonts[0] = strdup(xval.addr); + else + fonts[0] = strdup(fonts[0]); + if (XrmGetResource(xdb, "dmenu.background", "*", &type, &xval)) + colors[SchemeNorm][ColBg] = strdup(xval.addr); + else + colors[SchemeNorm][ColBg] = strdup(colors[SchemeNorm][ColBg]); + if (XrmGetResource(xdb, "dmenu.foreground", "*", &type, &xval)) + colors[SchemeNorm][ColFg] = strdup(xval.addr); + else + colors[SchemeNorm][ColFg] = strdup(colors[SchemeNorm][ColFg]); + if (XrmGetResource(xdb, "dmenu.selbackground", "*", &type, &xval)) + colors[SchemeSel][ColBg] = strdup(xval.addr); + else + colors[SchemeSel][ColBg] = strdup(colors[SchemeSel][ColBg]); + if (XrmGetResource(xdb, "dmenu.selforeground", "*", &type, &xval)) + colors[SchemeSel][ColFg] = strdup(xval.addr); + else + colors[SchemeSel][ColFg] = strdup(colors[SchemeSel][ColFg]); + + XrmDestroyDatabase(xdb); + } +} + int main(int argc, char *argv[]) { @@ -733,30 +810,38 @@ 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], "-c")) /* centers dmenu on screen */ + centered = 1; else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ fstrncmp = strncasecmp; fstrstr = cistrstr; } else if (i + 1 == argc) usage(); /* these options take one argument */ - else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ + else if (!strcmp(argv[i], "-g")) { /* number of columns in grid */ + columns = atoi(argv[++i]); + if (lines == 0) lines = 1; + } else if (!strcmp(argv[i], "-l")) { /* number of lines in grid */ lines = atoi(argv[++i]); - else if (!strcmp(argv[i], "-m")) + if (columns == 0) columns = 1; + } 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]; + tempfonts = argv[++i]; else if (!strcmp(argv[i], "-nb")) /* normal background color */ - colors[SchemeNorm][ColBg] = argv[++i]; + colortemp[0] = argv[++i]; else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ - colors[SchemeNorm][ColFg] = argv[++i]; + colortemp[1] = argv[++i]; else if (!strcmp(argv[i], "-sb")) /* selected background color */ - colors[SchemeSel][ColBg] = argv[++i]; + colortemp[2] = argv[++i]; else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ - colors[SchemeSel][ColFg] = argv[++i]; + colortemp[3] = argv[++i]; else if (!strcmp(argv[i], "-w")) /* embedding window id */ embed = argv[++i]; + else if (!strcmp(argv[i], "-bw")) + border_width = atoi(argv[++i]); /* border width */ else usage(); @@ -772,8 +857,23 @@ main(int argc, char *argv[]) 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))) + readxresources(); + /* Now we check whether to override xresources with commandline parameters */ + if ( tempfonts ) + fonts[0] = strdup(tempfonts); + if ( colortemp[0]) + colors[SchemeNorm][ColBg] = strdup(colortemp[0]); + if ( colortemp[1]) + colors[SchemeNorm][ColFg] = strdup(colortemp[1]); + if ( colortemp[2]) + colors[SchemeSel][ColBg] = strdup(colortemp[2]); + if ( colortemp[3]) + colors[SchemeSel][ColFg] = strdup(colortemp[3]); + + if (!drw_fontset_create(drw, (const char**)fonts, LENGTH(fonts))) die("no fonts could be loaded."); + + free(fonts[0]); lrpad = drw->fonts->h; #ifdef __OpenBSD__ diff --git a/.suckless/dwm/Makefile b/.suckless/dwm/Makefile index 088a4b9..ffa69b4 100644 --- a/.suckless/dwm/Makefile +++ b/.suckless/dwm/Makefile @@ -6,36 +6,21 @@ include config.mk SRC = drw.c dwm.c util.c OBJ = ${SRC:.c=.o} -# FreeBSD users, prefix all ifdef, else and endif statements with a . for this to work (e.g. .ifdef) - -ifdef YAJLLIBS -all: options dwm dwm-msg -else -all: options dwm -endif - -options: - @echo dwm build options: - @echo "CFLAGS = ${CFLAGS}" - @echo "LDFLAGS = ${LDFLAGS}" - @echo "CC = ${CC}" +all: dwm .c.o: ${CC} -c ${CFLAGS} $< ${OBJ}: config.h config.mk +config.h: + cp config.def.h $@ + dwm: ${OBJ} ${CC} -o $@ ${OBJ} ${LDFLAGS} -ifdef YAJLLIBS -dwm-msg: - ${CC} -o $@ patch/ipc/dwm-msg.c ${LDFLAGS} -endif - clean: rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz - rm -f dwm-msg dist: clean mkdir -p dwm-${VERSION} @@ -48,14 +33,7 @@ dist: clean install: all mkdir -p ${DESTDIR}${PREFIX}/bin cp -f dwm ${DESTDIR}${PREFIX}/bin -ifdef YAJLLIBS - cp -f dwm-msg ${DESTDIR}${PREFIX}/bin -endif - cp -f patch/dwmc ${DESTDIR}${PREFIX}/bin chmod 755 ${DESTDIR}${PREFIX}/bin/dwm -ifdef YAJLLIBS - chmod 755 ${DESTDIR}${PREFIX}/bin/dwm-msg -endif mkdir -p ${DESTDIR}${MANPREFIX}/man1 sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 @@ -64,4 +42,4 @@ uninstall: rm -f ${DESTDIR}${PREFIX}/bin/dwm\ ${DESTDIR}${MANPREFIX}/man1/dwm.1 -.PHONY: all options clean dist install uninstall +.PHONY: all clean dist install uninstall diff --git a/.suckless/dwm/README b/.suckless/dwm/README new file mode 100644 index 0000000..95d4fd0 --- /dev/null +++ b/.suckless/dwm/README @@ -0,0 +1,48 @@ +dwm - dynamic window manager +============================ +dwm is an extremely fast, small, and dynamic window manager for X. + + +Requirements +------------ +In order to build dwm you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dwm is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dwm (if +necessary as root): + + make clean install + + +Running dwm +----------- +Add the following line to your .xinitrc to start dwm using startx: + + exec dwm + +In order to connect dwm to a specific display, make sure that +the DISPLAY environment variable is set correctly, e.g.: + + DISPLAY=foo.bar:1 exec dwm + +(This will start dwm on display :1 of the host foo.bar.) + +In order to display status info in the bar, you can do something +like this in your .xinitrc: + + while xsetroot -name "`date` `uptime | sed 's/.*,//'`" + do + sleep 1 + done & + exec dwm + + +Configuration +------------- +The configuration of dwm is done by creating a custom config.h +and (re)compiling the source code. diff --git a/.suckless/dwm/attachaside.diff b/.suckless/dwm/attachaside.diff new file mode 100644 index 0000000..2f34ac2 --- /dev/null +++ b/.suckless/dwm/attachaside.diff @@ -0,0 +1,93 @@ +diff --git a/dwm.c b/dwm.c +index f1d86b2..8b04e0b 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -49,7 +49,8 @@ + #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 ISVISIBLEONTAG(C, T) ((C->tags & T)) ++#define ISVISIBLE(C) ISVISIBLEONTAG(C, 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) +@@ -147,6 +148,7 @@ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interac + static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); ++static void attachaside(Client *c); + static void attachstack(Client *c); + static void buttonpress(XEvent *e); + static void checkotherwm(void); +@@ -184,6 +186,7 @@ static void maprequest(XEvent *e); + static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); ++static Client *nexttagged(Client *c); + static Client *nexttiled(Client *c); + static void pop(Client *c); + static void propertynotify(XEvent *e); +@@ -408,6 +411,17 @@ attach(Client *c) + c->mon->clients = c; + } + ++void ++attachaside(Client *c) { ++ Client *at = nexttagged(c); ++ if(!at) { ++ attach(c); ++ return; ++ } ++ c->next = at->next; ++ at->next = c; ++} ++ + void + attachstack(Client *c) + { +@@ -1074,7 +1088,7 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); ++ attachaside(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1202,6 +1216,16 @@ movemouse(const Arg *arg) + } + } + ++Client * ++nexttagged(Client *c) { ++ Client *walked = c->mon->clients; ++ for(; ++ walked && (walked->isfloating || !ISVISIBLEONTAG(walked, c->tags)); ++ walked = walked->next ++ ); ++ return walked; ++} ++ + Client * + nexttiled(Client *c) + { +@@ -1427,7 +1451,7 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ attachaside(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1915,7 +1939,7 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ attachaside(c); + attachstack(c); + } + if (m == selmon) + diff --git a/.suckless/dwm/config.def.h b/.suckless/dwm/config.def.h new file mode 100644 index 0000000..ac4674b --- /dev/null +++ b/.suckless/dwm/config.def.h @@ -0,0 +1,129 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 2; /* border pixel of windows */ +static const unsigned int gappx = 14; /* gaps between windows */ +static const unsigned int snap = 3; /* snap pixel */ +static const int user_bh = 12; /* 2 is the default spacing around the bar's font */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const int vertpad = 10; /* vertical padding of bar */ +static const int sidepad = 16; /* horizontal padding of bar */ +static const char *fonts[] = {"CaskaydiaMonoNerdFont-Bold:size=9.2"}; +static const char dmenufont[] = "CaskaydiaMonoNerdFont-Bold:size=10.6"; +static char normbgcolor[] = "#222222"; +static char normbordercolor[] = "#444444"; +static char normfgcolor[] = "#bbbbbb"; +static char selfgcolor[] = "#eeeeee"; +static char selbordercolor[] = "#005577"; +static char selbgcolor[] = "#005577"; +static char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor }, +}; + +/* tagging */ +static const char *tags[] = { "","","","","", }; + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating monitor */ + { NULL, NULL, NULL, 0, 0, -1 }, +}; + +/* layout(s) */ +static const float mfact = 0.50; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + {"", tile}, /* first entry is default */ + {"󰭩", NULL}, /* no layout function means floating behavior */ + {"", monocle}, +}; + + + +/* 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 */ +static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ +static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbordercolor, "-sf", selfgcolor, NULL }; +static const char *termcmd[] = {"bash", "-c", "st -e bash & walrs -R -q ",NULL}; + +#include "movestack.c" +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_n, xrdb, {.v = NULL } }, + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + { MODKEY, XK_minus, setgaps, {.i = -1 } }, + { MODKEY, XK_equal, setgaps, {.i = +1 } }, + { MODKEY|ShiftMask, XK_equal, setgaps, {.i = 0 } }, + 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) + { MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { 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/.suckless/dwm/config.h b/.suckless/dwm/config.h index a2adaad..49b77c4 100644 --- a/.suckless/dwm/config.h +++ b/.suckless/dwm/config.h @@ -1,228 +1,82 @@ -/* - * - ** - ** All credits to suckless.org and Swindles McCoop - ** Coasteen's DWM configuration - ** https://www.github.com/coasteen/suckless/ - ** __ _ _ - ** ___ ___ _ __ / _(_) __ _ | |__ - ** / __/ _ \| '_ \| |_| |/ _` | | '_ \ - ** | (_| (_) | | | | _| | (_| |_| | | | - ** \___\___/|_| |_|_| |_|\__, (_)_| |_| - ** |___/ - ** - * - */ - -/* appearance */ -static const unsigned int borderpx = 3; /* border pixel of windows */ -static const unsigned int snap = 0; /* snap pixel */ -static const unsigned int barborderpx = 3; /* border pixel of bar */ -static const int swallowfloating = 1; /* 1 means swallow floating windows by default */ -static const unsigned int gappih = 10; /* horiz inner gap between windows */ -static const unsigned int gappiv = 10; /* vert inner gap between windows */ -static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ -static const unsigned int gappov = 30; /* vert outer gap between windows and screen edge */ -static const int smartgaps_fact = 1; /* gap factor when there is only one client; 0 = no gaps, 3 = 3x outer gaps */ -static const int showbar = 1; /* 0 means no bar */ -static const int topbar = 1; /* 0 means bottom bar */ -/* Status is to be shown on: -1 (all monitors), 0 (a specific monitor by index), 'A' (active monitor) */ -static const int statusmon = 'A'; - -/* Indicators: see patch/bar_indicators.h for options */ -static int tagindicatortype = INDICATOR_NONE; -static int tiledindicatortype = INDICATOR_NONE; -static int floatindicatortype = INDICATOR_NONE; -static const char *fonts[] = { "monospace:size=9" }; -static const char dmenufont[] = "monospacee:size=9"; - -static char c000000[] = "#000000"; // placeholder value - -static char normfgcolor[] = "#bbbbbb"; -static char normbgcolor[] = "#222222"; -static char normbordercolor[] = "#444444"; -static char normfloatcolor[] = "#db8fd9"; - -static char selfgcolor[] = "#eeeeee"; -static char selbgcolor[] = "#005577"; -static char selbordercolor[] = "#005577"; -static char selfloatcolor[] = "#005577"; - -static char titlenormfgcolor[] = "#bbbbbb"; -static char titlenormbgcolor[] = "#222222"; -static char titlenormbordercolor[] = "#444444"; -static char titlenormfloatcolor[] = "#db8fd9"; - -static char titleselfgcolor[] = "#eeeeee"; -static char titleselbgcolor[] = "#005577"; -static char titleselbordercolor[] = "#005577"; -static char titleselfloatcolor[] = "#005577"; - -static char tagsnormfgcolor[] = "#bbbbbb"; -static char tagsnormbgcolor[] = "#222222"; -static char tagsnormbordercolor[] = "#444444"; -static char tagsnormfloatcolor[] = "#db8fd9"; - -static char tagsselfgcolor[] = "#eeeeee"; -static char tagsselbgcolor[] = "#005577"; -static char tagsselbordercolor[] = "#005577"; -static char tagsselfloatcolor[] = "#005577"; - -static char hidnormfgcolor[] = "#005577"; -static char hidselfgcolor[] = "#227799"; -static char hidnormbgcolor[] = "#222222"; -static char hidselbgcolor[] = "#222222"; - -static char urgfgcolor[] = "#bbbbbb"; -static char urgbgcolor[] = "#222222"; -static char urgbordercolor[] = "#ff0000"; -static char urgfloatcolor[] = "#db8fd9"; +/* See LICENSE file for copyright and license details. */ -static const unsigned int baralpha = 0xd0; -static const unsigned int borderalpha = OPAQUE; -static const unsigned int alphas[][3] = { - /* fg bg border */ - [SchemeNorm] = { OPAQUE, baralpha, borderalpha }, - [SchemeSel] = { OPAQUE, baralpha, borderalpha }, - [SchemeTitleNorm] = { OPAQUE, baralpha, borderalpha }, - [SchemeTitleSel] = { OPAQUE, baralpha, borderalpha }, - [SchemeTagsNorm] = { OPAQUE, baralpha, borderalpha }, - [SchemeTagsSel] = { OPAQUE, baralpha, borderalpha }, - [SchemeHidNorm] = { OPAQUE, baralpha, borderalpha }, - [SchemeHidSel] = { OPAQUE, baralpha, borderalpha }, - [SchemeUrg] = { OPAQUE, baralpha, borderalpha }, -}; -static char *colors[][ColCount] = { - /* fg bg border float */ - [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor, normfloatcolor }, - [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor, selfloatcolor }, - [SchemeTitleNorm] = { titlenormfgcolor, titlenormbgcolor, titlenormbordercolor, titlenormfloatcolor }, - [SchemeTitleSel] = { titleselfgcolor, titleselbgcolor, titleselbordercolor, titleselfloatcolor }, - [SchemeTagsNorm] = { tagsnormfgcolor, tagsnormbgcolor, tagsnormbordercolor, tagsnormfloatcolor }, - [SchemeTagsSel] = { tagsselfgcolor, tagsselbgcolor, tagsselbordercolor, tagsselfloatcolor }, - [SchemeHidNorm] = { hidnormfgcolor, hidnormbgcolor, c000000, c000000 }, - [SchemeHidSel] = { hidselfgcolor, hidselbgcolor, c000000, c000000 }, - [SchemeUrg] = { urgfgcolor, urgbgcolor, urgbordercolor, urgfloatcolor }, -}; - - - -static const char *const autostart[] = { - //"sh", "-c", "$HOME/.xprofile", NULL, - NULL /* terminate */ -}; - -const char *spcmd1[] = {"st", NULL }; -static Sp scratchpads[] = { - /* name cmd */ - {"spterm", spcmd1}, -}; - -/* Tags - * In a traditional dwm the number of tags in use can be changed simply by changing the number - * of strings in the tags array. This build does things a bit different which has some added - * benefits. If you need to change the number of tags here then change the NUMTAGS macro in dwm.c. - * - * Examples: - * - * 1) static char *tagicons[][NUMTAGS*2] = { - * [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I" }, - * } - * - * 2) static char *tagicons[][1] = { - * [DEFAULT_TAGS] = { "•" }, - * } - * - * The first example would result in the tags on the first monitor to be 1 through 9, while the - * tags for the second monitor would be named A through I. A third monitor would start again at - * 1 through 9 while the tags on a fourth monitor would also be named A through I. Note the tags - * count of NUMTAGS*2 in the array initialiser which defines how many tag text / icon exists in - * the array. This can be changed to *3 to add separate icons for a third monitor. - * - * For the second example each tag would be represented as a bullet point. Both cases work the - * same from a technical standpoint - the icon index is derived from the tag index and the monitor - * index. If the icon index is is greater than the number of tag icons then it will wrap around - * until it an icon matches. Similarly if there are two tag icons then it would alternate between - * them. This works seamlessly with alternative tags and alttagsdecoration patches. - */ -static char *tagicons[][NUMTAGS] = { - [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, - [ALTERNATIVE_TAGS] = { "A", "B", "C", "D", "E", "F", "G", "H", "I" }, - [ALT_TAGS_DECORATION] = { "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" }, +/* appearance */ +static const unsigned int borderpx = 2; /* border pixel of windows */ +static const unsigned int gappx = 14; /* gaps between windows */ +static const unsigned int snap = 3; /* snap pixel */ +static const int user_bh = 12; /* 2 is the default spacing around the bar's font */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const int vertpad = 10; /* vertical padding of bar */ +static const int sidepad = 16; /* horizontal padding of bar */ +static const char *fonts[] = {"CaskaydiaMonoNerdFont-Bold:size=9.2"}; +static const char dmenufont[] = "CaskaydiaMonoNerdFont-Bold:size=10.6"; +static char normbgcolor[] = "#222222"; +static char normbordercolor[] = "#444444"; +static char normfgcolor[] = "#bbbbbb"; +static char selfgcolor[] = "#eeeeee"; +static char selbordercolor[] = "#722F37"; +static char selbgcolor[] = "#722F37"; +static char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor }, }; +/* tagging */ +static const char *tags[] = { "[one]", "[two]", "[three]", "[four]", "[five]", "[six]", "[seven]", "[eight]", "[nine]" }; -/* There are two options when it comes to per-client rules: - * - a typical struct table or - * - using the RULE macro - * - * A traditional struct table looks like this: - * // class instance title wintype tags mask isfloating monitor - * { "Gimp", NULL, NULL, NULL, 1 << 4, 0, -1 }, - * { "Firefox", NULL, NULL, NULL, 1 << 7, 0, -1 }, - * - * The RULE macro has the default values set for each field allowing you to only - * specify the values that are relevant for your rule, e.g. - * - * RULE(.class = "Gimp", .tags = 1 << 4) - * RULE(.class = "Firefox", .tags = 1 << 7) - * - * Refer to the Rule struct definition for the list of available fields depending on - * the patches you enable. - */ static const Rule rules[] = { /* xprop(1): * WM_CLASS(STRING) = instance, class * WM_NAME(STRING) = title - * WM_WINDOW_ROLE(STRING) = role - * _NET_WM_WINDOW_TYPE(ATOM) = wintype */ - RULE(.wintype = WTYPE "DIALOG", .isfloating = 1) - RULE(.wintype = WTYPE "UTILITY", .isfloating = 1) - RULE(.wintype = WTYPE "TOOLBAR", .isfloating = 1) - RULE(.wintype = WTYPE "SPLASH", .isfloating = 1) - //RULE(.class = "Gimp", .tags = 1 << 4) - //RULE(.class = "Firefox", .tags = 1 << 7) - RULE(.instance = "spterm", .tags = SPTAG(0), .isfloating = 1) -}; - - - -/* Bar rules allow you to configure what is shown where on the bar, as well as - * introducing your own bar modules. - * - * monitor: - * -1 show on all monitors - * 0 show on monitor 0 - * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) - * bar - bar index, 0 is default, 1 is extrabar - * alignment - how the module is aligned compared to other modules - * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions - * name - does nothing, intended for visual clue and for logging / debugging - */ -static const BarRule barrules[] = { - /* monitor bar alignment widthfunc drawfunc clickfunc hoverfunc name */ - { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, hover_tags, "tags" }, - { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, NULL, "layout" }, - { statusmon, 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, NULL, "status" }, - { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, NULL, "wintitle" }, + /* class instance title tags mask isfloating monitor */ + { NULL, NULL, NULL, 0, 0, -1 }, }; /* layout(s) */ -static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ +static const float mfact = 0.50; /* factor of master area size [0.05..0.95] */ static const int nmaster = 1; /* number of clients in master area */ static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ - - static const Layout layouts[] = { - /* symbol arrange function */ - { "[]=", tile }, /* first entry is default */ - { "><>", NULL }, /* no layout function means floating behavior */ + /* symbol arrange function */ + {"[]=", tile}, /* first entry is default */ + {"<#>", NULL}, /* no layout function means floating behavior */ + {"<@>", monocle}, }; +static const char *pcmanfm[] = { "pcmanfm", NULL }; +static const char *firemenu[] = { "firemenu", NULL }; + +#include +//different commands per OS +#ifdef __linux__ + #define VOL_UP "pamixer --allow-boost -i 10; kill -44 $(pidof dwmblocks)" + #define XK_UP "pamixer --allow-boost -i 5; kill -44 $(pidof dwmblocks)" + #define VOL_DOWN "pamixer --allow-boost -d 10; kill -44 $(pidof dwmblocks)" + #define XK_DOWN "pamixer --allow-boost -d 5; kill -44 $(pidof dwmblocks)" + #define VOL_MUTE "pamixer -t; kill -44 $(pidof dwmblocks)" + #define VOL_KILL "kill -44 $(pidof dwmblocks)" +#elif __OpenBSD__ + #define VOL_UP "sndioctl output.level=+0.10; pkill -SIGUSR1 dwmblocks" + #define XK_UP "sndioctl output.level=+0.05; pkill -SIGUSR1 dwmblocks" + #define VOL_DOWN "sndioctl output.level=-0.10; pkill -SIGUSR1 dwmblocks" + #define XK_DOWN "sndioctl output.level=-0.05; pkill -SIGUSR1 dwmblocks" + #define VOL_MUTE "sndioctl output.mute=!; pkill -SIGUSR1 dwmblocks" + #define VOL_KILL "pkill -SIGUSR1 dwmblocks" +#elif __FreeBSD__ + #define VOL_UP "sndioctl output.level=+0.10" + #define XK_UP "sndioctl output.level=+0.05" + #define VOL_DOWN "sndioctl output.level=-0.10" + #define XK_DOWN "sndioctl output.level=-0.05" + #define VOL_MUTE "sndioctl output.level=-1" + #define VOL_KILL "pkill -SIGUSR2 dwmblocks" +#endif + /* key definitions */ #define MODKEY Mod4Mask #define TAGKEYS(KEY,TAG) \ @@ -236,170 +90,71 @@ static const Layout layouts[] = { /* commands */ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ -static const char *dmenucmd[] = { - "dmenu_run", - "-m", dmenumon, - "-fn", dmenufont, - "-nb", normbgcolor, - "-nf", normfgcolor, - "-sb", selbgcolor, - "-sf", selfgcolor, - NULL -}; - -static const char *termcmd[] = { "st", NULL }; -static const char *caja[] = { "caja", NULL }; -//static const char *floorp[] = { "floorp", NULL }; -static const char *firemenu[] = { "firemenu", NULL }; - -///////////////////////////////////////////////KEYBINDS////////////////////////////////////////////// -#include -//different commands per OS -#ifdef __linux__ - #define VOL_UP "pamixer --allow-boost -i 10; kill -44 $(pidof dwmblocks)" - #define XK_UP "pamixer --allow-boost -i 5; kill -44 $(pidof dwmblocks)" - #define VOL_DOWN "pamixer --allow-boost -d 10; kill -44 $(pidof dwmblocks)" - #define XK_DOWN "pamixer --allow-boost -d 5; kill -44 $(pidof dwmblocks)" - #define VOL_MUTE "pamixer -t; kill -44 $(pidof dwmblocks)" - #define VOL_KILL "kill -44 $(pidof dwmblocks)" -#elif __OpenBSD__ - #define VOL_UP "sndioctl output.level=+0.10; pkill -SIGUSR1 dwmblocks" - #define XK_UP "sndioctl output.level=+0.05; pkill -SIGUSR1 dwmblocks" - #define VOL_DOWN "sndioctl output.level=-0.10; pkill -SIGUSR1 dwmblocks" - #define XK_DOWN "sndioctl output.level=-0.05; pkill -SIGUSR1 dwmblocks" - #define VOL_MUTE "sndioctl output.mute=!; pkill -SIGUSR1 dwmblocks" - #define VOL_KILL "pkill -SIGUSR1 dwmblocks" -#elif __FreeBSD__ - #define VOL_UP "sndioctl output.level=+0.10" - #define XK_UP "sndioctl output.level=+0.05" - #define VOL_DOWN "sndioctl output.level=-0.10" - #define XK_DOWN "sndioctl output.level=-0.05" - #define VOL_MUTE "sndioctl output.level=-1" - #define VOL_KILL "pkill -SIGUSR2 dwmblocks" -#endif +static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbordercolor, "-sf", selfgcolor, NULL }; +static const char *termcmd[] = {"zsh", "-c", "st -e zsh & walrs -R -q ",NULL}; +#include "movestack.c" static Key keys[] = { - /*modifierkey function argument */ - { Mod4Mask|ShiftMask, XK_r, spawn, {.v = firemenu } }, - { MODKEY, XK_f, spawn, {.v = caja } }, - { MODKEY, XK_r, spawn, {.v = dmenucmd } }, - { MODKEY, XK_j, focusstack, {.i = +1 } }, - { MODKEY, XK_k, focusstack, {.i = -1 } }, - { MODKEY, XK_Down, moveresize, {.v = "0x 25y 0w 0h" } }, - { MODKEY, XK_Up, moveresize, {.v = "0x -25y 0w 0h" } }, - { MODKEY, XK_Right, moveresize, {.v = "25x 0y 0w 0h" } }, - { MODKEY, XK_Left, moveresize, {.v = "-25x 0y 0w 0h" } }, - { MODKEY|ShiftMask, XK_Down, moveresize, {.v = "0x 0y 0w 25h" } }, - { MODKEY|ShiftMask, XK_Up, moveresize, {.v = "0x 0y 0w -25h" } }, - { MODKEY|ShiftMask, XK_Right, moveresize, {.v = "0x 0y 25w 0h" } }, - { MODKEY|ShiftMask, XK_Left, moveresize, {.v = "0x 0y -25w 0h" } }, - { MODKEY, XK_Tab, view, {0} }, - { MODKEY|ShiftMask, XK_F5, xrdb, {.v = NULL } }, - { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, - { MODKEY, XK_space, setlayout, {0} }, - { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, - { MODKEY, XK_grave, togglescratch, {.ui = 0 } }, - { MODKEY|ControlMask, XK_grave, setscratch, {.ui = 0 } }, - { MODKEY|ShiftMask, XK_grave, removescratch, {.ui = 0 } }, - { MODKEY, XK_w, togglefullscreen, {0} }, - { MODKEY, XK_0, view, {.ui = ~SPTAGMASK } }, - { MODKEY|ShiftMask, XK_0, tag, {.ui = ~SPTAGMASK } }, - - 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) - - { MODKEY, XK_h, setmfact, {.f = -0.05} }, - { MODKEY, XK_l, setmfact, {.f = +0.05} }, - { MODKEY|ShiftMask, XK_q,quit, {0} }, - //{ MODKEY, XK_b, spawn, {.v = floorp} }, - { MODKEY, XK_b, spawn, SHCMD("brave") }, - { MODKEY, XK_m, togglebar, {0} }, - { MODKEY, XK_o, incnmaster, {.i = +1 } }, - { MODKEY, XK_Return, spawn, {.v = termcmd } }, - { MODKEY|ShiftMask, XK_o, incnmaster, {.i = -1 } }, - { MODKEY, XK_s, killclient, {0} }, - { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } }, - { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } }, - { MODKEY|ShiftMask, XK_Return, togglescratch, {.ui = 0 } }, - { 0, XK_F12, togglescratch, {.ui = 0 } }, - - //volume control - { Mod1Mask, XK_equal, spawn, SHCMD(VOL_UP) }, - { Mod1Mask, XK_minus, spawn, SHCMD(VOL_DOWN) }, - { Mod1Mask, XK_minus, spawn, SHCMD(VOL_DOWN) }, - { 0, XF86XK_AudioMute, spawn, SHCMD(VOL_MUTE) }, - { 0, XF86XK_AudioRaiseVolume, spawn, SHCMD(XK_UP) }, - { 0, XF86XK_AudioLowerVolume, spawn, SHCMD(XK_DOWN) }, - //lock - { MODKEY|ShiftMask, XK_l, spawn, SHCMD("slock") }, - { 0, XK_Print, spawn, SHCMD("screenie") }, + /*modifierkey function argument */ + { Mod4Mask|ShiftMask, XK_r, spawn, {.v = firemenu } }, + { MODKEY, XK_f, spawn, {.v = pcmanfm } }, + { MODKEY, XK_r, spawn, {.v = dmenucmd } }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_F5, xrdb, {.v = NULL } }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_space, setlayout, {0} }, + + 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) + + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_q,quit, {0} }, + //{ MODKEY, XK_b, spawn, {.v = floorp} }, + { MODKEY, XK_b, spawn, SHCMD("floorp") }, + { MODKEY, XK_m, togglebar, {0} }, + { MODKEY, XK_o, incnmaster, {.i = +1 } }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { MODKEY|ShiftMask, XK_o, incnmaster, {.i = -1 } }, + { MODKEY, XK_s, killclient, {0} }, + { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } }, + + //volume control + { Mod1Mask, XK_equal, spawn, SHCMD(VOL_UP) }, + { Mod1Mask, XK_minus, spawn, SHCMD(VOL_DOWN) }, + { Mod1Mask, XK_minus, spawn, SHCMD(VOL_DOWN) }, + { 0, XF86XK_AudioMute, spawn, SHCMD(VOL_MUTE) }, + { 0, XF86XK_AudioRaiseVolume, spawn, SHCMD(XK_UP) }, + { 0, XF86XK_AudioLowerVolume, spawn, SHCMD(XK_DOWN) }, + //lock + { MODKEY|ShiftMask, XK_l, spawn, SHCMD("slock") }, + { 0, XK_Print, spawn, SHCMD("flameshot gui -r | xclip -selection clipboard -t image/png") }, }; -///////////////////////////////////////////////////////////////////////////////////////////////////// /* button definitions */ /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ -static 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} }, +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { 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} }, }; -/* signal definitions */ -/* signum must be greater than 0 */ -/* trigger signals using `xsetroot -name "fsignal: [ ]"` */ -static Signal signals[] = { - /* signum function */ - { "focusstack", focusstack }, - { "setmfact", setmfact }, - { "togglebar", togglebar }, - { "incnmaster", incnmaster }, - { "togglefloating", togglefloating }, - { "focusmon", focusmon }, - { "tagmon", tagmon }, - { "zoom", zoom }, - { "incrgaps", incrgaps }, - { "incrigaps", incrigaps }, - { "incrogaps", incrogaps }, - { "incrihgaps", incrihgaps }, - { "incrivgaps", incrivgaps }, - { "incrohgaps", incrohgaps }, - { "incrovgaps", incrovgaps }, - { "togglegaps", togglegaps }, - { "defaultgaps", defaultgaps }, - { "setgaps", setgapsex }, - { "view", view }, - { "viewall", viewallex }, - { "viewex", viewex }, - { "toggleview", toggleview }, - { "toggleviewex", toggleviewex }, - { "tag", tag }, - { "tagall", tagallex }, - { "tagex", tagex }, - { "toggletag", toggletag }, - { "toggletagex", toggletagex }, - { "togglefullscreen", togglefullscreen }, - { "fullscreen", fullscreen }, - { "togglescratch", togglescratch }, - { "killclient", killclient }, - { "xrdb", xrdb }, - { "quit", quit }, - { "setlayout", setlayout }, - { "setlayoutex", setlayoutex }, -}; diff --git a/.suckless/dwm/config.h~ b/.suckless/dwm/config.h~ new file mode 100644 index 0000000..479e5f4 --- /dev/null +++ b/.suckless/dwm/config.h~ @@ -0,0 +1,160 @@ + +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 2; /* border pixel of windows */ +static const unsigned int gappx = 14; /* gaps between windows */ +static const unsigned int snap = 3; /* snap pixel */ +static const int user_bh = 12; /* 2 is the default spacing around the bar's font */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const int vertpad = 10; /* vertical padding of bar */ +static const int sidepad = 16; /* horizontal padding of bar */ +static const char *fonts[] = {"CaskaydiaMonoNerdFont-Bold:size=9.2"}; +static const char dmenufont[] = "CaskaydiaMonoNerdFont-Bold:size=10.6"; +static char normbgcolor[] = "#222222"; +static char normbordercolor[] = "#444444"; +static char normfgcolor[] = "#bbbbbb"; +static char selfgcolor[] = "#eeeeee"; +static char selbordercolor[] = "#722F37"; +static char selbgcolor[] = "#722F37"; +static char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor }, +}; + +/* 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 */ + { NULL, NULL, NULL, 0, 0, -1 }, +}; + +/* layout(s) */ +static const float mfact = 0.50; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + {"[]=", tile}, /* first entry is default */ + {"><>", NULL}, /* no layout function means floating behavior */ + {"", monocle}, +}; + +static const char *pcmanfm[] = { "pcmanfm", NULL }; +static const char *firemenu[] = { "firemenu", NULL }; + +#include +//different commands per OS +#ifdef __linux__ + #define VOL_UP "pamixer --allow-boost -i 10; kill -44 $(pidof dwmblocks)" + #define XK_UP "pamixer --allow-boost -i 5; kill -44 $(pidof dwmblocks)" + #define VOL_DOWN "pamixer --allow-boost -d 10; kill -44 $(pidof dwmblocks)" + #define XK_DOWN "pamixer --allow-boost -d 5; kill -44 $(pidof dwmblocks)" + #define VOL_MUTE "pamixer -t; kill -44 $(pidof dwmblocks)" + #define VOL_KILL "kill -44 $(pidof dwmblocks)" +#elif __OpenBSD__ + #define VOL_UP "sndioctl output.level=+0.10; pkill -SIGUSR1 dwmblocks" + #define XK_UP "sndioctl output.level=+0.05; pkill -SIGUSR1 dwmblocks" + #define VOL_DOWN "sndioctl output.level=-0.10; pkill -SIGUSR1 dwmblocks" + #define XK_DOWN "sndioctl output.level=-0.05; pkill -SIGUSR1 dwmblocks" + #define VOL_MUTE "sndioctl output.mute=!; pkill -SIGUSR1 dwmblocks" + #define VOL_KILL "pkill -SIGUSR1 dwmblocks" +#elif __FreeBSD__ + #define VOL_UP "sndioctl output.level=+0.10" + #define XK_UP "sndioctl output.level=+0.05" + #define VOL_DOWN "sndioctl output.level=-0.10" + #define XK_DOWN "sndioctl output.level=-0.05" + #define VOL_MUTE "sndioctl output.level=-1" + #define VOL_KILL "pkill -SIGUSR2 dwmblocks" +#endif + +/* 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 */ +static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ +static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbordercolor, "-sf", selfgcolor, NULL }; +static const char *termcmd[] = {"zsh", "-c", "st -e zsh & walrs -R -q ",NULL}; + +#include "movestack.c" +static Key keys[] = { + /*modifierkey function argument */ + { Mod4Mask|ShiftMask, XK_r, spawn, {.v = firemenu } }, + { MODKEY, XK_f, spawn, {.v = pcmanfm } }, + { MODKEY, XK_r, spawn, {.v = dmenucmd } }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_F5, xrdb, {.v = NULL } }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_space, setlayout, {0} }, + + 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) + + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_q,quit, {0} }, + //{ MODKEY, XK_b, spawn, {.v = floorp} }, + { MODKEY, XK_b, spawn, SHCMD("floorp") }, + { MODKEY, XK_m, togglebar, {0} }, + { MODKEY, XK_o, incnmaster, {.i = +1 } }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { MODKEY|ShiftMask, XK_o, incnmaster, {.i = -1 } }, + { MODKEY, XK_s, killclient, {0} }, + { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } }, + + //volume control + { Mod1Mask, XK_equal, spawn, SHCMD(VOL_UP) }, + { Mod1Mask, XK_minus, spawn, SHCMD(VOL_DOWN) }, + { Mod1Mask, XK_minus, spawn, SHCMD(VOL_DOWN) }, + { 0, XF86XK_AudioMute, spawn, SHCMD(VOL_MUTE) }, + { 0, XF86XK_AudioRaiseVolume, spawn, SHCMD(XK_UP) }, + { 0, XF86XK_AudioLowerVolume, spawn, SHCMD(XK_DOWN) }, + //lock + { MODKEY|ShiftMask, XK_l, spawn, SHCMD("slock") }, + { 0, XK_Print, spawn, SHCMD("flameshot gui -r | xclip -selection clipboard -t image/png") }, +}; + + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { 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/.suckless/dwm/config.mk b/.suckless/dwm/config.mk index 7b3b027..8efca9a 100644 --- a/.suckless/dwm/config.mk +++ b/.suckless/dwm/config.mk @@ -1,5 +1,5 @@ # dwm version -VERSION = 6.3 +VERSION = 6.5 # Customize below to fit your system @@ -17,39 +17,23 @@ XINERAMAFLAGS = -DXINERAMA # freetype FREETYPELIBS = -lfontconfig -lXft FREETYPEINC = /usr/include/freetype2 - -# Uncomment this for the alpha patch and the winicon patch (BAR_ALPHA_PATCH, BAR_WINICON_PATCH) -XRENDER = -lXrender - -# Uncomment this for the mdpcontrol patch / MDPCONTROL_PATCH -#MPDCLIENT = -lmpdclient - -# Uncomment for the pango patch / BAR_PANGO_PATCH -#PANGOINC = `pkg-config --cflags xft pango pangoxft` -#PANGOLIB = `pkg-config --libs xft pango pangoxft` - -# Uncomment for the ipc patch / IPC_PATCH -#YAJLLIBS = -lyajl -#YAJLINC = -I/usr/include/yajl - -# Uncomment this for the rounded corners patch / ROUNDED_CORNERS_PATCH -#XEXTLIB = -lXext - -# Uncomment this for the swallow patch / SWALLOW_PATCH -XCBLIBS = -lX11-xcb -lxcb -lxcb-res - -# This is needed for the winicon and tagpreview patches / BAR_WINICON_PATCH / BAR_TAGPREVIEW_PATCH -#IMLIB2LIBS = -lImlib2 +# OpenBSD (uncomment) +#FREETYPEINC = ${X11INC}/freetype2 +#MANPREFIX = ${PREFIX}/man # includes and libs -INCS = -I${X11INC} -I${FREETYPEINC} ${YAJLINC} ${PANGOINC} -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XRENDER} ${MPDCLIENT} ${XEXTLIB} ${XCBLIBS} ${KVMLIB} ${PANGOLIB} ${YAJLLIBS} ${IMLIB2LIBS} +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} +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} #CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} -CFLAGS = -std=c99 -pedantic -Wall -Wno-unused-function -Wno-deprecated-declarations -Os ${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/.suckless/dwm/drw.c b/.suckless/dwm/drw.c index 2297da4..6be5dee 100644 --- a/.suckless/dwm/drw.c +++ b/.suckless/dwm/drw.c @@ -9,60 +9,45 @@ #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) +static int +utf8decode(const char *s_in, long *u, int *err) { - 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; - + static const unsigned char lens[] = { + /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */ + /* 110XX */ 2, 2, 2, 2, + /* 1110X */ 3, 3, + /* 11110 */ 4, + /* 11111 */ 0, /* invalid */ + }; + static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 }; + static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 }; + + const unsigned char *s = (const unsigned char *)s_in; + int len = lens[*s >> 3]; *u = UTF_INVALID; - if (!clen) - return 0; - udecoded = utf8decodebyte(c[0], &len); - if (!BETWEEN(len, 1, UTF_SIZ)) + *err = 1; + if (len == 0) return 1; - for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { - udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); - if (type) - return j; + + long cp = s[0] & leading_mask[len - 1]; + for (int i = 1; i < len; ++i) { + if (s[i] == '\0' || (s[i] & 0xC0) != 0x80) + return i; + cp = (cp << 6) | (s[i] & 0x3F); } - if (j < len) - return 0; - *u = udecoded; - utf8validate(u, len); + /* out of range, surrogate, overlong encoding */ + if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1]) + return len; + *err = 0; + *u = cp; return len; } Drw * -drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) { Drw *drw = ecalloc(1, sizeof(Drw)); @@ -71,12 +56,8 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h drw->root = root; drw->w = w; drw->h = h; - - drw->visual = visual; - drw->depth = depth; - drw->cmap = cmap; - drw->drawable = XCreatePixmap(dpy, root, w, h, depth); - drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL); + 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; @@ -92,7 +73,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h) drw->h = h; if (drw->drawable) XFreePixmap(drw->dpy, drw->drawable); - drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); } void @@ -138,19 +119,6 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) die("no font specified."); } - /* Do not allow using color fonts. This is a workaround for a BadLength - * error from Xft with color glyphs. Modelled on the Xterm workaround. See - * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 - * https://lists.suckless.org/dev/1701/30932.html - * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 - * and lots more all over the internet. - */ - FcBool iscol; - if (FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { - XftFontClose(drw->dpy, xfont); - return NULL; - } - font = ecalloc(1, sizeof(Fnt)); font->xfont = xfont; font->pattern = pattern; @@ -199,31 +167,22 @@ drw_fontset_free(Fnt *font) } void -drw_clr_create( - Drw *drw, - Clr *dest, - const char *clrname - , unsigned int alpha -) { +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ if (!drw || !dest || !clrname) return; - if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, + 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 = (dest->pixel & 0x00ffffffU) | (alpha << 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, - char *clrnames[], - const unsigned int alphas[], - size_t clrcount -) { +drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount) +{ size_t i; Clr *ret; @@ -232,7 +191,7 @@ drw_scm_create( return NULL; for (i = 0; i < clrcount; i++) - drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]); + drw_clr_create(drw, &ret[i], clrnames[i]); return ret; } @@ -250,7 +209,6 @@ drw_setscheme(Drw *drw, Clr *scm) drw->scheme = scm; } - void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) { @@ -264,49 +222,76 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int } int -drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool ignored) +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) { - char buf[1024]; - int ty; - unsigned int ew; + int ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1; XftDraw *d = NULL; Fnt *usedfont, *curfont, *nextfont; - size_t i, len; - int utf8strlen, utf8charlen, render = x || y || w || h; + int utf8strlen, utf8charlen, utf8err, render = x || y || w || h; long utf8codepoint = 0; const char *utf8str; FcCharSet *fccharset; FcPattern *fcpattern; FcPattern *match; XftResult result; - int charexists = 0; + int charexists = 0, overflow = 0; + /* keep track of a couple codepoints for which we have no match. */ + static unsigned int nomatches[128], ellipsis_width, invalid_width; + static const char invalid[] = "�"; - if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) return 0; if (!render) { - w = ~w; + 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, drw->visual, drw->cmap); + if (w < lpad) + return x + w; + 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, "..."); + if (!invalid_width && render) + invalid_width = drw_fontset_getwidth(drw, invalid); while (1) { - utf8strlen = 0; + ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0; utf8str = text; nextfont = NULL; while (*text) { - utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + utf8charlen = utf8decode(text, &utf8codepoint, &utf8err); for (curfont = drw->fonts; curfont; curfont = curfont->next) { charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); if (charexists) { - if (curfont == usedfont) { - utf8strlen += utf8charlen; + 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) { text += utf8charlen; + utf8strlen += utf8err ? 0 : utf8charlen; + ew += utf8err ? 0 : tmpw; } else { nextfont = curfont; } @@ -314,36 +299,31 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp } } - if (!charexists || nextfont) + if (overflow || !charexists || nextfont || utf8err) break; else charexists = 0; } if (utf8strlen) { - drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); - /* shorten text if necessary */ - for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; drw_font_getexts(usedfont, utf8str, len, &ew, NULL)) - len--; - - if (len) { - memcpy(buf, utf8str, len); - buf[len] = '\0'; - if (len < utf8strlen) - for (i = len; i && i > len - 3; buf[--i] = '.') - ; /* NOP */ - - if (render) { - ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; - XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], - usedfont->xfont, x, ty, (XftChar8 *)buf, len); - } - x += ew; - w -= ew; + 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 (utf8err && (!render || invalid_width < w)) { + if (render) + drw_text(drw, x, y, w, h, 0, invalid, invert); + x += invalid_width; + w -= invalid_width; + } + if (render && overflow) + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); - if (!*text) { + if (!*text || overflow) { break; } else if (nextfont) { charexists = 0; @@ -353,6 +333,15 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp * character must be drawn. */ charexists = 1; + hash = (unsigned int)utf8codepoint; + hash = ((hash >> 16) ^ hash) * 0x21F0AAAD; + hash = ((hash >> 15) ^ hash) * 0xD35A2D97; + h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches); + h1 = (hash >> 17) % LENGTH(nomatches); + /* avoid expensive XftFontMatch call when we know we won't find a match */ + if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint) + goto no_match; + fccharset = FcCharSetCreate(); FcCharSetAddChar(fccharset, utf8codepoint); @@ -364,7 +353,6 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp fcpattern = FcPatternDuplicate(drw->fonts->pattern); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); - FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); FcDefaultSubstitute(fcpattern); @@ -381,6 +369,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp curfont->next = usedfont; } else { xfont_free(usedfont); + nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint; +no_match: usedfont = drw->fonts; } } @@ -392,7 +382,6 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp return x + (render ? w : 0); } - void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) { @@ -404,11 +393,20 @@ drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) } unsigned int -drw_fontset_getwidth(Drw *drw, const char *text, Bool markup) +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, markup); + 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 @@ -448,4 +446,3 @@ drw_cur_free(Drw *drw, Cur *cursor) XFreeCursor(drw->dpy, cursor->cursor); free(cursor); } - diff --git a/.suckless/dwm/drw.h b/.suckless/dwm/drw.h index 0eff1ce..bdbf950 100644 --- a/.suckless/dwm/drw.h +++ b/.suckless/dwm/drw.h @@ -1,6 +1,5 @@ /* See LICENSE file for copyright and license details. */ - typedef struct { Cursor cursor; } Cur; @@ -13,7 +12,7 @@ typedef struct Fnt { struct Fnt *next; } Fnt; -enum { ColFg, ColBg, ColBorder, ColFloat, ColCount }; /* Clr scheme index */ +enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ typedef XftColor Clr; typedef struct { @@ -21,9 +20,6 @@ typedef struct { Display *dpy; int screen; Window root; - Visual *visual; - unsigned int depth; - Colormap cmap; Drawable drawable; GC gc; Clr *scheme; @@ -31,29 +27,20 @@ typedef struct { } Drw; /* Drawable abstraction */ -Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap); +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_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); void drw_fontset_free(Fnt* set); -unsigned int drw_fontset_getwidth(Drw *drw, const char *text, Bool markup); +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 - , unsigned int alpha -); -Clr *drw_scm_create( - Drw *drw, - char *clrnames[], - const unsigned int alphas[], - size_t clrcount -); +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +Clr *drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount); /* Cursor abstraction */ Cur *drw_cur_create(Drw *drw, int shape); @@ -65,8 +52,7 @@ 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, Bool markup); +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/.suckless/dwm/drw.o b/.suckless/dwm/drw.o index 3303c6df9300ef0f891e9c7e323a421b071b2983..dbafb1662bf561b45180fe2335afc7ce9362489f 100644 GIT binary patch literal 11296 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 literal 10368 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 HZu$QPaD#uy diff --git a/.suckless/dwm/dwm b/.suckless/dwm/dwm index e86d138eb94ebbeaff03e267835b972864089fc2..cbf4882d57f63d4603b0d5541bb97cb3f4595fe9 100755 GIT binary patch literal 74736 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 G()tx|0ksu`NX6axfWpaM!uDYjVJ+9psGp^#P> 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 diff --git a/.suckless/dwm/dwm.1 b/.suckless/dwm/dwm.1 index 6020871..7b6cadb 100644 --- a/.suckless/dwm/dwm.1 +++ b/.suckless/dwm/dwm.1 @@ -49,123 +49,122 @@ label toggles between tiled and floating layout. .B Button3 click on a tag label adds/removes all windows with that tag to/from the view. .TP -.B Super\-Button1 +.B Mod1\-Button1 click on a tag label applies that tag to the focused window. .TP -.B Super\-Button3 +.B Mod1\-Button3 click on a tag label adds/removes that tag to/from the focused window. .SS Keyboard commands .TP -.B Super\-Return +.B Mod1\-Shift\-Return Start .BR st(1). .TP -.B Super\-Shift\-Return -Start or toggle -.BR st(1) -as a floating (scratchpad) terminal. -.TP -.B Super\-d +.B Mod1\-p Spawn .BR dmenu(1) for launching other programs. .TP -.B Super\-f -Toggles fullscreen state of focused window. -.TP -.B Super\-, +.B Mod1\-, Focus previous screen, if any. .TP -.B Super\-. +.B Mod1\-. Focus next screen, if any. .TP -.B Super\-Shift\-, +.B Mod1\-Shift\-, Send focused window to previous screen, if any. .TP -.B Super\-Shift\-. +.B Mod1\-Shift\-. Send focused window to next screen, if any. .TP -.B Super\-b +.B Mod1\-b Toggles bar on and off. .TP -.B Super\-t +.B Mod1\-t Sets tiled layout. .TP -.B Super\-m -Zooms/cycles focused window to/from master area (tiled layouts only). +.B Mod1\-f +Sets floating layout. .TP -.B Super\-space -Toggles between floating and tiled layout. +.B Mod1\-m +Sets monocle layout. .TP -.B Super\-j +.B Mod1\-space +Toggles between current and previous layout. +.TP +.B Mod1\-j Focus next window. .TP -.B Super\-k +.B Mod1\-k Focus previous window. .TP -.B Super\-Shift\-j -Move focused stack window downward. -.TP -.B Super\-Shift\-k -Move focused stack window upward. -.TP -.B Super\-o +.B Mod1\-i Increase number of windows in master area. .TP -.B Super\-Shift\-o +.B Mod1\-d Decrease number of windows in master area. .TP -.B Super\-l +.B Mod1\-l Increase master area size. .TP -.B Super\-h +.B Mod1\-h Decrease master area size. .TP -.B Super\-m +.B Mod1\-Return Zooms/cycles focused window to/from master area (tiled layouts only). .TP -.B Super\-q +.B Mod1\-Shift\-c Close focused window. .TP -.B Super\-Shift\-space +.B Mod1\-Shift\-space Toggle focused window between tiled and floating state. .TP -.B Super\-Tab +.B Mod1\-Tab Toggles to the previously selected tags. .TP -.B Super\-Shift\-[1..n] +.B Mod1\-Shift\-[1..n] Apply nth tag to focused window. .TP -.B Super\-Shift\-0 +.B Mod1\-Shift\-0 Apply all tags to focused window. .TP -.B Super\-Control\-Shift\-[1..n] +.B Mod1\-Control\-Shift\-[1..n] Add/remove nth tag to/from focused window. .TP -.B Super\-[1..n] +.B Mod1\-[1..n] View all windows with nth tag. .TP -.B Super\-0 +.B Mod1\-0 View all windows with any tag. .TP -.B Super\-Control\-[1..n] +.B Mod1\-Control\-[1..n] Add/remove all windows with nth tag to/from the view. .TP -.B Super\-Shift\-q, Super\-Shift\-e +.B Mod1\-Shift\-q Quit dwm. +.TP +.B Mod1\-Control\-Shift\-q +Restart dwm. .SS Mouse commands .TP -.B Super\-Button1 +.B Mod1\-Button1 Move focused window while dragging. Tiled windows will be toggled to the floating state. .TP -.B Super\-Button2 +.B Mod1\-Button2 Toggles focused window between floating and tiled state. .TP -.B Super\-Button3 +.B Mod1\-Button3 Resize focused window while dragging. Tiled windows will be toggled to the floating state. .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/.suckless/dwm/dwm.c b/.suckless/dwm/dwm.c index 7c87101..3c2c1b6 100644 --- a/.suckless/dwm/dwm.c +++ b/.suckless/dwm/dwm.c @@ -35,8 +35,8 @@ #include #include #include +#include #include - #ifdef XINERAMA #include #endif /* XINERAMA */ @@ -45,96 +45,43 @@ #include "drw.h" #include "util.h" - - - - /* macros */ -#define Button6 6 -#define Button7 7 -#define Button8 8 -#define Button9 9 -#define NUMTAGS 9 -#define BARRULES 20 #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 ISVISIBLEONTAG(C, T) ((C->tags & T)) #define ISVISIBLE(C) ISVISIBLEONTAG(C, 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 WTYPE "_NET_WM_WINDOW_TYPE_" -#define TOTALTAGS (NUMTAGS + LENGTH(scratchpads)) -#define TAGMASK ((1 << TOTALTAGS) - 1) -#define SPTAG(i) ((1 << NUMTAGS) << (i)) -#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << NUMTAGS) -#define TEXTWM(X) (drw_fontset_getwidth(drw, (X), True) + lrpad) -#define TEXTW(X) (drw_fontset_getwidth(drw, (X), False) + lrpad) -#define HIDDEN(C) ((getstate(C->win) == IconicState)) +#define TAGMASK ((1 << LENGTH(tags)) - 1) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define XRDB_LOAD_COLOR(R,V) if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) { \ + if (value.addr != NULL && strnlen(value.addr, 8) == 7 && value.addr[0] == '#') { \ + int i = 1; \ + for (; i <= 6; i++) { \ + if (value.addr[i] < 48) break; \ + if (value.addr[i] > 57 && value.addr[i] < 65) break; \ + if (value.addr[i] > 70 && value.addr[i] < 97) break; \ + if (value.addr[i] > 102) break; \ + } \ + if (i == 7) { \ + strncpy(V, value.addr, 7); \ + V[7] = '\0'; \ + } \ + } \ + } /* enums */ -enum { - CurNormal, - CurResize, - CurMove, - CurLast -}; /* cursor */ - -enum { - SchemeNorm, - SchemeSel, - SchemeTitleNorm, - SchemeTitleSel, - SchemeTagsNorm, - SchemeTagsSel, - SchemeHidNorm, - SchemeHidSel, - SchemeUrg, -}; /* color schemes */ - -enum { - NetSupported, NetWMName, NetWMState, NetWMCheck, - NetWMFullscreen, NetActiveWindow, NetWMWindowType, - NetClientList, - NetLast -}; /* EWMH atoms */ - -enum { - WMProtocols, - WMDelete, - WMState, - WMTakeFocus, - WMLast -}; /* default atoms */ - - -enum { - ClkTagBar, - ClkLtSymbol, - ClkStatusText, - ClkWinTitle, - ClkClientWin, - ClkRootWin, - ClkLast -}; /* clicks */ - -enum { - BAR_ALIGN_LEFT, - BAR_ALIGN_CENTER, - BAR_ALIGN_RIGHT, - BAR_ALIGN_LEFT_LEFT, - BAR_ALIGN_LEFT_RIGHT, - BAR_ALIGN_LEFT_CENTER, - BAR_ALIGN_NONE, - BAR_ALIGN_RIGHT_LEFT, - BAR_ALIGN_RIGHT_RIGHT, - BAR_ALIGN_RIGHT_CENTER, - BAR_ALIGN_LAST -}; /* bar alignment */ - +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, ClkClientWin, + ClkRootWin, ClkLast }; /* clicks */ typedef union { int i; @@ -143,42 +90,6 @@ typedef union { const void *v; } Arg; -typedef struct Monitor Monitor; -typedef struct Bar Bar; -struct Bar { - Window win; - Monitor *mon; - Bar *next; - int idx; - int showbar; - int topbar; - int external; - int borderpx; - int borderscheme; - int bx, by, bw, bh; /* bar geometry */ - int w[BARRULES]; // width, array length == barrules, then use r index for lookup purposes - int x[BARRULES]; // x position, array length == ^ -}; - -typedef struct { - int x; - int y; - int h; - int w; -} BarArg; - -typedef struct { - int monitor; - int bar; - int alignment; // see bar alignment enum - int (*widthfunc)(Bar *bar, BarArg *a); - int (*drawfunc)(Bar *bar, BarArg *a); - int (*clickfunc)(Bar *bar, Arg *arg, BarArg *a); - int (*hoverfunc)(Bar *bar, BarArg *a, XMotionEvent *ev); - char *name; // for debugging - int x, w; // position, width for internal use -} BarRule; - typedef struct { unsigned int click; unsigned int mask; @@ -187,7 +98,7 @@ typedef struct { const Arg arg; } Button; - +typedef struct Monitor Monitor; typedef struct Client Client; struct Client { char name[256]; @@ -198,11 +109,8 @@ struct Client { int bw, oldbw; unsigned int tags; int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; - int isterminal, noswallow; - pid_t pid; Client *next; Client *snext; - Client *swallowing; Monitor *mon; Window win; }; @@ -214,33 +122,30 @@ typedef struct { const Arg arg; } Key; - typedef struct { const char *symbol; void (*arrange)(Monitor *); } Layout; - 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 */ - int gappih; /* horizontal gap between windows */ - int gappiv; /* vertical gap between windows */ - int gappoh; /* horizontal outer gaps */ - int gappov; /* vertical outer gaps */ + int gappx; /* gaps between windows */ unsigned int seltags; unsigned int sellt; unsigned int tagset[2]; int showbar; + int topbar; Client *clients; Client *sel; Client *stack; Monitor *next; - Bar *bar; + Window barwin; const Layout *lt[2]; }; @@ -248,32 +153,18 @@ typedef struct { const char *class; const char *instance; const char *title; - const char *wintype; unsigned int tags; int isfloating; - int isterminal; - int noswallow; int monitor; } Rule; -#define RULE(...) { .monitor = -1, __VA_ARGS__ }, - -/* Cross patch compatibility rule macro helper macros */ -#define FLOATING , .isfloating = 1 -#define CENTERED -#define PERMANENT -#define FAKEFULLSCREEN -#define NOSWALLOW , .noswallow = 1 -#define TERMINAL , .isterminal = 1 -#define SWITCHTAG - - /* 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 attachaside(Client *c); static void attachstack(Client *c); static void buttonpress(XEvent *e); static void checkotherwm(void); @@ -290,14 +181,13 @@ static void detachstack(Client *c); static Monitor *dirtomon(int dir); static void drawbar(Monitor *m); static void drawbars(void); -static void drawbarwin(Bar *bar); 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, Atom req); +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); @@ -306,13 +196,16 @@ static void grabkeys(void); static void incnmaster(const Arg *arg); static void keypress(XEvent *e); static void killclient(const Arg *arg); +static void loadxrdb(void); 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 *nexttagged(Client *c); static Client *nexttiled(Client *c); -static void pop(Client *); +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); @@ -327,20 +220,21 @@ 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 setgaps(const Arg *arg); 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 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 toggletag(const Arg *arg); static void toggleview(const Arg *arg); -static void unfocus(Client *c, int setfocus, Client *nextfocus); +static void unfocus(Client *c, int setfocus); static void unmanage(Client *c, int destroyed); static void unmapnotify(XEvent *e); static void updatebarpos(Monitor *m); @@ -351,32 +245,30 @@ 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 xrdb(const Arg *arg); static void zoom(const Arg *arg); +static void sighup(int unused); +static void sigterm(int unused); -/* bar functions */ - -#include "patch/include.h" /* variables */ static const char broken[] = "broken"; -static char stext[512]; - +static char stext[256]; static int screen; static int sw, sh; /* X display screen geometry width, height */ -static int bh; /* bar geometry */ +static int bh; /* bar height */ static int lrpad; /* sum of left and right padding for text */ -/* Some clients (e.g. alacritty) helpfully send configure requests with a new size or position - * when they detect that they have been moved to another monitor. This can cause visual glitches - * when moving (or resizing) client windows from one monitor to another. This variable is used - * internally to ignore such configure requests while movemouse or resizemouse are being used. */ -static int ignoreconfigurerequests = 0; +static int vp; /* vertical padding for bar */ +static int sp; /* side padding for bar */ static int (*xerrorxlib)(Display *, XErrorEvent *); static unsigned int numlockmask = 0; static void (*handler[LASTEvent]) (XEvent *) = { @@ -396,6 +288,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; @@ -407,58 +300,44 @@ static Window root, wmcheckwin; /* configuration, allows nested code to access above variables */ #include "config.h" -#include "patch/include.c" - /* compile-time check if all tags fit into an unsigned int bit array. */ -struct NumTags { char limitexceeded[NUMTAGS > 31 ? -1 : 1]; }; +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; /* function implementations */ void applyrules(Client *c) { const char *class, *instance; - Atom wintype; unsigned int i; const Rule *r; Monitor *m; XClassHint ch = { NULL, NULL }; /* rule matching */ - c->noswallow = -1; 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; - wintype = getatomprop(c, netatom[NetWMWindowType], XA_ATOM); - 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)) - && (!r->wintype || wintype == XInternAtom(dpy, r->wintype, False))) + && (!r->instance || strstr(instance, r->instance))) { - c->isterminal = r->isterminal; - c->noswallow = r->noswallow; 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); + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; } int @@ -558,6 +437,17 @@ attach(Client *c) c->mon->clients = c; } +void +attachaside(Client *c) { + Client *at = nexttagged(c); + if(!at) { + attach(c); + return; + } + c->next = at->next; + at->next = c; +} + void attachstack(Client *c) { @@ -568,66 +458,41 @@ attachstack(Client *c) void buttonpress(XEvent *e) { - int click, i, r; + unsigned int i, x, click; Arg arg = {0}; Client *c; Monitor *m; - Bar *bar; XButtonPressedEvent *ev = &e->xbutton; - const BarRule *br; - BarArg carg = { 0, 0, 0, 0 }; - click = ClkRootWin; - + click = ClkRootWin; /* focus monitor if necessary */ - if ((m = wintomon(ev->window)) && m != selmon - ) { - unfocus(selmon->sel, 1, NULL); + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); selmon = m; focus(NULL); } - - for (bar = selmon->bar; bar; bar = bar->next) { - if (ev->window == bar->win) { - for (r = 0; r < LENGTH(barrules); r++) { - br = &barrules[r]; - if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) - continue; - if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->num) - continue; - if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { - carg.x = ev->x - bar->x[r]; - carg.y = ev->y - bar->borderpx; - carg.w = bar->w[r]; - carg.h = bar->bh - 2 * bar->borderpx; - click = br->clickfunc(bar, &arg, &carg); - if (click < 0) - return; - break; - } - } - break; - } - } - - - if (click == ClkRootWin && (c = wintoclient(ev->window))) { + 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 + click = ClkStatusText; + } else if ((c = wintoclient(ev->window))) { focus(c); restack(selmon); XAllowEvents(dpy, ReplayPointer, CurrentTime); click = ClkClientWin; } - - for (i = 0; i < LENGTH(buttons); i++) { + 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 - ); - } - } + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); } void @@ -644,19 +509,12 @@ checkotherwm(void) void cleanup(void) { - Monitor *m; + Arg a = {.ui = ~0}; Layout foo = { "", NULL }; + Monitor *m; size_t i; - - /* kill child processes */ - for (i = 0; i < autostart_len; i++) { - if (0 < autostart_pids[i]) { - kill(autostart_pids[i], SIGTERM); - waitpid(autostart_pids[i], NULL, 0); - } - } - + view(&a); selmon->lt[selmon->sellt] = &foo; for (m = mons; m; m = m->next) while (m->stack) @@ -674,14 +532,12 @@ cleanup(void) XSync(dpy, False); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); XDeleteProperty(dpy, root, netatom[NetActiveWindow]); - } void cleanupmon(Monitor *mon) { Monitor *m; - Bar *bar; if (mon == mons) mons = mons->next; @@ -689,14 +545,8 @@ cleanupmon(Monitor *mon) for (m = mons; m && m->next != mon; m = m->next); m->next = mon->next; } - for (bar = mon->bar; bar; bar = mon->bar) { - if (!bar->external) { - XUnmapWindow(dpy, bar->win); - XDestroyWindow(dpy, bar->win); - } - mon->bar = bar->next; - free(bar); - } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); free(mon); } @@ -706,17 +556,13 @@ 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]) { + || 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 - ))); - } + || (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); @@ -746,24 +592,23 @@ void configurenotify(XEvent *e) { Monitor *m; - Bar *bar; 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, sh); + 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); - for (bar = m->bar; bar; bar = bar->next) - XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + XMoveResizeWindow(dpy, m->barwin, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh); } focus(NULL); arrange(NULL); @@ -779,9 +624,6 @@ configurerequest(XEvent *e) XConfigureRequestEvent *ev = &e->xconfigurerequest; XWindowChanges wc; - if (ignoreconfigurerequests) - return; - if ((c = wintoclient(ev->window))) { if (ev->value_mask & CWBorderWidth) c->bw = ev->border_width; @@ -804,7 +646,7 @@ configurerequest(XEvent *e) 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 */ + 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))) @@ -829,53 +671,18 @@ configurerequest(XEvent *e) Monitor * createmon(void) { - Monitor *m, *mon; - int i, n, mi, max_bars = 2, istopbar = topbar; - - const BarRule *br; - Bar *bar; + Monitor *m; m = ecalloc(1, sizeof(Monitor)); m->tagset[0] = m->tagset[1] = 1; m->mfact = mfact; m->nmaster = nmaster; m->showbar = showbar; - m->gappih = gappih; - m->gappiv = gappiv; - m->gappoh = gappoh; - m->gappov = gappov; - for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index - m->num = mi; + m->topbar = topbar; + m->gappx = gappx; m->lt[0] = &layouts[0]; m->lt[1] = &layouts[1 % LENGTH(layouts)]; strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); - - /* Derive the number of bars for this monitor based on bar rules */ - for (n = -1, i = 0; i < LENGTH(barrules); i++) { - br = &barrules[i]; - if (br->monitor == 'A' || br->monitor == -1 || br->monitor == m->num) - n = MAX(br->bar, n); - } - - m->bar = NULL; - for (i = 0; i <= n && i < max_bars; i++) { - bar = ecalloc(1, sizeof(Bar)); - bar->mon = m; - bar->idx = i; - bar->next = m->bar; - bar->topbar = istopbar; - m->bar = bar; - istopbar = !istopbar; - bar->showbar = 1; - bar->external = 0; - bar->borderpx = 0; - bar->bh = bh + bar->borderpx * 2; - bar->borderscheme = SchemeNorm; - } - - - - return m; } @@ -887,8 +694,6 @@ destroynotify(XEvent *e) if ((c = wintoclient(ev->window))) unmanage(c, 1); - else if ((c = swallowingclient(ev->window))) - unmanage(c->swallowing, 1); } void @@ -898,7 +703,6 @@ detach(Client *c) for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); *tc = c->next; - c->next = NULL; } void @@ -913,7 +717,6 @@ detachstack(Client *c) for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); c->mon->sel = t; } - c->snext = NULL; } Monitor * @@ -934,140 +737,50 @@ dirtomon(int dir) void drawbar(Monitor *m) { - Bar *bar; - - if (m->showbar) - for (bar = m->bar; bar; bar = bar->next) - drawbarwin(bar); -} - -void -drawbars(void) -{ - Monitor *m; - for (m = mons; m; m = m->next) - drawbar(m); -} + int x, w, tw = 0; + unsigned int i, occ = 0, urg = 0; + Client *c; -void -drawbarwin(Bar *bar) -{ - if (!bar || !bar->win || bar->external) + if (!m->showbar) return; - int r, w, total_drawn = 0; - int rx, lx, rw, lw; // bar size, split between left and right if a center module is added - const BarRule *br; - if (bar->borderpx) { - XSetForeground(drw->dpy, drw->gc, scheme[bar->borderscheme][ColBorder].pixel); - XFillRectangle(drw->dpy, drw->drawable, drw->gc, 0, 0, bar->bw, bar->bh); + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon || 1) { /* 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 - 2 * sp, 0, tw, bh, 0, stext, 0); } - BarArg warg = { 0 }; - BarArg darg = { 0 }; - warg.h = bar->bh - 2 * bar->borderpx; - - rw = lw = bar->bw - 2 * bar->borderpx; - rx = lx = bar->borderpx; - + 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); + x += w; + } + w = TEXTW(m->ltsymbol); drw_setscheme(drw, scheme[SchemeNorm]); - drw_rect(drw, lx, bar->borderpx, lw, bar->bh - 2 * bar->borderpx, 1, 1); - for (r = 0; r < LENGTH(barrules); r++) { - br = &barrules[r]; - if (br->bar != bar->idx || !br->widthfunc || (br->monitor == 'A' && bar->mon != selmon)) - continue; - if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->num) - continue; - drw_setscheme(drw, scheme[SchemeNorm]); - warg.w = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); - - w = br->widthfunc(bar, &warg); - w = MIN(warg.w, w); - - if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa - lw = rw; - lx = rx; - } else if (rw <= 0) { - rw = lw; - rx = lx; - } + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); - switch(br->alignment) { - default: - case BAR_ALIGN_NONE: - case BAR_ALIGN_LEFT_LEFT: - case BAR_ALIGN_LEFT: - bar->x[r] = lx; - if (lx == rx) { - rx += w; - rw -= w; - } - lx += w; - lw -= w; - break; - case BAR_ALIGN_LEFT_RIGHT: - case BAR_ALIGN_RIGHT: - bar->x[r] = lx + lw - w; - if (lx == rx) - rw -= w; - lw -= w; - break; - case BAR_ALIGN_LEFT_CENTER: - case BAR_ALIGN_CENTER: - bar->x[r] = lx + lw / 2 - w / 2; - if (lx == rx) { - rw = rx + rw - bar->x[r] - w; - rx = bar->x[r] + w; - } - lw = bar->x[r] - lx; - break; - case BAR_ALIGN_RIGHT_LEFT: - bar->x[r] = rx; - if (lx == rx) { - lx += w; - lw -= w; - } - rx += w; - rw -= w; - break; - case BAR_ALIGN_RIGHT_RIGHT: - bar->x[r] = rx + rw - w; - if (lx == rx) - lw -= w; - rw -= w; - break; - case BAR_ALIGN_RIGHT_CENTER: - bar->x[r] = rx + rw / 2 - w / 2; - if (lx == rx) { - lw = lx + lw - bar->x[r] + w; - lx = bar->x[r] + w; - } - rw = bar->x[r] - rx; - break; - } - bar->w[r] = w; - darg.x = bar->x[r]; - darg.y = bar->borderpx; - darg.h = bar->bh - 2 * bar->borderpx; - darg.w = bar->w[r]; - if (br->drawfunc) - total_drawn += br->drawfunc(bar, &darg); + if ((w = m->ww - tw - x) > bh) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w - 2 * sp, bh, 1, 1); } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +} - if (total_drawn == 0 && bar->showbar) { - bar->showbar = 0; - updatebarpos(bar->mon); - XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); - arrange(bar->mon); - } - else if (total_drawn > 0 && !bar->showbar) { - bar->showbar = 1; - updatebarpos(bar->mon); - XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); - drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); - arrange(bar->mon); - } else - drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); +void +drawbars(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawbar(m); } void @@ -1082,7 +795,7 @@ enternotify(XEvent *e) c = wintoclient(ev->window); m = c ? c->mon : wintomon(ev->window); if (m != selmon) { - unfocus(selmon->sel, 1, c); + unfocus(selmon->sel, 1); selmon = m; } else if (!c || c == selmon->sel) return; @@ -1095,9 +808,8 @@ expose(XEvent *e) Monitor *m; XExposeEvent *ev = &e->xexpose; - if (ev->count == 0 && (m = wintomon(ev->window))) { + if (ev->count == 0 && (m = wintomon(ev->window))) drawbar(m); - } } void @@ -1106,7 +818,7 @@ 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, c); + unfocus(selmon->sel, 0); if (c) { if (c->mon != selmon) selmon = c->mon; @@ -1115,10 +827,7 @@ focus(Client *c) detachstack(c); attachstack(c); grabbuttons(c, 1); - if (c->isfloating) - XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel); - else - XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); setfocus(c); } else { XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); @@ -1126,7 +835,6 @@ focus(Client *c) } selmon->sel = c; drawbars(); - } /* there are some broken focus acquiring clients needing extra handling */ @@ -1148,9 +856,10 @@ focusmon(const Arg *arg) return; if ((m = dirtomon(arg->i)) == selmon) return; - unfocus(selmon->sel, 0, NULL); + unfocus(selmon->sel, 0); selmon = m; focus(NULL); + warp(selmon->sel); } void @@ -1180,17 +889,14 @@ focusstack(const Arg *arg) } Atom -getatomprop(Client *c, Atom prop, Atom req) +getatomprop(Client *c, Atom prop) { int di; unsigned long dl; unsigned char *p = NULL; Atom da, atom = None; - - /* FIXME getatomprop should return the number of items and a pointer to - * the stored data instead of this workaround */ - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, &da, &di, &dl, &dl, &p) == Success && p) { atom = *(Atom *)p; XFree(p); @@ -1238,13 +944,11 @@ gettextprop(Window w, Atom atom, char *text, unsigned int size) text[0] = '\0'; if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) return 0; - if (name.encoding == XA_STRING) + 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); - } + } 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); @@ -1263,8 +967,7 @@ grabbuttons(Client *c, int 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 - ) + if (buttons[i].click == ClkClientWin) for (j = 0; j < LENGTH(modifiers); j++) XGrabButton(dpy, buttons[i].button, buttons[i].mask | modifiers[j], @@ -1278,16 +981,26 @@ grabkeys(void) { updatenumlockmask(); { - unsigned int i, j; + unsigned int i, j, k; unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; - KeyCode code; + int start, end, skip; + KeySym *syms; 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); + XDisplayKeycodes(dpy, &start, &end); + syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); + if (!syms) + return; + for (k = start; k <= end; k++) + for (i = 0; i < LENGTH(keys); i++) + /* skip modifier codes, we do that ourselves */ + if (keys[i].keysym == syms[(k - start) * skip]) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, k, + keys[i].mod | modifiers[j], + root, True, + GrabModeAsync, GrabModeAsync); + XFree(syms); } } @@ -1314,18 +1027,16 @@ void keypress(XEvent *e) { unsigned int i; - int keysyms_return; - KeySym* keysym; + KeySym keysym; XKeyEvent *ev; ev = &e->xkey; - keysym = XGetKeyboardMapping(dpy, (KeyCode)ev->keycode, 1, &keysyms_return); + 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) + if (keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) keys[i].func(&(keys[i].arg)); - XFree(keysym); } void @@ -1333,8 +1044,7 @@ killclient(const Arg *arg) { if (!selmon->sel) return; - if (!sendevent(selmon->sel, wmatom[WMDelete])) - { + if (!sendevent(selmon->sel, wmatom[WMDelete])) { XGrabServer(dpy); XSetErrorHandler(xerrordummy); XSetCloseDownMode(dpy, DestroyAll); @@ -1345,86 +1055,95 @@ killclient(const Arg *arg) } } +void +loadxrdb() +{ + Display *display; + char * resm; + XrmDatabase xrdb; + char *type; + XrmValue value; + + display = XOpenDisplay(NULL); + + if (display != NULL) { + resm = XResourceManagerString(display); + + if (resm != NULL) { + xrdb = XrmGetStringDatabase(resm); + + if (xrdb != NULL) { + XRDB_LOAD_COLOR("dwm.color2", normbordercolor); + XRDB_LOAD_COLOR("dwm.background", normbgcolor); + XRDB_LOAD_COLOR("dwm.color7", normfgcolor); + XRDB_LOAD_COLOR("dwm.color6", selbordercolor); + XRDB_LOAD_COLOR("dwm.color11", selbgcolor); + XRDB_LOAD_COLOR("dwm.color15", selfgcolor); + } + } + } + + XCloseDisplay(display); +} + void manage(Window w, XWindowAttributes *wa) { Client *c, *t = NULL; - Client *term = NULL; Window trans = None; XWindowChanges wc; c = ecalloc(1, sizeof(Client)); c->win = w; - c->pid = winpid(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); - + updatetitle(c); if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { c->mon = t->mon; c->tags = t->tags; - c->bw = borderpx; } else { c->mon = selmon; - c->bw = borderpx; applyrules(c); - term = termforwin(c); - if (term) - c->mon = term->mon; } - if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) - c->x = c->mon->mx + c->mon->mw - WIDTH(c); - if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) - c->y = c->mon->my + c->mon->mh - HEIGHT(c); - c->x = MAX(c->x, c->mon->mx); - /* only fix client y-offset, if the client center might cover the bar */ - c->y = MAX(c->y, ((!c->mon->bar || c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) - && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + 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); - if (c->isfloating) - XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel); - else - XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); updatesizehints(c); - if (getatomprop(c, netatom[NetWMState], XA_ATOM) == netatom[NetWMFullscreen]) - setfullscreen(c, 1); updatewmhints(c); - - XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); grabbuttons(c, 0); - if (!c->isfloating) c->isfloating = c->oldstate = trans != None || c->isfixed; - if (c->isfloating) { + if (c->isfloating) XRaiseWindow(dpy, c->win); - XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel); - } - attachx(c); + attachaside(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); + unfocus(selmon->sel, 0); c->mon->sel = c; - if (!(term && swallow(term, c))) { - arrange(c->mon); - XMapWindow(dpy, c->win); - } + arrange(c->mon); + XMapWindow(dpy, c->win); focus(NULL); - } void @@ -1443,33 +1162,38 @@ maprequest(XEvent *e) static XWindowAttributes wa; XMapRequestEvent *ev = &e->xmaprequest; - - if (!XGetWindowAttributes(dpy, ev->window, &wa)) - return; - if (wa.override_redirect) + 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, ""); + 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; - Bar *bar; XMotionEvent *ev = &e->xmotion; - if ((bar = wintobar(ev->window))) { - barhover(e, bar); - return; - } - - if (ev->window != root) return; if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { - unfocus(selmon->sel, 1, NULL); + unfocus(selmon->sel, 1); selmon = m; focus(NULL); } @@ -1490,14 +1214,13 @@ movemouse(const Arg *arg) if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ return; restack(selmon); - nx = ocx = c->x; - ny = ocy = c->y; + 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; - ignoreconfigurerequests = 1; do { XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); switch(ev.type) { @@ -1522,27 +1245,29 @@ movemouse(const Arg *arg) 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)) { + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) togglefloating(NULL); - } - if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { + 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) { - if (c->tags & SPTAGMASK) { - c->mon->tagset[c->mon->seltags] ^= (c->tags & SPTAGMASK); - m->tagset[m->seltags] |= (c->tags & SPTAGMASK); - } sendmon(c, m); selmon = m; focus(NULL); } - ignoreconfigurerequests = 0; +} + +Client * +nexttagged(Client *c) { + Client *walked = c->mon->clients; + for(; + walked && (walked->isfloating || !ISVISIBLEONTAG(walked, c->tags)); + walked = walked->next + ); + return walked; } Client * @@ -1568,13 +1293,11 @@ propertynotify(XEvent *e) Window trans; XPropertyEvent *ev = &e->xproperty; - - if ((ev->window == root) && (ev->atom == XA_WM_NAME)) { - if (!fake_signal()) - updatestatus(); - } else if (ev->state == PropertyDelete) { + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) return; /* ignore */ - } else if ((c = wintoclient(ev->window))) { + else if ((c = wintoclient(ev->window))) { switch(ev->atom) { default: break; case XA_WM_TRANSIENT_FOR: @@ -1587,21 +1310,20 @@ propertynotify(XEvent *e) break; case XA_WM_HINTS: updatewmhints(c); - if (c->isurgent) - drawbars(); + drawbars(); break; } - if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + 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; } @@ -1644,7 +1366,7 @@ resizeclient(Client *c, int x, int y, int w, int h) void resizemouse(const Arg *arg) { - int ocx, ocy, nw, nh, nx, ny; + int x, y, ocw, och, nw, nh; Client *c; Monitor *m; XEvent ev; @@ -1655,15 +1377,13 @@ resizemouse(const Arg *arg) if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ return; restack(selmon); - nx = ocx = c->x; - ny = ocy = c->y; - nh = c->h; - nw = c->w; + ocw = c->w; + och = c->h; 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); - ignoreconfigurerequests = 1; + if(!getrootptr(&x, &y)) + return; do { XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); switch(ev.type) { @@ -1677,42 +1397,33 @@ resizemouse(const Arg *arg) 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); + nw = MAX(ocw + (ev.xmotion.x - x), 1); + nh = MAX(och + (ev.xmotion.y - y), 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)) { + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) togglefloating(NULL); - } - } - if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { - resize(c, nx, ny, nw, nh, 1); } + 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) { - if (c->tags & SPTAGMASK) { - c->mon->tagset[c->mon->seltags] ^= (c->tags & SPTAGMASK); - m->tagset[m->seltags] |= (c->tags & SPTAGMASK); - } sendmon(c, m); selmon = m; focus(NULL); } - ignoreconfigurerequests = 0; } void restack(Monitor *m) { - Client *c, *f = NULL; + Client *c; XEvent ev; XWindowChanges wc; @@ -1723,19 +1434,15 @@ restack(Monitor *m) XRaiseWindow(dpy, m->sel->win); if (m->lt[m->sellt]->arrange) { wc.stack_mode = Below; - if (m->bar) { - wc.sibling = m->bar->win; - } else { - for (f = m->stack; f && (f->isfloating || !ISVISIBLE(f)); f = f->snext); // find first tiled stack client - if (f) - wc.sibling = f->win; - } + wc.sibling = m->barwin; for (c = m->stack; c; c = c->snext) - if (!c->isfloating && ISVISIBLE(c) && c != f) { + if (!c->isfloating && ISVISIBLE(c)) { XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); wc.sibling = c->win; } } + if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle) + warp(m->sel); XSync(dpy, False); while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); } @@ -1746,19 +1453,14 @@ run(void) XEvent ev; /* main event loop */ XSync(dpy, False); - while (running && !XNextEvent(dpy, &ev)) { - - + while (running && !XNextEvent(dpy, &ev)) if (handler[ev.type]) handler[ev.type](&ev); /* call handler */ - } } void scan(void) { - scanner = 1; - char swin[256]; unsigned int i, num; Window d1, d2, *wins = NULL; XWindowAttributes wa; @@ -1770,8 +1472,6 @@ scan(void) continue; if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) manage(wins[i], &wa); - else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin)) - manage(wins[i], &wa); } for (i = 0; i < num; i++) { /* now the transients */ if (!XGetWindowAttributes(dpy, wins[i], &wa)) @@ -1780,9 +1480,9 @@ scan(void) && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) manage(wins[i], &wa); } - XFree(wins); + if (wins) + XFree(wins); } - scanner = 0; } void @@ -1790,13 +1490,12 @@ sendmon(Client *c, Monitor *m) { if (c->mon == m) return; - unfocus(c, 1, NULL); + unfocus(c, 1); detach(c); detachstack(c); c->mon = m; - if (!(c->tags & SPTAGMASK)) c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ - attachx(c); + attachaside(c); attachstack(c); focus(NULL); arrange(NULL); @@ -1824,7 +1523,6 @@ sendevent(Client *c, Atom proto) exists = protocols[n] == proto; XFree(protocols); } - if (exists) { ev.type = ClientMessage; ev.xclient.window = c->win; @@ -1856,8 +1554,8 @@ setfullscreen(Client *c, int fullscreen) XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); c->isfullscreen = 1; - c->oldbw = c->bw; 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); @@ -1866,8 +1564,8 @@ setfullscreen(Client *c, int fullscreen) XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, PropModeReplace, (unsigned char*)0, 0); c->isfullscreen = 0; - c->bw = c->oldbw; c->isfloating = c->oldstate; + c->bw = c->oldbw; c->x = c->oldx; c->y = c->oldy; c->w = c->oldw; @@ -1877,15 +1575,23 @@ setfullscreen(Client *c, int fullscreen) } } +void +setgaps(const Arg *arg) +{ + if ((arg->i == 0) || (selmon->gappx + arg->i < 0)) + selmon->gappx = 0; + else + selmon->gappx += arg->i; + arrange(selmon); +} + void setlayout(const Arg *arg) { - if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) { + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) selmon->sellt ^= 1; - } if (arg && arg->v) selmon->lt[selmon->sellt] = (Layout *)arg->v; - strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); if (selmon->sel) arrange(selmon); @@ -1914,26 +1620,35 @@ setup(void) int i; XSetWindowAttributes wa; Atom utf8string; + struct sigaction sa; - /* clean up any zombies immediately */ - sigchld(0); + /* do not transform children into zombies when they terminate */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + /* clean up any zombies (inherited from .xinitrc etc) immediately */ + while (waitpid(-1, NULL, WNOHANG) > 0); - /* the one line of bloat that would have saved a lot of time for a lot of people */ - putenv("_JAVA_AWT_WM_NONREPARENTING=1"); + signal(SIGHUP, sighup); + signal(SIGTERM, sigterm); /* init screen */ screen = DefaultScreen(dpy); sw = DisplayWidth(dpy, screen); sh = DisplayHeight(dpy, screen); root = RootWindow(dpy, screen); - xinitvisual(); - drw = drw_create(dpy, screen, root, sw, sh, visual, depth, cmap); + 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; + bh = drw->fonts->h + user_bh; + + sp = sidepad; + vp = (topbar == 1) ? vertpad : - vertpad; updategeom(); + /* init atoms */ utf8string = XInternAtom(dpy, "UTF8_STRING", False); wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); @@ -1947,6 +1662,7 @@ setup(void) 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); @@ -1955,11 +1671,10 @@ setup(void) /* init appearance */ scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); for (i = 0; i < LENGTH(colors); i++) - scheme[i] = drw_scm_create(drw, colors[i], alphas[i], ColCount); - + 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, @@ -1979,13 +1694,10 @@ setup(void) |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); XSelectInput(dpy, root, wa.event_mask); - - grabkeys(); focus(NULL); } - void seturgent(Client *c, int urg) { @@ -2005,15 +1717,9 @@ 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 - ) + 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 { @@ -2024,50 +1730,44 @@ showhide(Client *c) } void -sigchld(int unused) +sighup(int unused) { - pid_t pid; - if (signal(SIGCHLD, sigchld) == SIG_ERR) - die("can't install SIGCHLD handler:"); - while (0 < (pid = waitpid(-1, NULL, WNOHANG))) { - pid_t *p, *lim; - - if (!(p = autostart_pids)) - continue; - lim = &p[autostart_len]; + Arg a = {.i = 1}; + quit(&a); +} - for (; p < lim; p++) { - if (*p == pid) { - *p = -1; - break; - } - } - } +void +sigterm(int unused) +{ + Arg a = {.i = 0}; + quit(&a); } void spawn(const Arg *arg) { + struct sigaction sa; + if (arg->v == dmenucmd) dmenumon[0] = '0' + selmon->num; - - if (fork() == 0) - { + if (fork() == 0) { if (dpy) close(ConnectionNumber(dpy)); - setsid(); + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &sa, NULL); + execvp(((char **)arg->v)[0], (char **)arg->v); - fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); - perror(" failed"); - exit(EXIT_SUCCESS); + 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); @@ -2078,45 +1778,60 @@ tag(const Arg *arg) void tagmon(const Arg *arg) { - Client *c = selmon->sel; - Monitor *dest; - if (!c || !mons->next) + 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; - dest = dirtomon(arg->i); - sendmon(c, dest); + + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww - m->gappx; + for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx; + resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0); + if (my + HEIGHT(c) + m->gappx < m->wh) + my += HEIGHT(c) + m->gappx; + } else { + h = (m->wh - ty) / (n - i) - m->gappx; + resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0); + if (ty + HEIGHT(c) + m->gappx < m->wh) + ty += HEIGHT(c) + m->gappx; + } } void togglebar(const Arg *arg) { - Bar *bar; selmon->showbar = !selmon->showbar; updatebarpos(selmon); - for (bar = selmon->bar; bar; bar = bar->next) - XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, bh); arrange(selmon); } void togglefloating(const Arg *arg) { - Client *c = selmon->sel; - if (arg && arg->v) - c = (Client*)arg->v; - if (!c) + if (!selmon->sel) return; - if (c->isfullscreen) /* no support for fullscreen windows */ + if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ return; - c->isfloating = !c->isfloating || c->isfixed; - if (c->isfloating) - XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel); - else - XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); - if (c->isfloating) { - resize(c, c->x, c->y, c->w, c->h, 0); - } - arrange(c->mon); - + 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 @@ -2137,27 +1852,22 @@ toggletag(const Arg *arg) void toggleview(const Arg *arg) { - unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);; - + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; - focus(NULL); arrange(selmon); } } void -unfocus(Client *c, int setfocus, Client *nextfocus) +unfocus(Client *c, int setfocus) { if (!c) return; grabbuttons(c, 0); - if (c->isfloating) - XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColFloat].pixel); - else - XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); if (setfocus) { XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); XDeleteProperty(dpy, root, netatom[NetActiveWindow]); @@ -2170,20 +1880,6 @@ unmanage(Client *c, int destroyed) Monitor *m = c->mon; XWindowChanges wc; - if (c->swallowing) { - unswallow(c); - return; - } - - Client *s = swallowingclient(c->win); - if (s) { - free(s->swallowing); - s->swallowing = NULL; - arrange(m); - focus(NULL); - return; - } - detach(c); detachstack(c); if (!destroyed) { @@ -2198,11 +1894,7 @@ unmanage(Client *c, int destroyed) XSetErrorHandler(xerror); XUngrabServer(dpy); } - - free(c); - if (s) - return; focus(NULL); updateclientlist(); arrange(m); @@ -2225,69 +1917,40 @@ unmapnotify(XEvent *e) void updatebars(void) { - Bar *bar; Monitor *m; XSetWindowAttributes wa = { .override_redirect = True, - .background_pixel = 0, - .border_pixel = 0, - .colormap = cmap, + .background_pixmap = ParentRelative, .event_mask = ButtonPressMask|ExposureMask }; XClassHint ch = {"dwm", "dwm"}; for (m = mons; m; m = m->next) { - for (bar = m->bar; bar; bar = bar->next) { - if (bar->external) - continue; - if (!bar->win) { - bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, - InputOutput, visual, - CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); - XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); - XMapRaised(dpy, bar->win); - XSetClassHint(dpy, bar->win, &ch); - } - } + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx + sp, m->by + vp, m->ww - 2 * sp, 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->wx = m->mx; m->wy = m->my; - m->ww = m->mw; m->wh = m->mh; - Bar *bar; - int y_pad = 0; - int x_pad = 0; - - - for (bar = m->bar; bar; bar = bar->next) { - bar->bx = m->wx + x_pad; - bar->bw = m->ww - 2 * x_pad; - } - - for (bar = m->bar; bar; bar = bar->next) - if (!m->showbar || !bar->showbar) - bar->by = -bar->bh - y_pad; - - - if (!m->showbar) - return; - for (bar = m->bar; bar; bar = bar->next) { - if (!bar->showbar) - continue; - if (bar->topbar) - m->wy = m->wy + bar->bh + y_pad; - m->wh -= y_pad + bar->bh; - bar->by = (bar->topbar ? m->wy - bar->bh : m->wy + m->wh); - } + if (m->showbar) { + m->wh = m->wh - vertpad - bh; + m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; + m->wy = m->topbar ? m->wy + bh + vp : m->wy; + } else + m->by = -bh - vp; } void -updateclientlist() +updateclientlist(void) { Client *c; Monitor *m; @@ -2298,7 +1961,6 @@ updateclientlist() XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, (unsigned char *) &(c->win), 1); - } int @@ -2322,6 +1984,7 @@ updategeom(void) 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); @@ -2351,7 +2014,7 @@ updategeom(void) m->clients = c->next; detachstack(c); c->mon = mons; - attach(c); + attachaside(c); attachstack(c); } if (m == selmon) @@ -2441,22 +2104,32 @@ updatesizehints(Client *c) void updatestatus(void) { - Monitor *m; + Monitor* m; if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) strcpy(stext, "dwm-"VERSION); - for (m = mons; m; m = m->next) + for(m = mons; m; m = m->next) drawbar(m); } 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 @@ -2470,12 +2143,6 @@ updatewmhints(Client *c) XSetWMHints(dpy, c->win, wmh); } else c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; - if (c->isurgent) { - if (c->isfloating) - XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColFloat].pixel); - else - XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColBorder].pixel); - } if (wmh->flags & InputHint) c->neverfocus = !wmh->input; else @@ -2488,9 +2155,7 @@ void view(const Arg *arg) { 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; @@ -2498,6 +2163,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) { @@ -2517,14 +2204,12 @@ wintomon(Window w) int x, y; Client *c; Monitor *m; - Bar *bar; if (w == root && getrootptr(&x, &y)) return recttomon(x, y, 1, 1); for (m = mons; m; m = m->next) - for (bar = m->bar; bar; bar = bar->next) - if (w == bar->win) - return m; + if (w == m->barwin) + return m; if ((c = wintoclient(w))) return c->mon; return selmon; @@ -2566,25 +2251,26 @@ xerrorstart(Display *dpy, XErrorEvent *ee) return -1; } +void +xrdb(const Arg *arg) +{ + loadxrdb(); + int i; + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + focus(NULL); + arrange(NULL); +} + void zoom(const Arg *arg) { Client *c = selmon->sel; - if (arg && arg->v) - c = (Client*)arg->v; - if (!c) - return; - - - if (!c->mon->lt[c->mon->sellt]->arrange - || (c && c->isfloating) - ) + if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) + return; + if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) return; - - if (c == nexttiled(c->mon->clients)) - if (!c || !(c = nexttiled(c->next))) - return; pop(c); } @@ -2599,21 +2285,18 @@ main(int argc, char *argv[]) fputs("warning: no locale support\n", stderr); if (!(dpy = XOpenDisplay(NULL))) die("dwm: cannot open display"); - if (!(xcon = XGetXCBConnection(dpy))) - die("dwm: cannot get xcb connection\n"); checkotherwm(); - XrmInitialize(); - loadxrdb(); - autostart_exec(); + XrmInitialize(); + loadxrdb(); setup(); #ifdef __OpenBSD__ - if (pledge("stdio rpath proc exec ps", NULL) == -1) + if (pledge("stdio rpath proc exec", NULL) == -1) die("pledge"); #endif /* __OpenBSD__ */ scan(); run(); + if(restart) execvp(argv[0], argv); cleanup(); XCloseDisplay(dpy); return EXIT_SUCCESS; } - diff --git a/.suckless/dwm/dwm.c.orig b/.suckless/dwm/dwm.c.orig new file mode 100644 index 0000000..7fd5c88 --- /dev/null +++ b/.suckless/dwm/dwm.c.orig @@ -0,0 +1,2278 @@ +/* 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 +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#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 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 TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define XRDB_LOAD_COLOR(R,V) if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) { \ + if (value.addr != NULL && strnlen(value.addr, 8) == 7 && value.addr[0] == '#') { \ + int i = 1; \ + for (; i <= 6; i++) { \ + if (value.addr[i] < 48) break; \ + if (value.addr[i] > 57 && value.addr[i] < 65) break; \ + if (value.addr[i] > 70 && value.addr[i] < 97) break; \ + if (value.addr[i] > 102) break; \ + } \ + if (i == 7) { \ + strncpy(V, value.addr, 7); \ + V[7] = '\0'; \ + } \ + } \ + } + +/* 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, 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; + +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 */ + int gappx; /* gaps between windows */ + 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]; +}; + +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 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 loadxrdb(void); +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 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 setgaps(const Arg *arg); +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 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 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 xrdb(const Arg *arg); +static void zoom(const Arg *arg); +static void sighup(int unused); +static void sigterm(int unused); + + +/* 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 vp; /* vertical padding for bar */ +static int sp; /* side padding for bar */ +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" + +/* 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; + 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]; +} + +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 +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 + click = ClkStatusText; + } 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 + sp, m->by + vp, m->ww - 2 * sp, 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; + + 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->gappx = gappx; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + 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; + 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 || 1) { /* 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 - 2 * sp, 0, tw, bh, 0, 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); + 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) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w - 2 * sp, 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, k; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + int start, end, skip; + KeySym *syms; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XDisplayKeycodes(dpy, &start, &end); + syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); + if (!syms) + return; + for (k = start; k <= end; k++) + for (i = 0; i < LENGTH(keys); i++) + /* skip modifier codes, we do that ourselves */ + if (keys[i].keysym == syms[(k - start) * skip]) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, k, + keys[i].mod | modifiers[j], + root, True, + GrabModeAsync, GrabModeAsync); + XFree(syms); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = 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 +loadxrdb() +{ + Display *display; + char * resm; + XrmDatabase xrdb; + char *type; + XrmValue value; + + display = XOpenDisplay(NULL); + + if (display != NULL) { + resm = XResourceManagerString(display); + + if (resm != NULL) { + xrdb = XrmGetStringDatabase(resm); + + if (xrdb != NULL) { + XRDB_LOAD_COLOR("dwm.color2", normbordercolor); + XRDB_LOAD_COLOR("dwm.background", normbgcolor); + XRDB_LOAD_COLOR("dwm.color7", normfgcolor); + XRDB_LOAD_COLOR("dwm.color6", selbordercolor); + XRDB_LOAD_COLOR("dwm.color11", selbgcolor); + XRDB_LOAD_COLOR("dwm.color15", selfgcolor); + } + } + } + + XCloseDisplay(display); +} + +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); + 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); + attach(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, ""); + 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 (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 x, y, ocw, och, 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); + ocw = c->w; + och = c->h; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->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; + + nw = MAX(ocw + (ev.xmotion.x - x), 1); + nh = MAX(och + (ev.xmotion.y - y), 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); + 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; + } + } + if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle) + warp(m->sel); + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +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 +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 */ + attach(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 +setgaps(const Arg *arg) +{ + if ((arg->i == 0) || (selmon->gappx + arg->i < 0)) + selmon->gappx = 0; + else + selmon->gappx += arg->i; + arrange(selmon); +} + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt ^= 1; + if (arg && arg->v) + selmon->lt[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 = f; + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + struct sigaction sa; + + /* do not transform children into zombies when they terminate */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + + /* clean up any zombies (inherited from .xinitrc etc) immediately */ + while (waitpid(-1, NULL, WNOHANG) > 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; + bh = drw->fonts->h + user_bh; + + sp = sidepad; + vp = (topbar == 1) ? vertpad : - vertpad; + 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)) { + /* 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 +sighup(int unused) +{ + Arg a = {.i = 1}; + quit(&a); +} + +void +sigterm(int unused) +{ + Arg a = {.i = 0}; + quit(&a); +} + +void +spawn(const Arg *arg) +{ + struct sigaction sa; + + if (arg->v == dmenucmd) + dmenumon[0] = '0' + selmon->num; + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &sa, NULL); + + 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 - m->gappx; + for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx; + resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0); + if (my + HEIGHT(c) + m->gappx < m->wh) + my += HEIGHT(c) + m->gappx; + } else { + h = (m->wh - ty) / (n - i) - m->gappx; + resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0); + if (ty + HEIGHT(c) + m->gappx < m->wh) + ty += HEIGHT(c) + m->gappx; + } +} + +void +togglebar(const Arg *arg) +{ + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, 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 +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); + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + 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 + sp, m->by + vp, m->ww - 2 * sp, 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 = m->wh - vertpad - bh; + m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; + m->wy = m->topbar ? m->wy + bh + vp : m->wy; + } else + m->by = -bh - vp; +} + +void +updateclientlist(void) +{ + 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; + attach(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) +{ + Monitor* m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + for(m = mons; m; m = m->next) + drawbar(m); +} + +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) +{ + 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; + 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 +xrdb(const Arg *arg) +{ + loadxrdb(); + int i; + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + focus(NULL); + arrange(NULL); +} + +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(); + XrmInitialize(); + loadxrdb(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + run(); + if(restart) execvp(argv[0], argv); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} diff --git a/.suckless/dwm/dwm.c.rej b/.suckless/dwm/dwm.c.rej new file mode 100644 index 0000000..a9d5891 --- /dev/null +++ b/.suckless/dwm/dwm.c.rej @@ -0,0 +1,20 @@ +--- dwm.c ++++ dwm.c +@@ -1461,7 +1485,7 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ attachaside(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1914,7 +1938,7 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ attachaside(c); + attachstack(c); + } + if (m == selmon) diff --git a/.suckless/dwm/dwm.o b/.suckless/dwm/dwm.o index 871e25a880d0852d02eb26175cf0a2d14f455030..79ab0a87902b87826cd4caf362f2960da33ed1b3 100644 GIT binary patch literal 62464 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 literal 100416 zcmeFa4R}=5wLg3&2@oL61dTN+)!{PK1VIyxnvtk;;7oFY1B4JD0!l&>3>ZjEW*{m; z=n&!bI7+Kl?Z++cz3sJs+pDcr0V^hiFHtmLTk$)3Z9C%#;uj!R=KZa`*O^R)jNab& z`TyVNeP7R$%sJ<`_g;JNwbx#I?X~wgXRX&)nr5>p)?rhwQTp#JR8c(N?MFQV_9(f^ z*~%7zd$y#}uUVSavn8Fbbn`5Vqjw(kPtwgPHF{^4|9sutxCycK&gcBYb@$fIxb~0M z&5jOSMBmiS%J%4sgvpYtK1&*-AGQ-fO;0#gEKn~zoDV%$^o>mP2m=ES<) zAGHm)$QQk_XdJ* zYYa>TKailye*ySG6*X`1nZDmujoV073Q$c%x;J!VN~0(V-S+6l2}+d5i~t9jaS_y3 zkeTK)tG-t?Zj<25J3WP6#t>?Qp4nO`qfj@$O*g9Xxo(bc$R|jx)@e6OCyF z=FB2vS}~d;I~qZVIddTe=AvvvgSU-Q0-5HtlH;!$(~ufb`Z}KrjO+=vBVdGXda}Wo zz(=@uD*mBnMTu=5=oOgriZ7}tG3FK9_KA=o7wIL&KBJ;2`a*(qju^98b=0c6^@hZ*@F5cWlSW3I20BPEPZW=r~#G zAJTDhN?=IG=fk6CbeznhtITL-$H_5)VI7~32xLXC?KnA>&=Jw0OV+J%4!_1fX^w7u zIa@bA(Lq3RyK)O-B1O5e3$Y#RvquxYM^Ygm2KsTp z=n;{kzzPrgLYga6H{PMlWPx?#uOeY=Z(y0v^t-aBqbt^I&qQ868<-4yV|{&N zi+*F7e=|>kZS>8o?VHJ?HK`wLx}=*;F8Emv^Nb#@h=dt^#~q3_qyJvj5aqu+qt}fi zgU;yTR$%wSX*2r&bH#u=LDbM^)zs8nx%K)*8Nz$wAHvn&>0EGwx=_7Ay|G7~v;9Hz zq-WtoQpnw+&JuA7pVQ$v*|PtItZOMmH>PImlU~3buB!DdBY@FA?n>m{`MBFNmArTa zYPu;r9pui*gtMzX)*Xe9NMOcwxn6h(L(W#e)aF5#>48B;TT zq3%ko!pL<8(FNHC0;d|St z7;pv6=+%vcP9x--^aZbXWsdv8a zpQ)QS!-0F)@6kh@deN4F52Nrr&qH`cJ3Mp77nQpCNC%Y-HJKc|!=(hAx)~Ty)YI?1 zXyXr=o1zfWt<@0R1vf-M327<+jPdQbc8pZ#Ntc^+1{(Xx9hhhOT#cH!+Et?)epjw$ z*1Os@^FCJxQoP(}z{S_u>sbFjIOpv==ve;_F3OEBEYIXY&3JzMa70zN4#h9Bt1$IU zWAE`}g$Iq@s?)< z-fPuyf43T{JQX~dwd!2skY@Dwq`6%gRs!@%-dT?i6VXsay~u5{mcJu9CPhj$RCPhj=#Fj-}jtdGuzPc6nO1XPMV_=Bjvd>ruMJ#>8#*wL^ zs!6sA8A(<^jqOsage+9g_nC-0HKa|lY3>uNhX!}0Q7-0>N#3L1pH59`$0fRXsAYT^u}N3m(Ao1bFUq zBuMX)bmK?JbxVlL88i-9l1joOOFFJanjgT%n`h1G8Dzoq)M-wHtg3!aV zr9KeKht4r$b* zBaZFJ_S@;&34N1?kmv}EJwADTY==h%79g;I0*4pIFksN-u^mqT89EjO@{l-BHy$sh z@R5bbXl+Z}5NTxNb2+0UXgLbm_*}Yfm>x+91E{#{c(=np;SY)EDcaX9Oj4JGM->c!uabH(7 z4GU+%YTWDz>iESUSfaVA2O0Zou(}oepb?9KnTUpgKF)J#CFsz4)u^VF0SIrlr&{ehU;6D3Gia`0UuymR6){QARb5-PCA|Z#Cz~jib#qwHTF>R7nmEU zNCR3Qy#K;@ieO_yE`H4oS#Cun}Gy8n#puoMP&kHdG!y$knh@Q*IJw*4(rg)&*+ zy^*9L*q*64I?q8}DFR6%RzZEn-^1tsBNkK5Ke+M$Q7uV=Rr4;_L~;J3i{w~y?+y&# zf(r!;(nIq<%YVMG>l4+Hz1~Ig?3j1ZamkLAa|yZ2MZLmt$-b3k=sR!X|7Azsf1v}t z>$v2dmEZcras9qe9P^+1*ikK3jX!owj6}bU;Flej^sdZ7a1VmJ5G=GIf)AmGWl)Y@ zl&Ju;sbMmW@I&hH&-4FGU{M@AB*2$#M^x0K7%_1O64jGXJn(_V((D`7Ow9O-7Vj3cm zNCaM2-9K?rTLlhSik|v$UOAT7rJU6J%q*cbeKZ=C8y}Ib%tqr#L)18+hcs6X)`ZbX zD=MTc2X{J2bZkZ8bFP6p)NDb5|zd`_2-MV@uM_Hh+?hy?eJOepu-{NaBLNzP7{Ta z70o16{2=AbM!%c0bqt7#<>GZ8(0oQWC*hY=iKW!myGTN5;(;Y(U#Ombnp~NmJYtc@ zP*Ri80S&6TT97aFndWNeISXB*(ohVrbRFx-I?&CZA~Grh^XFb%1aEgi^aT{0tORb8 zh1SgLN;Gq7v3XsQIU5R8TJ=?pBPI|9!P%I=mf0$nvC+=EP-G|uRSZwqps-sv&tUy? z8K+FT^%?IovLHKBk2cZG zuE@`kVt1Ni?AIY-#wjj~4lM_9lSUAl?=L#6<^sk4Vr)NMIWGu^;WSq_-nS;H(K0J- z$mhxu!b&1#k!a6Dx-4bQSRV}h>~a&j8*B4tA~k4;ijrbMVU*n&7lD~*R&ggZ2nkE^ zAj0CIx}EO{uZ538-@~%NH0Wu9WU%qt_6w0u_%#G}pm8yQu_pNo zg!#3gXU;P;Q=>VNI!nmJctQ7Z5@AzV#pYa}v1i=sA67Zmzlf-gZGFSrW(1!WTGa0~ zgET;evJjcNyKQf}NuB+n<6jzk%+ey9^G~FgPBemIDBMeUw^>?D_}=u=0wYM8#Jrb6 zO_Q#5^KnrpDMl<9K~bCvQrNv4Q3#>1{ll$NObR~TgrrKqWtvn9^IoC^yC3y6A1B(H z8Kh!`&-k3g>&F{$9T_Dg>}k3Ve+CYj_xf-t>oK4#At)bN9JTu9gQs}HM^jfhTF%zj zeb9&OA%GLv{QHgT5Eaw-Y~Z-9$0bHo3=KA{@b$?z6B7tE~qjXAiXl!rvzY1T6WM@elulxVVs(}1Bx$p-ZEXRnIz(i<<+_9oK z8cZDY3csrunC=u|RY_g$cQg)&2>whBgZYYYE{$hI1u3m%T%UJw}~ zjH|Pd3z`_i*ln0r$H?x!M{wP(JN-i_tm64X$9b(DC^CwU(z{P&ViITssrbRC=~wY%hZ~P=iJcXLp~vcy zL*arch-To|DF_vI9rG$S|M0@D;2p5F?t|G+%><9i{KWn=FTQkwiSrLGUo!3!Bh;AwMTnqvkaGmX#7Fs zR2*ltCU_#vanEofQn%1GM%_-zmI`hqFM`k$E@T3+D$^xM;}J z`tZ7))Zp-oSZ1QSs7LLWhu{a86vp*y6Q-=vRMO2G(EDJ_lz;)~hK}`@;llLNSg~s( z@rI0h|0otr$NC(KhgLgJHYxZ@>PL2Nzy(4VO%T5&Aabp2Xg4*qM^pu-jWc*6)6uyP z<>ld`%H@J=bgXurJI|kK>qX;dM9=OY8@;alz3yK4b!pI$WU1~QYiDb4 zwq;HY<(!G2(h5^p5#;AX$CHJjVGxe-{N8ji@N4;p5Q{je!5$H&3iy;Aw_U}20z=|y zQW@NdabC`AAdj=*YyEtCjHGJa2V`js&`(4j1wdEGq;}?|0x2_=fFVo*1~$4vZv7L8 zlZb1LE+RCx*FO}V5ws++1=*38GDA1-6H_o0a^R$A-A*bC%_rn6p0=M##o!A);>3an zhG$Hb5_%6N1z%cP9}Se@crqaUnB~S!+Rs|<&Ts|BOC@ifdD)zb;NG<3y<>YH{xaY| zIBf#<57k4r=ZZe>^tq#fy_Q)^4xG$E7?~>>32%edMqP5NEUXzZnuv;`0ELHDGsETH z)#i3Q+Eb1;&)mKiJm*xlu+gP`uZaDh#k&KJEvUR=yb2$>AQRgaeKyt;{30{(E>Ur2fxe+?1OR@ zjQ$T|FwJ~3l4idBt7BvbiL}An^K5~k3nJ;Lh_yGF96Z+cfh&dvb;;`E4N{lxUxu0(paetN{IoffD5@b#Qx_IJkCz!lqL6JC>E> z-izfZY@x?wAA%qFG@51IA>>o6F-bSz02#`Ir6a64%&zlk$f1V;*5-u|_tFgH7I=r{ z4(}ca@A2K(=B@bcrs^@od@2$FYHp%A777ICgAi=9od&fN8xfa1R;v*~H=lk`(7=xZ zXNH_<1#@w0ss-s~W8~cHIOe`GoaRPGaEu5qN-uNDIoR>t85E9Dh;)e&bc*m|L);$U zoymbTav4FF2rMy#Zj~uCbJ3X2gXT;Zrdp&3v8=QMiw+}ITbJtYT~pv)f6#xCoMOq< z!B9p8d8AaM%h*d7Mz@yVJtdTJjx4Wc&UBhH^W?nEe3}Xka>H$V+^?*;n26;wF)<5~ z#7-D{1g#TBcmC%fHQcHit+c=d_*+&0itp|4A0pHSJgM}V+NLUA01C)?pPY~K_@|j- zMvH0@V_1lVz`;WVVmEAV}KA z@!lrI7`=PY84gWvjZFgjmPD%1lPiTucuf^p9-@qXvr` zy84PFD)WU}uI@qAq znhNU2(Xqu`P_^yO-!;ASaNr8vY?Z}=7L#h7*5Eike5K25aIQ&1K)t@OOPwD%A1w1p zm!;7S%WVs3gusw3xj11$orbxAobOx0a*$01Q3qI6S+;I>q!k9YY#l3DH9lZw48#Sf z=I>MwKytVVD_~Q%K#bclY*q47f(%oQhz75jS%J`&Vv13L@>ie|8O%debZV%m$S8r> zi=1GM84;{sBAz6q7!POfU$>-s|LnfCA3p0j-K$59n;@eq@tk>UPTFzqp4l2{2qA`1MpQlhvTS zAYd2?U}n>l9*p?!xE%&eQyK>h16Yv?u3e}&*8dJVh%y8Be%3*aDpUncTppkh_+I@Z z&8aS+S;Dq57vRw=4Bv{sQm?yiqBVWD+A3#XLR(pCM6@GZx5czN*56iY1qNhuYKion zt74`1_B;Sss`JUN;TVmQX=WWtCt44t^|gfQn(-wj1NnbKxzP3^1F!K{-7FA|Y0kz} zWNL|KoPzog2`!WUon^LA#u;k9>~A-s0E^72xvIM~!_oPBw0XHX14iU?j`hF9jcN`J zo=^j$EJ<`v&5UM|0}ROQ%*j)79i3|s%?g$|`A{UZF%~kd(x~QKEMNCQ`_T$$cJSm- zsN7tvSnWboErv-DY{^{cdsiNukbvCXj+uMmWP!`gaW@UQUU>mTOR8w5AODq2T!003t%Y~Hldsz|jj3b8fjw}iilqZ8goFseiB_yxNC^XT_e>>A1Z{hJX84+~ zOJ-3c`R-SBf}$YsF-S$avHa8@kv0ohQANba&k^o5K7eFLheDQB<3pOwZx}~TJ>K1A zwK&<2a66o4X*VX!33>JAC@O)TZyD4-5pEAM!{7db24b=E#<6}9$re9dTVl?JO1kf5 z5*9sp8=MpqV3c$SW-MrdgO*G%=!rL}S+I3M!FCdAn@CVZ5u+^kI#DrmD#)$xk>ud> z2>gyid8m&35b1)?kWmR+6Fads;tK0RfoK#@dUigDJF`MsL!DK)@xK9-hH{Z>G~06R zA*pbn!wNyt9}j9sy`j6K{#OQi5MTpst3lqPut?QU`aij#G!@RT806RM7KcA*NFtA&~6aEf2xxZerxX9c&4rV3yYZ7-g07MZ_@tOA#lqXi4S|6EER3QC&= zr=yCrBo#P!y19{vmb(v>JG7(a?rxm+PRD+&H;DQ>#EX@>yVs9LrUi3+R{SoXL)(F5 zIAPZRJtE>WI_N}8U0J&OD7KgwcS_(nJfsbFQ&j{$dA( z{c8s`2CU?9U#W#)IcG-?u}mz8{NYO}BM_$r44U)Mr|x@2_9^P3Q$BHIW8+J|HtR6GwGfb~NJ?$HMN99ofPV#47#XC5}rDtjfW!h`DM6_QaHBM`j@lu}WcXXac)B zk4fh|xtgya_OTHxx3(;^!H7csg63A5o}IpHu~XX-BU(-*uMi!B=1XVq%CP)GuPKk<}HMvH5rL^KVT#;;f` zVYZ3x3!CF!((NdkA&O?oVwp~W=Ht2eHN--lobzIWC8m|4q=s0>GsN1RA&RTX$!6Gm zgBn;&dFNk`!a(OyLxe@rz` zpDfY_uC|D{RS$eylT|%Xh4A9-Lr2U8;X>mw6M9+?W!AN%baR`~znNRGM@cbP`Cjh3Ona)z_%5k} z$axq-`s;`(7K(qK>DWebb#n`)eED5Nyo(_6;Yq|bPV{?1Y0r+{Wt3@!T@W|@C^Vkl z3(HI2Yk&pwN{pGF$TlQ0v0*{Ts=GfMEcH0p17CoA2?Cw659sJu` zKn_5&N&xNXKA9POcP-}dYOrtZ!ySG&w^a{MHUlH}2v!gWG^0Cm6MRrxY4`Ou`q@r9 zub-u>$PBTPAmW-=nz{#X6)@Yi^cFo*ln`<)hqUPF?ONpAgb)WMXu+&ROV>RSB_ZT& zN+UxmN+N#~dPC-OEx{ry+bN>AFCnZymF++}(*0*bSbr&OpW9B#w09$Xwl!U#Q8C97 z0<&7KO^A&aa*f*2c_-=|kC5nMhkq&48f4E7K3y$D#}1eCz0{FuDJ^R6@4L8{M!1#a zYZt%IdM(~{7DV=7%eC+*2%~JP5G85-$UOtUdP}$g&a^z!2{YzoSHqVd6J0nV;A)ti z9N3o-7+hPd1kS}&VXZSfkNPp>csRP@{Kz)Mf)_wHPFfQQ>~E%?0al7_idoo5GWo9K zq=@|^gGX(K48~OL42m|w!1~sGF9|(uuHQpveqDp)5%u+8dm~<1F+Z~ZIP!|FjV~=-rfut{<@Lj3NIE?mBO)&T4VDmw`qbAxDe~tSdeaCs06aCP7i-x zi5?Oi&BP?s5LdeS2f44o*b93V!zb3gyRv_8i+%z}7m6ZPijn1tVvNjj(dH$&!Ef*a zWkcmIh`fpx7LMM*T>opG`ef1afy?yZ9eEc9CWhC(OD*o`d|$|CB4tPC-v|rR1U&K_ zhOpor#Wu(KZh+x${}Fs-U*=;Xr8f^+GS3gM^$CqCa$=l+Ui4;`(!k<@1B`CGD+UWl zf-V*d_dhMsaAxa4ik0R8Ut3x zV4YjtFnQ%03tCqWTqql`6@kYB-d!MVp6e5q#KmSg79I?`(gGMDtyryqC5cTP5H_*d z0+J}27G~nIH1auQR|p&WALb4Apz;}GKGy52dk&7uMnZ*7HrWlfp7uTJnB{PTj zV|Dmv!7XA)^k)ngE5yQTj2^%e(5qWW-(eA_Tk8x5MyoJ~jhEDMtMA0dqrV`e+$_s9 z-ojLMjM<(W_KE%|s!6+bcMErlsHNNw4v*LvOqQ|)nHMb^WgN`wEqNdoIAa{v-GiUo z^mDrOU|%1)n}^(NiS~(GY`n5IjhnT+T(4&nY*) z1g{GZL%Ej0CFK#^Jy&v<-%P|T)(#B+bMzfA_ItlBNWp7YZqCVkxeqKIZcg)!)4o^b zcbT`LkjKKAVqELzp=95;!5+odqsDft!BfKnTdZe<=%Fh#zqb(0UvB)x+A2)VpDW5G zB#;Yt-r$8(hrbf-D?H4FGhP*odoTBCohSNBHx8vU-U{C=I&yO1CW*rBQr#D?9xhlH zcp?gMhwO{$ibcSoq^JtJ61ix-k%u&433~`vqrb>D74;?ycZ9H~M4n=kXyNN3h~C)4 zogPw64bUFz!E<4Ct6dq}<$Bk9A|iH>kR`s$jSZ!1YRcXY*j-I>7Pz}u1FDLG3ZrrY!L^H9o%V`7JN-SC(3w#=WZZ95wF}&^3tnhYR z@~8sJ(OtffHv`HAeVU$1%J8~haNI4t3?Q;bbv#>)e?1L96&d*PV2|*tcjUO&j0rg0 zGn|1D?zyhO5O-;we}b`QB0saX2JfLdt{F3OjTr^T45u--$e8OgN{fxsyzty7NR6&+ zS7Y7f!LF?6_k3 zSnT1X7o7>84W{eXWdXQG9scp7SeyI{estpzig+wI2fvu8n_l#h&MHH6 z0uqu@6v_0q&;wIJDRVcpgntAlLN#ozpTGjP=Gi{e(@4q&Q*+6-V0*jbr44exzB zhKGFj;PKhrky;cS<02bLLWg2}!~kqrNYBvBO4vv~b3Ey1a3PC_AebPgLs%{^|8;uj zdye%}Q0#JSZGt+(!q5cVp{tNeGu|I}FUc)2%dtL91c;5^;g&z4M!^&5f&77nwdjSg zn>Qn_U|INa^am6$iaC?05uCj+N;Xxgg%L{)uoa3&lf|A<#K2IKgGd>UZSM?k8(J7K zaLt>n=N7pStQjR8P_&YO!@q>)CyNQwCNZd4F~c80Xhc9X-DiAJPD?#wT#^0gHPh*N+Q<`hg|VQ8 zo`gZ$3AI@mgXx2uzE>4~6rK)i*x!kj`*QjION7(=9My3T5SRVaupSv+FQO(I%z-J^ zm;h6)-@b@^PA+E_6YT!V2H%D^ZK?`iD0EpOh5q?PLVCy#S=79cKn({OvCNb|%AMrW zQo#Z=gc5rk4@0O__Hj8n-^N|o-(QHrgI@DLGEot)`6z9im@8N1vJkEwS*TQ45y@~tk z`kg=)^Fr8dwB6T{4QYN&ifJ8q6g-jTKX>hj;EAFB@saaDH+UikTl`O)?dTkZJDxK` z5MnvqkuOkmdhcO{&p4{*htUDD9e2_smCL*pmoU&Z*FuzbDzTPhu9>Li_vQCG z^loo{Sk3PZ>9#E*E_`I%gtnK=iiv8d(dK?-bxV3hk^7akOU*R}aJS&$vsYH%kX}&? zXfD_<{qsJW$0t6R5IBeVP0!xa-bRw92VrtWHPtPi_X`YH-H#IQ{Fg%a($fAPq3NZ( zbKe|D(*N6dcK_xxKEu*m?hr@kBM|n1jr82WMymX8G?A1n*v!uOmJT*oUK_9!gGa*d zp(2AMZG=(Hr%1F^^H~zKaKMF@ zV@!5U!ATA>MH@=I@R-XYb_-&>;zq6qBD3a9@4>L0l{NdZZ9z%9@ z4|#p_!XJV`lwgFk{f#}*Z-uY^rNm&Dh?WpR^CSNvk_FdRKxVOSyVe;P8f>ph!&HVH zmp9ItJuCdLL{PRQ<~R>h>qaxfC#kd1BQ;WYxDWZy#_~X;w1d6_yOvcR~9XxC$a&=#y5oj|D@d}#2aGz9tfjS?SyvVMd4;Y^GQ!za8 zJGnJCFikV>+OSEK(xbXRT6-0?*l+Mm4NW}9ePrzMcmMgbcLd)86td0ni>@-WqR2|eb6 zR|5|N|ASgySiMxS@YtP_W2gXHhFiDCKc>HR_4gB5)g?TprBJ^Q_eXY+l8tNzGlE5m;-E2!5^xhIrS%9%#c$(w)S-Wq|zl;O8;8nBn%E zJR>(S&-{a6pNdi3<9=~%8LZR|o++U*cK54muLA#6Y(CQRKO%!S2mf%^{w;q8*oQ|N z%y}jGJ8*#=M^&y&@Q|LwzIANmX(}eYmw5*>a~YT!wUdB}rLo|NApuW0HmfDc?ipGy4tISBM5>GM>^I}rg$KnBBB=(fE!blBwfmyLwYqs7?alIEp+fsr zVKY`>BF`AGoAEO26Y75$;7mA6u!8gV;O)7JA5Sshfq(;FyZf3=-~YbZtDB4DI@*~q z7x-CEN9R!7gx3hCzGz_0{5LX1cn6yK2$?69g$Mr`tM_pvA z0pcw#)exD5*wLlg-oFU%bh)U?m18ZV%wy>{@tlphe(y#k5PQM{7s%Ne&&2T3FXL^= zk?^;+LwHp8BgCXYrduq%`wPkM0Q<89gB^0id`s{$nXLcUR>qrs#+Ph<{sZRcz@bKN zl{W9qMi%Dd)By7FrSM-jBdTQ{?(&!N3>_O$;@hw->yV#1o?{t@_gA3`crY(~46;nd zq3{8R{{}7o&l`udOiTMaMJ08)JR2AhU5$O71@iY`S2k>%m=F<+eA_-{e-}as!Vi2EWh) z&tpj6jUfZW7(Kca*?EAnl(w$}qmXn!P4@l1id4nXS+t%y#b-Js?$0oM->m|PAV_dl z)sY9Vc>GUz!$L7in__8A(7|Y$!4@l`4e$n4e05^0^@JbGvHms4esG(xyB(eT>E>Ex z4L0y>6D-HDjPX=_g?NV?9$+J- zfmWEvtsowv{cQWg;D>k~L9#febHNQOPO zG*~Alr7_KE@RoekbO=_EqOi60;7Kof%1W+Hk{7Ik9+9Y~H>6d2!3_-XP>VYDr64Tt z80Fy~At-BRadd?BorCC$)`MqiFc$Ow+-p43jv|^~6z43N}-VCpn)=cOT{No+^UtSscTEL=Qf*NyL3mGk-~40z2LPhgA1J{G(8gm%^7|l7tD^5s+UH z?Xn1CJ|mnIL^LXVst_4}M>))qn&|s+ny+f+6O!gJ|9-2u?o)#FGvPnMOqZnnk6{xr z$vpE2QK|K~xysEDg{kKK)G(oC`aZ!Y6sUSY*&2 zi&<#6+p%pSg z3zeDPzL6_l*#=V8gkWC|D&B}ooTy&V=)XXIl8fQr-j7_xPSCAm5t{|ua^lO}kqK5` z@85{TY?Szn#+LGs*lUjW2?;hrK&Y*x(W087MY$4QGekWcfqER0uh_!&u8FaE_y*yEpAX#KTe9{p4swFbT~(+7k7iOk$8O` zY8{@7AI=>kajBS>h&Of&lnzk{jK@CXx70iNtscw>!8FM~OQ-M_d}f^?n50Wyl^cS- z#WGUEuRVp$P(t#Q>iL26a^tYnmh`Rz>jOrKg@S^wVSdX_Gk~|;FXLqtga=A_BC&x^ zIlDNo|+dg+!1s3{@I~m4~>uolmb)5CYN(^6P~aS(z8Nj66YFG772Reh*@-q zllX7LyXPK6Bqh)y;#pmjoB|2)0+r~MtJBEn05Pl-9SYyP1@*%sp#M9JiF(L4poH#o zJuGrYyaU*j0Pc(yvGCZPNc`q!7~OV7=Ha*Sbx~en%}ieC%4l0^yeSKb@ut7^LBcm! z3By)RE*2%Er#MkUnNK(zKt55-m}H^D;U^)hqR_8mH_crlY2t*AI6jCNP&-L6pigZX zFT*mmwj$~Do?UZo4W4A5`~%R&t}t0y@ku&0wj=BU9DUylLnucxdcE#HsgBZ}-u!p5 zUW>gipOiZ$>;t_|bjO5VvR`zs`>$=ecuijq-rhIiExd_u2X_1Se4;rf9*v$&mR0Olx2IvSSeIhw(nJmlu8V{s!r z_<^pQ%{B0b6|pxA?`$fk?R_7H7ZFudESo%!$SJlv>;n{y($fTg2Ot$5o`%EjFV$S5 zd(9;Ujz@dFc<+|iTv6h9^n{nz!5oimcjO~uyRs}Zx*L$5cSn~et}gtE=YiwYNlR}-Gc zOAEmodavQcLm-re^Q(A{`vQJP%5cIojG^!yX|5vbUwJU>E8vP4gXKNZ&5Eg;6$$x3 z^$QP&Z`_1M5Zd(wBNH8seDbO}Bge#xim|6-f7=pdjHL$0yyws`m^z^Q6|`kq4{g{E zqN%!#S1|)YynXoxpo}%+T|C1`<}O-$VK+5!SNLzg0iQ`)&5cCZ+(e^JC}$eHG2mNt zxp73~lVjyGG5^KMepgX)PPD3rs^9^U3Hy*IaufOFA3z;ZC@Z)2)VbM2|CepOD_nCA z9%o7y1}UD|JgtWWcj^gRlC%j96kI95|G4_a#Y^hl%D5#<3UDanE)|C|Zm~G}?=Ae& zORv1rjrauxmtTJ6m8s7S;^?PeB#wT17X4Io{?DZs^(KDMQNL>WRnFQK&Hl!QR_Cgv zE9#q9IhWV2sJ#idOWT~aO|1>J^|v}(11nZ6U2&6)zNFS)+vIF$ZEbG7%Gug*OQ506 z@2qRCZ@7AV{X~gdw<9RU#VeXymoK@guDPkXHGaQ1`M$ZezM*wsoFz@owf=snt)VHA z3T_jL&}|Y8N-&Uwf2qHzA-OnlFp*q8m_#%lFc|5iB8s?4<&0y4(H*43wKuh$R$#^k zl-I(N$y(`?7y&rBuo6lpO2r?9=IJGETw0%8P=*po0!qqPB8hPVtv4k$G;WhZlM6%% z`s-I$yW&E>bLomUh-s5^c6r%U-KRMlYgg1aHMCwu5~z%C8^417m8Ht~rAh$50cD9Y z9)I=Y>zo6BluKG#o9iwaAGibr{YtAcenP-GenK1m5j7zDL_>Q+-O3i{_%_A4q;_dj zL%pI>z@#fKzD$|4QVF!7M_uIv;#@Fk77wkKBMgFLIpW3XoE`GOn#}sJ~d5Q&n2LXm(X4 z;wea3Q8}|}=G2+K*>t0MecmeXqIu;rw3+h=m|dk-dFi4`z0SL+bmr7Kvz0|Nyj6?T zsa553<1qy^drn2g%*raSCSxGc40XCUej`aG#zYCI>{E5`)awRDTU0v7=bJsX((9cO z50HggRCRp?)12xnM|BtZ%4b(8OPcEfZD_H&o0YZ(|MDfZb$-R)eA7)$4U21Am8C1{ zRxGb=^Eb3IDCCD!oJhC4d4=Myy@^h@H8(G(=&d)^wzP?hrSYrg_|?+J{>zo|%jSNx zS)#H8B0_z`lG;F%UtFVv;=FQc!zzV-Ai3h)0GFpIvBuPY< z1e%)K>RKBbRwxOVk{)oV*+%8&rAV^U9x!Tic8uW zl}UAqtijR-r44=NW~HTe`OCQv#{WQlyK?SCS2TFyRt!9vb42fdBci;QdhgIR%!Dq z^>nuggSRqixiV=9PAeAUe~B`w4d=Ecm`7Z#TvKul2~eA2wKKF_piO}>Acm^JdfOtc zT=mT?8#HK6Rk^Rc>iR*$s%FmgO;IZc4Vzuz!`B~_wifis|I6`z1^%zZe=+{u*C^<}&225Ubq!aw zExoPbYPYm`{-6A%$AEhAPv`&L*Hq6q2jVPP+}eCIw7`HJmpaVB0h9e=0C=!GmPW7M zT8Zrx{Zn7QhA&}gbX4pmyhlL>@{5JAEF?V)a=L_&NAM<4EJ#Yh$hOc~3luL2wx2IvV|%7KHHY3NlIZx+2W$vLDJ&L7htqR7!5&W( zn=s`ISbhBZKFM!MW;(u@j(4{1PGGf9JZlU`u?Oq?qll!;gV`r%?DCE3lAtCb_6zbF znG`MYjz`QS6Ip-#jpW`+5*@)hUhCo29V!5xGL}sc>`F^)Jy`{83QUyXBoLgHO`U`% zmc^1Li_kY=1Nzc-tzV!LdAYqk1zwbdi?6&tOOI$Kau#pEl8xz;LRcOu`kBvoH*vo} zd2lE83%r%EUjUEFVY|S7Sqy6*(0EX`N-Vks^$*dnDiimS7apXCAQQHY!?Uq0buN=~;*o|1udBl6FSLGjQUDn?XdhQ~|Q%I2XGkrmkT|DK`djky_xTc>G^uS`1r|bezSEu$qX3q*eFr`=Jp-uJA69;G;43cvUy%-s|DTrgE2{oY(Emp`V9ADc zPOg@P)84Yd4hv7!zb)K4C(|HH$%B6@pH$&rm)8sLHc&hX& z!suv~r_+8tj4U}VL;_a$*QH;Ul-??@mHy_Wa7)iw;Z-R{2`jwvYrHW&!hZXJ|JQ%AO@<@XVQCgSox<4xAIRFZsngU+#_;P;OQNBWb&<* zf2wdR|5V{t{;9$}oR65yCKQ2OZ**AsrwX_7PZe(EpDNrVa#7&r9(XwU*2+IsxRrmZ za4Y{*;U3QCNjC9D%{FHRk)Q;v6V5OJNRtnpDNtSKUKJuf2wc~=X2W9 zfK3Ki`KJoE@=q0R<)13t%D<7zVV(BB;CqIyJq$GQ**YOyxJ<14Z?OWB&K|yBoP=Aq zR`_xYNIF~L%}F@3Ctki$DaIeGe97sP$M?1*`qr(LK9&7og{QI~tngIjv+{W^Mfoi~ zB$#bQPB>fPlM=AR%dZQ!;+_%9v{a4-4LG0qHQ}REgj@AbmY)H{aZ{V3{>kyax&MX( z|NrGc-?cw_h>l-hyF?sEu2qzdYn8I8Q?GL7mCcxw?<~Ch;=+pyotG9|dU-+7rI#xi zV3rgAL-3CXc-x1qi!dAh>9FxVEoRV>BTtI$wo1j;o^3lXYgi`jk|*UbhQlmh6JZ5| zVAhyL`Orb}s>PL}B&M}=LJ5aaybivuPr&ZuYfl39FkiPOVDwNg9hNOj^m+w?SDf%U z7w7l*dM%w*dv-7_?G8H>CB?TGb1z-vAdGgM(orB!0?T8}n%5HFN*EJ71l>vy%>!P= z_!>%|Hq)McXPRcu4W?gbch0oumi5@P)!mdo@wbEHPN2A=Yz-sqnNvoph~NRVnZs`y zjhiX|{XwnZ`^y zBP5jOv*+HGj+l26jo^?e_JZ`wZ1z0O?o<=fQvIE2_`$K7eo($U(-0#_Y1{32)9nQ{ z_B`G0EVt((jLJ;BsNytNahi7E#Fr+GoVL&X-LHevso{NeoAs?@24O z=iW{A4rNTS=iNp1zH{hQdvS1>X0J)>8(}Z@j!b*U-Z;zNQfhBhfqBJEgS~Nzy{6XQ zSZ;5*UR*D*H`m;GcSH~v^EKkB=P>#H$-9^=;o zr?y?l_%(z>0&bw3&_D%WgH3W;mZ^*5 z2>C^D+TD`&A;xzhsg>4~wp}Fk0Xo6qEyQObS7<|)D~;sJOLCQ-mo`F(1=>f7ly~Hu z(`3vA5_$%Ha4e!9&0bJzFRrkAO6|I8uP7HalJefd@qSJ5xK7TrUx;#50ougjrEytB zyC=$OnZ3)F_A~olo7diJn{Gd3n_};?srE3=M{V;c5F!cjOc3OWpc`ho?^C|0%3UPK zcaj_jGf05m6e&<3bQ@}$TvDJoX48)-XWDh>m|p6bMSzMq{2FeXJJU+3UwG}#beC-e zq*>I6qFX&f>XQ~ucbw>oa-iY49A);7w6u5a&Z#1e?7!3p|DDr3L}^4>=GZ;U?0Tua zqSRhpYOfIj-%@V(wA!;Zd!`G~4sjY`r;0Sq_FVMT#e(*AkWkittf0R`v~f_~au{nQ zpuddhr@@T0S*V<3lT|rUP%5X7)9s;jh{L*q`uCGE6_{tG!ljOA9h@ecRNh zmHIhaK0W;-o4pv~bz1*i4_YrUtxaD~OS5mXrOmfL2npJ3E44q2|Ho|A0MpO10WD8J z=TX$@0G9x)LewdUS`P5~!8C;v<)|8l9~?iUAHn+y`{uN?yR3doJZ|SWzoIzcaftL= zqMTRSJ!#(^D9NPDALDelbGp0Ju8a3uFonm1v<0Hq#`PNdC*ps=X~Xffh;Z6iQfaS9 z^V_ST3hV6E=)H}n4J*_?iZh9bqMS`XqK-4I?x8FMRL$XMaz9QSw}gJN#;vsF_G&2T za#GC@;fhrv>-g>Ixl6ef+Z2#b8Rd_|iT@Wk&n49UqD-3IlfEd;;yc*_M>!7hQpQ;s z?+aOMG~`ei4yvb#t5JGdpe<2X!x3Guh#7~vjx|t*xNOG2Sgc~cCf*V`evB( zy$jc}oU`qoMRpyDR7;152Jkcs9ny$sV@6P9MfsA(3Q^XSeN#ivLT42RXrC`t*=1&mb^UD4i4N!dHopk6B= zXhJ$>*>k4>x?Sr2xWEw27EV8oX*zLU&zMOVXr^5+l8_-N#v4laFvqd{YJ~lfF&f|K zAgq@$(lK-p_8wym0+HAW#?DE=va;mOxe1ta5UgkrOdkYW$Qbn_I;boygJA0!b5bN6 z6#qfSo+3c@t4A$`xpfcJYX`3q&6$x<@%o$Uqrvc8e&&C|rJK zNKu6U2!nW9O@9Q^3k71c4xUTKHS8QGEPG)u=#2M|(G+NkzC>-&$l?9=g0Q18l%-8e z7#q{xlQJX9Ta0v@IbA2^2N}N;DnZ&AX-g>`8_fx$s@k&~QJwgIgVQd-HQ8jh+lx!d zCLIO!ILZN9dUO>9Bf^`vvQh(`;R; z^j~eK^{s^jJCiTQ4H*)--4>gO zw@wZhHUM-}i24QV1)Y2+P$v+g0W(hO$JLz9OLQ07v+KBDwlJRD?>91@*e{9BX2w4x zI%#j%v!~lLow(e>_!Pq7VhX0+JOaj#uou)G_*wwJ!EyeKaZ&1Vj2(l|t!854oc@EM zm{zNpR-+H>wdpjMew~py;tb&6pq!Lrr;iGZL)2OD@(zoYLeDpGxY$7|cnLk9sAJ3Q zed*~BpC$;@-yY_)FH#0#jDSlb(O&^Qk1^u+$zwzXqQA#!=fq_Q`3}wpvZK?!#IUs( z7wP8>E;kQ?X7O265WG()&fBk$%Z~K)@1@x{rq8i&O0UIPwF7A&qyoqk`-Ax3oL&xe z8n9Ww76Q|O)dBMXn+xw=)X9Ve~R!Y3B=(MztCei6Ml^ETNBQNH*(bq8DGuuA7Y%RSIRozHt;Q*af?$1 z$NvT6YnjfyB7TP45RYrC{Ny90e8-Z^;-GkR{0IIC{{!O%0#%g98Gny)8Xm19T^V9s z#?Q~p?Gkn!zq3hg}w@b*h&rKxDm(w0?$zB89h2IJwxjzME`nLl%%KV_%+A(aD0zI z73Ft~_wl_|-X|Hi^0V-5O#jbJU##&U?GDBr!XczxLD5t{yNh?k(4jp zrpL8D2jWB@+@N~(v2$W92YtYF`nX<*B#w_6|3eagPB0ytSgcVLJ}JYXl8F8o2E>{T zD4ogpe8$CE4e+sy-^;jIqXAyXcuW|V{d#*c<3A8i2}Ri@N{Mw!%*QC-dFM+&tjFL| zM|{$^T}kD?j`2Sx;WL>29;c*3;~X8=Gya{i^8VNIq%3CqCC0^i3&NH&{VtAYwRU-8Wnc#g+_XEabjL&BLUxC{o52Rn|U>COXOO9XZ zlDP1z;PO8i-!xw0$K^@cBIsnq*IS*8Kg;nqd`AMpe}l`tj5{xs_{|*u0OKa(oJ@I@ z@!v7t$npP+@pJMdu!nISehX^fOs<#3=Z`pkAIBH#B1l6%SkeR5;bA!+f`%Y|9_4{o ztbHI-Hsc+WWQw10!m*5BaIpk_#kdDJSz|R^9w%p2An=U%Iy7#@F@fpqVmh}oUc$KN z5($JE_cETx3Yyh{GMn*h85ipXNHY(3zjdg@%e9E(SF#~i#`GH*e~59h27s_z1)U7# zF|HSG#qoWPAG=K6TjRrK#;+`rxMdeTBIuwI_^{|a$?>n_`1f#lH{-7{9%Ouvpp&Ka zvEkdoc(1_wEo*LGZY zg^V|F{9iNP&A8=9e}eJX80X0Z{C?NcRTEB;>?&r8DJVSEbXJj^Ij#&1l* zKV$qh#+_XLQ;h$J@uwIc1py;|&e9|>i}7^ zjN=lH+xxe0{FfKX0O9+@XyP5J9;~k7!?fxS!&tAr@cK>(Ahtx?rA2R(%8UK{= zDU25~egzj?_#zRu1-S4haJ)=D-;>7ej{ZRh&Hw3mhT}gbZx!yY$}Xlqk_FJJS1-qR za{P^a{xip~;P}E9iLlogf1_DO6uv@E(?5%20H?Q^zSYmI@-KzFQoVL^xvg>@5%IH> z4!fjb#s8G)SbE38DOP%dy(d%2-}JcGtRIar{jMyDWpmIl5F|Tla}u7-_+v@7_UjfuVvhmgjX@{Ov0Bkt|Z}W81EY?Gqm!(m+`J7{9(p7C*jX9-jReK6!_4% zAL$IP*Bb&y3v+wnRvhn%3sydi(J8|CB?%Z^9$}ojX*~23#&3xu^5O*J>yq#<8NWXX zPa96akhs6d3LeI|HGW&T+{-dV*=+%Q9?kJzO~Sv$cr*zg!#IR!!0|7PpOu8?F@A9p zelg?LJkugp#Q6NA_*XH$A_@b5CdJPF^(_}V1=ZpOc#gx|;b z<|O<_j6a%$Z)TkQJ&DJ!82?ie{u_a_(o0D2dyfA}Qv5B9A5X%!GY+LV;MmD{S`xmG z@gYfgFXNd>_+J<`!8S{CkWClkiQ9|0D_jA>&UZ;Xh&gxg`7- zz>{T!%Jr)h_-|9-Po%(~NrCT6fxnXiKb8U?0{dXF@{dY^J5%5nr@*gGftRJg=cT}p zL0$(d=S?Z%H>bc?r@+q{Jve*qwv% zGdl%-UJ5)v1%6cuyeb7=p8{W&0>32%en$#Clmg$J0)I3G{&Wg_PYV1{3jFO9_}^3D z$5Y@LnD-9Wj-ylH=cd4&DRA27IT-ybQ{dO7z!#;!TT!huTtPorNCbh^s|%-@Rbg$EnZF$|E&~wGzESv1wI5Osg;+^ zS^j3Hz`vaWpO6A~r@$*y;J2i}A4q{ekpe%M0`E(K|2+l%NeZ0y=?=!XGXb7~NxHpR z^6^Dx>KK6wr-DGlel=W;O+hCw1%7D?yf_7}r@-f@z#CKG%_;DeDe%q|_)k;dzfFN} zNr69~0)H(9ek27xG=<$YIt6}y3Y@;taB-c2*XY;Xyr{|FcI)!R%}snupN|kPR{$~1s0Y0}ur$9@6t)ISq(9$el?%r&@>tB4yr9QBH`K=1xrjM_>fN(3m0U)mV z)ZVmou~OUGTD#(=2BJovju0OS=>HrFf6yfkSl{gE2NFIb5(g8$+aa)okAqnE0|+5J z@#`N}oJ2C#7f-AL@qL_(b)Q%qD^34bMCg_HiG`83iA0i%!U_6GBz+Mj>1!whzl1XI zD<}zHKuP%e30)=>I_aw?_)?EwwvG5+Lz{xOZVfaw;G&URU$%JDQhXX<1tbabp|mzP z<2wN@w<>K*Z>no-s)wvL)UF7$P$Sp3)gh~ zZ9NCX1)fq`(ZqE;M2aE3j=vEf7-&=Q{gNe1ZwjNNREU!kSdPNHc%PbO}pj&FjPw6}6RNLHk0L)~PI>Cn8>?cJ6K-IN6$wk5G1btt)Yg6T;C0$+8KWQ7Zk#JO&XybJ%Z)SU#yNB2thsUCyf|-OC@;>N7w653 z^UAO6h<{$3H!sec7w653^XA8S^W(hvA=o%?ew;Ty&XphM%8zs9$GHmPTm^Bif;d+} zsAHU~ATF^W&RY=YEr|0L#Cb=>c}K;0N5y$Z#d$}?c}K;0N5y$Z#d$}?c}K;0N5^?b z$9YG`c}K^2N5^?b$9YG`c}K^2N5^?b$9c!ZdB?Icc9Uao#bbWfvl2 zPm|2G*AOkLZ;*ZPBKg>`xIzvAN@vJyW5qVvSn14EV_kDYMQVDO4sceJh_8uC8)`2} zHH8nwG7;dya@o8CM26T;rrA?m9Jb}Oa$=1Pekh|%j}c@zs;IiQOs7qYQZf^o><^>T z;#5tlDOF;lVN^O%Mv!aLpO=(ZH^~+lmCn&|V{SZep=OE)4*Mv_D`k2VtOqV-@s^ru zDwDlcJQ&6>K7J{c>dc*fnM_ewUNbp9Bq^OG!`h_yu%L7zcBm+0*3!ii-sR1yhNUHS zBA4N!bYgty1x0acQT5`~#Bd~1I=#GpW_fj^Ob92oO%rR%8ymHhNQ<2<$0+G4)-L4F zv+MNJG(A)+y;$b$3N4=gtc@J6v*|MEq3F0O;@DItV_#!jM))dSiUcubaeZ^s#X3D& zP)zTswloAS$E`XcKH${SbdAc};)>dO87Zn8>nhGWudGqW%tcXYTt|d4S57j}IE{;~ zlP%Jz?9v$*g^Y_Tv_HggOie9aSXbUqDJ`Z)KQWtLS6RKNI@Qp_L?q`_v+LwtXPgXe z1C?|I?Gbt$CEtqy7G|av$=;~y3nc{Yg!tep$g!-gD*agwNKq*@o0V#iqo8y|8*Xe6$wGXL1bFqxW#WSJM$HN-R0HA?plvj#@8#}_mCB=fClXO-Q2 zqwG>@#YR@w#d2JCO@r<|q_*NZQ~I=Q46-*;ZCvMxy8oFj$B?1dN+)Y7vxAGcpUG)k zqt5FlR-yAr2W=>?EuYdjUZxI|nZrsA#Ql0!JWpJsOvNR;vfN0!FgF6$tZ)=)HN-bz z%hSfw3e-rvahHoq$LpzW9HSncNJT4Z%cL+p;Fh@zWovCLli5TYmX?L1oHE_VCVe65 z`WiW7O~|Kt%i^7_p1VNR<6HLi@|xyUcuo9PPp#u_nBHt<1CW1F>6G}WWW1h1#REvZ zt&DGyF;Wgl}@|+$`Uf&I+?XHcksv zO-mchtdj%Hry#>hcjV65-@iD~fjH{0pbwK`WY9c+yf4$S=??vt; z{*L5FJok_f$Mb)Qb3C7rkH)h{gORkK^c>GY_UIVm|4)!d#}fa$FvN4Bakb-kMib|F zZXh3x2mSD7(sMj3oc?2r=RxATDV|5khvWGfagL|IJV&Yt_3a8Dqr;4Ay*Qquoc=x2 z4=4VvFtpE^69zdd}6pL5nl-##Zf{Z7;85r1D8;u&My_aA+pS(9>(=YI0h_IX7`k{%*Gw~s#e zs|oSz-vev<332U%i09|z!|n4XagHa`Zc)Jhh1lKxzFwAn`<(6czcGCg@qbV}lZ^ZJ zSwNiQd4hbjeYTkYM$&Wp{Lty2H~p`O|C8duZ?2GMl zo^iD2v!=g{_=gnFEb`&?wU{`^^JDVS_StCu&yt?or^D%gX!_q1@1}Tmk`KqTmpI20 zo)b&A&wBG8FVFvI^8M!$<7l7n1z9wo_(#IfpUcUI<5@F+lEN5plXjCejFA8wzcj|>@o`;;0-`z$yAt4PoB zR5<-g)3*@cOY!LQIhuU?tR~LwqtD4`LOjdD$Eb_+9M5}Bf3xX7C;o{rw2wYNqshnf z72+JvBIAgsDP)hXB|XP;ozq`$`fn5eR2bq3lOTREp2x|bAx1_(LKcfEe!G8Pd?nA zHxcJ}_LGmsGvE9pd9FZ{){Emg)Hv4f!dNALA5Z*0!Vpg;`EWdAiE}(RlaIzT+x%}O zJ;(DMr=Mr~M~UyJc%C31j^}ye9M55LUtSaHJ1u;S1{wG5GsNj@=k|Hh>GMqgbK;*4gCW|d!2HiL?%OBF>CZR)B;xuvHfWzq$%o@f5$AX|l8?5}aPxnP^xQtrIQ?0s ze}(u#6whnq!|{AToa4zqCK#f9hM9k%ao;`{I{m4ppG92H>Cry;eKqyrcpAx{<9U{R zw0(w{|8u10_Ic6iPcZ#X<`mDL$%o_l4{?rXyaj;vvE6iZv2ou%_-!@lt=Xe;^65|U zEFvF{=O*GD&sOr$_R;b+{g(9HKCe6d;XxjClD?4Q!Edms?|H=kP5SeRpY-KmsQR(Q zD~)SA=yx9kS=2)Obz%B@PnvFVe7^Ak_WL#7f4Fh@<9c72<4qEirbUi-1{~EouKNZ} z4aT)x{hM#}=cP`Mdfn$Z%3bd`%6-Ohl)J^amU{t}`!;d?+j_+FA^99a{1ejib<0DJ z)7ph~auMkV8%KMhUZ*DTg^nZsYskNd{2LPVOGrPS^ec%^ApVK-*%PW06%Gvxh`-Bt zHSx*9&~7bup&5Gk{FiZU|A|y?SAu?^4LDwZMFOuj?#sPDLBA8Fr>I&m)dPUo{l@@RU}ar~ZH{#U~Hh!f-Rd&Cc=au3T)>zThsTu zkUi>uN?1_srR0ASajsXkal{Fq>m0|p*Y0?imHT3Xf0xt4=crRdMwGk7d`1%g(n0Bd zl_IWnd%^VAJC68QIDWwNcR2ov@zuuFdn%Rtu+!uI+T)JHe>3^$ZysX1cs@b@E7DIV z{c8#OH%WgP>HD1)Dx&f8`n}Bf!NT=B#Cav@^*8(w=S{@-Db%JsDFSGdb97nlVkq?iP&BVEV);XV5=Ks9o@c#?>TtV@FMx5h6 zW<c$fLV>^S_l zk$)-0|JMY4|EzTTe3kTP8u#s!O?vjf(CHE9JjW4dE%|V{tDL^SwZj_6;qx^4%%k{E zIWt|aD~V?rN4+|&Jxdbw)x_tM&l>X4-!#Fv^f2kKBK}*_A4dE=(zE};cA-GyJe>3c zjq7&G{zFKw=VgdzB=IB2r-*#G+*zd8eF%I~#0L_uOYpgw^n*zMbb|g5#CbbC>g=$B zu)ff*t|WdG`QK#Rx6hK1y?xMszHMCfSCh}(PLFYc95$R8HdiYNz&h2w6arHt!yu)$Cf4}4Kf6};mR8c&eoE|PmJuaW**;}{3vf1Bf|@BQR+E%|gfJ$!ym`Wn)2C%vv0 z^uvtY5WkP-c;i|x_8Cu{>pRE!bOqa}+VKyJHze?O$MJii|8X4tx8(O7|4QQ;e=W7c zuM_nDLwY`+eKSG7i}ZXxyO%h(PrriRaiSegPT*sS_miyq!()J-PjVdV@iO9CXS7e5 zp#JIO5qzKG%`Yj}r9HlAhPg zOT;<;-zNCHMSQ!oMdy*)YI0sfoZI=|#C3fkeP&#>jTHZ3qk_V3cgGr6yACLtlZB@&;Eyw4g#z%_#Z`Ff7=f2ImEcuwV3$n zq}T7@lTDm`_{KeQHU6J;mRYpudOo-yr=H3HoOpM}K&Z zeE9n6_%Wd!(Eb=7t~9Q(^LcbLaXyY(Nu0;$|8@T8Z~Y5D@d>P znjMOZZX!MRw;|(t$AkJFZ(QTy^SHbO{duJ4{oPLz^e>T~k8}Q*pns3_H&FW=dO?U^ z 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 AMgRZ+ diff --git a/.suckless/dwm/movestack.c b/.suckless/dwm/movestack.c new file mode 100644 index 0000000..520f4ae --- /dev/null +++ b/.suckless/dwm/movestack.c @@ -0,0 +1,48 @@ +void +movestack(const Arg *arg) { + Client *c = NULL, *p = NULL, *pc = NULL, *i; + + if(arg->i > 0) { + /* find the client after selmon->sel */ + for(c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + if(!c) + for(c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + + } + else { + /* find the client before selmon->sel */ + for(i = selmon->clients; i != selmon->sel; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + if(!c) + for(; i; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + } + /* find the client before selmon->sel and c */ + for(i = selmon->clients; i && (!p || !pc); i = i->next) { + if(i->next == selmon->sel) + p = i; + if(i->next == c) + pc = i; + } + + /* swap c and selmon->sel selmon->clients in the selmon->clients list */ + if(c && c != selmon->sel) { + Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next; + selmon->sel->next = c->next==selmon->sel?c:c->next; + c->next = temp; + + if(p && p != c) + p->next = c; + if(pc && pc != selmon->sel) + pc->next = selmon->sel; + + if(selmon->sel == selmon->clients) + selmon->clients = c; + else if(c == selmon->clients) + selmon->clients = selmon->sel; + + arrange(selmon); + } +} \ No newline at end of file diff --git a/.suckless/dwm/patches/dwm-attachaside-20160718-56a31dc.diff b/.suckless/dwm/patches/dwm-attachaside-20160718-56a31dc.diff new file mode 100644 index 0000000..b8471af --- /dev/null +++ b/.suckless/dwm/patches/dwm-attachaside-20160718-56a31dc.diff @@ -0,0 +1,92 @@ +diff --git a/dwm.c b/dwm.c +index b2bc9bd..58a86fa 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -49,7 +49,8 @@ + #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 ISVISIBLEONTAG(C, T) ((C->tags & T)) ++#define ISVISIBLE(C) ISVISIBLEONTAG(C, 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) +@@ -148,6 +149,7 @@ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interac + static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); ++static void attachaside(Client *c); + static void attachstack(Client *c); + static void buttonpress(XEvent *e); + static void checkotherwm(void); +@@ -185,6 +187,7 @@ static void maprequest(XEvent *e); + static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); ++static Client *nexttagged(Client *c); + static Client *nexttiled(Client *c); + static void pop(Client *); + static void propertynotify(XEvent *e); +@@ -408,6 +411,17 @@ attach(Client *c) + } + + void ++attachaside(Client *c) { ++ Client *at = nexttagged(c); ++ if(!at) { ++ attach(c); ++ return; ++ } ++ c->next = at->next; ++ at->next = c; ++} ++ ++void + attachstack(Client *c) + { + c->snext = c->mon->stack; +@@ -1079,7 +1093,7 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); ++ attachaside(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1213,6 +1227,16 @@ movemouse(const Arg *arg) + } + + Client * ++nexttagged(Client *c) { ++ Client *walked = c->mon->clients; ++ for(; ++ walked && (walked->isfloating || !ISVISIBLEONTAG(walked, c->tags)); ++ walked = walked->next ++ ); ++ return walked; ++} ++ ++Client * + nexttiled(Client *c) + { + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); +@@ -1437,7 +1461,7 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ attachaside(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1890,7 +1914,7 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ attachaside(c); + attachstack(c); + } + if (m == selmon) diff --git a/.suckless/dwm/patches/dwm-bar-height-spacing-6.3.diff b/.suckless/dwm/patches/dwm-bar-height-spacing-6.3.diff new file mode 100644 index 0000000..cbdeb9a --- /dev/null +++ b/.suckless/dwm/patches/dwm-bar-height-spacing-6.3.diff @@ -0,0 +1,25 @@ +diff --git a/config.def.h b/config.def.h +index 1c0b587..9814500 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,7 @@ 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 user_bh = 2; /* 2 is the default spacing around the bar's font */ + 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 4465af1..2c27cb3 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1545,7 +1545,7 @@ setup(void) + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; +- bh = drw->fonts->h + 2; ++ bh = drw->fonts->h + user_bh; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); diff --git a/.suckless/dwm/patches/dwm-barpadding-20211020-a786211.diff b/.suckless/dwm/patches/dwm-barpadding-20211020-a786211.diff new file mode 100755 index 0000000..7842181 --- /dev/null +++ b/.suckless/dwm/patches/dwm-barpadding-20211020-a786211.diff @@ -0,0 +1,118 @@ +From a3cfb215f7f647d83d67e33df8f33a73e43bd65f Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Wed, 20 Oct 2021 09:14:07 +0200 +Subject: [PATCH] barpadding: adds space between the statusbar and the edge of + the screen + +--- + config.def.h | 2 ++ + dwm.c | 25 +++++++++++++++---------- + 2 files changed, 17 insertions(+), 10 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..f0b739f 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 vertpad = 10; /* vertical padding of bar */ ++static const int sidepad = 10; /* horizontal padding of bar */ + 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 5e4d494..df6d0d7 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -242,6 +242,8 @@ static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh, blw = 0; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ ++static int vp; /* vertical padding for bar */ ++static int sp; /* side padding for bar */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; + static void (*handler[LASTEvent]) (XEvent *) = { +@@ -568,7 +570,7 @@ configurenotify(XEvent *e) + 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); ++ XMoveResizeWindow(dpy, m->barwin, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh); + } + focus(NULL); + arrange(NULL); +@@ -706,7 +708,7 @@ drawbar(Monitor *m) + 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); ++ drw_text(drw, m->ww - tw - 2 * sp, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { +@@ -732,12 +734,12 @@ drawbar(Monitor *m) + 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); ++ drw_text(drw, x, 0, w - 2 * sp, 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_rect(drw, x, 0, w - 2 * sp, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +@@ -1547,7 +1549,10 @@ setup(void) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; ++ sp = sidepad; ++ vp = (topbar == 1) ? vertpad : - vertpad; + updategeom(); ++ + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); +@@ -1704,7 +1709,7 @@ togglebar(const Arg *arg) + { + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, bh); + arrange(selmon); + } + +@@ -1814,7 +1819,7 @@ updatebars(void) + 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), ++ m->barwin = XCreateWindow(dpy, root, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +@@ -1829,11 +1834,11 @@ 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; ++ m->wh = m->wh - vertpad - bh; ++ m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; ++ m->wy = m->topbar ? m->wy + bh + vp : m->wy; + } else +- m->by = -bh; ++ m->by = -bh - vp; + } + + void +-- +2.33.0 + diff --git a/.suckless/dwm/patches/dwm-fullgaps-6.4.diff b/.suckless/dwm/patches/dwm-fullgaps-6.4.diff new file mode 100755 index 0000000..dc52139 --- /dev/null +++ b/.suckless/dwm/patches/dwm-fullgaps-6.4.diff @@ -0,0 +1,94 @@ +diff -up a/config.def.h b/config.def.h +--- a/config.def.h ++++ b/config.def.h +@@ -2,6 +2,7 @@ + + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ ++static const unsigned int gappx = 5; /* gaps between 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 */ +@@ -85,6 +86,9 @@ static const Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY, XK_minus, setgaps, {.i = -1 } }, ++ { MODKEY, XK_equal, setgaps, {.i = +1 } }, ++ { MODKEY|ShiftMask, XK_equal, setgaps, {.i = 0 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff -up a/dwm.c b/dwm.c +--- a/dwm.c 2023-04-30 ++++ b/dwm.c 2023-04-30 +@@ -119,6 +119,7 @@ struct Monitor { + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ ++ int gappx; /* gaps between windows */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; +@@ -200,6 +201,7 @@ static void sendmon(Client *c, Monitor * + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); ++static void setgaps(const Arg *arg); + static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); +@@ -641,6 +643,7 @@ createmon(void) + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; ++ m->gappx = gappx; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); +@@ -1508,6 +1511,16 @@ setfullscreen(Client *c, int fullscreen) + } + + void ++setgaps(const Arg *arg) ++{ ++ if ((arg->i == 0) || (selmon->gappx + arg->i < 0)) ++ selmon->gappx = 0; ++ else ++ selmon->gappx += arg->i; ++ arrange(selmon); ++} ++ ++void + setlayout(const Arg *arg) + { + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) +@@ -1697,18 +1710,18 @@ tile(Monitor *m) + 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); ++ mw = m->ww - m->gappx; ++ for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx; ++ resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0); ++ if (my + HEIGHT(c) + m->gappx < m->wh) ++ my += HEIGHT(c) + m->gappx; + } 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); ++ h = (m->wh - ty) / (n - i) - m->gappx; ++ resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0); ++ if (ty + HEIGHT(c) + m->gappx < m->wh) ++ ty += HEIGHT(c) + m->gappx; + } + } diff --git a/.suckless/dwm/patches/dwm-notitle-20210715-138b405.diff b/.suckless/dwm/patches/dwm-notitle-20210715-138b405.diff new file mode 100755 index 0000000..bc8a3e5 --- /dev/null +++ b/.suckless/dwm/patches/dwm-notitle-20210715-138b405.diff @@ -0,0 +1,81 @@ +From a3a7e94f59553689656871a65ea9ce90169a7c91 Mon Sep 17 00:00:00 2001 +From: birdalicous +Date: Thu, 15 Jul 2021 12:28:29 +0100 +Subject: [PATCH] notitle patch applied# + +--- + config.def.h | 1 - + dwm.c | 20 ++++---------------- + 2 files changed, 4 insertions(+), 17 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..eac20b4 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -103,7 +103,6 @@ static 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} }, +diff --git a/dwm.c b/dwm.c +index 5e4d494..6cd9fb7 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -64,8 +64,8 @@ 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 */ ++enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkClientWin, ++ ClkRootWin, ClkLast }; /* clicks */ + + typedef union { + int i; +@@ -440,10 +440,8 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + blw) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - (int)TEXTW(stext)) +- click = ClkStatusText; + else +- click = ClkWinTitle; ++ click = ClkStatusText; + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); +@@ -730,15 +728,8 @@ drawbar(Monitor *m) + 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); + } +@@ -1236,11 +1227,8 @@ propertynotify(XEvent *e) + drawbars(); + break; + } +- if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { ++ 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); + } +-- +2.32.0 + diff --git a/.suckless/dwm/patches/dwm-restartsig-20180523-6.2.diff b/.suckless/dwm/patches/dwm-restartsig-20180523-6.2.diff new file mode 100755 index 0000000..f1f8680 --- /dev/null +++ b/.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/.suckless/dwm/patches/dwm-statusallmons-6.2.diff b/.suckless/dwm/patches/dwm-statusallmons-6.2.diff new file mode 100755 index 0000000..9d9633d --- /dev/null +++ b/.suckless/dwm/patches/dwm-statusallmons-6.2.diff @@ -0,0 +1,25 @@ +diff -up a/dwm.c b/dwm.c +--- a/dwm.c 2020-07-09 16:49:10.023585649 +0200 ++++ b/dwm.c 2020-07-09 16:49:43.497542191 +0200 +@@ -702,7 +702,7 @@ drawbar(Monitor *m) + Client *c; + + /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ ++ if (m == selmon || 1) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ + drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0); +@@ -1987,9 +1987,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor* m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for(m = mons; m; m = m->next) ++ drawbar(m); + } + + void diff --git a/.suckless/dwm/patches/dwm-warp-6.4.diff b/.suckless/dwm/patches/dwm-warp-6.4.diff new file mode 100755 index 0000000..02fcdba --- /dev/null +++ b/.suckless/dwm/patches/dwm-warp-6.4.diff @@ -0,0 +1,79 @@ +From a229c36f51ad6f8b40109ed53c643f242351962a Mon Sep 17 00:00:00 2001 +From: Jonas Dujava +Date: Fri, 26 May 2023 22:14:48 +0200 +Subject: [PATCH] Warp patch + +Warps the mouse cursor to the center of the currently focused +window or screen when the mouse cursor is + (a) on a different screen, or + (b) on top of a different window. + +This version properly handles warping to windows that have not been +mapped yet (before it resulted in a change of the stack order). +See the discussion in (thanks goes to Bakkeby): + https://github.com/bakkeby/patches/issues/60 +--- + dwm.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/dwm.c b/dwm.c +index e5efb6a..7ea6c14 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -228,6 +228,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); +@@ -834,6 +835,7 @@ focusmon(const Arg *arg) + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); ++ warp(selmon->sel); + } + + void +@@ -1366,6 +1368,8 @@ restack(Monitor *m) + wc.sibling = c->win; + } + } ++ if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle) ++ warp(m->sel); + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } +@@ -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) + { +-- +2.40.1 + diff --git a/.suckless/dwm/patches/dwm-xrdb-6.4.diff b/.suckless/dwm/patches/dwm-xrdb-6.4.diff new file mode 100755 index 0000000..929b4e6 --- /dev/null +++ b/.suckless/dwm/patches/dwm-xrdb-6.4.diff @@ -0,0 +1,203 @@ +From e7c65d2ce902a19a20daa751b42f8ba0209fdb61 Mon Sep 17 00:00:00 2001 +From: NekoCWD +Date: Sun, 22 Jan 2023 23:42:57 +0300 +Subject: [PATCH] [dwm] xrdb update 6.4 + +--- + config.def.h | 22 ++++++++++--------- + drw.c | 2 +- + drw.h | 2 +- + dwm.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 76 insertions(+), 12 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 061ad66..686b947 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -7,15 +7,16 @@ static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; +-static const char col_gray1[] = "#222222"; +-static const char col_gray2[] = "#444444"; +-static const char col_gray3[] = "#bbbbbb"; +-static const char col_gray4[] = "#eeeeee"; +-static const char col_cyan[] = "#005577"; +-static const char *colors[][3] = { +- /* fg bg border */ +- [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, +- [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++static char normbgcolor[] = "#222222"; ++static char normbordercolor[] = "#444444"; ++static char normfgcolor[] = "#bbbbbb"; ++static char selfgcolor[] = "#eeeeee"; ++static char selbordercolor[] = "#005577"; ++static char selbgcolor[] = "#005577"; ++static char *colors[][3] = { ++ /* fg bg border */ ++ [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor }, ++ [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor }, + }; + + /* tagging */ +@@ -56,7 +57,7 @@ static const Layout layouts[] = { + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + + /* commands */ +-static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; ++static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbordercolor, "-sf", selfgcolor, NULL }; + static const char *termcmd[] = { "st", NULL }; + + static const Key keys[] = { +@@ -84,6 +85,7 @@ static const Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY, XK_F5, xrdb, {.v = NULL } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff --git a/drw.c b/drw.c +index a58a2b4..f8a82f5 100644 +--- a/drw.c ++++ b/drw.c +@@ -195,7 +195,7 @@ drw_clr_create(Drw *drw, Clr *dest, const char *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) ++drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount) + { + size_t i; + Clr *ret; +diff --git a/drw.h b/drw.h +index 6471431..bdbf950 100644 +--- a/drw.h ++++ b/drw.h +@@ -40,7 +40,7 @@ void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned in + + /* 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); ++Clr *drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount); + + /* Cursor abstraction */ + Cur *drw_cur_create(Drw *drw, int shape); +diff --git a/dwm.c b/dwm.c +index e5efb6a..3fe76be 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -35,6 +35,7 @@ + #include + #include + #include ++#include + #include + #ifdef XINERAMA + #include +@@ -56,6 +57,21 @@ + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) ++#define XRDB_LOAD_COLOR(R,V) if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) { \ ++ if (value.addr != NULL && strnlen(value.addr, 8) == 7 && value.addr[0] == '#') { \ ++ int i = 1; \ ++ for (; i <= 6; i++) { \ ++ if (value.addr[i] < 48) break; \ ++ if (value.addr[i] > 57 && value.addr[i] < 65) break; \ ++ if (value.addr[i] > 70 && value.addr[i] < 97) break; \ ++ if (value.addr[i] > 102) break; \ ++ } \ ++ if (i == 7) { \ ++ strncpy(V, value.addr, 7); \ ++ V[7] = '\0'; \ ++ } \ ++ } \ ++ } + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +@@ -178,6 +194,7 @@ static void grabkeys(void); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); ++static void loadxrdb(void); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); +@@ -233,6 +250,7 @@ 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 xrdb(const Arg *arg); + static void zoom(const Arg *arg); + + /* variables */ +@@ -1019,6 +1037,37 @@ killclient(const Arg *arg) + } + } + ++void ++loadxrdb() ++{ ++ Display *display; ++ char * resm; ++ XrmDatabase xrdb; ++ char *type; ++ XrmValue value; ++ ++ display = XOpenDisplay(NULL); ++ ++ if (display != NULL) { ++ resm = XResourceManagerString(display); ++ ++ if (resm != NULL) { ++ xrdb = XrmGetStringDatabase(resm); ++ ++ if (xrdb != NULL) { ++ XRDB_LOAD_COLOR("dwm.normbordercolor", normbordercolor); ++ XRDB_LOAD_COLOR("dwm.normbgcolor", normbgcolor); ++ XRDB_LOAD_COLOR("dwm.normfgcolor", normfgcolor); ++ XRDB_LOAD_COLOR("dwm.selbordercolor", selbordercolor); ++ XRDB_LOAD_COLOR("dwm.selbgcolor", selbgcolor); ++ XRDB_LOAD_COLOR("dwm.selfgcolor", selfgcolor); ++ } ++ } ++ } ++ ++ XCloseDisplay(display); ++} ++ + void + manage(Window w, XWindowAttributes *wa) + { +@@ -2110,6 +2159,17 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + return -1; + } + ++void ++xrdb(const Arg *arg) ++{ ++ loadxrdb(); ++ int i; ++ for (i = 0; i < LENGTH(colors); i++) ++ scheme[i] = drw_scm_create(drw, colors[i], 3); ++ focus(NULL); ++ arrange(NULL); ++} ++ + void + zoom(const Arg *arg) + { +@@ -2134,6 +2194,8 @@ main(int argc, char *argv[]) + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + checkotherwm(); ++ XrmInitialize(); ++ loadxrdb(); + setup(); + #ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) +-- +2.38.2 + diff --git a/.suckless/dwm/transient.c b/.suckless/dwm/transient.c index 158460f..040adb5 100644 --- a/.suckless/dwm/transient.c +++ b/.suckless/dwm/transient.c @@ -40,4 +40,3 @@ int main(void) { XCloseDisplay(d); exit(0); } - diff --git a/.suckless/dwm/util.c b/.suckless/dwm/util.c index bcecb12..8e26a51 100644 --- a/.suckless/dwm/util.c +++ b/.suckless/dwm/util.c @@ -1,4 +1,5 @@ /* See LICENSE file for copyright and license details. */ +#include #include #include #include @@ -6,31 +7,31 @@ #include "util.h" -void * -ecalloc(size_t nmemb, size_t size) -{ - void *p; - - if (!(p = calloc(nmemb, size))) - die("calloc:"); - return p; -} - void -die(const char *fmt, ...) { +die(const char *fmt, ...) +{ va_list ap; + int saved_errno; + + saved_errno = errno; 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); - } + if (fmt[0] && fmt[strlen(fmt)-1] == ':') + fprintf(stderr, " %s", strerror(saved_errno)); + 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/.suckless/dwm/util.h b/.suckless/dwm/util.h index 1e3cf9a..c0a50d4 100644 --- a/.suckless/dwm/util.h +++ b/.suckless/dwm/util.h @@ -1,19 +1,9 @@ /* See LICENSE file for copyright and license details. */ -#ifndef MAX #define MAX(A, B) ((A) > (B) ? (A) : (B)) -#endif -#ifndef MIN #define MIN(A, B) ((A) < (B) ? (A) : (B)) -#endif #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) - -#ifdef _DEBUG -#define DEBUG(...) fprintf(stderr, __VA_ARGS__) -#else -#define DEBUG(...) -#endif +#define LENGTH(X) (sizeof (X) / sizeof (X)[0]) void die(const char *fmt, ...); void *ecalloc(size_t nmemb, size_t size); - diff --git a/.suckless/dwm/util.o b/.suckless/dwm/util.o index 04939521a548413d374b3f570d141b93a5cb2fc1..26adb1831cb1957b8850b271f4925c4b70bde3aa 100644 GIT binary patch delta 742 zcmY+B&nrYx6vxk<_wF0xN0=}vVc}=;W0GDGlPrv)JI_MLa7#5;}i@ zow8wPqfnHEg|cQ+VrQ*r-22?ob?Tmb-sha}_rBXbF3tE4nn>obdAsZ#ScG?b-OYIk zJ5Re!hZil~b`Mx>VdvTH+v`fUd%4E>_&2@R^((p#UJC94tRk&yZT%A95NW_PnIPYI zL-tz%D5zW^g`d@aAb;drR28`836MrS^RyG)t^3^E?&d)^kMLYMCLD6gO5re$rApmq z+wedT=HmuLB8F)uQ;|d}I%~yKNm#Px8G)5p+KeZy82dI82FE7DDHCGpWh)BCYTN=N zI-6iRVZ11Ieie$<|0#=6yrVjqLZXFO?$<)TDWNMwN7Kn1Ua`&w5^spu*w6AQZh1qV4vN>_zH;EF fvaoX^R#Pm`Yw_9QfN31c)AMJjI|gfjJGA--X2Z?Q delta 548 zcmaDLv_f!#2BXGA&30aoZd(Q*@aTNx(Rpv;5p5HXZe5T-gEd2msz>Mj7hp~e^M9*S zF^|r7`xStEu$bY0F!h=jC^}h_QFn3_quS(p#!$u&ldm%NiwZF?f^>qx2PiE#c_NcI zqXd{#1d|#da`FKt2}V;8OVSFc5~LHRLlUaf8mz8?nKvXQGnJvZBqg<|h@mX4peQr1 zBn`+f%1O;*NGm8UNoFVjO63Fr+3Y=H%ojLk%g0(o8^_6^Q@*hkzxMA2N&c zu11!fEXyL!%Z{Yx%w$0pammY2H7r1y6G`?FNS5)_k66byz@YkgHfGpXJcz)PUOO UG?|gpoO1=pQ9w{V`5>D-0NQkm@c;k- diff --git a/.suckless/slstatus/LICENSE b/.suckless/slstatus/LICENSE index 9fae663..8bee9c8 100644 --- a/.suckless/slstatus/LICENSE +++ b/.suckless/slstatus/LICENSE @@ -1,6 +1,6 @@ ISC License -Copyright 2016-2025 Aaron Marcher +Copyright 2016-2022 Aaron Marcher Copyright 2016 Roy Freytag Copyright 2016 Vincent Loupmon @@ -29,9 +29,6 @@ Copyright 2020 Daniel Moch Copyright 2022 Nickolas Raymond Kaczynski Copyright 2022 Patrick Iacob Copyright 2021-2022 Steven Ward -Copyright 2025 Joakim Sindholt -Copyright 2025 Al -Copyright 2025 sewn Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/.suckless/slstatus/components/battery.c b/.suckless/slstatus/components/battery.c index 1c753f9..d5ef9ea 100644 --- a/.suckless/slstatus/components/battery.c +++ b/.suckless/slstatus/components/battery.c @@ -56,10 +56,10 @@ char *state; char *symbol; } map[] = { - { "Charging", "+" }, - { "Discharging", "-" }, - { "Full", "o" }, - { "Not charging", "o" }, + { "Charging", "󰚥" }, + { "Discharging", "󰚦" }, + { "Full", "󰚥" }, + { "Not charging", "󰚥"}, }; size_t i; char path[PATH_MAX], state[12]; diff --git a/.suckless/slstatus/components/battery.o b/.suckless/slstatus/components/battery.o index 3deff79222a4a3a127f6674b2a4a2b757e868717..7f1c3e83e6588bd6ba08c61bf06c4399a5ee5ca3 100644 GIT binary patch delta 219 zcmZ3Wu|i{l2IGs3n(tYpKW~_|lmSRAV{j|Y$zkxzFHuO&NGwXv%uAna$m%-Tft77@ z87mW~q$DE)7$`s}1`wA8h~*|v+z1uwwE~A$!If ilYauq7n3c8?HLbDt^|@mabbH-4}OT13nm{FkOu(H1U9k& delta 177 zcmZ3Xu|Q*j2IGT`n(tY-v>9|6+)8tD81g5pv4(7}V`buG6r0@0CoU<$$N&Z!5Q+iB zWdUN@$s2*P@=#d|blJ@h`Q|e+R!nXbIL@du*;CM*(O`0;pgp6);myfB$j*q-shWJe&mV{#*q1d0Pmhslg0>Ht^`Cr1DP diff --git a/.suckless/slstatus/components/ip.c b/.suckless/slstatus/components/ip.c index 2cdad46..9476549 100644 --- a/.suckless/slstatus/components/ip.c +++ b/.suckless/slstatus/components/ip.c @@ -1,7 +1,6 @@ /* See LICENSE file for copyright and license details. */ #include #include -#include #include #include #if defined(__OpenBSD__) @@ -60,28 +59,3 @@ ipv6(const char *interface) { return ip(interface, AF_INET6); } - -const char * -up(const char *interface) -{ - struct ifaddrs *ifaddr, *ifa; - - if (getifaddrs(&ifaddr) < 0) { - warn("getifaddrs:"); - return NULL; - } - - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr) - continue; - - if (!strcmp(ifa->ifa_name, interface)) { - freeifaddrs(ifaddr); - return ifa->ifa_flags & IFF_UP ? "up" : "down"; - } - } - - freeifaddrs(ifaddr); - - return NULL; -} diff --git a/.suckless/slstatus/components/ip.o b/.suckless/slstatus/components/ip.o index ef0faf7a7f5a69354f45e82315f4c609542a187f..66982f1e22a01ef2de6aa0f1eaa694d40c9eb3cf 100644 GIT binary patch delta 380 zcmY+9y-EW?6ouzb_HSl4LQHf;lr^};pkNThl?Z{@7_2NTECel5*x1+@5Q|`CAq0*^ zI%8)etgR^M8{`qp1K6hs=s3F;-r}2cX3o7cGngBfdy03Q=ao9a>b19|m*^pb6~dP&FzVhrci}>ZnRCzi&H0~u?lAMKVI{N_ z!7KW!V<}}WJvfvj#ceB}ExRodgiyAWcl9%+T~gZmPE)k7e#at6o6f{p-d}m#_l)m`(%4lJHY$Tt*HI+g%o*214IzBNrnQ~gD5WVt~GZSd~210$A zOv;J$^$)c=@nkd}jXQ~0A{pyUB-t(qUMQ~>3kGmQEJo9393r&J)*jP(jMHZ5p)YMZ zMfJ29!%t0zg0yM%m^zH$CYZ;|?Feb8SqQ7kK^|zxtpc$gfuPS;+PP0~-_3n5_XFHt z=6;O(8Tjb4Tklcz9;Bc~#cNY5y4@Gt6?_$b`dth1LuEe;#14NIz9 z$HcpTJ-Z7#lzvhJ;ebT>>1nRgyAJ-Swqn9QtwX7WUaccsFI9e}F*C1nQM2bPnpL}} z#aQgAA@Qu>SSYLYIjLTrvLJDJ&UuN~dHv7xb3Kwa@JkynOI(-u>i>;+pEnY>;H8!e zKBp>XY;<)21f4i&EXT{&IzyM~`BsU3xzxB<2) zi3-xhNt^ECvr-3|p2P4&?>6Q|-3QpzyNq>qHfqBVHoAhu=Vf2S&;dAX*l^nj84>=w VA@qQEUo%|8VW+1y>rGG-_y=*?1v>x$ diff --git a/.suckless/slstatus/components/keymap.c b/.suckless/slstatus/components/keymap.c index 22224f3..f8a2a47 100644 --- a/.suckless/slstatus/components/keymap.c +++ b/.suckless/slstatus/components/keymap.c @@ -29,8 +29,8 @@ get_layout(char *syms, int grp_num) int grp; layout = NULL; - tok = strtok(syms, "+:_"); - for (grp = 0; tok && grp <= grp_num; tok = strtok(NULL, "+:_")) { + tok = strtok(syms, "+:"); + for (grp = 0; tok && grp <= grp_num; tok = strtok(NULL, "+:")) { if (!valid_layout_or_variant(tok)) { continue; } else if (strlen(tok) == 1 && isdigit(tok[0])) { diff --git a/.suckless/slstatus/components/keymap.o b/.suckless/slstatus/components/keymap.o index d565c9a015393abd11347c930643464e8c40e098..0737998ce3c9fcda0bc7c04bd8847669bcaaf171 100644 GIT binary patch delta 97 zcmdlWzd?S(cc#fdm=ZTfFh5|Fyv~3M%)wk%hFhBhS>st587FV#5|?CWgeVe#ss(YG aftY9VLoRVf{>?wR#F-dHHVg93X957*%@I2Q delta 99 zcmdlWzd?S(cP7U8$={h0H%BtxXOz6bfC|jPTvmqLn*&+nSs9roZ{!k} - #include - #include - #include - #include - - static int nlsock = -1; - static uint32_t seq = 1; - static char resp[4096]; - - static char * - findattr(int attr, const char *p, const char *e, size_t *len) - { - while (p < e) { - struct nlattr nla; - memcpy(&nla, p, sizeof(nla)); - if (nla.nla_type == attr) { - *len = nla.nla_len - NLA_HDRLEN; - return (char *)(p + NLA_HDRLEN); - } - p += NLA_ALIGN(nla.nla_len); - } - return NULL; - } + #include + #include - static uint16_t - nl80211fam(void) - { - static const char family[] = "nl80211"; - static uint16_t id; - ssize_t r; - size_t len; - char ctrl[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(sizeof(family))] = {0}, *p = ctrl; - - if (id) - return id; - - memcpy(p, &(struct nlmsghdr){ - .nlmsg_len = sizeof(ctrl), - .nlmsg_type = GENL_ID_CTRL, - .nlmsg_flags = NLM_F_REQUEST, - .nlmsg_seq = seq++, - .nlmsg_pid = 0, - }, sizeof(struct nlmsghdr)); - p += NLMSG_HDRLEN; - memcpy(p, &(struct genlmsghdr){ - .cmd = CTRL_CMD_GETFAMILY, - .version = 1, - }, sizeof(struct genlmsghdr)); - p += GENL_HDRLEN; - memcpy(p, &(struct nlattr){ - .nla_len = NLA_HDRLEN+sizeof(family), - .nla_type = CTRL_ATTR_FAMILY_NAME, - }, sizeof(struct nlattr)); - p += NLA_HDRLEN; - memcpy(p, family, sizeof(family)); - - if (nlsock < 0) - nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); - if (nlsock < 0) { - warn("socket 'AF_NETLINK':"); - return 0; - } - if (send(nlsock, ctrl, sizeof(ctrl), 0) != sizeof(ctrl)) { - warn("send 'AF_NETLINK':"); - return 0; - } - r = recv(nlsock, resp, sizeof(resp), 0); - if (r < 0) { - warn("recv 'AF_NETLINK':"); - return 0; - } - if ((size_t)r <= sizeof(ctrl)) - return 0; - p = findattr(CTRL_ATTR_FAMILY_ID, resp + sizeof(ctrl), resp + r, &len); - if (p && len == 2) - memcpy(&id, p, 2); - - return id; - } - - static int - ifindex(const char *interface) - { - static struct ifreq ifr; - static int ifsock = -1; - - if (ifsock < 0) - ifsock = socket(AF_UNIX, SOCK_DGRAM, 0); - if (ifsock < 0) { - warn("socket 'AF_UNIX':"); - return -1; - } - if (strcmp(ifr.ifr_name, interface) != 0) { - strcpy(ifr.ifr_name, interface); - if (ioctl(ifsock, SIOCGIFINDEX, &ifr) != 0) { - warn("ioctl 'SIOCGIFINDEX':"); - return -1; - } - } - return ifr.ifr_ifindex; - } + #define NET_OPERSTATE "/sys/class/net/%s/operstate" const char * - wifi_essid(const char *interface) + wifi_perc(const char *interface) { - uint16_t fam = nl80211fam(); - ssize_t r; - size_t len; - char req[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(4)] = {0}, *p = req; - int idx = ifindex(interface); - if (!fam) { - fprintf(stderr, "nl80211 family not found\n"); + int cur; + size_t i; + char *p, *datastart; + char path[PATH_MAX]; + char status[5]; + FILE *fp; + + if (esnprintf(path, sizeof(path), NET_OPERSTATE, interface) < 0) return NULL; - } - if (idx < 0) { - fprintf(stderr, "interface %s not found\n", interface); + if (!(fp = fopen(path, "r"))) { + warn("fopen '%s':", path); return NULL; } + p = fgets(status, 5, fp); + fclose(fp); + if (!p || strcmp(status, "up\n") != 0) + return NULL; - memcpy(p, &(struct nlmsghdr){ - .nlmsg_len = sizeof(req), - .nlmsg_type = fam, - .nlmsg_flags = NLM_F_REQUEST, - .nlmsg_seq = seq++, - .nlmsg_pid = 0, - }, sizeof(struct nlmsghdr)); - p += NLMSG_HDRLEN; - memcpy(p, &(struct genlmsghdr){ - .cmd = NL80211_CMD_GET_INTERFACE, - .version = 1, - }, sizeof(struct genlmsghdr)); - p += GENL_HDRLEN; - memcpy(p, &(struct nlattr){ - .nla_len = NLA_HDRLEN+4, - .nla_type = NL80211_ATTR_IFINDEX, - }, sizeof(struct nlattr)); - p += NLA_HDRLEN; - memcpy(p, &(uint32_t){idx}, 4); - - if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) { - warn("send 'AF_NETLINK':"); + if (!(fp = fopen("/proc/net/wireless", "r"))) { + warn("fopen '/proc/net/wireless':"); return NULL; } - r = recv(nlsock, resp, sizeof(resp), 0); - if (r < 0) { - warn("recv 'AF_NETLINK':"); + + for (i = 0; i < 3; i++) + if (!(p = fgets(buf, sizeof(buf) - 1, fp))) + break; + + fclose(fp); + if (i < 2 || !p) return NULL; - } - if ((size_t)r <= NLMSG_HDRLEN + GENL_HDRLEN) + if (!(datastart = strstr(buf, interface))) return NULL; - p = findattr(NL80211_ATTR_SSID, resp + NLMSG_HDRLEN + GENL_HDRLEN, resp + r, &len); - if (p) - p[len] = 0; - return p; + datastart = (datastart+(strlen(interface)+1)); + sscanf(datastart + 1, " %*d %d %*d %*d\t\t %*d\t " + "%*d\t\t%*d\t\t %*d\t %*d\t\t %*d", &cur); + + /* 70 is the max of /proc/net/wireless */ + return bprintf("%d", (int)((float)cur / 70 * 100)); } const char * - wifi_perc(const char *interface) + wifi_essid(const char *interface) { - static char strength[4]; - struct nlmsghdr hdr; - uint16_t fam = nl80211fam(); - ssize_t r; - size_t len; - char req[NLMSG_HDRLEN + GENL_HDRLEN + NLA_HDRLEN + NLA_ALIGN(4)] = {0}, *p = req, *e; - int idx = ifindex(interface); + static char id[IW_ESSID_MAX_SIZE+1]; + int sockfd; + struct iwreq wreq; - if (idx < 0) { - fprintf(stderr, "interface %s not found\n", interface); + memset(&wreq, 0, sizeof(struct iwreq)); + wreq.u.essid.length = IW_ESSID_MAX_SIZE+1; + if (esnprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "%s", + interface) < 0) return NULL; - } - memcpy(p, &(struct nlmsghdr){ - .nlmsg_len = sizeof(req), - .nlmsg_type = fam, - .nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP, - .nlmsg_seq = seq++, - .nlmsg_pid = 0, - }, sizeof(struct nlmsghdr)); - p += NLMSG_HDRLEN; - memcpy(p, &(struct genlmsghdr){ - .cmd = NL80211_CMD_GET_STATION, - .version = 1, - }, sizeof(struct genlmsghdr)); - p += GENL_HDRLEN; - memcpy(p, &(struct nlattr){ - .nla_len = NLA_HDRLEN + 4, - .nla_type = NL80211_ATTR_IFINDEX, - }, sizeof(struct nlattr)); - p += NLA_HDRLEN; - memcpy(p, &idx, 4); - - if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) { - warn("send 'AF_NETLINK':"); + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + warn("socket 'AF_INET':"); return NULL; } - - *strength = 0; - while (1) { - r = recv(nlsock, resp, sizeof(resp), 0); - if (r < 0) { - warn("recv 'AF_NETLINK':"); - return NULL; - } - if ((size_t)r < sizeof(hdr)) - return NULL; - - for (p = resp; p != resp + r && (size_t)(resp + r-p) >= sizeof(hdr); p = e) { - memcpy(&hdr, p, sizeof(hdr)); - e = resp + r - p < hdr.nlmsg_len ? resp + r : p + hdr.nlmsg_len; - - if (!*strength && hdr.nlmsg_len > NLMSG_HDRLEN+GENL_HDRLEN) { - p += NLMSG_HDRLEN+GENL_HDRLEN; - p = findattr(NL80211_ATTR_STA_INFO, p, e, &len); - if (p) - p = findattr(NL80211_STA_INFO_SIGNAL_AVG, p, e, &len); - if (p && len == 1) - snprintf(strength, sizeof(strength), "%d", RSSI_TO_PERC(*p)); - } - if (hdr.nlmsg_type == NLMSG_DONE) - return *strength ? strength : NULL; - } + wreq.u.essid.pointer = id; + if (ioctl(sockfd,SIOCGIWESSID, &wreq) < 0) { + warn("ioctl 'SIOCGIWESSID':"); + close(sockfd); + return NULL; } + + close(sockfd); + + if (!strcmp(id, "")) + return NULL; + + return id; } #elif defined(__OpenBSD__) #include diff --git a/.suckless/slstatus/components/wifi.o b/.suckless/slstatus/components/wifi.o index 4d633356ead3d228187323022d3eea797a9b4c52..52d77f842adca64afd396059d5e0c7b65af08247 100644 GIT binary patch literal 4320 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 nH;O2O;wyt9#?Cld zOl6apyGhLE=3aN#-DosfbrTqZ4@@?Rx^X2Em8{v=h^~*sXtL3{e^qz8WODC!+mAj~ z|2cK))TvXCt|<@Q<2&y0P3FD}lR5TeD}*!G?i})8L3`qr?4J0Y=Tn2;Pf>F3r;xsU zs`JObN>-zsQOWZl0r;z4uH>sX{*>1BQSx1}3f0O9$kK9$Rw($PO+TeKsbV}TB`y5s zurC%V*h5N+g$4aBr3&gxKT|b)MotTl_5Z)@Yw}Y{H<28_1rg^`;@58(;C=eF*~62c zs{KpmlN0v*fgJR+L>E9xYbp{Jj_)f_tdLyy8_HXW&ELf5(>L)1`IC|U5qu8W3G6#k z?w`gVXX(rIO+5Z)vOU1eZ(>*#xdr|VR^GsnP*SX$%r9i==h*uz#%*HnUF?0IY}T3h zOqPC^zDYJT-e7qnIUQhT{FhnjN`}6}(i_>khrOR*?>{nbIZJ=a((~AR1w}|xA2a_) zmhvFwV5yEiX40-R^Tn)8DSOwlGFuqG3GJ)boB3FVon!n?nkHR9w=(p<89J58X9t@3 z28Mpetlr3EcLS|qQYRSiD&tKfe9&awaKz34a!Uy3qnX5RCLz&xmIs{_#`WX4txC7* z6~d*UOkXa9+b$}=yDF{+PUS7+`ye@>#<6;F^*Yk^h?(4f65KYxxV`$NOYqR%#>i4k>10`2a3l&A+(w1q>4%7pLYAvb;Z+tmbR`EeqtJ zcZ8Y`XXOQMJo5CxcyzLa=82-gEX&oY169L06|eWf4aJK5b|2iV;!QsIABq(DmmcB? z3eZn2u=^oH!DGozgUR{VnM!V-54XQs>W~kv$$Nb8oEeIqb{~AQil6eq3l!YRb@(ve zo~0=2^1;VsE4b`~Pkmg$@A=?qV-=iJCpgXDvs8IK!(oNSC|4Oqp8}d9vk$&PmACld zbtEjzl)rb3QhmA)w?QqH?Sub*oFXs!;J+lVhnbuQMkw5cKHUFMwJr6*-&FBxA6#oo zoe#cWm9O`~-&62hZo3adQ`F>xA6FH<S% zuXUA{U+9BRAjRRP=!I%XSNm`)$YyYttK51Y?rO3h+%+n<$%p$C`4Zd)mAl)A`!orQ zFhxJ3a`*XgpCviq5|!)m;l4;#f%}TeJ>|oFgS-yzZ&hxW5BDu{8r*kO?oA)=5yD5B z%3D+}*YHyx9#wI@4}Ma`jXwCNDsJ|{&k|dt$=0cIvwgT%N|ahfAH0RMMw+5q)v;dc z!)+tCB2CU?DtD<5_k@a9``{;4yv_&zNX6@Y@Q+n|w-2t`JcQfh!}wG!u+IlSL-t3R ziqEQCj}Ny)#oK-GP8C1pgI`qf4zfSeG_%Wx`<2SwqpU7$FQ-#{%-;KY;aH#^K8JeY zsU*Z?ik$S2$8qf%mn<{IuuYf7w5SU@tz#N#G%eu$$v;e!dHsPDvsd5;lav?}85R>9 zlD%0eLie3R$vmKQffn^bQx3RePVmt=nrhISz1X>AMVu*mLN6|-1~gTfM_!3Dl~3%& zHTL38?uDCs;rYFAOD}v%FFd&yKJ`Fr++zMY%Om`64+LBC{O#VvBl9?JX)ffuJ^%80 zy@_wrJhifWAItxa=JRNNH_QKq=Bb_CO{CD8DwJ1~P1d2fv{6f8&sNmSB+jFCT}dY63@ zd?;_Hrnu~@XnGj%MvtQuHEv30PJ3wpyd#3Yb2V9!)Zh8w`-e77R3zp=0{oe)QN9Vf zT)OEB!LxmSk0oV#0nD)k9$N{fz>Du4YrEXT`}u# zhF^Qc!RKy^h9T#W+oX5iHW+okN5!Ba2SCNa?Y5J*4YONs8%jTb+^|zr$>tyGPv6uV zUTX#WFinbsPm5BX)j~C$R@qPKsj~S@S+DY&)`o_fovx zhD?74?UG8R4fFbQoJ&`}iZyww@IdL~TzF*SDza}-Ou?ah%)k91ArFIp)E?~_!d#$7 zanXe?ML#VOhx#WXmNf`bQTF@yVGw-(zET;QwULTt+@Z{?%wct} zBi~JnffpK@kL z84ety$`gmu@KdsnpPb(^Kc!PYA)wlFN?^k*#M{gnqVz9$H*BC&N3bYegwxrym@Z1k zkiPQ-ZQlpYUO!YU-hrHSBVRfyfBX##=SZbQu`)->#u#cwPikS~SB|^QK{P7d_5)BM zS5XV#7{B8jS8sm}5a+-It$P6}W|G)&jrPh7Ixo2G+sIQXCi90lwn6Vih|9)WWf;&* zU-J8LWYsv6b2}^$8;+XW#Ld^5SWS_M2vz7P8_02;DQ1?*T`0Pz7z^A}(hJln^B)cBX_Ysm#ulXeE{0nUbKS%$+GzKT7C}M0aj*QIXgn8;jue zzVYO-)X{NtW80ZhkN3sa&Xnyc-N(=)x!r#ol-HQJRByjTg+8P@?(0uDN*e?6lcA?i)s+f2;JJBECu zrlu0ZK#nvKo6RIx`mqXc++Y<-K9ct_dgZ-Qlxy{6ZECRgad1qmnd;Ary8+l0OWeT^0o{)_X@>o^(at^-qYp?Z>F0 z(s2k`6m1Ej^iSYNfCC(B!fG2Vx)5ISIoQ>6fevIdy1#c%UcXMtMq>YS>o7h8{mkQh|k1Rwb)FQU#6u<+4q$7EEU#YC8tLY z5#}!-_eYw9U-givj1kFA*J$Bmnr6RPW=<{+g1a}u7TI$Ze7D_5R%Mu+J6Vgcw7Tsv zOwiUT>!_$dMA_V5VohqMXQ=WvrJMy~Inq(seJ0;E5i{ux(N%4fx6@kPb{n|)(s5Mo zIAC};&zgiOYXYzW>Fz|E7h9EJIaF(Tr(rAUGb+@16x&_VWfP?WtXNpD=;*Ni!ScFydqxj zsDKgg(AnFom?^b&*J90wxrZ^N@(QE}=&wxlB&we24f%JV zXt_?5F7zOF>`U01Ab(^e9i!regWcrrs5IvUW~v$`8IX{dVQs?Fk${a^iPeaA0o@eX zCxExl4_QozYvFV{hRwZWB@3Dmfyr%j^~ZwT9|hPZUmxwN9hb2l| zt)w*5FnUth!>z~^I+sQULkw>C4kM99xs2A*!Ey;6>%_k5`c!Q9LZ*^eGh-&SFjIcM zA#usDX7f>6Z`nM}r03y5Uz4^>CHa~0nFN9uEp$7i*iej0XgsJkWcb)HoZ0YFJb5j% zpELOXFrwp1ZzC{Q*sil>z8W~zZW?%x(_vzvu0tkObeCd)WqcD&8-7VXghl}V0?;KD zdJ|B?J-*?Bx91owT7mr7GB$r*MeF9-JUMf_pP}BCE>d6GccGvxhQbXt58ZL7vo)dm z-1fF;%+`^Xe5p&6zLbZd9S!&Vu+F>2T9ui&^-7Q&A4b=CbNAP*gR#yxT+4(>HHwP) zvgD*=hCXt zmCCNm4M^E_Z7n6|9!(aGnFv32B6&pG+?0#UFnP%iFYNp%1&_uS5wZ`oKJDne{$b&C?y;2)K3Aa9@O#Rs=(l&M= zeiynjcF6RXAjMW?xZ&-5_r^#b>i>!w3#8kk_aj*k`@2gq57|6_rZbJa0DHbf5bt$@78QA!A5-*d*_6b5l2<fcGLy*Md~t}?sCyWcCT?|!)JXR}Dn-$~}U z(flm(`WUm&pOza$_K&j+{T9Mnr~YKxY55YC(`iQy>~VGAO8CUitDH=aC@6|5WqbY~`F!x0M5=%8(OXw)Vhl*T?inR3i5W-B6 zF1qrp`ol(yE+{WIl9ust!gqn>;`k`%JJ5#-$O3PbwH3($)JtxAJhaZCBcr<+CswoM zG$@r$$fZ;m;n7!g;V{Dj>UwlcF5U`W{+ZsH6i9dRIPUMgGd}i~s&BRFr-JoSwy5gqi z_F8aaUhwLCL@xXs2%8DS_ZeIiPy#s1&B?0t}jt+NozQv& z(a1F;HaR(+#)0nqc$7VX9t(uO>*1cBc-u*OhNVAZp;ALG!k~tG76;u@)++9MfQ@NS zxy$}4%Hres=^U7T@(BC*mi{U4`GSSWT*xB6>OQ5V*Nnhi??mV6P@vYL91OjVLlYW- zVPeBrt56$M>EbFS-2iBV=VCx;sMA^e;ShiJ0yXAnrqrP28d}yRFgbsmoQi-7jWCWD#11+8fK%x;5tVC0U?WAm0`k3z9(7my>IOd4k3R_+F zXHocJFkF;hJJ0%J>=-WDJ+m2?!`8hMyZ=%efcR(!^p#!Mnox!Xdln!V^#~|wC;N*{) zGpLjO6a$x|b=_~kFaP$ebb`5?I+(iLW#2RQe|K^-kHICjew|L8MqRxDf{Lr1hr)j5 z>-pgRKfZp8y3lRs=(dLXdiZhOpJ6dq;7*J{PrK~#(4o4Ve$Sw&PTT^z~GC>@YFw8^;k*>JSItq`WnM6kVx^xvUSIIE9Ois<#)? z>S}8m?nkVt5*v<3(B6{Hx8O^blP}|tly($$;ODW%wcuBOPX0s%v+2m@YqOr=4lm(Q z|JYA9MHre-jr}!?PWO(!?T|K1YsZKigM#c^f{s7?1vE*P(Gaoqx}~z=zF%$Mdix20 zm_BqK(AwZ0J(iemJAwx#e79XIXi*j%^aPde#czw!^ape&T#O#S^eEJSNCU`~@>itm zALSfLboU#Da`hYiyklxjm=;J>psj!LpT3G&)wG0dG~%0nKwCBxOSFom^{*0Ihy0Xd6VuCiV(Ah zMw6Dw@xqHi1!1vw3zaoXXmu`9<&b?j=;$#|n>GX?jeSJzFmp1Oe_WUB@ zxgtF&FNz_9ithLgf#@9-AE;N+nbY!1QEu7F#g65cK{N6nn=m$B6{{=D7gtykYkDyVnI-a1lJm2M^EMLlc&T${QOOu8 zveCj#D4MAyYZj#}uCA@EDSx7ZD=94cDeS0NQOy-jETm{6EjZ(GCc)DA4~5sRtxm5Q zo?e+=JA#|_SV1oxYgR6=T~Wd1=9G*XsMd`N7nM6;@;WXt*OItom?bfPOk!b5Vl~c& z<4V(>;-)V)C2n8pr3(G!Cb4?;ldnfGU#H^&eD&Pz3 zdC(Hjy`b-cwuAP=*KHT*QBXa`Oed%X)Qk`2Y|u2&QqW=?f;55BW1s(=jYJO)rReWA zYjFm05A+s}S*+L{hvGbNA?Q-j=RxCf!nhB#6|^067fxEQf$qTBtR7v}fYb71Q0E<# z3nIdtq&V>tuQ!SIYF;0#P#kB)A6+JI9)lCe#5n^6{!{XcIirM$pOW9q8Nlx)$L2gL zq@N-}*(m-F$tp{WejFV=pO(!FwFt9A^}~>KARt~Lo6FMp+hl)P0zZ{R&rKjV%l!E! za;I#dFzPJnH+KNPf!OB`jy>PpDLw$l~CJD*=D|IWHM8FF)WvkzUb3w#=KN`xeOuK39^eiN$&jNalc0LEyQI7VCJWVp?V9F2Z8@ccFs?T??9>sk=omn zRN^M^!(Wh3=Eny_AXpPFkTGRRK_WPL$Zozs1}vDgtO?1FvFV%+n5i!e)kh$?2Y4TZ z`&tE07gj5{2Y4m$-tzeR<~o4y2EIs>pQreO>iQPEugU2JXfa}cco(_4U_ksxBoAWz z;Pak!9O`9WEdYL?iws%VFK#)Kr+h@HceVoml$0+_bJFg754pn_fEiQ~o@D%;0(Kr) z1%io2#Wo=?)ZE~g7h2@^I3H@B5Nga}=2^l15whVJpF5E5C5x^X8d#S@lkw|XrCci817f^!ajH~3s^5JnikThY6pnjxJHvV=4`?eL+28muY zfIoO(?4rGaPVFY6c5~5COvNz)sZ_g(s9ijaRdMa=pmw!T%WGE$wX1^a)j&?W2{yW`kU#{{#Ot4VZSTHhwM!!?>Pn9Qk54bozAUmABH)z+7 z4y#vl^~)8ROfq|YTHwR~uRwOLA04ROg4;t*ub<3|Bzi+yVD=V8KARM87~NO9kS^CK bXi+42dBZ4PCZ{)y5At&;U^q|C*T(-3kOAjp diff --git a/.suckless/slstatus/slstatus.c b/.suckless/slstatus/slstatus.c index 16d88fe..fd31313 100644 --- a/.suckless/slstatus/slstatus.c +++ b/.suckless/slstatus/slstatus.c @@ -58,7 +58,6 @@ main(int argc, char *argv[]) ARGBEGIN { case 'v': die("slstatus-"VERSION); - break; case '1': done = 1; /* FALLTHROUGH */ diff --git a/.suckless/slstatus/slstatus.h b/.suckless/slstatus/slstatus.h index 394281c..8ef5874 100644 --- a/.suckless/slstatus/slstatus.h +++ b/.suckless/slstatus/slstatus.h @@ -30,7 +30,6 @@ const char *hostname(const char *unused); /* ip */ const char *ipv4(const char *interface); const char *ipv6(const char *interface); -const char *up(const char *interface); /* kernel_release */ const char *kernel_release(const char *unused); diff --git a/.suckless/slstatus/slstatus.o b/.suckless/slstatus/slstatus.o index b0ecf9767c5f487e01891d0bc741e167d385090d..8c298c3544175886b0136d7ade824bb2152e5286 100644 GIT binary patch delta 1242 zcmZ8gYiJZ#6rQu$T{BI#yR(yIG7;N}?CM4vH>;6VswOQFuOe*sZjF(3nJKiXJ*Tg1NWSJ z&Ue1=o;#1Z_G0_#2z2IJ)!Fe1(WJ&YrY32_V*cz&N{H%5_2}TRs_Ri> zzm`d=!2dn%>Q)G+7GFQrmS5ZPAK7_)l_?NI(Nc} z-~~9-?A;PG0#BOA*IPMivEpMEN*?Bct|!;=9oOGb#LZQIz@O-MuR69n)JUd!Le;Tn z{tt^}sw)&AYuzETx!mdh_IJ`UN3wTG%Yy~tU-N(#zpP&H2TVB{eM~lXf5E@!w*`Oi z4DEU21{0aM>AyHbT4qT02KjfEWP3sihjmk9 zp^mN!$%BF*`65*ev&IhL3PHl%Lan^jt};$NT@_9j9uy>bmjh*6w|(c2@d`yhcPOrO zaF3hlqyt^$K+hlqCHRKv!!*W%JA?p5nX#|QOV`hMFc_sT=t@wkzs&$@ZB{UQ*5IAs za=C0*IvogO-$oF{jS;)bLbtuI=+D}D?~?jo7!#vS&+Te@3CaT|#C7#irb6ws70iJm z_=~t)e$z%SI}kR|4xAUo70!Y3OFG`i<~j+~!g$>zz%23&B}ikrAaDCy$aZdw2q=hl$ pPU?FxD|hh9GD~DXmgP8qs>aH8qR>P$_9;>O5zFsvlN23>MFtMa+?}d78E5C zXdguM2V3~yW3VEtm)S!q70kfYhXU!v=*1)mLa1}@y~a)GgL}U3`~80B_nmvVH&wP+ zHsHdML3b(k4xSqBtq%mt?arwEq8%Lw)UTA$rjq7JJRTL#hET9J@Gl$-g(Il;U@b+R zbmQ@pl0vGF{1tb1M?BF|&lyjA2Rcz#qw?tLXp4tKsH!qpUZBh-1DnKHEYx`cIitu~ zWp}o0wZn6KyC`R`H(oHVMIyw>p72gC znHtBIObxGzY-^f&T>W)rYEf?*8YA=zS_eOWPs@yu){E%XH)F5!9# zK4+F;O_J5_#PCR)uWR2fqB{tV>h9q*eAR8lPrWFs?#aW>3nad@4y zZj#m#X)UEIzlYp~8D*hNdSDVOxjK1%?Rs1hkHlKZ4R^-dPd9gj!(w|2>J-DLBcaY_ z6zvuxa@88?Dnl(|EF_6r+sXBJiEXGUeg2EJTD>8m(Zdk#URnZvb!EgU`$=gk~{ zR<)m&%SwL@5`^vl4?6%;guSm~ZK^4cCI6SeTMk2y#f4u%!opeK5Csj7oTM+%4eu@a z{09Z8P%%1Ee*(<1@Xlf|2Ual)aD?5i`GMdzyN#Eeh~VZbQ5KA`Y@u60X}Y^gVT121 zXDy=t0*MhhDF-Xe%@>OmW>m${gY8oAlnO6VYMLfgIMr7w`t&NiI(aPj2F8mpXC22u zNZ5SL@QM`XZ9a|O24~_gM A(EtDd diff --git a/.suckless/st/LEGACY b/.suckless/st/LEGACY new file mode 100644 index 0000000..bf28b1e --- /dev/null +++ b/.suckless/st/LEGACY @@ -0,0 +1,17 @@ +A STATEMENT ON LEGACY SUPPORT + +In the terminal world there is much cruft that comes from old and unsup‐ +ported terminals that inherit incompatible modes and escape sequences +which noone is able to know, except when he/she comes from that time and +developed a graphical vt100 emulator at that time. + +One goal of st is to only support what is really needed. When you en‐ +counter a sequence which you really need, implement it. But while you +are at it, do not add the other cruft you might encounter while sneek‐ +ing at other terminal emulators. History has bloated them and there is +no real evidence that most of the sequences are used today. + + +Christoph Lohmann <20h@r-36.net> +2012-09-13T07:00:36.081271045+02:00 + diff --git a/.suckless/st/Makefile b/.suckless/st/Makefile index dfcea0f..fa43eea 100644 --- a/.suckless/st/Makefile +++ b/.suckless/st/Makefile @@ -4,7 +4,7 @@ include config.mk -SRC = st.c x.c hb.c +SRC = st.c x.c boxdraw.c rowcolumn_diacritics_helpers.c graphics.c OBJ = $(SRC:.c=.o) all: st @@ -16,8 +16,8 @@ config.h: $(CC) $(STCFLAGS) -c $< st.o: config.h st.h win.h -x.o: arg.h config.h st.h win.h hb.h -hb.o: st.h +x.o: arg.h config.h st.h win.h graphics.h +boxdraw.o: config.h st.h boxdraw_data.h $(OBJ): config.h config.mk diff --git a/.suckless/st/README b/.suckless/st/README new file mode 100644 index 0000000..6a846ed --- /dev/null +++ b/.suckless/st/README @@ -0,0 +1,34 @@ +st - simple terminal +-------------------- +st is a simple terminal emulator for X which sucks less. + + +Requirements +------------ +In order to build st you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (st is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install st (if +necessary as root): + + make clean install + + +Running st +---------- +If you did not install st with make clean install, you must compile +the st terminfo entry with the following command: + + tic -sx st.info + +See the man page for additional details. + +Credits +------- +Based on Aurélien APTEL bt source code. + diff --git a/.suckless/st/TODO b/.suckless/st/TODO new file mode 100644 index 0000000..5f74cd5 --- /dev/null +++ b/.suckless/st/TODO @@ -0,0 +1,28 @@ +vt emulation +------------ + +* double-height support + +code & interface +---------------- + +* add a simple way to do multiplexing + +drawing +------- +* add diacritics support to xdraws() + * switch to a suckless font drawing library +* make the font cache simpler +* add better support for brightening of the upper colors + +bugs +---- + +* fix shift up/down (shift selection in emacs) +* remove DEC test sequence when appropriate + +misc +---- + + $ grep -nE 'XXX|TODO' st.c + diff --git a/.suckless/st/boxdraw.c b/.suckless/st/boxdraw.c new file mode 100644 index 0000000..28a92d0 --- /dev/null +++ b/.suckless/st/boxdraw.c @@ -0,0 +1,194 @@ +/* + * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih + * MIT/X Consortium License + */ + +#include +#include "st.h" +#include "boxdraw_data.h" + +/* Rounded non-negative integers division of n / d */ +#define DIV(n, d) (((n) + (d) / 2) / (d)) + +static Display *xdpy; +static Colormap xcmap; +static XftDraw *xd; +static Visual *xvis; + +static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort); +static void drawboxlines(int, int, int, int, XftColor *, ushort); + +/* public API */ + +void +boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis) +{ + xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis; +} + +int +isboxdraw(Rune u) +{ + Rune block = u & ~0xff; + return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) || + (boxdraw_braille && block == 0x2800); +} + +/* the "index" is actually the entire shape data encoded as ushort */ +ushort +boxdrawindex(const Glyph *g) +{ + if (boxdraw_braille && (g->u & ~0xff) == 0x2800) + return BRL | (uint8_t)g->u; + if (boxdraw_bold && (g->mode & ATTR_BOLD)) + return BDB | boxdata[(uint8_t)g->u]; + return boxdata[(uint8_t)g->u]; +} + +void +drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg, + const XftGlyphFontSpec *specs, int len) +{ + for ( ; len-- > 0; x += cw, specs++) + drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph); +} + +/* implementation */ + +void +drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd) +{ + ushort cat = bd & ~(BDB | 0xff); /* mask out bold and data */ + if (bd & (BDL | BDA)) { + /* lines (light/double/heavy/arcs) */ + drawboxlines(x, y, w, h, fg, bd); + + } else if (cat == BBD) { + /* lower (8-X)/8 block */ + int d = DIV((uint8_t)bd * h, 8); + XftDrawRect(xd, fg, x, y + d, w, h - d); + + } else if (cat == BBU) { + /* upper X/8 block */ + XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8)); + + } else if (cat == BBL) { + /* left X/8 block */ + XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h); + + } else if (cat == BBR) { + /* right (8-X)/8 block */ + int d = DIV((uint8_t)bd * w, 8); + XftDrawRect(xd, fg, x + d, y, w - d, h); + + } else if (cat == BBQ) { + /* Quadrants */ + int w2 = DIV(w, 2), h2 = DIV(h, 2); + if (bd & TL) + XftDrawRect(xd, fg, x, y, w2, h2); + if (bd & TR) + XftDrawRect(xd, fg, x + w2, y, w - w2, h2); + if (bd & BL) + XftDrawRect(xd, fg, x, y + h2, w2, h - h2); + if (bd & BR) + XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2); + + } else if (bd & BBS) { + /* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */ + int d = (uint8_t)bd; + XftColor xfc; + XRenderColor xrc = { .alpha = 0xffff }; + + xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4); + xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4); + xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4); + + XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc); + XftDrawRect(xd, &xfc, x, y, w, h); + XftColorFree(xdpy, xvis, xcmap, &xfc); + + } else if (cat == BRL) { + /* braille, each data bit corresponds to one dot at 2x4 grid */ + int w1 = DIV(w, 2); + int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4); + + if (bd & 1) XftDrawRect(xd, fg, x, y, w1, h1); + if (bd & 2) XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1); + if (bd & 4) XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2); + if (bd & 8) XftDrawRect(xd, fg, x + w1, y, w - w1, h1); + if (bd & 16) XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1); + if (bd & 32) XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2); + if (bd & 64) XftDrawRect(xd, fg, x, y + h3, w1, h - h3); + if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3); + + } +} + +void +drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd) +{ + /* s: stem thickness. width/8 roughly matches underscore thickness. */ + /* We draw bold as 1.5 * normal-stem and at least 1px thicker. */ + /* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */ + int mwh = MIN(w, h); + int base_s = MAX(1, DIV(mwh, 8)); + int bold = (bd & BDB) && mwh >= 6; /* possibly ignore boldness */ + int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s; + int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2); + /* the s-by-s square (x + w2, y + h2, s, s) is the center texel. */ + /* The base length (per direction till edge) includes this square. */ + + int light = bd & (LL | LU | LR | LD); + int double_ = bd & (DL | DU | DR | DD); + + if (light) { + /* d: additional (negative) length to not-draw the center */ + /* texel - at arcs and avoid drawing inside (some) doubles */ + int arc = bd & BDA; + int multi_light = light & (light - 1); + int multi_double = double_ & (double_ - 1); + /* light crosses double only at DH+LV, DV+LH (ref. shapes) */ + int d = arc || (multi_double && !multi_light) ? -s : 0; + + if (bd & LL) + XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s); + if (bd & LU) + XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d); + if (bd & LR) + XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s); + if (bd & LD) + XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d); + } + + /* double lines - also align with light to form heavy when combined */ + if (double_) { + /* + * going clockwise, for each double-ray: p is additional length + * to the single-ray nearer to the previous direction, and n to + * the next. p and n adjust from the base length to lengths + * which consider other doubles - shorter to avoid intersections + * (p, n), or longer to draw the far-corner texel (n). + */ + int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD; + if (dl) { + int p = dd ? -s : 0, n = du ? -s : dd ? s : 0; + XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s); + XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s); + } + if (du) { + int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0; + XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p); + XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n); + } + if (dr) { + int p = du ? -s : 0, n = dd ? -s : du ? s : 0; + XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s); + XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s); + } + if (dd) { + int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0; + XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p); + XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n); + } + } +} diff --git a/.suckless/st/boxdraw.o b/.suckless/st/boxdraw.o new file mode 100644 index 0000000000000000000000000000000000000000..f5eb599827dcba235b2b39e2950b4cf6d14e4a1f GIT binary patch literal 7600 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 BaY_IH literal 0 HcmV?d00001 diff --git a/.suckless/st/boxdraw_data.h b/.suckless/st/boxdraw_data.h new file mode 100644 index 0000000..7890500 --- /dev/null +++ b/.suckless/st/boxdraw_data.h @@ -0,0 +1,214 @@ +/* + * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih + * MIT/X Consortium License + */ + +/* + * U+25XX codepoints data + * + * References: + * http://www.unicode.org/charts/PDF/U2500.pdf + * http://www.unicode.org/charts/PDF/U2580.pdf + * + * Test page: + * https://github.com/GNOME/vte/blob/master/doc/boxes.txt + */ + +/* Each shape is encoded as 16-bits. Higher bits are category, lower are data */ +/* Categories (mutually exclusive except BDB): */ +/* For convenience, BDL/BDA/BBS/BDB are 1 bit each, the rest are enums */ +#define BDL (1<<8) /* Box Draw Lines (light/double/heavy) */ +#define BDA (1<<9) /* Box Draw Arc (light) */ + +#define BBD (1<<10) /* Box Block Down (lower) X/8 */ +#define BBL (2<<10) /* Box Block Left X/8 */ +#define BBU (3<<10) /* Box Block Upper X/8 */ +#define BBR (4<<10) /* Box Block Right X/8 */ +#define BBQ (5<<10) /* Box Block Quadrants */ +#define BRL (6<<10) /* Box Braille (data is lower byte of U28XX) */ + +#define BBS (1<<14) /* Box Block Shades */ +#define BDB (1<<15) /* Box Draw is Bold */ + +/* (BDL/BDA) Light/Double/Heavy x Left/Up/Right/Down/Horizontal/Vertical */ +/* Heavy is light+double (literally drawing light+double align to form heavy) */ +#define LL (1<<0) +#define LU (1<<1) +#define LR (1<<2) +#define LD (1<<3) +#define LH (LL+LR) +#define LV (LU+LD) + +#define DL (1<<4) +#define DU (1<<5) +#define DR (1<<6) +#define DD (1<<7) +#define DH (DL+DR) +#define DV (DU+DD) + +#define HL (LL+DL) +#define HU (LU+DU) +#define HR (LR+DR) +#define HD (LD+DD) +#define HH (HL+HR) +#define HV (HU+HD) + +/* (BBQ) Quadrants Top/Bottom x Left/Right */ +#define TL (1<<0) +#define TR (1<<1) +#define BL (1<<2) +#define BR (1<<3) + +/* Data for U+2500 - U+259F except dashes/diagonals */ +static const unsigned short boxdata[256] = { + /* light lines */ + [0x00] = BDL + LH, /* light horizontal */ + [0x02] = BDL + LV, /* light vertical */ + [0x0c] = BDL + LD + LR, /* light down and right */ + [0x10] = BDL + LD + LL, /* light down and left */ + [0x14] = BDL + LU + LR, /* light up and right */ + [0x18] = BDL + LU + LL, /* light up and left */ + [0x1c] = BDL + LV + LR, /* light vertical and right */ + [0x24] = BDL + LV + LL, /* light vertical and left */ + [0x2c] = BDL + LH + LD, /* light horizontal and down */ + [0x34] = BDL + LH + LU, /* light horizontal and up */ + [0x3c] = BDL + LV + LH, /* light vertical and horizontal */ + [0x74] = BDL + LL, /* light left */ + [0x75] = BDL + LU, /* light up */ + [0x76] = BDL + LR, /* light right */ + [0x77] = BDL + LD, /* light down */ + + /* heavy [+light] lines */ + [0x01] = BDL + HH, + [0x03] = BDL + HV, + [0x0d] = BDL + HR + LD, + [0x0e] = BDL + HD + LR, + [0x0f] = BDL + HD + HR, + [0x11] = BDL + HL + LD, + [0x12] = BDL + HD + LL, + [0x13] = BDL + HD + HL, + [0x15] = BDL + HR + LU, + [0x16] = BDL + HU + LR, + [0x17] = BDL + HU + HR, + [0x19] = BDL + HL + LU, + [0x1a] = BDL + HU + LL, + [0x1b] = BDL + HU + HL, + [0x1d] = BDL + HR + LV, + [0x1e] = BDL + HU + LD + LR, + [0x1f] = BDL + HD + LR + LU, + [0x20] = BDL + HV + LR, + [0x21] = BDL + HU + HR + LD, + [0x22] = BDL + HD + HR + LU, + [0x23] = BDL + HV + HR, + [0x25] = BDL + HL + LV, + [0x26] = BDL + HU + LD + LL, + [0x27] = BDL + HD + LU + LL, + [0x28] = BDL + HV + LL, + [0x29] = BDL + HU + HL + LD, + [0x2a] = BDL + HD + HL + LU, + [0x2b] = BDL + HV + HL, + [0x2d] = BDL + HL + LD + LR, + [0x2e] = BDL + HR + LL + LD, + [0x2f] = BDL + HH + LD, + [0x30] = BDL + HD + LH, + [0x31] = BDL + HD + HL + LR, + [0x32] = BDL + HR + HD + LL, + [0x33] = BDL + HH + HD, + [0x35] = BDL + HL + LU + LR, + [0x36] = BDL + HR + LU + LL, + [0x37] = BDL + HH + LU, + [0x38] = BDL + HU + LH, + [0x39] = BDL + HU + HL + LR, + [0x3a] = BDL + HU + HR + LL, + [0x3b] = BDL + HH + HU, + [0x3d] = BDL + HL + LV + LR, + [0x3e] = BDL + HR + LV + LL, + [0x3f] = BDL + HH + LV, + [0x40] = BDL + HU + LH + LD, + [0x41] = BDL + HD + LH + LU, + [0x42] = BDL + HV + LH, + [0x43] = BDL + HU + HL + LD + LR, + [0x44] = BDL + HU + HR + LD + LL, + [0x45] = BDL + HD + HL + LU + LR, + [0x46] = BDL + HD + HR + LU + LL, + [0x47] = BDL + HH + HU + LD, + [0x48] = BDL + HH + HD + LU, + [0x49] = BDL + HV + HL + LR, + [0x4a] = BDL + HV + HR + LL, + [0x4b] = BDL + HV + HH, + [0x78] = BDL + HL, + [0x79] = BDL + HU, + [0x7a] = BDL + HR, + [0x7b] = BDL + HD, + [0x7c] = BDL + HR + LL, + [0x7d] = BDL + HD + LU, + [0x7e] = BDL + HL + LR, + [0x7f] = BDL + HU + LD, + + /* double [+light] lines */ + [0x50] = BDL + DH, + [0x51] = BDL + DV, + [0x52] = BDL + DR + LD, + [0x53] = BDL + DD + LR, + [0x54] = BDL + DR + DD, + [0x55] = BDL + DL + LD, + [0x56] = BDL + DD + LL, + [0x57] = BDL + DL + DD, + [0x58] = BDL + DR + LU, + [0x59] = BDL + DU + LR, + [0x5a] = BDL + DU + DR, + [0x5b] = BDL + DL + LU, + [0x5c] = BDL + DU + LL, + [0x5d] = BDL + DL + DU, + [0x5e] = BDL + DR + LV, + [0x5f] = BDL + DV + LR, + [0x60] = BDL + DV + DR, + [0x61] = BDL + DL + LV, + [0x62] = BDL + DV + LL, + [0x63] = BDL + DV + DL, + [0x64] = BDL + DH + LD, + [0x65] = BDL + DD + LH, + [0x66] = BDL + DD + DH, + [0x67] = BDL + DH + LU, + [0x68] = BDL + DU + LH, + [0x69] = BDL + DH + DU, + [0x6a] = BDL + DH + LV, + [0x6b] = BDL + DV + LH, + [0x6c] = BDL + DH + DV, + + /* (light) arcs */ + [0x6d] = BDA + LD + LR, + [0x6e] = BDA + LD + LL, + [0x6f] = BDA + LU + LL, + [0x70] = BDA + LU + LR, + + /* Lower (Down) X/8 block (data is 8 - X) */ + [0x81] = BBD + 7, [0x82] = BBD + 6, [0x83] = BBD + 5, [0x84] = BBD + 4, + [0x85] = BBD + 3, [0x86] = BBD + 2, [0x87] = BBD + 1, [0x88] = BBD + 0, + + /* Left X/8 block (data is X) */ + [0x89] = BBL + 7, [0x8a] = BBL + 6, [0x8b] = BBL + 5, [0x8c] = BBL + 4, + [0x8d] = BBL + 3, [0x8e] = BBL + 2, [0x8f] = BBL + 1, + + /* upper 1/2 (4/8), 1/8 block (X), right 1/2, 1/8 block (8-X) */ + [0x80] = BBU + 4, [0x94] = BBU + 1, + [0x90] = BBR + 4, [0x95] = BBR + 7, + + /* Quadrants */ + [0x96] = BBQ + BL, + [0x97] = BBQ + BR, + [0x98] = BBQ + TL, + [0x99] = BBQ + TL + BL + BR, + [0x9a] = BBQ + TL + BR, + [0x9b] = BBQ + TL + TR + BL, + [0x9c] = BBQ + TL + TR + BR, + [0x9d] = BBQ + TR, + [0x9e] = BBQ + BL + TR, + [0x9f] = BBQ + BL + TR + BR, + + /* Shades, data is an alpha value in 25% units (1/4, 1/2, 3/4) */ + [0x91] = BBS + 1, [0x92] = BBS + 2, [0x93] = BBS + 3, + + /* U+2504 - U+250B, U+254C - U+254F: unsupported (dashes) */ + /* U+2571 - U+2573: unsupported (diagonals) */ +}; diff --git a/.suckless/st/config.def.h b/.suckless/st/config.def.h new file mode 100644 index 0000000..e3c30ed --- /dev/null +++ b/.suckless/st/config.def.h @@ -0,0 +1,517 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static const char *font = "CaskaydiaMonoNerdFontMono-Bold:size=14:style=Bold"; +/* Spare fonts */ + +static char *font2[] = { + "CaskaydiaMonoNerdFontMono-Bold:pixelsize=12:antialias=true:autohint=true"}; +static int borderpx = 6; + +/* How to align the content in the window when the size of the terminal + * doesn't perfectly match the size of the window. The values are percentages. + * 50 means center, 0 means flush left/top, 100 means flush right/bottom. + */ +static int anysize_halign = 50; +static int anysize_valign = 50; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/sh"; +char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +/* By default, use the same one as kitty. */ + +char *vtiden = "\033[?62c"; +//char *vtiden = "\x1b[?62;4c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1; +static float chscale = 1; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 1; +static double maxlatency = 10; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 100; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 2; + +/* + * 1: render most of the lines/blocks characters without using the font for + * perfect alignment between cells (U2500 - U259F except dashes/diagonals). + * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. + * 0: disable (render all U25XX glyphs normally from the font). + */ +const int boxdraw = 1; +const int boxdraw_bold = 1; + +/* braille (U28XX): 1: render as adjacent "pixels", 0: use font */ +const int boxdraw_braille = 0; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "st"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "black", "red3", "green3", "yellow3", "blue2", "magenta3", "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", "red", "green", "yellow", "#5c5cff", "magenta", "cyan", "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", "#555555", "gray90", /* default foreground colour */ + "black", /* default background colour */ +}; + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 258; +unsigned int defaultbg = 259; +unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +static unsigned int cols = 80; +static unsigned int rows = 24; + +/* + * Default colour and shape of the mouse cursor + */ +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static unsigned int defaultattr = 11; + +/* + * Graphics configuration + */ + +/// The template for the cache directory. +const char graphics_cache_dir_template[] = "/tmp/st-images-XXXXXX"; +/// The max size of a single image file, in bytes. +unsigned graphics_max_single_image_file_size = 20 * 1024 * 1024; +/// The max size of the cache, in bytes. +unsigned graphics_total_file_cache_size = 300 * 1024 * 1024; +/// The max ram size of an image or placement, in bytes. +unsigned graphics_max_single_image_ram_size = 100 * 1024 * 1024; +/// The max total size of all images loaded into RAM. +unsigned graphics_max_total_ram_size = 300 * 1024 * 1024; +/// The max total number of image placements and images. +unsigned graphics_max_total_placements = 4096; +/// The ratio by which limits can be exceeded. This is to reduce the frequency +/// of image removal. +double graphics_excess_tolerance_ratio = 0.05; +/// The minimum delay between redraws caused by animations, in milliseconds. +unsigned graphics_animation_min_delay = 20; + +/* + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). + * Note that if you want to use ShiftMask with selmasks, set this to an other + * modifier, set to 0 to not use it. + */ +static uint forcemousemod = ShiftMask; + +/* Internal keyboard shortcuts. */ +#define MODKEY Mod1Mask +#define TERMMOD (ControlMask | ShiftMask) + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* mask button function argument release */ + {TERMMOD, Button3, previewimage, {.s = "feh"}}, + {TERMMOD, Button2, showimageinfo, {}, 1}, + {XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1}, + {ShiftMask, Button4, kscrollup, {.i = 1}}, + {ShiftMask, Button5, kscrolldown, {.i = 1}}, + {ShiftMask, Button4, ttysend, {.s = "\033[5;2~"}}, + {XK_ANY_MOD, Button4, ttysend, {.s = "\031"}}, + {ShiftMask, Button5, ttysend, {.s = "\033[6;2~"}}, + {XK_ANY_MOD, Button5, ttysend, {.s = "\005"}}, +}; + +/* Internal keyboard shortcuts. */ + +static Shortcut shortcuts[] = { + /* mask keysym function argument */ + {TERMMOD, XK_F1, togglegrdebug, {.i = 0}}, + {TERMMOD, XK_F6, dumpgrstate, {.i = 0}}, + {TERMMOD, XK_F7, unloadimages, {.i = 0}}, + {TERMMOD, XK_F8, toggleimages, {.i = 0}}, + {XK_ANY_MOD, XK_Break, sendbreak, {.i = 0}}, + {ControlMask, XK_Print, toggleprinter, {.i = 0}}, + {ShiftMask, XK_Print, printscreen, {.i = 0}}, + {XK_ANY_MOD, XK_Print, printsel, {.i = 0}}, + {TERMMOD, XK_C, clipcopy, {.i = 0}}, + {TERMMOD, XK_V, clippaste, {.i = 0}}, + {TERMMOD, XK_Y, selpaste, {.i = 0}}, + {ShiftMask, XK_Page_Up, kscrollup, {.i = -1}}, + {ShiftMask, XK_Page_Down, kscrolldown, {.i = -1}}, + {ShiftMask, XK_Insert, selpaste, {.i = 0}}, + {TERMMOD, XK_Num_Lock, numlock, {.i = 0}}, + {TERMMOD, XK_plus, zoom, {.f = +1}}, + {TERMMOD, XK_underscore, zoom, {.f = -1}}, + {TERMMOD, XK_equal, zoomreset, {0}}, +}; + +/* + * Special keys (change & recompile st.info accordingly) + * + * Mask value: + * * Use XK_ANY_MOD to match the key no matter modifiers state + * * Use XK_NO_MOD to match the key alone (no modifiers) + * appkey value: + * * 0: no value + * * > 0: keypad application mode enabled + * * = 2: term.numlock = 1 + * * < 0: keypad application mode disabled + * appcursor value: + * * 0: no value + * * > 0: cursor application mode enabled + * * < 0: cursor application mode disabled + * + * Be careful with the order of the definitions because st searches in + * this table sequentially, so any XK_ANY_MOD must be in the last + * position for a key. + */ + +/* + * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) + * to be mapped below, add them to this array. + */ +static KeySym mappedkeys[] = {-1}; + +/* + * State bits to ignore when matching key or button events. By default, + * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. + */ +static uint ignoremod = Mod2Mask | XK_SWITCH_MOD; + +/* + * This is the huge key array which defines all compatibility to the Linux + * world. Please decide about changes wisely. + */ +static Key key[] = { + /* keysym mask string appkey appcursor */ + {XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + {XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + {XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + {XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + {XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + {XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + {XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + {XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + {XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + {XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + {XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + {XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + {XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + {XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + {XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + {XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + {XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + {XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + {XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + {XK_KP_End, ControlMask, "\033[J", -1, 0}, + {XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + {XK_KP_End, ShiftMask, "\033[K", -1, 0}, + {XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + {XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + {XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + {XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + {XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + {XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + {XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + {XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + {XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + {XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + {XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + {XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + {XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + {XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + {XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + {XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + {XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + {XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + {XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + {XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + {XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + {XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + {XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + {XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + {XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + {XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + {XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + {XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + {XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + {XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + {XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + {XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + {XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + {XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + {XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + {XK_Up, ShiftMask | Mod1Mask, "\033[1;4A", 0, 0}, + {XK_Up, ControlMask, "\033[1;5A", 0, 0}, + {XK_Up, ShiftMask | ControlMask, "\033[1;6A", 0, 0}, + {XK_Up, ControlMask | Mod1Mask, "\033[1;7A", 0, 0}, + {XK_Up, ShiftMask | ControlMask | Mod1Mask, "\033[1;8A", 0, 0}, + {XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + {XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + {XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + {XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + {XK_Down, ShiftMask | Mod1Mask, "\033[1;4B", 0, 0}, + {XK_Down, ControlMask, "\033[1;5B", 0, 0}, + {XK_Down, ShiftMask | ControlMask, "\033[1;6B", 0, 0}, + {XK_Down, ControlMask | Mod1Mask, "\033[1;7B", 0, 0}, + {XK_Down, ShiftMask | ControlMask | Mod1Mask, "\033[1;8B", 0, 0}, + {XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + {XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + {XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + {XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + {XK_Left, ShiftMask | Mod1Mask, "\033[1;4D", 0, 0}, + {XK_Left, ControlMask, "\033[1;5D", 0, 0}, + {XK_Left, ShiftMask | ControlMask, "\033[1;6D", 0, 0}, + {XK_Left, ControlMask | Mod1Mask, "\033[1;7D", 0, 0}, + {XK_Left, ShiftMask | ControlMask | Mod1Mask, "\033[1;8D", 0, 0}, + {XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + {XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + {XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + {XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + {XK_Right, ShiftMask | Mod1Mask, "\033[1;4C", 0, 0}, + {XK_Right, ControlMask, "\033[1;5C", 0, 0}, + {XK_Right, ShiftMask | ControlMask, "\033[1;6C", 0, 0}, + {XK_Right, ControlMask | Mod1Mask, "\033[1;7C", 0, 0}, + {XK_Right, ShiftMask | ControlMask | Mod1Mask, "\033[1;8C", 0, 0}, + {XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + {XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + {XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + {XK_Return, Mod1Mask, "\033\r", 0, 0}, + {XK_Return, XK_ANY_MOD, "\r", 0, 0}, + {XK_Insert, ShiftMask, "\033[4l", -1, 0}, + {XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + {XK_Insert, ControlMask, "\033[L", -1, 0}, + {XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + {XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + {XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + {XK_Delete, ControlMask, "\033[M", -1, 0}, + {XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + {XK_Delete, ShiftMask, "\033[2K", -1, 0}, + {XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + {XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + {XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + {XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + {XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + {XK_Home, ShiftMask, "\033[2J", 0, -1}, + {XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + {XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + {XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + {XK_End, ControlMask, "\033[J", -1, 0}, + {XK_End, ControlMask, "\033[1;5F", +1, 0}, + {XK_End, ShiftMask, "\033[K", -1, 0}, + {XK_End, ShiftMask, "\033[1;2F", +1, 0}, + {XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + {XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + {XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + {XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + {XK_Next, ControlMask, "\033[6;5~", 0, 0}, + {XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + {XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + {XK_F1, XK_NO_MOD, "\033OP", 0, 0}, + {XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + {XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + {XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + {XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + {XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + {XK_F2, XK_NO_MOD, "\033OQ", 0, 0}, + {XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + {XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + {XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + {XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + {XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + {XK_F3, XK_NO_MOD, "\033OR", 0, 0}, + {XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + {XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + {XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + {XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + {XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + {XK_F4, XK_NO_MOD, "\033OS", 0, 0}, + {XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + {XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + {XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + {XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + {XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + {XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + {XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + {XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + {XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + {XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + {XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + {XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + {XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + {XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + {XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + {XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + {XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + {XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + {XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + {XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + {XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + {XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + {XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + {XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + {XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + {XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + {XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + {XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + {XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + {XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + {XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + {XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + {XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + {XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + {XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + {XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + {XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + {XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + {XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + {XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + {XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + {XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + {XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + {XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + {XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + {XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + {XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + {XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + {XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + {XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + {XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + {XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + {XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + {XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + {XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + {XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + {XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + {XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + {XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + {XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + {XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + {XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + {XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + {XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + {XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + {XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + {XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, +}; + +/* + * Selection types' masks. + * Use the same masks as usual. + * Button1Mask is always unset, to make masks match between ButtonPress. + * ButtonRelease and MotionNotify. + * If no match is found, regular selection is used. + */ +static uint selmasks[] = { + [SEL_RECTANGULAR] = Mod1Mask, +}; + +/* + * Printable characters in ASCII, used to estimate the advance width + * of single wide characters. + */ +static char ascii_printable[] = " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/.suckless/st/config.h b/.suckless/st/config.h index e2c1566..c99cd99 100644 --- a/.suckless/st/config.h +++ b/.suckless/st/config.h @@ -5,8 +5,19 @@ * * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html */ -static char *font = "monospace:antialias=true:autohint=true"; -static int borderpx = 2; +static const char *font = "CaskaydiaMonoNerdFontMono-Bold:size=14:style=Bold"; +/* Spare fonts */ + +static char *font2[] = { + "CaskaydiaMonoNerdFontMono-Bold:pixelsize=12:antialias=true:autohint=true"}; +static int borderpx = 6; + +/* How to align the content in the window when the size of the terminal + * doesn't perfectly match the size of the window. The values are percentages. + * 50 means center, 0 means flush left/top, 100 means flush right/bottom. + */ +static int anysize_halign = 50; +static int anysize_valign = 50; /* * What program is execed by st depends of these precedence rules: @@ -16,18 +27,21 @@ static int borderpx = 2; * 4: value of shell in /etc/passwd * 5: value of shell in config.h */ -static char *shell = "/bin/sh"; +static char *shell = "/usr/bin/zsh"; char *utmp = NULL; /* scroll program: to enable use a string like "scroll" */ char *scroll = NULL; char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; /* identification sequence returned in DA and DECID */ -char *vtiden = "\033[?6c"; +/* By default, use the same one as kitty. */ + +char *vtiden = "\033[?62c"; +//char *vtiden = "\x1b[?62;4c"; /* Kerning / character bounding-box multipliers */ -static float cwscale = 1.0; -static float chscale = 1.0; +static float cwscale = 1; +static float chscale = 1; /* * word delimiter string @@ -53,20 +67,32 @@ int allowwindowops = 0; * near minlatency, but it waits longer for slow updates to avoid partial draw. * low minlatency will tear/flicker more, as it can "detect" idle too early. */ -static double minlatency = 2; -static double maxlatency = 33; +static double minlatency = 1; +static double maxlatency = 10; /* * blinking timeout (set to 0 to disable blinking) for the terminal blinking * attribute. */ -static unsigned int blinktimeout = 800; +static unsigned int blinktimeout = 100; /* * thickness of underline and bar cursors */ static unsigned int cursorthickness = 2; +/* + * 1: render most of the lines/blocks characters without using the font for + * perfect alignment between cells (U2500 - U259F except dashes/diagonals). + * Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. + * 0: disable (render all U25XX glyphs normally from the font). + */ +const int boxdraw = 1; +const int boxdraw_bold = 1; + +/* braille (U28XX): 1: render as adjacent "pixels", 0: use font */ +const int boxdraw_braille = 0; + /* * bell volume. It must be a value between -100 and 100. Use 0 for disabling * it @@ -74,7 +100,7 @@ static unsigned int cursorthickness = 2; static int bellvolume = 0; /* default TERM value */ -char *termname = "st-256color"; +char *termname = "st"; /* * spaces per tab @@ -93,43 +119,28 @@ char *termname = "st-256color"; */ unsigned int tabspaces = 8; -/* bg opacity */ -float alpha = 0.9; - -/* Background opacity */ -float alpha_def; - /* Terminal colors (16 first used in escape sequence) */ static const char *colorname[] = { - "#282828", /* hard contrast: #1d2021 / soft contrast: #32302f */ - "#cc241d", - "#98971a", - "#d79921", - "#458588", - "#b16286", - "#689d6a", - "#a89984", - "#928374", - "#fb4934", - "#b8bb26", - "#fabd2f", - "#83a598", - "#d3869b", - "#8ec07c", - "#ebdbb2", - [255] = 0, - /* more colors can be added after 255 to use with DefaultXX */ - "#add8e6", /* 256 -> cursor */ - "#555555", /* 257 -> rev cursor*/ - "#282828", /* 258 -> bg */ - "#ebdbb2", /* 259 -> fg */ + /* 8 normal colors */ + "black", "red3", "green3", "yellow3", "blue2", "magenta3", "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", "red", "green", "yellow", "#5c5cff", "magenta", "cyan", "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", "#555555", "gray90", /* default foreground colour */ + "black", /* default background colour */ }; + /* * Default colors (colorname index) * foreground, background, cursor, reverse cursor */ -unsigned int defaultfg = 256; -unsigned int defaultbg = 257; +unsigned int defaultfg = 258; +unsigned int defaultbg = 259; unsigned int defaultcs = 256; static unsigned int defaultrcs = 257; @@ -140,14 +151,14 @@ static unsigned int defaultrcs = 257; * 6: Bar ("|") * 7: Snowman ("☃") */ -static unsigned int cursorshape = 6; +static unsigned int cursorshape = 2; /* * Default columns and rows numbers */ -static unsigned int cols = 140; -static unsigned int rows = 40; +static unsigned int cols = 80; +static unsigned int rows = 24; /* * Default colour and shape of the mouse cursor @@ -162,6 +173,28 @@ static unsigned int mousebg = 0; */ static unsigned int defaultattr = 11; +/* + * Graphics configuration + */ + +/// The template for the cache directory. +const char graphics_cache_dir_template[] = "/tmp/st-images-XXXXXX"; +/// The max size of a single image file, in bytes. +unsigned graphics_max_single_image_file_size = 20 * 1024 * 1024; +/// The max size of the cache, in bytes. +unsigned graphics_total_file_cache_size = 300 * 1024 * 1024; +/// The max ram size of an image or placement, in bytes. +unsigned graphics_max_single_image_ram_size = 100 * 1024 * 1024; +/// The max total size of all images loaded into RAM. +unsigned graphics_max_total_ram_size = 300 * 1024 * 1024; +/// The max total number of image placements and images. +unsigned graphics_max_total_placements = 4096; +/// The ratio by which limits can be exceeded. This is to reduce the frequency +/// of image removal. +double graphics_excess_tolerance_ratio = 0.05; +/// The minimum delay between redraws caused by animations, in milliseconds. +unsigned graphics_animation_min_delay = 20; + /* * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). * Note that if you want to use ShiftMask with selmasks, set this to an other @@ -169,41 +202,49 @@ static unsigned int defaultattr = 11; */ static uint forcemousemod = ShiftMask; +/* Internal keyboard shortcuts. */ +#define MODKEY Mod1Mask +#define TERMMOD (ControlMask | ShiftMask) + /* * Internal mouse shortcuts. * Beware that overloading Button1 will disable the selection. */ static MouseShortcut mshortcuts[] = { - /* mask button function argument release */ - { XK_NO_MOD, Button4, kscrollup, {.i = 1} }, - { XK_NO_MOD, Button5, kscrolldown, {.i = 1} }, - { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, - { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, - { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, - { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, - { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, + /* mask button function argument release */ + {TERMMOD, Button3, previewimage, {.s = "feh"}}, + {TERMMOD, Button2, showimageinfo, {}, 1}, + {XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1}, + {ShiftMask, Button4, kscrollup, {.i = 1}}, + {ShiftMask, Button5, kscrolldown, {.i = 1}}, + {ShiftMask, Button4, ttysend, {.s = "\033[5;2~"}}, + {XK_ANY_MOD, Button4, ttysend, {.s = "\031"}}, + {ShiftMask, Button5, ttysend, {.s = "\033[6;2~"}}, + {XK_ANY_MOD, Button5, ttysend, {.s = "\005"}}, }; /* Internal keyboard shortcuts. */ -#define MODKEY ControlMask -#define TERMMOD (Mod1Mask|ShiftMask) static Shortcut shortcuts[] = { - /* mask keysym function argument */ - { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, - { ControlMask, XK_Print, toggleprinter, {.i = 0} }, - { ShiftMask, XK_Print, printscreen, {.i = 0} }, - { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, - { ControlMask, XK_equal, zoom, {.f = +1} }, - { ControlMask, XK_minus, zoom, {.f = -1} }, - { TERMMOD, XK_Home, zoomreset, {.f = 0} }, - { MODKEY, XK_c, clipcopy, {.i = 0} }, - { MODKEY, XK_v, clippaste, {.i = 0} }, - { TERMMOD, XK_Y, selpaste, {.i = 0} }, - { ShiftMask, XK_Insert, selpaste, {.i = 0} }, - { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, - { MODKEY, XK_k, kscrollup, {.i = 1} }, - { MODKEY, XK_j, kscrolldown, {.i = 1} }, + /* mask keysym function argument */ + {TERMMOD, XK_F1, togglegrdebug, {.i = 0}}, + {TERMMOD, XK_F6, dumpgrstate, {.i = 0}}, + {TERMMOD, XK_F7, unloadimages, {.i = 0}}, + {TERMMOD, XK_F8, toggleimages, {.i = 0}}, + {XK_ANY_MOD, XK_Break, sendbreak, {.i = 0}}, + {ControlMask, XK_Print, toggleprinter, {.i = 0}}, + {ShiftMask, XK_Print, printscreen, {.i = 0}}, + {XK_ANY_MOD, XK_Print, printsel, {.i = 0}}, + {TERMMOD, XK_C, clipcopy, {.i = 0}}, + {TERMMOD, XK_V, clippaste, {.i = 0}}, + {TERMMOD, XK_Y, selpaste, {.i = 0}}, + {ShiftMask, XK_Page_Up, kscrollup, {.i = -1}}, + {ShiftMask, XK_Page_Down, kscrolldown, {.i = -1}}, + {ShiftMask, XK_Insert, selpaste, {.i = 0}}, + {TERMMOD, XK_Num_Lock, numlock, {.i = 0}}, + {TERMMOD, XK_plus, zoom, {.f = +1}}, + {TERMMOD, XK_underscore, zoom, {.f = -1}}, + {TERMMOD, XK_equal, zoomreset, {0}}, }; /* @@ -231,229 +272,229 @@ static Shortcut shortcuts[] = { * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) * to be mapped below, add them to this array. */ -static KeySym mappedkeys[] = { -1 }; +static KeySym mappedkeys[] = {-1}; /* * State bits to ignore when matching key or button events. By default, * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. */ -static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; +static uint ignoremod = Mod2Mask | XK_SWITCH_MOD; /* * This is the huge key array which defines all compatibility to the Linux * world. Please decide about changes wisely. */ static Key key[] = { - /* keysym mask string appkey appcursor */ - { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, - { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, - { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, - { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, - { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, - { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, - { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, - { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, - { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, - { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, - { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, - { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, - { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, - { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, - { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, - { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, - { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, - { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, - { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, - { XK_KP_End, ControlMask, "\033[J", -1, 0}, - { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, - { XK_KP_End, ShiftMask, "\033[K", -1, 0}, - { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, - { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, - { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, - { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, - { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, - { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, - { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, - { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, - { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, - { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, - { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, - { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, - { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, - { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, - { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, - { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, - { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, - { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, - { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, - { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, - { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, - { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, - { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, - { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, - { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, - { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, - { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, - { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, - { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, - { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, - { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, - { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, - { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, - { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, - { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, - { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, - { XK_Up, ControlMask, "\033[1;5A", 0, 0}, - { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, - { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, - { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, - { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, - { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, - { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, - { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, - { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, - { XK_Down, ControlMask, "\033[1;5B", 0, 0}, - { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, - { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, - { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, - { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, - { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, - { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, - { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, - { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, - { XK_Left, ControlMask, "\033[1;5D", 0, 0}, - { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, - { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, - { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, - { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, - { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, - { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, - { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, - { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, - { XK_Right, ControlMask, "\033[1;5C", 0, 0}, - { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, - { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, - { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, - { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, - { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, - { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, - { XK_Return, Mod1Mask, "\033\r", 0, 0}, - { XK_Return, XK_ANY_MOD, "\r", 0, 0}, - { XK_Insert, ShiftMask, "\033[4l", -1, 0}, - { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, - { XK_Insert, ControlMask, "\033[L", -1, 0}, - { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, - { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, - { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, - { XK_Delete, ControlMask, "\033[M", -1, 0}, - { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, - { XK_Delete, ShiftMask, "\033[2K", -1, 0}, - { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, - { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, - { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, - { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, - { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, - { XK_Home, ShiftMask, "\033[2J", 0, -1}, - { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, - { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, - { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, - { XK_End, ControlMask, "\033[J", -1, 0}, - { XK_End, ControlMask, "\033[1;5F", +1, 0}, - { XK_End, ShiftMask, "\033[K", -1, 0}, - { XK_End, ShiftMask, "\033[1;2F", +1, 0}, - { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, - { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, - { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, - { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, - { XK_Next, ControlMask, "\033[6;5~", 0, 0}, - { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, - { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, - { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, - { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, - { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, - { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, - { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, - { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, - { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, - { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, - { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, - { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, - { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, - { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, - { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, - { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, - { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, - { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, - { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, - { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, - { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, - { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, - { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, - { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, - { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, - { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, - { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, - { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, - { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, - { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, - { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, - { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, - { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, - { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, - { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, - { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, - { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, - { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, - { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, - { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, - { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, - { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, - { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, - { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, - { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, - { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, - { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, - { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, - { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, - { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, - { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, - { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, - { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, - { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, - { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, - { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, - { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, - { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, - { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, - { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, - { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, - { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, - { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, - { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, - { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, - { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, - { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, - { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, - { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, - { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, - { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, - { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, - { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, - { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, - { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, - { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, - { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, - { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, - { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, - { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, - { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, - { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, - { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, - { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, - { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, - { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, - { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, - { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, + /* keysym mask string appkey appcursor */ + {XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + {XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + {XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + {XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + {XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + {XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + {XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + {XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + {XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + {XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + {XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + {XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + {XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + {XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + {XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + {XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + {XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + {XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + {XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + {XK_KP_End, ControlMask, "\033[J", -1, 0}, + {XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + {XK_KP_End, ShiftMask, "\033[K", -1, 0}, + {XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + {XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + {XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + {XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + {XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + {XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + {XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + {XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + {XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + {XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + {XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + {XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + {XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + {XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + {XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + {XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + {XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + {XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + {XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + {XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + {XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + {XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + {XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + {XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + {XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + {XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + {XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + {XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + {XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + {XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + {XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + {XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + {XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + {XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + {XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + {XK_Up, ShiftMask | Mod1Mask, "\033[1;4A", 0, 0}, + {XK_Up, ControlMask, "\033[1;5A", 0, 0}, + {XK_Up, ShiftMask | ControlMask, "\033[1;6A", 0, 0}, + {XK_Up, ControlMask | Mod1Mask, "\033[1;7A", 0, 0}, + {XK_Up, ShiftMask | ControlMask | Mod1Mask, "\033[1;8A", 0, 0}, + {XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + {XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + {XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + {XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + {XK_Down, ShiftMask | Mod1Mask, "\033[1;4B", 0, 0}, + {XK_Down, ControlMask, "\033[1;5B", 0, 0}, + {XK_Down, ShiftMask | ControlMask, "\033[1;6B", 0, 0}, + {XK_Down, ControlMask | Mod1Mask, "\033[1;7B", 0, 0}, + {XK_Down, ShiftMask | ControlMask | Mod1Mask, "\033[1;8B", 0, 0}, + {XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + {XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + {XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + {XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + {XK_Left, ShiftMask | Mod1Mask, "\033[1;4D", 0, 0}, + {XK_Left, ControlMask, "\033[1;5D", 0, 0}, + {XK_Left, ShiftMask | ControlMask, "\033[1;6D", 0, 0}, + {XK_Left, ControlMask | Mod1Mask, "\033[1;7D", 0, 0}, + {XK_Left, ShiftMask | ControlMask | Mod1Mask, "\033[1;8D", 0, 0}, + {XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + {XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + {XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + {XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + {XK_Right, ShiftMask | Mod1Mask, "\033[1;4C", 0, 0}, + {XK_Right, ControlMask, "\033[1;5C", 0, 0}, + {XK_Right, ShiftMask | ControlMask, "\033[1;6C", 0, 0}, + {XK_Right, ControlMask | Mod1Mask, "\033[1;7C", 0, 0}, + {XK_Right, ShiftMask | ControlMask | Mod1Mask, "\033[1;8C", 0, 0}, + {XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + {XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + {XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + {XK_Return, Mod1Mask, "\033\r", 0, 0}, + {XK_Return, XK_ANY_MOD, "\r", 0, 0}, + {XK_Insert, ShiftMask, "\033[4l", -1, 0}, + {XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + {XK_Insert, ControlMask, "\033[L", -1, 0}, + {XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + {XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + {XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + {XK_Delete, ControlMask, "\033[M", -1, 0}, + {XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + {XK_Delete, ShiftMask, "\033[2K", -1, 0}, + {XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + {XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + {XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + {XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + {XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + {XK_Home, ShiftMask, "\033[2J", 0, -1}, + {XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + {XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + {XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + {XK_End, ControlMask, "\033[J", -1, 0}, + {XK_End, ControlMask, "\033[1;5F", +1, 0}, + {XK_End, ShiftMask, "\033[K", -1, 0}, + {XK_End, ShiftMask, "\033[1;2F", +1, 0}, + {XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + {XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + {XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + {XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + {XK_Next, ControlMask, "\033[6;5~", 0, 0}, + {XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + {XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + {XK_F1, XK_NO_MOD, "\033OP", 0, 0}, + {XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + {XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + {XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + {XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + {XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + {XK_F2, XK_NO_MOD, "\033OQ", 0, 0}, + {XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + {XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + {XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + {XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + {XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + {XK_F3, XK_NO_MOD, "\033OR", 0, 0}, + {XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + {XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + {XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + {XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + {XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + {XK_F4, XK_NO_MOD, "\033OS", 0, 0}, + {XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + {XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + {XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + {XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + {XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + {XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + {XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + {XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + {XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + {XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + {XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + {XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + {XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + {XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + {XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + {XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + {XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + {XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + {XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + {XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + {XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + {XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + {XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + {XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + {XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + {XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + {XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + {XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + {XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + {XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + {XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + {XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + {XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + {XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + {XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + {XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + {XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + {XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + {XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + {XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + {XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + {XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + {XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + {XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + {XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + {XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + {XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + {XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + {XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + {XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + {XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + {XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + {XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + {XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + {XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + {XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + {XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + {XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + {XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + {XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + {XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + {XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + {XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + {XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + {XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + {XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + {XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, }; /* @@ -464,14 +505,13 @@ static Key key[] = { * If no match is found, regular selection is used. */ static uint selmasks[] = { - [SEL_RECTANGULAR] = Mod1Mask, + [SEL_RECTANGULAR] = Mod1Mask, }; /* * Printable characters in ASCII, used to estimate the advance width * of single wide characters. */ -static char ascii_printable[] = - " !\"#$%&'()*+,-./0123456789:;<=>?" - "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" - "`abcdefghijklmnopqrstuvwxyz{|}~"; +static char ascii_printable[] = " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/.suckless/st/config.mk b/.suckless/st/config.mk index b6458af..cb2875c 100644 --- a/.suckless/st/config.mk +++ b/.suckless/st/config.mk @@ -14,13 +14,14 @@ PKG_CONFIG = pkg-config # includes and libs INCS = -I$(X11INC) \ + `$(PKG_CONFIG) --cflags imlib2` \ `$(PKG_CONFIG) --cflags fontconfig` \ - `$(PKG_CONFIG) --cflags freetype2` \ - `$(PKG_CONFIG) --cflags harfbuzz` + `$(PKG_CONFIG) --cflags freetype2` LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender \ + `$(PKG_CONFIG) --libs imlib2` \ + `$(PKG_CONFIG) --libs zlib` \ `$(PKG_CONFIG) --libs fontconfig` \ - `$(PKG_CONFIG) --libs freetype2` \ - `$(PKG_CONFIG) --libs harfbuzz` + `$(PKG_CONFIG) --libs freetype2` # flags STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 diff --git a/.suckless/st/dmenu/LICENSE b/.suckless/st/dmenu/LICENSE new file mode 100644 index 0000000..2a64b28 --- /dev/null +++ b/.suckless/st/dmenu/LICENSE @@ -0,0 +1,30 @@ +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2008 Sander van Dijk +© 2006-2007 Michał Janeczek +© 2007 Kris Maglione +© 2009 Gottox +© 2009 Markus Schnalke +© 2009 Evan Gates +© 2010-2012 Connor Lane Smith +© 2014-2022 Hiltjo Posthuma +© 2015-2019 Quentin Rameau + +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/.suckless/st/dmenu/Makefile b/.suckless/st/dmenu/Makefile new file mode 100644 index 0000000..458c524 --- /dev/null +++ b/.suckless/st/dmenu/Makefile @@ -0,0 +1,58 @@ +# 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: dmenu stest + +.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 clean dist install uninstall diff --git a/.suckless/st/dmenu/README b/.suckless/st/dmenu/README new file mode 100644 index 0000000..a8fcdfe --- /dev/null +++ b/.suckless/st/dmenu/README @@ -0,0 +1,24 @@ +dmenu - dynamic menu +==================== +dmenu is an efficient dynamic menu for X. + + +Requirements +------------ +In order to build dmenu you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dmenu is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dmenu +(if necessary as root): + + make clean install + + +Running dmenu +------------- +See the man page for details. diff --git a/.suckless/st/dmenu/arg.h b/.suckless/st/dmenu/arg.h new file mode 100644 index 0000000..e94e02b --- /dev/null +++ b/.suckless/st/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/.suckless/st/dmenu/config.def.h b/.suckless/st/dmenu/config.def.h new file mode 100644 index 0000000..1edb647 --- /dev/null +++ b/.suckless/st/dmenu/config.def.h @@ -0,0 +1,23 @@ +/* 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 */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "monospace: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] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; +/* -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/.suckless/st/dmenu/config.h b/.suckless/st/dmenu/config.h new file mode 100644 index 0000000..1edb647 --- /dev/null +++ b/.suckless/st/dmenu/config.h @@ -0,0 +1,23 @@ +/* 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 */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "monospace: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] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; +/* -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/.suckless/st/dmenu/config.mk b/.suckless/st/dmenu/config.mk new file mode 100644 index 0000000..137f7c8 --- /dev/null +++ b/.suckless/st/dmenu/config.mk @@ -0,0 +1,32 @@ +# dmenu version +VERSION = 5.3 + +# 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_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/.suckless/st/dmenu/dmenu b/.suckless/st/dmenu/dmenu new file mode 100755 index 0000000000000000000000000000000000000000..656bab89d05ef91e4c08f9e592538f3236578d6e GIT binary patch literal 42928 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 literal 0 HcmV?d00001 diff --git a/.suckless/st/dmenu/dmenu.1 b/.suckless/st/dmenu/dmenu.1 new file mode 100644 index 0000000..323f93c --- /dev/null +++ b/.suckless/st/dmenu/dmenu.1 @@ -0,0 +1,194 @@ +.TH DMENU 1 dmenu\-VERSION +.SH NAME +dmenu \- dynamic menu +.SH SYNOPSIS +.B dmenu +.RB [ \-bfiv ] +.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 +.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/.suckless/st/dmenu/dmenu.c b/.suckless/st/dmenu/dmenu.c new file mode 100644 index 0000000..fd49549 --- /dev/null +++ b/.suckless/st/dmenu/dmenu.c @@ -0,0 +1,795 @@ +/* See LICENSE file for copyright and license details. */ +#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 TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ + +struct item { + char *text; + struct item *left, *right; + int out; +}; + +static char text[BUFSIZ] = ""; +static char *embed; +static int bh, mw, mh; +static int inputw = 0, promptw; +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; + + XUngrabKeyboard(dpy, CurrentTime); + 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 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]); + + return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); +} + +static void +drawmenu(void) +{ + unsigned int curpos; + struct item *item; + int x = 0, y = 0, w; + + 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]); + 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"); +} + +static void +match(void) +{ + 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[64]; + int len; + KeySym ksym = NoSymbol; + Status status; + + len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); + switch (status) { + default: /* XLookupNone, XBufferOverflow */ + return; + case XLookupChars: /* composed string from input method */ + goto insert; + case XLookupKeySym: + case XLookupBoth: /* a KeySym and a string are returned: use keysym */ + 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 +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, itemsiz = 0, linesiz = 0; + ssize_t len; + + /* read each line from stdin and add it to the item list */ + for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) { + if (i + 1 >= itemsiz) { + itemsiz += 256; + if (!(items = realloc(items, itemsiz * sizeof(*items)))) + die("cannot realloc %zu bytes:", itemsiz * sizeof(*items)); + } + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + if (!(items[i].text = strdup(line))) + die("strdup:"); + + items[i].out = 0; + } + free(line); + 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 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; + win = XCreateWindow(dpy, root, 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) { + XReparentWindow(dpy, win, parentwin, x, y); + 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 [-bfiv] [-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], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; + } 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/.suckless/st/dmenu/dmenu.o b/.suckless/st/dmenu/dmenu.o new file mode 100644 index 0000000000000000000000000000000000000000..e4915920c257b8588ba36d0e1a7d63156f322041 GIT binary patch literal 31968 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 literal 0 HcmV?d00001 diff --git a/.suckless/st/dmenu/dmenu_path b/.suckless/st/dmenu/dmenu_path new file mode 100755 index 0000000..3a7cda7 --- /dev/null +++ b/.suckless/st/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/.suckless/st/dmenu/dmenu_run b/.suckless/st/dmenu/dmenu_run new file mode 100755 index 0000000..834ede5 --- /dev/null +++ b/.suckless/st/dmenu/dmenu_run @@ -0,0 +1,2 @@ +#!/bin/sh +dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & diff --git a/.suckless/st/dmenu/drw.c b/.suckless/st/dmenu/drw.c new file mode 100644 index 0000000..c41e6af --- /dev/null +++ b/.suckless/st/dmenu/drw.c @@ -0,0 +1,448 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD + +static int +utf8decode(const char *s_in, long *u, int *err) +{ + static const unsigned char lens[] = { + /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */ + /* 110XX */ 2, 2, 2, 2, + /* 1110X */ 3, 3, + /* 11110 */ 4, + /* 11111 */ 0, /* invalid */ + }; + static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 }; + static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 }; + + const unsigned char *s = (const unsigned char *)s_in; + int len = lens[*s >> 3]; + *u = UTF_INVALID; + *err = 1; + if (len == 0) + return 1; + + long cp = s[0] & leading_mask[len - 1]; + for (int i = 1; i < len; ++i) { + if (s[i] == '\0' || (s[i] & 0xC0) != 0x80) + return i; + cp = (cp << 6) | (s[i] & 0x3F); + } + /* out of range, surrogate, overlong encoding */ + if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1]) + return len; + + *err = 0; + *u = cp; + 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 ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + int utf8strlen, utf8charlen, utf8err, 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. */ + static unsigned int nomatches[128], ellipsis_width, invalid_width; + static const char invalid[] = "�"; + + 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); + if (w < lpad) + return x + w; + 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, "..."); + if (!invalid_width && render) + invalid_width = drw_fontset_getwidth(drw, invalid); + while (1) { + ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, &utf8err); + 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) { + text += utf8charlen; + utf8strlen += utf8err ? 0 : utf8charlen; + ew += utf8err ? 0 : tmpw; + } else { + nextfont = curfont; + } + break; + } + } + + if (overflow || !charexists || nextfont || utf8err) + 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 (utf8err && (!render || invalid_width < w)) { + if (render) + drw_text(drw, x, y, w, h, 0, invalid, invert); + x += invalid_width; + w -= invalid_width; + } + 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; + + hash = (unsigned int)utf8codepoint; + hash = ((hash >> 16) ^ hash) * 0x21F0AAAD; + hash = ((hash >> 15) ^ hash) * 0xD35A2D97; + h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches); + h1 = (hash >> 17) % LENGTH(nomatches); + /* avoid expensive XftFontMatch call when we know we won't find a match */ + if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint) + 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[nomatches[h0] ? h1 : h0] = 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/.suckless/st/dmenu/drw.h b/.suckless/st/dmenu/drw.h new file mode 100644 index 0000000..fd7631b --- /dev/null +++ b/.suckless/st/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/.suckless/st/dmenu/drw.o b/.suckless/st/dmenu/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..dbafb1662bf561b45180fe2335afc7ce9362489f GIT binary patch literal 11296 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 literal 0 HcmV?d00001 diff --git a/.suckless/st/dmenu/stest b/.suckless/st/dmenu/stest new file mode 100755 index 0000000000000000000000000000000000000000..45f7c0494f9cebae7c41eb0b7ba1f7069124d605 GIT binary patch literal 16408 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 +© 2007-2011 Peter Hartlich +© 2007-2009 Szabolcs Nagy +© 2007-2009 Christof Musik +© 2007-2009 Premysl Hruby +© 2007-2008 Enno Gottox Boland +© 2008 Martin Hurton +© 2008 Neale Pickett +© 2009 Mate Nagy +© 2010-2016 Hiltjo Posthuma +© 2010-2012 Connor Lane Smith +© 2011 Christoph Lohmann <20h@r-36.net> +© 2015-2016 Quentin Rameau +© 2015-2016 Eric Pruitt +© 2016-2017 Markus Teich +© 2020-2022 Chris Down + +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/.suckless/st/dwm/Makefile b/.suckless/st/dwm/Makefile new file mode 100644 index 0000000..088a4b9 --- /dev/null +++ b/.suckless/st/dwm/Makefile @@ -0,0 +1,67 @@ +# 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} + +# FreeBSD users, prefix all ifdef, else and endif statements with a . for this to work (e.g. .ifdef) + +ifdef YAJLLIBS +all: options dwm dwm-msg +else +all: options dwm +endif + +options: + @echo dwm build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + ${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk + +dwm: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +ifdef YAJLLIBS +dwm-msg: + ${CC} -o $@ patch/ipc/dwm-msg.c ${LDFLAGS} +endif + +clean: + rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz + rm -f dwm-msg + +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 +ifdef YAJLLIBS + cp -f dwm-msg ${DESTDIR}${PREFIX}/bin +endif + cp -f patch/dwmc ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm +ifdef YAJLLIBS + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm-msg +endif + 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/.suckless/st/dwm/README b/.suckless/st/dwm/README new file mode 100644 index 0000000..95d4fd0 --- /dev/null +++ b/.suckless/st/dwm/README @@ -0,0 +1,48 @@ +dwm - dynamic window manager +============================ +dwm is an extremely fast, small, and dynamic window manager for X. + + +Requirements +------------ +In order to build dwm you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dwm is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dwm (if +necessary as root): + + make clean install + + +Running dwm +----------- +Add the following line to your .xinitrc to start dwm using startx: + + exec dwm + +In order to connect dwm to a specific display, make sure that +the DISPLAY environment variable is set correctly, e.g.: + + DISPLAY=foo.bar:1 exec dwm + +(This will start dwm on display :1 of the host foo.bar.) + +In order to display status info in the bar, you can do something +like this in your .xinitrc: + + while xsetroot -name "`date` `uptime | sed 's/.*,//'`" + do + sleep 1 + done & + exec dwm + + +Configuration +------------- +The configuration of dwm is done by creating a custom config.h +and (re)compiling the source code. diff --git a/.suckless/st/dwm/README.md b/.suckless/st/dwm/README.md new file mode 100644 index 0000000..70e3d98 --- /dev/null +++ b/.suckless/st/dwm/README.md @@ -0,0 +1,30 @@ +## DWM +This is one of my favorite window managers, it's great - very customizable. +Credits to Swindles McCoop on Github and YouTube, I've cloned their dwm build and made my own configurations to it. + +## Compiling +### Dependencies +X11, Xinerama, xcb, freetype, rofi, slock, Alacritty +### Instructions +Run `./configure` to properly set up `config.mk`. +#### Linux +`make && sudo make install` +#### BSD +`gmake && doas gmake install` + +## Keybinds: +- `MODKEY + Enter` - Spawn Alacritty (MUST BE INSTALLED) +- `MODKEY + Shift + Enter`, `F12` - Toggle scratchpad terminal +- `MODKEY + r` - Rofi (MUST BE INSTALLED) +- `MODKEY + s` - Kill window +- `MODKEY + m` - Toggle status bar +- `MODKEY + f` - Toggle fullscreen +- `MODKEY + o/O` - Increase/decrease number of masters +- `MODKEY + j/k` - Move focus down/up +- `MODKEY + J/K` - Move window in stack down/up +- `MODKEY + SHIFTMASK + q` - Kill DWM +- `MODKEY + Space` - Set all windows to Floating mode +- `MODKEY + K/J` - Change focused window in on the direction of K/J on a qwerty keyboard +- `MODKEY + SHIFTMASK + K/J` - Move window in on the direction of K/J on a qwerty keyboard +- `MODKEY + H/L` - Expand/shrink master window in direction of H/L on a qwerty keyboard +- `MODKEY + SHIFTMASK + L` - Lock using slock (MUST BE INSTALLED) \ No newline at end of file diff --git a/.suckless/st/dwm/attachaside.diff b/.suckless/st/dwm/attachaside.diff new file mode 100644 index 0000000..2f34ac2 --- /dev/null +++ b/.suckless/st/dwm/attachaside.diff @@ -0,0 +1,93 @@ +diff --git a/dwm.c b/dwm.c +index f1d86b2..8b04e0b 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -49,7 +49,8 @@ + #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 ISVISIBLEONTAG(C, T) ((C->tags & T)) ++#define ISVISIBLE(C) ISVISIBLEONTAG(C, 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) +@@ -147,6 +148,7 @@ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interac + static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); ++static void attachaside(Client *c); + static void attachstack(Client *c); + static void buttonpress(XEvent *e); + static void checkotherwm(void); +@@ -184,6 +186,7 @@ static void maprequest(XEvent *e); + static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); ++static Client *nexttagged(Client *c); + static Client *nexttiled(Client *c); + static void pop(Client *c); + static void propertynotify(XEvent *e); +@@ -408,6 +411,17 @@ attach(Client *c) + c->mon->clients = c; + } + ++void ++attachaside(Client *c) { ++ Client *at = nexttagged(c); ++ if(!at) { ++ attach(c); ++ return; ++ } ++ c->next = at->next; ++ at->next = c; ++} ++ + void + attachstack(Client *c) + { +@@ -1074,7 +1088,7 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); ++ attachaside(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1202,6 +1216,16 @@ movemouse(const Arg *arg) + } + } + ++Client * ++nexttagged(Client *c) { ++ Client *walked = c->mon->clients; ++ for(; ++ walked && (walked->isfloating || !ISVISIBLEONTAG(walked, c->tags)); ++ walked = walked->next ++ ); ++ return walked; ++} ++ + Client * + nexttiled(Client *c) + { +@@ -1427,7 +1451,7 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ attachaside(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1915,7 +1939,7 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ attachaside(c); + attachstack(c); + } + if (m == selmon) + diff --git a/.suckless/st/dwm/config.def.h b/.suckless/st/dwm/config.def.h new file mode 100644 index 0000000..ac4674b --- /dev/null +++ b/.suckless/st/dwm/config.def.h @@ -0,0 +1,129 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 2; /* border pixel of windows */ +static const unsigned int gappx = 14; /* gaps between windows */ +static const unsigned int snap = 3; /* snap pixel */ +static const int user_bh = 12; /* 2 is the default spacing around the bar's font */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const int vertpad = 10; /* vertical padding of bar */ +static const int sidepad = 16; /* horizontal padding of bar */ +static const char *fonts[] = {"CaskaydiaMonoNerdFont-Bold:size=9.2"}; +static const char dmenufont[] = "CaskaydiaMonoNerdFont-Bold:size=10.6"; +static char normbgcolor[] = "#222222"; +static char normbordercolor[] = "#444444"; +static char normfgcolor[] = "#bbbbbb"; +static char selfgcolor[] = "#eeeeee"; +static char selbordercolor[] = "#005577"; +static char selbgcolor[] = "#005577"; +static char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor }, +}; + +/* tagging */ +static const char *tags[] = { "","","","","", }; + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating monitor */ + { NULL, NULL, NULL, 0, 0, -1 }, +}; + +/* layout(s) */ +static const float mfact = 0.50; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + {"", tile}, /* first entry is default */ + {"󰭩", NULL}, /* no layout function means floating behavior */ + {"", monocle}, +}; + + + +/* 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 */ +static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ +static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbordercolor, "-sf", selfgcolor, NULL }; +static const char *termcmd[] = {"bash", "-c", "st -e bash & walrs -R -q ",NULL}; + +#include "movestack.c" +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_n, xrdb, {.v = NULL } }, + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } }, + { MODKEY, XK_Return, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + { MODKEY, XK_minus, setgaps, {.i = -1 } }, + { MODKEY, XK_equal, setgaps, {.i = +1 } }, + { MODKEY|ShiftMask, XK_equal, setgaps, {.i = 0 } }, + 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) + { MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { 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/.suckless/st/dwm/config.h b/.suckless/st/dwm/config.h new file mode 100644 index 0000000..a2adaad --- /dev/null +++ b/.suckless/st/dwm/config.h @@ -0,0 +1,405 @@ +/* + * + ** + ** All credits to suckless.org and Swindles McCoop + ** Coasteen's DWM configuration + ** https://www.github.com/coasteen/suckless/ + ** __ _ _ + ** ___ ___ _ __ / _(_) __ _ | |__ + ** / __/ _ \| '_ \| |_| |/ _` | | '_ \ + ** | (_| (_) | | | | _| | (_| |_| | | | + ** \___\___/|_| |_|_| |_|\__, (_)_| |_| + ** |___/ + ** + * + */ + +/* appearance */ +static const unsigned int borderpx = 3; /* border pixel of windows */ +static const unsigned int snap = 0; /* snap pixel */ +static const unsigned int barborderpx = 3; /* border pixel of bar */ +static const int swallowfloating = 1; /* 1 means swallow floating windows by default */ +static const unsigned int gappih = 10; /* horiz inner gap between windows */ +static const unsigned int gappiv = 10; /* vert inner gap between windows */ +static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ +static const unsigned int gappov = 30; /* vert outer gap between windows and screen edge */ +static const int smartgaps_fact = 1; /* gap factor when there is only one client; 0 = no gaps, 3 = 3x outer gaps */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +/* Status is to be shown on: -1 (all monitors), 0 (a specific monitor by index), 'A' (active monitor) */ +static const int statusmon = 'A'; + +/* Indicators: see patch/bar_indicators.h for options */ +static int tagindicatortype = INDICATOR_NONE; +static int tiledindicatortype = INDICATOR_NONE; +static int floatindicatortype = INDICATOR_NONE; +static const char *fonts[] = { "monospace:size=9" }; +static const char dmenufont[] = "monospacee:size=9"; + +static char c000000[] = "#000000"; // placeholder value + +static char normfgcolor[] = "#bbbbbb"; +static char normbgcolor[] = "#222222"; +static char normbordercolor[] = "#444444"; +static char normfloatcolor[] = "#db8fd9"; + +static char selfgcolor[] = "#eeeeee"; +static char selbgcolor[] = "#005577"; +static char selbordercolor[] = "#005577"; +static char selfloatcolor[] = "#005577"; + +static char titlenormfgcolor[] = "#bbbbbb"; +static char titlenormbgcolor[] = "#222222"; +static char titlenormbordercolor[] = "#444444"; +static char titlenormfloatcolor[] = "#db8fd9"; + +static char titleselfgcolor[] = "#eeeeee"; +static char titleselbgcolor[] = "#005577"; +static char titleselbordercolor[] = "#005577"; +static char titleselfloatcolor[] = "#005577"; + +static char tagsnormfgcolor[] = "#bbbbbb"; +static char tagsnormbgcolor[] = "#222222"; +static char tagsnormbordercolor[] = "#444444"; +static char tagsnormfloatcolor[] = "#db8fd9"; + +static char tagsselfgcolor[] = "#eeeeee"; +static char tagsselbgcolor[] = "#005577"; +static char tagsselbordercolor[] = "#005577"; +static char tagsselfloatcolor[] = "#005577"; + +static char hidnormfgcolor[] = "#005577"; +static char hidselfgcolor[] = "#227799"; +static char hidnormbgcolor[] = "#222222"; +static char hidselbgcolor[] = "#222222"; + +static char urgfgcolor[] = "#bbbbbb"; +static char urgbgcolor[] = "#222222"; +static char urgbordercolor[] = "#ff0000"; +static char urgfloatcolor[] = "#db8fd9"; + + +static const unsigned int baralpha = 0xd0; +static const unsigned int borderalpha = OPAQUE; +static const unsigned int alphas[][3] = { + /* fg bg border */ + [SchemeNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeTitleNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeTitleSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeTagsNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeTagsSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeHidNorm] = { OPAQUE, baralpha, borderalpha }, + [SchemeHidSel] = { OPAQUE, baralpha, borderalpha }, + [SchemeUrg] = { OPAQUE, baralpha, borderalpha }, +}; +static char *colors[][ColCount] = { + /* fg bg border float */ + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor, normfloatcolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor, selfloatcolor }, + [SchemeTitleNorm] = { titlenormfgcolor, titlenormbgcolor, titlenormbordercolor, titlenormfloatcolor }, + [SchemeTitleSel] = { titleselfgcolor, titleselbgcolor, titleselbordercolor, titleselfloatcolor }, + [SchemeTagsNorm] = { tagsnormfgcolor, tagsnormbgcolor, tagsnormbordercolor, tagsnormfloatcolor }, + [SchemeTagsSel] = { tagsselfgcolor, tagsselbgcolor, tagsselbordercolor, tagsselfloatcolor }, + [SchemeHidNorm] = { hidnormfgcolor, hidnormbgcolor, c000000, c000000 }, + [SchemeHidSel] = { hidselfgcolor, hidselbgcolor, c000000, c000000 }, + [SchemeUrg] = { urgfgcolor, urgbgcolor, urgbordercolor, urgfloatcolor }, +}; + + + +static const char *const autostart[] = { + //"sh", "-c", "$HOME/.xprofile", NULL, + NULL /* terminate */ +}; + +const char *spcmd1[] = {"st", NULL }; +static Sp scratchpads[] = { + /* name cmd */ + {"spterm", spcmd1}, +}; + +/* Tags + * In a traditional dwm the number of tags in use can be changed simply by changing the number + * of strings in the tags array. This build does things a bit different which has some added + * benefits. If you need to change the number of tags here then change the NUMTAGS macro in dwm.c. + * + * Examples: + * + * 1) static char *tagicons[][NUMTAGS*2] = { + * [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I" }, + * } + * + * 2) static char *tagicons[][1] = { + * [DEFAULT_TAGS] = { "•" }, + * } + * + * The first example would result in the tags on the first monitor to be 1 through 9, while the + * tags for the second monitor would be named A through I. A third monitor would start again at + * 1 through 9 while the tags on a fourth monitor would also be named A through I. Note the tags + * count of NUMTAGS*2 in the array initialiser which defines how many tag text / icon exists in + * the array. This can be changed to *3 to add separate icons for a third monitor. + * + * For the second example each tag would be represented as a bullet point. Both cases work the + * same from a technical standpoint - the icon index is derived from the tag index and the monitor + * index. If the icon index is is greater than the number of tag icons then it will wrap around + * until it an icon matches. Similarly if there are two tag icons then it would alternate between + * them. This works seamlessly with alternative tags and alttagsdecoration patches. + */ +static char *tagicons[][NUMTAGS] = { + [DEFAULT_TAGS] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + [ALTERNATIVE_TAGS] = { "A", "B", "C", "D", "E", "F", "G", "H", "I" }, + [ALT_TAGS_DECORATION] = { "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" }, +}; + + +/* There are two options when it comes to per-client rules: + * - a typical struct table or + * - using the RULE macro + * + * A traditional struct table looks like this: + * // class instance title wintype tags mask isfloating monitor + * { "Gimp", NULL, NULL, NULL, 1 << 4, 0, -1 }, + * { "Firefox", NULL, NULL, NULL, 1 << 7, 0, -1 }, + * + * The RULE macro has the default values set for each field allowing you to only + * specify the values that are relevant for your rule, e.g. + * + * RULE(.class = "Gimp", .tags = 1 << 4) + * RULE(.class = "Firefox", .tags = 1 << 7) + * + * Refer to the Rule struct definition for the list of available fields depending on + * the patches you enable. + */ +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + * WM_WINDOW_ROLE(STRING) = role + * _NET_WM_WINDOW_TYPE(ATOM) = wintype + */ + RULE(.wintype = WTYPE "DIALOG", .isfloating = 1) + RULE(.wintype = WTYPE "UTILITY", .isfloating = 1) + RULE(.wintype = WTYPE "TOOLBAR", .isfloating = 1) + RULE(.wintype = WTYPE "SPLASH", .isfloating = 1) + //RULE(.class = "Gimp", .tags = 1 << 4) + //RULE(.class = "Firefox", .tags = 1 << 7) + RULE(.instance = "spterm", .tags = SPTAG(0), .isfloating = 1) +}; + + + +/* Bar rules allow you to configure what is shown where on the bar, as well as + * introducing your own bar modules. + * + * monitor: + * -1 show on all monitors + * 0 show on monitor 0 + * 'A' show on active monitor (i.e. focused / selected) (or just -1 for active?) + * bar - bar index, 0 is default, 1 is extrabar + * alignment - how the module is aligned compared to other modules + * widthfunc, drawfunc, clickfunc - providing bar module width, draw and click functions + * name - does nothing, intended for visual clue and for logging / debugging + */ +static const BarRule barrules[] = { + /* monitor bar alignment widthfunc drawfunc clickfunc hoverfunc name */ + { -1, 0, BAR_ALIGN_LEFT, width_tags, draw_tags, click_tags, hover_tags, "tags" }, + { -1, 0, BAR_ALIGN_LEFT, width_ltsymbol, draw_ltsymbol, click_ltsymbol, NULL, "layout" }, + { statusmon, 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, NULL, "status" }, + { -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, NULL, "wintitle" }, +}; + +/* layout(s) */ +static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + + + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ +}; + +/* 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 */ +static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ +static const char *dmenucmd[] = { + "dmenu_run", + "-m", dmenumon, + "-fn", dmenufont, + "-nb", normbgcolor, + "-nf", normfgcolor, + "-sb", selbgcolor, + "-sf", selfgcolor, + NULL +}; + +static const char *termcmd[] = { "st", NULL }; +static const char *caja[] = { "caja", NULL }; +//static const char *floorp[] = { "floorp", NULL }; +static const char *firemenu[] = { "firemenu", NULL }; + +///////////////////////////////////////////////KEYBINDS////////////////////////////////////////////// +#include +//different commands per OS +#ifdef __linux__ + #define VOL_UP "pamixer --allow-boost -i 10; kill -44 $(pidof dwmblocks)" + #define XK_UP "pamixer --allow-boost -i 5; kill -44 $(pidof dwmblocks)" + #define VOL_DOWN "pamixer --allow-boost -d 10; kill -44 $(pidof dwmblocks)" + #define XK_DOWN "pamixer --allow-boost -d 5; kill -44 $(pidof dwmblocks)" + #define VOL_MUTE "pamixer -t; kill -44 $(pidof dwmblocks)" + #define VOL_KILL "kill -44 $(pidof dwmblocks)" +#elif __OpenBSD__ + #define VOL_UP "sndioctl output.level=+0.10; pkill -SIGUSR1 dwmblocks" + #define XK_UP "sndioctl output.level=+0.05; pkill -SIGUSR1 dwmblocks" + #define VOL_DOWN "sndioctl output.level=-0.10; pkill -SIGUSR1 dwmblocks" + #define XK_DOWN "sndioctl output.level=-0.05; pkill -SIGUSR1 dwmblocks" + #define VOL_MUTE "sndioctl output.mute=!; pkill -SIGUSR1 dwmblocks" + #define VOL_KILL "pkill -SIGUSR1 dwmblocks" +#elif __FreeBSD__ + #define VOL_UP "sndioctl output.level=+0.10" + #define XK_UP "sndioctl output.level=+0.05" + #define VOL_DOWN "sndioctl output.level=-0.10" + #define XK_DOWN "sndioctl output.level=-0.05" + #define VOL_MUTE "sndioctl output.level=-1" + #define VOL_KILL "pkill -SIGUSR2 dwmblocks" +#endif + +static Key keys[] = { + /*modifierkey function argument */ + { Mod4Mask|ShiftMask, XK_r, spawn, {.v = firemenu } }, + { MODKEY, XK_f, spawn, {.v = caja } }, + { MODKEY, XK_r, spawn, {.v = dmenucmd } }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_Down, moveresize, {.v = "0x 25y 0w 0h" } }, + { MODKEY, XK_Up, moveresize, {.v = "0x -25y 0w 0h" } }, + { MODKEY, XK_Right, moveresize, {.v = "25x 0y 0w 0h" } }, + { MODKEY, XK_Left, moveresize, {.v = "-25x 0y 0w 0h" } }, + { MODKEY|ShiftMask, XK_Down, moveresize, {.v = "0x 0y 0w 25h" } }, + { MODKEY|ShiftMask, XK_Up, moveresize, {.v = "0x 0y 0w -25h" } }, + { MODKEY|ShiftMask, XK_Right, moveresize, {.v = "0x 0y 25w 0h" } }, + { MODKEY|ShiftMask, XK_Left, moveresize, {.v = "0x 0y -25w 0h" } }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_F5, xrdb, {.v = NULL } }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_grave, togglescratch, {.ui = 0 } }, + { MODKEY|ControlMask, XK_grave, setscratch, {.ui = 0 } }, + { MODKEY|ShiftMask, XK_grave, removescratch, {.ui = 0 } }, + { MODKEY, XK_w, togglefullscreen, {0} }, + { MODKEY, XK_0, view, {.ui = ~SPTAGMASK } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~SPTAGMASK } }, + + 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) + + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_q,quit, {0} }, + //{ MODKEY, XK_b, spawn, {.v = floorp} }, + { MODKEY, XK_b, spawn, SHCMD("brave") }, + { MODKEY, XK_m, togglebar, {0} }, + { MODKEY, XK_o, incnmaster, {.i = +1 } }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { MODKEY|ShiftMask, XK_o, incnmaster, {.i = -1 } }, + { MODKEY, XK_s, killclient, {0} }, + { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } }, + { MODKEY|ShiftMask, XK_Return, togglescratch, {.ui = 0 } }, + { 0, XK_F12, togglescratch, {.ui = 0 } }, + + //volume control + { Mod1Mask, XK_equal, spawn, SHCMD(VOL_UP) }, + { Mod1Mask, XK_minus, spawn, SHCMD(VOL_DOWN) }, + { Mod1Mask, XK_minus, spawn, SHCMD(VOL_DOWN) }, + { 0, XF86XK_AudioMute, spawn, SHCMD(VOL_MUTE) }, + { 0, XF86XK_AudioRaiseVolume, spawn, SHCMD(XK_UP) }, + { 0, XF86XK_AudioLowerVolume, spawn, SHCMD(XK_DOWN) }, + //lock + { MODKEY|ShiftMask, XK_l, spawn, SHCMD("slock") }, + { 0, XK_Print, spawn, SHCMD("screenie") }, +}; +///////////////////////////////////////////////////////////////////////////////////////////////////// + + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static 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} }, +}; + +/* signal definitions */ +/* signum must be greater than 0 */ +/* trigger signals using `xsetroot -name "fsignal: [ ]"` */ +static Signal signals[] = { + /* signum function */ + { "focusstack", focusstack }, + { "setmfact", setmfact }, + { "togglebar", togglebar }, + { "incnmaster", incnmaster }, + { "togglefloating", togglefloating }, + { "focusmon", focusmon }, + { "tagmon", tagmon }, + { "zoom", zoom }, + { "incrgaps", incrgaps }, + { "incrigaps", incrigaps }, + { "incrogaps", incrogaps }, + { "incrihgaps", incrihgaps }, + { "incrivgaps", incrivgaps }, + { "incrohgaps", incrohgaps }, + { "incrovgaps", incrovgaps }, + { "togglegaps", togglegaps }, + { "defaultgaps", defaultgaps }, + { "setgaps", setgapsex }, + { "view", view }, + { "viewall", viewallex }, + { "viewex", viewex }, + { "toggleview", toggleview }, + { "toggleviewex", toggleviewex }, + { "tag", tag }, + { "tagall", tagallex }, + { "tagex", tagex }, + { "toggletag", toggletag }, + { "toggletagex", toggletagex }, + { "togglefullscreen", togglefullscreen }, + { "fullscreen", fullscreen }, + { "togglescratch", togglescratch }, + { "killclient", killclient }, + { "xrdb", xrdb }, + { "quit", quit }, + { "setlayout", setlayout }, + { "setlayoutex", setlayoutex }, +}; diff --git a/.suckless/st/dwm/config.h~ b/.suckless/st/dwm/config.h~ new file mode 100644 index 0000000..479e5f4 --- /dev/null +++ b/.suckless/st/dwm/config.h~ @@ -0,0 +1,160 @@ + +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 2; /* border pixel of windows */ +static const unsigned int gappx = 14; /* gaps between windows */ +static const unsigned int snap = 3; /* snap pixel */ +static const int user_bh = 12; /* 2 is the default spacing around the bar's font */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const int vertpad = 10; /* vertical padding of bar */ +static const int sidepad = 16; /* horizontal padding of bar */ +static const char *fonts[] = {"CaskaydiaMonoNerdFont-Bold:size=9.2"}; +static const char dmenufont[] = "CaskaydiaMonoNerdFont-Bold:size=10.6"; +static char normbgcolor[] = "#222222"; +static char normbordercolor[] = "#444444"; +static char normfgcolor[] = "#bbbbbb"; +static char selfgcolor[] = "#eeeeee"; +static char selbordercolor[] = "#722F37"; +static char selbgcolor[] = "#722F37"; +static char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor }, + [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor }, +}; + +/* 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 */ + { NULL, NULL, NULL, 0, 0, -1 }, +}; + +/* layout(s) */ +static const float mfact = 0.50; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + {"[]=", tile}, /* first entry is default */ + {"><>", NULL}, /* no layout function means floating behavior */ + {"", monocle}, +}; + +static const char *pcmanfm[] = { "pcmanfm", NULL }; +static const char *firemenu[] = { "firemenu", NULL }; + +#include +//different commands per OS +#ifdef __linux__ + #define VOL_UP "pamixer --allow-boost -i 10; kill -44 $(pidof dwmblocks)" + #define XK_UP "pamixer --allow-boost -i 5; kill -44 $(pidof dwmblocks)" + #define VOL_DOWN "pamixer --allow-boost -d 10; kill -44 $(pidof dwmblocks)" + #define XK_DOWN "pamixer --allow-boost -d 5; kill -44 $(pidof dwmblocks)" + #define VOL_MUTE "pamixer -t; kill -44 $(pidof dwmblocks)" + #define VOL_KILL "kill -44 $(pidof dwmblocks)" +#elif __OpenBSD__ + #define VOL_UP "sndioctl output.level=+0.10; pkill -SIGUSR1 dwmblocks" + #define XK_UP "sndioctl output.level=+0.05; pkill -SIGUSR1 dwmblocks" + #define VOL_DOWN "sndioctl output.level=-0.10; pkill -SIGUSR1 dwmblocks" + #define XK_DOWN "sndioctl output.level=-0.05; pkill -SIGUSR1 dwmblocks" + #define VOL_MUTE "sndioctl output.mute=!; pkill -SIGUSR1 dwmblocks" + #define VOL_KILL "pkill -SIGUSR1 dwmblocks" +#elif __FreeBSD__ + #define VOL_UP "sndioctl output.level=+0.10" + #define XK_UP "sndioctl output.level=+0.05" + #define VOL_DOWN "sndioctl output.level=-0.10" + #define XK_DOWN "sndioctl output.level=-0.05" + #define VOL_MUTE "sndioctl output.level=-1" + #define VOL_KILL "pkill -SIGUSR2 dwmblocks" +#endif + +/* 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 */ +static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ +static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbordercolor, "-sf", selfgcolor, NULL }; +static const char *termcmd[] = {"zsh", "-c", "st -e zsh & walrs -R -q ",NULL}; + +#include "movestack.c" +static Key keys[] = { + /*modifierkey function argument */ + { Mod4Mask|ShiftMask, XK_r, spawn, {.v = firemenu } }, + { MODKEY, XK_f, spawn, {.v = pcmanfm } }, + { MODKEY, XK_r, spawn, {.v = dmenucmd } }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_F5, xrdb, {.v = NULL } }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_space, setlayout, {0} }, + + 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) + + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_q,quit, {0} }, + //{ MODKEY, XK_b, spawn, {.v = floorp} }, + { MODKEY, XK_b, spawn, SHCMD("floorp") }, + { MODKEY, XK_m, togglebar, {0} }, + { MODKEY, XK_o, incnmaster, {.i = +1 } }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { MODKEY|ShiftMask, XK_o, incnmaster, {.i = -1 } }, + { MODKEY, XK_s, killclient, {0} }, + { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } }, + + //volume control + { Mod1Mask, XK_equal, spawn, SHCMD(VOL_UP) }, + { Mod1Mask, XK_minus, spawn, SHCMD(VOL_DOWN) }, + { Mod1Mask, XK_minus, spawn, SHCMD(VOL_DOWN) }, + { 0, XF86XK_AudioMute, spawn, SHCMD(VOL_MUTE) }, + { 0, XF86XK_AudioRaiseVolume, spawn, SHCMD(XK_UP) }, + { 0, XF86XK_AudioLowerVolume, spawn, SHCMD(XK_DOWN) }, + //lock + { MODKEY|ShiftMask, XK_l, spawn, SHCMD("slock") }, + { 0, XK_Print, spawn, SHCMD("flameshot gui -r | xclip -selection clipboard -t image/png") }, +}; + + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { 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/.suckless/st/dwm/config.mk b/.suckless/st/dwm/config.mk new file mode 100644 index 0000000..7b3b027 --- /dev/null +++ b/.suckless/st/dwm/config.mk @@ -0,0 +1,55 @@ +# dwm version +VERSION = 6.3 + +# 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 + +# Uncomment this for the alpha patch and the winicon patch (BAR_ALPHA_PATCH, BAR_WINICON_PATCH) +XRENDER = -lXrender + +# Uncomment this for the mdpcontrol patch / MDPCONTROL_PATCH +#MPDCLIENT = -lmpdclient + +# Uncomment for the pango patch / BAR_PANGO_PATCH +#PANGOINC = `pkg-config --cflags xft pango pangoxft` +#PANGOLIB = `pkg-config --libs xft pango pangoxft` + +# Uncomment for the ipc patch / IPC_PATCH +#YAJLLIBS = -lyajl +#YAJLINC = -I/usr/include/yajl + +# Uncomment this for the rounded corners patch / ROUNDED_CORNERS_PATCH +#XEXTLIB = -lXext + +# Uncomment this for the swallow patch / SWALLOW_PATCH +XCBLIBS = -lX11-xcb -lxcb -lxcb-res + +# This is needed for the winicon and tagpreview patches / BAR_WINICON_PATCH / BAR_TAGPREVIEW_PATCH +#IMLIB2LIBS = -lImlib2 + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} ${YAJLINC} ${PANGOINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XRENDER} ${MPDCLIENT} ${XEXTLIB} ${XCBLIBS} ${KVMLIB} ${PANGOLIB} ${YAJLLIBS} ${IMLIB2LIBS} + +# 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-unused-function -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/.suckless/st/dwm/configure b/.suckless/st/dwm/configure new file mode 100644 index 0000000..69e51e6 --- /dev/null +++ b/.suckless/st/dwm/configure @@ -0,0 +1,40 @@ +#!/bin/sh + +#OS=$(printf 'Linux\nOpenBSD\nFreeBSD\nSolaris' | fzf --layout=reverse --height 40%) +OS=$(uname) + +case $OS in + Linux) cp -f mkconfig/config.mk.linux config.mk ;; + OpenBSD) cp -f mkconfig/config.mk.openbsd config.mk ;; + FreeBSD) cp -f mkconfig/config.mk.freebsd config.mk ;; + Solaris) cp -f mkconfig/config.mk.solaris config.mk ;; +esac + +#sed -i 's/##/#/g' config.mk +#sed -i '14 s/^/#/' config.mk +#sed -i '15 s/^/#/' config.mk +#sed -i '25 s/^/#/' config.mk +#sed -i '27 s/^/#/' config.mk +#sed -i '29 s/^/#/' config.mk +#sed -i '65 s/^/#/' config.mk +#sed -i '66 s/^/#/' config.mk +#sed -i 's/##/#/g' config.mk +# +#if [ "$OS" = "Linux" ]; then +# true +#else if [ "$OS" = "OpenBSD" ]; then +# sed -i '27 s/.//' config.mk +# sed -i '29 s/.//' config.mk +#else if [ "$OS" = "FreeBSD" ]; then +# sed -i '14 s/.//' config.mk +# sed -i '15 s/.//' config.mk +# sed -i '25 s/.//' config.mk +#else if [ "$OS" = "Solaris" ]; then +# sed -i '65 s/.//' config.mk +# sed -i '66 s/.//' config.mk +#fi +#fi +#fi +#fi +# +#sed -i 's/##/#/g' config.mk diff --git a/.suckless/st/dwm/drw.c b/.suckless/st/dwm/drw.c new file mode 100644 index 0000000..2297da4 --- /dev/null +++ b/.suckless/st/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, Visual *visual, unsigned int depth, Colormap cmap) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + + drw->visual = visual; + drw->depth = depth; + drw->cmap = cmap; + drw->drawable = XCreatePixmap(dpy, root, w, h, depth); + drw->gc = XCreateGC(dpy, drw->drawable, 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, drw->depth); +} + +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."); + } + + /* Do not allow using color fonts. This is a workaround for a BadLength + * error from Xft with color glyphs. Modelled on the Xterm workaround. See + * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + * https://lists.suckless.org/dev/1701/30932.html + * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 + * and lots more all over the internet. + */ + FcBool iscol; + if (FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { + XftFontClose(drw->dpy, xfont); + return NULL; + } + + 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 + , unsigned int alpha +) { + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); + + dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 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, + char *clrnames[], + const unsigned int alphas[], + 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], alphas[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, Bool ignored) +{ + char buf[1024]; + int ty; + unsigned int ew; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + size_t i, len; + 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; + + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + return 0; + + if (!render) { + w = ~w; + } 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, drw->visual, drw->cmap); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + while (1) { + 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) { + if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + } else { + nextfont = curfont; + } + break; + } + } + + if (!charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); + /* shorten text if necessary */ + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; drw_font_getexts(usedfont, utf8str, len, &ew, NULL)) + len--; + + if (len) { + memcpy(buf, utf8str, len); + buf[len] = '\0'; + if (len < utf8strlen) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ + + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, len); + } + x += ew; + w -= ew; + } + } + + if (!*text) { + 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; + + 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); + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + + 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); + 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, Bool markup) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0, markup); +} + +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/.suckless/st/dwm/drw.h b/.suckless/st/dwm/drw.h new file mode 100644 index 0000000..0eff1ce --- /dev/null +++ b/.suckless/st/dwm/drw.h @@ -0,0 +1,72 @@ +/* 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, ColFloat, ColCount }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Visual *visual; + unsigned int depth; + Colormap cmap; + 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, Visual *visual, unsigned int depth, Colormap cmap); +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_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text, Bool markup); + +/* Colorscheme abstraction */ +void drw_clr_create( + Drw *drw, + Clr *dest, + const char *clrname + , unsigned int alpha +); +Clr *drw_scm_create( + Drw *drw, + char *clrnames[], + const unsigned int alphas[], + 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, Bool markup); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); + diff --git a/.suckless/st/dwm/drw.o b/.suckless/st/dwm/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..3303c6df9300ef0f891e9c7e323a421b071b2983 GIT binary patch literal 10368 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 HZu$QPaD#uy literal 0 HcmV?d00001 diff --git a/.suckless/st/dwm/dwm b/.suckless/st/dwm/dwm new file mode 100755 index 0000000000000000000000000000000000000000..e86d138eb94ebbeaff03e267835b972864089fc2 GIT binary patch literal 100176 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 literal 0 HcmV?d00001 diff --git a/.suckless/st/dwm/dwm.1 b/.suckless/st/dwm/dwm.1 new file mode 100644 index 0000000..6020871 --- /dev/null +++ b/.suckless/st/dwm/dwm.1 @@ -0,0 +1,187 @@ +.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. +.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 Super\-Button1 +click on a tag label applies that tag to the focused window. +.TP +.B Super\-Button3 +click on a tag label adds/removes that tag to/from the focused window. +.SS Keyboard commands +.TP +.B Super\-Return +Start +.BR st(1). +.TP +.B Super\-Shift\-Return +Start or toggle +.BR st(1) +as a floating (scratchpad) terminal. +.TP +.B Super\-d +Spawn +.BR dmenu(1) +for launching other programs. +.TP +.B Super\-f +Toggles fullscreen state of focused window. +.TP +.B Super\-, +Focus previous screen, if any. +.TP +.B Super\-. +Focus next screen, if any. +.TP +.B Super\-Shift\-, +Send focused window to previous screen, if any. +.TP +.B Super\-Shift\-. +Send focused window to next screen, if any. +.TP +.B Super\-b +Toggles bar on and off. +.TP +.B Super\-t +Sets tiled layout. +.TP +.B Super\-m +Zooms/cycles focused window to/from master area (tiled layouts only). +.TP +.B Super\-space +Toggles between floating and tiled layout. +.TP +.B Super\-j +Focus next window. +.TP +.B Super\-k +Focus previous window. +.TP +.B Super\-Shift\-j +Move focused stack window downward. +.TP +.B Super\-Shift\-k +Move focused stack window upward. +.TP +.B Super\-o +Increase number of windows in master area. +.TP +.B Super\-Shift\-o +Decrease number of windows in master area. +.TP +.B Super\-l +Increase master area size. +.TP +.B Super\-h +Decrease master area size. +.TP +.B Super\-m +Zooms/cycles focused window to/from master area (tiled layouts only). +.TP +.B Super\-q +Close focused window. +.TP +.B Super\-Shift\-space +Toggle focused window between tiled and floating state. +.TP +.B Super\-Tab +Toggles to the previously selected tags. +.TP +.B Super\-Shift\-[1..n] +Apply nth tag to focused window. +.TP +.B Super\-Shift\-0 +Apply all tags to focused window. +.TP +.B Super\-Control\-Shift\-[1..n] +Add/remove nth tag to/from focused window. +.TP +.B Super\-[1..n] +View all windows with nth tag. +.TP +.B Super\-0 +View all windows with any tag. +.TP +.B Super\-Control\-[1..n] +Add/remove all windows with nth tag to/from the view. +.TP +.B Super\-Shift\-q, Super\-Shift\-e +Quit dwm. +.SS Mouse commands +.TP +.B Super\-Button1 +Move focused window while dragging. Tiled windows will be toggled to the floating state. +.TP +.B Super\-Button2 +Toggles focused window between floating and tiled state. +.TP +.B Super\-Button3 +Resize focused window while dragging. Tiled windows will be toggled to the floating state. +.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 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/.suckless/st/dwm/dwm.c b/.suckless/st/dwm/dwm.c new file mode 100644 index 0000000..7c87101 --- /dev/null +++ b/.suckless/st/dwm/dwm.c @@ -0,0 +1,2619 @@ +/* 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 "drw.h" +#include "util.h" + + + + + +/* macros */ +#define Button6 6 +#define Button7 7 +#define Button8 8 +#define Button9 9 +#define NUMTAGS 9 +#define BARRULES 20 +#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 ISVISIBLEONTAG(C, T) ((C->tags & T)) +#define ISVISIBLE(C) ISVISIBLEONTAG(C, 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 WTYPE "_NET_WM_WINDOW_TYPE_" +#define TOTALTAGS (NUMTAGS + LENGTH(scratchpads)) +#define TAGMASK ((1 << TOTALTAGS) - 1) +#define SPTAG(i) ((1 << NUMTAGS) << (i)) +#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << NUMTAGS) +#define TEXTWM(X) (drw_fontset_getwidth(drw, (X), True) + lrpad) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X), False) + lrpad) +#define HIDDEN(C) ((getstate(C->win) == IconicState)) + +/* enums */ +enum { + CurNormal, + CurResize, + CurMove, + CurLast +}; /* cursor */ + +enum { + SchemeNorm, + SchemeSel, + SchemeTitleNorm, + SchemeTitleSel, + SchemeTagsNorm, + SchemeTagsSel, + SchemeHidNorm, + SchemeHidSel, + SchemeUrg, +}; /* color schemes */ + +enum { + NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetClientList, + NetLast +}; /* EWMH atoms */ + +enum { + WMProtocols, + WMDelete, + WMState, + WMTakeFocus, + WMLast +}; /* default atoms */ + + +enum { + ClkTagBar, + ClkLtSymbol, + ClkStatusText, + ClkWinTitle, + ClkClientWin, + ClkRootWin, + ClkLast +}; /* clicks */ + +enum { + BAR_ALIGN_LEFT, + BAR_ALIGN_CENTER, + BAR_ALIGN_RIGHT, + BAR_ALIGN_LEFT_LEFT, + BAR_ALIGN_LEFT_RIGHT, + BAR_ALIGN_LEFT_CENTER, + BAR_ALIGN_NONE, + BAR_ALIGN_RIGHT_LEFT, + BAR_ALIGN_RIGHT_RIGHT, + BAR_ALIGN_RIGHT_CENTER, + BAR_ALIGN_LAST +}; /* bar alignment */ + + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct Monitor Monitor; +typedef struct Bar Bar; +struct Bar { + Window win; + Monitor *mon; + Bar *next; + int idx; + int showbar; + int topbar; + int external; + int borderpx; + int borderscheme; + int bx, by, bw, bh; /* bar geometry */ + int w[BARRULES]; // width, array length == barrules, then use r index for lookup purposes + int x[BARRULES]; // x position, array length == ^ +}; + +typedef struct { + int x; + int y; + int h; + int w; +} BarArg; + +typedef struct { + int monitor; + int bar; + int alignment; // see bar alignment enum + int (*widthfunc)(Bar *bar, BarArg *a); + int (*drawfunc)(Bar *bar, BarArg *a); + int (*clickfunc)(Bar *bar, Arg *arg, BarArg *a); + int (*hoverfunc)(Bar *bar, BarArg *a, XMotionEvent *ev); + char *name; // for debugging + int x, w; // position, width for internal use +} BarRule; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + + +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; + int isterminal, noswallow; + pid_t pid; + Client *next; + Client *snext; + Client *swallowing; + 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; + + +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + int gappih; /* horizontal gap between windows */ + int gappiv; /* vertical gap between windows */ + int gappoh; /* horizontal outer gaps */ + int gappov; /* vertical outer gaps */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Bar *bar; + const Layout *lt[2]; +}; + +typedef struct { + const char *class; + const char *instance; + const char *title; + const char *wintype; + unsigned int tags; + int isfloating; + int isterminal; + int noswallow; + int monitor; +} Rule; + +#define RULE(...) { .monitor = -1, __VA_ARGS__ }, + +/* Cross patch compatibility rule macro helper macros */ +#define FLOATING , .isfloating = 1 +#define CENTERED +#define PERMANENT +#define FAKEFULLSCREEN +#define NOSWALLOW , .noswallow = 1 +#define TERMINAL , .isterminal = 1 +#define SWITCHTAG + + +/* 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 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 drawbarwin(Bar *bar); +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, Atom req); +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 motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static Client *nexttiled(Client *c); +static void pop(Client *); +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 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 spawn(const Arg *arg); +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void unfocus(Client *c, int setfocus, Client *nextfocus); +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 updatewmhints(Client *c); +static void view(const Arg *arg); +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); + +/* bar functions */ + +#include "patch/include.h" + +/* variables */ +static const char broken[] = "broken"; +static char stext[512]; + +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar geometry */ +static int lrpad; /* sum of left and right padding for text */ +/* Some clients (e.g. alacritty) helpfully send configure requests with a new size or position + * when they detect that they have been moved to another monitor. This can cause visual glitches + * when moving (or resizing) client windows from one monitor to another. This variable is used + * internally to ignore such configure requests while movemouse or resizemouse are being used. */ +static int ignoreconfigurerequests = 0; +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 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" + +#include "patch/include.c" + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[NUMTAGS > 31 ? -1 : 1]; }; + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + Atom wintype; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->noswallow = -1; + 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; + wintype = getatomprop(c, netatom[NetWMWindowType], XA_ATOM); + + + 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)) + && (!r->wintype || wintype == XInternAtom(dpy, r->wintype, False))) + { + c->isterminal = r->isterminal; + c->noswallow = r->noswallow; + 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 +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +buttonpress(XEvent *e) +{ + int click, i, r; + Arg arg = {0}; + Client *c; + Monitor *m; + Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; + const BarRule *br; + BarArg carg = { 0, 0, 0, 0 }; + click = ClkRootWin; + + + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon + ) { + unfocus(selmon->sel, 1, NULL); + selmon = m; + focus(NULL); + } + + for (bar = selmon->bar; bar; bar = bar->next) { + if (ev->window == bar->win) { + for (r = 0; r < LENGTH(barrules); r++) { + br = &barrules[r]; + if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) + continue; + if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->num) + continue; + if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { + carg.x = ev->x - bar->x[r]; + carg.y = ev->y - bar->borderpx; + carg.w = bar->w[r]; + carg.h = bar->bh - 2 * bar->borderpx; + click = br->clickfunc(bar, &arg, &carg); + if (click < 0) + return; + break; + } + } + break; + } + } + + + if (click == ClkRootWin && (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) +{ + Monitor *m; + Layout foo = { "", NULL }; + size_t i; + + + /* kill child processes */ + for (i = 0; i < autostart_len; i++) { + if (0 < autostart_pids[i]) { + kill(autostart_pids[i], SIGTERM); + waitpid(autostart_pids[i], NULL, 0); + } + } + + 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; + Bar *bar; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + for (bar = mon->bar; bar; bar = mon->bar) { + if (!bar->external) { + XUnmapWindow(dpy, bar->win); + XDestroyWindow(dpy, bar->win); + } + mon->bar = bar->next; + free(bar); + } + 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; + Bar *bar; + 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, sh); + 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); + for (bar = m->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if (ignoreconfigurerequests) + return; + + 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, *mon; + int i, n, mi, max_bars = 2, istopbar = topbar; + + const BarRule *br; + Bar *bar; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; + for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->num = mi; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + + /* Derive the number of bars for this monitor based on bar rules */ + for (n = -1, i = 0; i < LENGTH(barrules); i++) { + br = &barrules[i]; + if (br->monitor == 'A' || br->monitor == -1 || br->monitor == m->num) + n = MAX(br->bar, n); + } + + m->bar = NULL; + for (i = 0; i <= n && i < max_bars; i++) { + bar = ecalloc(1, sizeof(Bar)); + bar->mon = m; + bar->idx = i; + bar->next = m->bar; + bar->topbar = istopbar; + m->bar = bar; + istopbar = !istopbar; + bar->showbar = 1; + bar->external = 0; + bar->borderpx = 0; + bar->bh = bh + bar->borderpx * 2; + bar->borderscheme = SchemeNorm; + } + + + + + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); + else if ((c = swallowingclient(ev->window))) + unmanage(c->swallowing, 1); +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; + c->next = NULL; +} + +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; + } + c->snext = NULL; +} + +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) +{ + Bar *bar; + + if (m->showbar) + for (bar = m->bar; bar; bar = bar->next) + drawbarwin(bar); +} + +void +drawbars(void) +{ + Monitor *m; + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +drawbarwin(Bar *bar) +{ + if (!bar || !bar->win || bar->external) + return; + int r, w, total_drawn = 0; + int rx, lx, rw, lw; // bar size, split between left and right if a center module is added + const BarRule *br; + + if (bar->borderpx) { + XSetForeground(drw->dpy, drw->gc, scheme[bar->borderscheme][ColBorder].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, 0, 0, bar->bw, bar->bh); + } + + BarArg warg = { 0 }; + BarArg darg = { 0 }; + warg.h = bar->bh - 2 * bar->borderpx; + + rw = lw = bar->bw - 2 * bar->borderpx; + rx = lx = bar->borderpx; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, lx, bar->borderpx, lw, bar->bh - 2 * bar->borderpx, 1, 1); + for (r = 0; r < LENGTH(barrules); r++) { + br = &barrules[r]; + if (br->bar != bar->idx || !br->widthfunc || (br->monitor == 'A' && bar->mon != selmon)) + continue; + if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->num) + continue; + drw_setscheme(drw, scheme[SchemeNorm]); + warg.w = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); + + w = br->widthfunc(bar, &warg); + w = MIN(warg.w, w); + + if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa + lw = rw; + lx = rx; + } else if (rw <= 0) { + rw = lw; + rx = lx; + } + + switch(br->alignment) { + default: + case BAR_ALIGN_NONE: + case BAR_ALIGN_LEFT_LEFT: + case BAR_ALIGN_LEFT: + bar->x[r] = lx; + if (lx == rx) { + rx += w; + rw -= w; + } + lx += w; + lw -= w; + break; + case BAR_ALIGN_LEFT_RIGHT: + case BAR_ALIGN_RIGHT: + bar->x[r] = lx + lw - w; + if (lx == rx) + rw -= w; + lw -= w; + break; + case BAR_ALIGN_LEFT_CENTER: + case BAR_ALIGN_CENTER: + bar->x[r] = lx + lw / 2 - w / 2; + if (lx == rx) { + rw = rx + rw - bar->x[r] - w; + rx = bar->x[r] + w; + } + lw = bar->x[r] - lx; + break; + case BAR_ALIGN_RIGHT_LEFT: + bar->x[r] = rx; + if (lx == rx) { + lx += w; + lw -= w; + } + rx += w; + rw -= w; + break; + case BAR_ALIGN_RIGHT_RIGHT: + bar->x[r] = rx + rw - w; + if (lx == rx) + lw -= w; + rw -= w; + break; + case BAR_ALIGN_RIGHT_CENTER: + bar->x[r] = rx + rw / 2 - w / 2; + if (lx == rx) { + lw = lx + lw - bar->x[r] + w; + lx = bar->x[r] + w; + } + rw = bar->x[r] - rx; + break; + } + bar->w[r] = w; + darg.x = bar->x[r]; + darg.y = bar->borderpx; + darg.h = bar->bh - 2 * bar->borderpx; + darg.w = bar->w[r]; + if (br->drawfunc) + total_drawn += br->drawfunc(bar, &darg); + } + + if (total_drawn == 0 && bar->showbar) { + bar->showbar = 0; + updatebarpos(bar->mon); + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(bar->mon); + } + else if (total_drawn > 0 && !bar->showbar) { + bar->showbar = 1; + updatebarpos(bar->mon); + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); + arrange(bar->mon); + } else + drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); +} + +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, c); + 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, c); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel); + else + 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, NULL); + selmon = m; + focus(NULL); +} + +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, Atom req) +{ + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + + /* FIXME getatomprop should return the number of items and a pointer to + * the stored data instead of this workaround */ + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &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 = 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; + int keysyms_return; + KeySym* keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XGetKeyboardMapping(dpy, (KeyCode)ev->keycode, 1, &keysyms_return); + 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)); + XFree(keysym); +} + +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; + Client *term = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + c->pid = winpid(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; + c->bw = borderpx; + } else { + c->mon = selmon; + c->bw = borderpx; + applyrules(c); + term = termforwin(c); + if (term) + c->mon = term->mon; + } + + if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) + c->x = c->mon->mx + c->mon->mw - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ + c->y = MAX(c->y, ((!c->mon->bar || c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + if (c->isfloating) + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel); + else + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatesizehints(c); + if (getatomprop(c, netatom[NetWMState], XA_ATOM) == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + updatewmhints(c); + + + 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); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel); + } + attachx(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); + c->mon->sel = c; + if (!(term && swallow(term, 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)) + return; + if (wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +void +motionnotify(XEvent *e) +{ + static Monitor *mon = NULL; + Monitor *m; + Bar *bar; + XMotionEvent *ev = &e->xmotion; + + if ((bar = wintobar(ev->window))) { + barhover(e, bar); + return; + } + + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + unfocus(selmon->sel, 1, NULL); + 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); + nx = ocx = c->x; + ny = ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + ignoreconfigurerequests = 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; + + 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) { + if (c->tags & SPTAGMASK) { + c->mon->tagset[c->mon->seltags] ^= (c->tags & SPTAGMASK); + m->tagset[m->seltags] |= (c->tags & SPTAGMASK); + } + sendmon(c, m); + selmon = m; + focus(NULL); + } + ignoreconfigurerequests = 0; +} + +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)) { + if (!fake_signal()) + 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); + if (c->isurgent) + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + } +} + +void +quit(const Arg *arg) +{ + 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, nx, ny; + 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); + nx = ocx = c->x; + ny = ocy = c->y; + nh = c->h; + nw = c->w; + 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); + ignoreconfigurerequests = 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, nx, ny, 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) { + if (c->tags & SPTAGMASK) { + c->mon->tagset[c->mon->seltags] ^= (c->tags & SPTAGMASK); + m->tagset[m->seltags] |= (c->tags & SPTAGMASK); + } + sendmon(c, m); + selmon = m; + focus(NULL); + } + ignoreconfigurerequests = 0; +} + +void +restack(Monitor *m) +{ + Client *c, *f = NULL; + 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; + if (m->bar) { + wc.sibling = m->bar->win; + } else { + for (f = m->stack; f && (f->isfloating || !ISVISIBLE(f)); f = f->snext); // find first tiled stack client + if (f) + wc.sibling = f->win; + } + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c) && c != f) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +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 +scan(void) +{ + scanner = 1; + char swin[256]; + 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); + else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin)) + 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); + } + XFree(wins); + } + scanner = 0; +} + +void +sendmon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + unfocus(c, 1, NULL); + detach(c); + detachstack(c); + c->mon = m; + if (!(c->tags & SPTAGMASK)) + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attachx(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->oldbw = c->bw; + c->oldstate = c->isfloating; + 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->bw = c->oldbw; + c->isfloating = c->oldstate; + 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 ^= 1; + } + if (arg && arg->v) + selmon->lt[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 = f; + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + + /* clean up any zombies immediately */ + sigchld(0); + + + /* the one line of bloat that would have saved a lot of time for a lot of people */ + putenv("_JAVA_AWT_WM_NONREPARENTING=1"); + + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + xinitvisual(); + drw = drw_create(dpy, screen, root, sw, sh, visual, depth, cmap); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; + 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[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], alphas[i], ColCount); + + 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) +{ + pid_t pid; + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + while (0 < (pid = waitpid(-1, NULL, WNOHANG))) { + pid_t *p, *lim; + + if (!(p = autostart_pids)) + continue; + lim = &p[autostart_len]; + + for (; p < lim; p++) { + if (*p == pid) { + *p = -1; + break; + } + } + } +} + +void +spawn(const Arg *arg) +{ + if (arg->v == dmenucmd) + dmenumon[0] = '0' + selmon->num; + + if (fork() == 0) + { + if (dpy) + close(ConnectionNumber(dpy)); + + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); + perror(" failed"); + exit(EXIT_SUCCESS); + } +} + +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) +{ + Client *c = selmon->sel; + Monitor *dest; + if (!c || !mons->next) + return; + dest = dirtomon(arg->i); + sendmon(c, dest); +} + +void +togglebar(const Arg *arg) +{ + Bar *bar; + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + for (bar = selmon->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); +} + +void +togglefloating(const Arg *arg) +{ + Client *c = selmon->sel; + if (arg && arg->v) + c = (Client*)arg->v; + if (!c) + return; + if (c->isfullscreen) /* no support for fullscreen windows */ + return; + c->isfloating = !c->isfloating || c->isfixed; + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel); + else + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + if (c->isfloating) { + resize(c, c->x, c->y, c->w, c->h, 0); + } + arrange(c->mon); + +} + +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);; + + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + + focus(NULL); + arrange(selmon); + } +} + +void +unfocus(Client *c, int setfocus, Client *nextfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColFloat].pixel); + else + 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; + + if (c->swallowing) { + unswallow(c); + return; + } + + Client *s = swallowingclient(c->win); + if (s) { + free(s->swallowing); + s->swallowing = NULL; + arrange(m); + focus(NULL); + return; + } + + 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); + if (s) + return; + 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) +{ + Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixel = 0, + .border_pixel = 0, + .colormap = cmap, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + for (bar = m->bar; bar; bar = bar->next) { + if (bar->external) + continue; + if (!bar->win) { + bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, + InputOutput, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); + XMapRaised(dpy, bar->win); + XSetClassHint(dpy, bar->win, &ch); + } + } + } +} + +void +updatebarpos(Monitor *m) +{ + + m->wx = m->mx; + m->wy = m->my; + m->ww = m->mw; + m->wh = m->mh; + Bar *bar; + int y_pad = 0; + int x_pad = 0; + + + for (bar = m->bar; bar; bar = bar->next) { + bar->bx = m->wx + x_pad; + bar->bw = m->ww - 2 * x_pad; + } + + for (bar = m->bar; bar; bar = bar->next) + if (!m->showbar || !bar->showbar) + bar->by = -bar->bh - y_pad; + + + if (!m->showbar) + return; + for (bar = m->bar; bar; bar = bar->next) { + if (!bar->showbar) + continue; + if (bar->topbar) + m->wy = m->wy + bar->bh + y_pad; + m->wh -= y_pad + bar->bh; + bar->by = (bar->topbar ? m->wy - bar->bh : m->wy + m->wh); + } +} + +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; + attach(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) +{ + Monitor *m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + for (m = mons; m; m = m->next) + drawbar(m); +} + +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 +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 (c->isurgent) { + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColFloat].pixel); + else + XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColBorder].pixel); + } + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} + +void +view(const Arg *arg) +{ + 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; + focus(NULL); + arrange(selmon); +} + +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; + Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + for (bar = m->bar; bar; bar = bar->next) + if (w == bar->win) + 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 (arg && arg->v) + c = (Client*)arg->v; + if (!c) + return; + + + + if (!c->mon->lt[c->mon->sellt]->arrange + || (c && c->isfloating) + ) + return; + + if (c == nexttiled(c->mon->clients)) + if (!c || !(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"); + if (!(xcon = XGetXCBConnection(dpy))) + die("dwm: cannot get xcb connection\n"); + checkotherwm(); + XrmInitialize(); + loadxrdb(); + autostart_exec(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec ps", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + run(); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} + diff --git a/.suckless/st/dwm/dwm.c.orig b/.suckless/st/dwm/dwm.c.orig new file mode 100644 index 0000000..7fd5c88 --- /dev/null +++ b/.suckless/st/dwm/dwm.c.orig @@ -0,0 +1,2278 @@ +/* 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 +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#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 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 TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define XRDB_LOAD_COLOR(R,V) if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) { \ + if (value.addr != NULL && strnlen(value.addr, 8) == 7 && value.addr[0] == '#') { \ + int i = 1; \ + for (; i <= 6; i++) { \ + if (value.addr[i] < 48) break; \ + if (value.addr[i] > 57 && value.addr[i] < 65) break; \ + if (value.addr[i] > 70 && value.addr[i] < 97) break; \ + if (value.addr[i] > 102) break; \ + } \ + if (i == 7) { \ + strncpy(V, value.addr, 7); \ + V[7] = '\0'; \ + } \ + } \ + } + +/* 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, 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; + +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 */ + int gappx; /* gaps between windows */ + 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]; +}; + +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 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 loadxrdb(void); +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 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 setgaps(const Arg *arg); +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 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 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 xrdb(const Arg *arg); +static void zoom(const Arg *arg); +static void sighup(int unused); +static void sigterm(int unused); + + +/* 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 vp; /* vertical padding for bar */ +static int sp; /* side padding for bar */ +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" + +/* 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; + 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]; +} + +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 +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 + click = ClkStatusText; + } 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 + sp, m->by + vp, m->ww - 2 * sp, 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; + + 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->gappx = gappx; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + 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; + 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 || 1) { /* 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 - 2 * sp, 0, tw, bh, 0, 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); + 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) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w - 2 * sp, 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, k; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + int start, end, skip; + KeySym *syms; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XDisplayKeycodes(dpy, &start, &end); + syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); + if (!syms) + return; + for (k = start; k <= end; k++) + for (i = 0; i < LENGTH(keys); i++) + /* skip modifier codes, we do that ourselves */ + if (keys[i].keysym == syms[(k - start) * skip]) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, k, + keys[i].mod | modifiers[j], + root, True, + GrabModeAsync, GrabModeAsync); + XFree(syms); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = 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 +loadxrdb() +{ + Display *display; + char * resm; + XrmDatabase xrdb; + char *type; + XrmValue value; + + display = XOpenDisplay(NULL); + + if (display != NULL) { + resm = XResourceManagerString(display); + + if (resm != NULL) { + xrdb = XrmGetStringDatabase(resm); + + if (xrdb != NULL) { + XRDB_LOAD_COLOR("dwm.color2", normbordercolor); + XRDB_LOAD_COLOR("dwm.background", normbgcolor); + XRDB_LOAD_COLOR("dwm.color7", normfgcolor); + XRDB_LOAD_COLOR("dwm.color6", selbordercolor); + XRDB_LOAD_COLOR("dwm.color11", selbgcolor); + XRDB_LOAD_COLOR("dwm.color15", selfgcolor); + } + } + } + + XCloseDisplay(display); +} + +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); + 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); + attach(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, ""); + 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 (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 x, y, ocw, och, 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); + ocw = c->w; + och = c->h; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->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; + + nw = MAX(ocw + (ev.xmotion.x - x), 1); + nh = MAX(och + (ev.xmotion.y - y), 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); + 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; + } + } + if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle) + warp(m->sel); + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +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 +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 */ + attach(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 +setgaps(const Arg *arg) +{ + if ((arg->i == 0) || (selmon->gappx + arg->i < 0)) + selmon->gappx = 0; + else + selmon->gappx += arg->i; + arrange(selmon); +} + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt ^= 1; + if (arg && arg->v) + selmon->lt[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 = f; + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + struct sigaction sa; + + /* do not transform children into zombies when they terminate */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + + /* clean up any zombies (inherited from .xinitrc etc) immediately */ + while (waitpid(-1, NULL, WNOHANG) > 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; + bh = drw->fonts->h + user_bh; + + sp = sidepad; + vp = (topbar == 1) ? vertpad : - vertpad; + 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)) { + /* 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 +sighup(int unused) +{ + Arg a = {.i = 1}; + quit(&a); +} + +void +sigterm(int unused) +{ + Arg a = {.i = 0}; + quit(&a); +} + +void +spawn(const Arg *arg) +{ + struct sigaction sa; + + if (arg->v == dmenucmd) + dmenumon[0] = '0' + selmon->num; + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &sa, NULL); + + 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 - m->gappx; + for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx; + resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0); + if (my + HEIGHT(c) + m->gappx < m->wh) + my += HEIGHT(c) + m->gappx; + } else { + h = (m->wh - ty) / (n - i) - m->gappx; + resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0); + if (ty + HEIGHT(c) + m->gappx < m->wh) + ty += HEIGHT(c) + m->gappx; + } +} + +void +togglebar(const Arg *arg) +{ + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, 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 +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); + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + 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 + sp, m->by + vp, m->ww - 2 * sp, 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 = m->wh - vertpad - bh; + m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; + m->wy = m->topbar ? m->wy + bh + vp : m->wy; + } else + m->by = -bh - vp; +} + +void +updateclientlist(void) +{ + 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; + attach(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) +{ + Monitor* m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + for(m = mons; m; m = m->next) + drawbar(m); +} + +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) +{ + 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; + 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 +xrdb(const Arg *arg) +{ + loadxrdb(); + int i; + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + focus(NULL); + arrange(NULL); +} + +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(); + XrmInitialize(); + loadxrdb(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + run(); + if(restart) execvp(argv[0], argv); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} diff --git a/.suckless/st/dwm/dwm.c.rej b/.suckless/st/dwm/dwm.c.rej new file mode 100644 index 0000000..a9d5891 --- /dev/null +++ b/.suckless/st/dwm/dwm.c.rej @@ -0,0 +1,20 @@ +--- dwm.c ++++ dwm.c +@@ -1461,7 +1485,7 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ attachaside(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1914,7 +1938,7 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ attachaside(c); + attachstack(c); + } + if (m == selmon) diff --git a/.suckless/st/dwm/dwm.o b/.suckless/st/dwm/dwm.o new file mode 100644 index 0000000000000000000000000000000000000000..871e25a880d0852d02eb26175cf0a2d14f455030 GIT binary patch literal 100416 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 z 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 AMgRZ+ literal 0 HcmV?d00001 diff --git a/.suckless/st/dwm/dwm.png b/.suckless/st/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/.suckless/st/dwm/mkconfig/config.mk.freebsd b/.suckless/st/dwm/mkconfig/config.mk.freebsd new file mode 100644 index 0000000..e3fb9ab --- /dev/null +++ b/.suckless/st/dwm/mkconfig/config.mk.freebsd @@ -0,0 +1,59 @@ +# dwm version +VERSION = 6.3 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +X11INC = /usr/local/include +X11LIB = /usr/local/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +FREETYPEINC = /usr/local/include/freetype2 + +# Uncomment this for the alpha patch and the winicon patch (BAR_ALPHA_PATCH, BAR_WINICON_PATCH) +XRENDER = -lXrender + +# Uncomment this for the mdpcontrol patch / MDPCONTROL_PATCH +#MPDCLIENT = -lmpdclient + +# Uncomment for the pango patch / BAR_PANGO_PATCH +#PANGOINC = `pkg-config --cflags xft pango pangoxft` +#PANGOLIB = `pkg-config --libs xft pango pangoxft` + +# Uncomment for the ipc patch / IPC_PATCH +#YAJLLIBS = -lyajl +#YAJLINC = -I/usr/include/yajl + +# Uncomment this for the rounded corners patch / ROUNDED_CORNERS_PATCH +#XEXTLIB = -lXext + +# Uncomment this for the swallow patch / SWALLOW_PATCH +XCBLIBS = -lX11-xcb -lxcb -lxcb-res + +# This is needed for the winicon and tagpreview patches / BAR_WINICON_PATCH / BAR_TAGPREVIEW_PATCH +#IMLIB2LIBS = -lImlib2 + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} ${YAJLINC} ${PANGOINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XRENDER} ${MPDCLIENT} ${XEXTLIB} ${XCBLIBS} ${KVMLIB} ${PANGOLIB} ${YAJLLIBS} ${IMLIB2LIBS} + +# 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-unused-function -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/.suckless/st/dwm/mkconfig/config.mk.linux b/.suckless/st/dwm/mkconfig/config.mk.linux new file mode 100644 index 0000000..7b3b027 --- /dev/null +++ b/.suckless/st/dwm/mkconfig/config.mk.linux @@ -0,0 +1,55 @@ +# dwm version +VERSION = 6.3 + +# 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 + +# Uncomment this for the alpha patch and the winicon patch (BAR_ALPHA_PATCH, BAR_WINICON_PATCH) +XRENDER = -lXrender + +# Uncomment this for the mdpcontrol patch / MDPCONTROL_PATCH +#MPDCLIENT = -lmpdclient + +# Uncomment for the pango patch / BAR_PANGO_PATCH +#PANGOINC = `pkg-config --cflags xft pango pangoxft` +#PANGOLIB = `pkg-config --libs xft pango pangoxft` + +# Uncomment for the ipc patch / IPC_PATCH +#YAJLLIBS = -lyajl +#YAJLINC = -I/usr/include/yajl + +# Uncomment this for the rounded corners patch / ROUNDED_CORNERS_PATCH +#XEXTLIB = -lXext + +# Uncomment this for the swallow patch / SWALLOW_PATCH +XCBLIBS = -lX11-xcb -lxcb -lxcb-res + +# This is needed for the winicon and tagpreview patches / BAR_WINICON_PATCH / BAR_TAGPREVIEW_PATCH +#IMLIB2LIBS = -lImlib2 + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} ${YAJLINC} ${PANGOINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XRENDER} ${MPDCLIENT} ${XEXTLIB} ${XCBLIBS} ${KVMLIB} ${PANGOLIB} ${YAJLLIBS} ${IMLIB2LIBS} + +# 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-unused-function -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/.suckless/st/dwm/mkconfig/config.mk.openbsd b/.suckless/st/dwm/mkconfig/config.mk.openbsd new file mode 100644 index 0000000..6651dbb --- /dev/null +++ b/.suckless/st/dwm/mkconfig/config.mk.openbsd @@ -0,0 +1,57 @@ +# dwm version +VERSION = 6.3 + +# 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 +FREETYPEINC = ${X11INC}/freetype2 +KVMLIB = -lkvm + +# Uncomment this for the alpha patch and the winicon patch (BAR_ALPHA_PATCH, BAR_WINICON_PATCH) +XRENDER = -lXrender + +# Uncomment this for the mdpcontrol patch / MDPCONTROL_PATCH +#MPDCLIENT = -lmpdclient + +# Uncomment for the pango patch / BAR_PANGO_PATCH +#PANGOINC = `pkg-config --cflags xft pango pangoxft` +#PANGOLIB = `pkg-config --libs xft pango pangoxft` + +# Uncomment for the ipc patch / IPC_PATCH +#YAJLLIBS = -lyajl +#YAJLINC = -I/usr/include/yajl + +# Uncomment this for the rounded corners patch / ROUNDED_CORNERS_PATCH +#XEXTLIB = -lXext + +# Uncomment this for the swallow patch / SWALLOW_PATCH +XCBLIBS = -lX11-xcb -lxcb -lxcb-res + +# This is needed for the winicon and tagpreview patches / BAR_WINICON_PATCH / BAR_TAGPREVIEW_PATCH +#IMLIB2LIBS = -lImlib2 + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} ${YAJLINC} ${PANGOINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XRENDER} ${MPDCLIENT} ${XEXTLIB} ${XCBLIBS} ${KVMLIB} ${PANGOLIB} ${YAJLLIBS} ${IMLIB2LIBS} + +# 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-unused-function -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/.suckless/st/dwm/mkconfig/config.mk.solaris b/.suckless/st/dwm/mkconfig/config.mk.solaris new file mode 100644 index 0000000..7a6d93f --- /dev/null +++ b/.suckless/st/dwm/mkconfig/config.mk.solaris @@ -0,0 +1,58 @@ +# dwm version +VERSION = 6.3 + +# 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 + +# Uncomment this for the alpha patch and the winicon patch (BAR_ALPHA_PATCH, BAR_WINICON_PATCH) +XRENDER = -lXrender + +# Uncomment this for the mdpcontrol patch / MDPCONTROL_PATCH +#MPDCLIENT = -lmpdclient + +# Uncomment for the pango patch / BAR_PANGO_PATCH +#PANGOINC = `pkg-config --cflags xft pango pangoxft` +#PANGOLIB = `pkg-config --libs xft pango pangoxft` + +# Uncomment for the ipc patch / IPC_PATCH +#YAJLLIBS = -lyajl +#YAJLINC = -I/usr/include/yajl + +# Uncomment this for the rounded corners patch / ROUNDED_CORNERS_PATCH +#XEXTLIB = -lXext + +# Uncomment this for the swallow patch / SWALLOW_PATCH +XCBLIBS = -lX11-xcb -lxcb -lxcb-res + +# This is needed for the winicon and tagpreview patches / BAR_WINICON_PATCH / BAR_TAGPREVIEW_PATCH +#IMLIB2LIBS = -lImlib2 + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} ${YAJLINC} ${PANGOINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XRENDER} ${MPDCLIENT} ${XEXTLIB} ${XCBLIBS} ${KVMLIB} ${PANGOLIB} ${YAJLLIBS} ${IMLIB2LIBS} + +# 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-unused-function -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/.suckless/st/dwm/movestack.c b/.suckless/st/dwm/movestack.c new file mode 100644 index 0000000..520f4ae --- /dev/null +++ b/.suckless/st/dwm/movestack.c @@ -0,0 +1,48 @@ +void +movestack(const Arg *arg) { + Client *c = NULL, *p = NULL, *pc = NULL, *i; + + if(arg->i > 0) { + /* find the client after selmon->sel */ + for(c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + if(!c) + for(c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + + } + else { + /* find the client before selmon->sel */ + for(i = selmon->clients; i != selmon->sel; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + if(!c) + for(; i; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + } + /* find the client before selmon->sel and c */ + for(i = selmon->clients; i && (!p || !pc); i = i->next) { + if(i->next == selmon->sel) + p = i; + if(i->next == c) + pc = i; + } + + /* swap c and selmon->sel selmon->clients in the selmon->clients list */ + if(c && c != selmon->sel) { + Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next; + selmon->sel->next = c->next==selmon->sel?c:c->next; + c->next = temp; + + if(p && p != c) + p->next = c; + if(pc && pc != selmon->sel) + pc->next = selmon->sel; + + if(selmon->sel == selmon->clients) + selmon->clients = c; + else if(c == selmon->clients) + selmon->clients = selmon->sel; + + arrange(selmon); + } +} \ No newline at end of file diff --git a/.suckless/st/dwm/patch/attachx.c b/.suckless/st/dwm/patch/attachx.c new file mode 100644 index 0000000..c683dce --- /dev/null +++ b/.suckless/st/dwm/patch/attachx.c @@ -0,0 +1,20 @@ +void +attachx(Client *c) +{ + Client *at; + + + unsigned int n; + for (at = c->mon->clients, n = 0; at; at = at->next) + if (!at->isfloating && ISVISIBLEONTAG(at, c->tags)) + if (++n >= c->mon->nmaster) + break; + + if (at && c->mon->nmaster) { + c->next = at->next; + at->next = c; + return; + } + attach(c); // master (default) +} + diff --git a/.suckless/st/dwm/patch/attachx.h b/.suckless/st/dwm/patch/attachx.h new file mode 100644 index 0000000..e522d27 --- /dev/null +++ b/.suckless/st/dwm/patch/attachx.h @@ -0,0 +1,2 @@ +static void attachx(Client *c); + diff --git a/.suckless/st/dwm/patch/bar.c b/.suckless/st/dwm/patch/bar.c new file mode 100644 index 0000000..65e1a69 --- /dev/null +++ b/.suckless/st/dwm/patch/bar.c @@ -0,0 +1,39 @@ +void +barhover(XEvent *e, Bar *bar) +{ + const BarRule *br; + Monitor *m = bar->mon; + XMotionEvent *ev = &e->xmotion; + BarArg barg = { 0, 0, 0, 0 }; + int r; + + for (r = 0; r < LENGTH(barrules); r++) { + br = &barrules[r]; + if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->hoverfunc == NULL) + continue; + if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->num) + continue; + if (bar->x[r] > ev->x || ev->x > bar->x[r] + bar->w[r]) + continue; + + barg.x = ev->x - bar->x[r]; + barg.y = ev->y - bar->borderpx; + barg.w = bar->w[r]; + barg.h = bar->bh - 2 * bar->borderpx; + + br->hoverfunc(bar, &barg, ev); + break; + } +} + +Bar * +wintobar(Window win) +{ + Monitor *m; + Bar *bar; + for (m = mons; m; m = m->next) + for (bar = m->bar; bar; bar = bar->next) + if (bar->win == win) + return bar; + return NULL; +} diff --git a/.suckless/st/dwm/patch/bar.h b/.suckless/st/dwm/patch/bar.h new file mode 100644 index 0000000..3e006dc --- /dev/null +++ b/.suckless/st/dwm/patch/bar.h @@ -0,0 +1,2 @@ +static void barhover(XEvent *e, Bar *bar); +static Bar *wintobar(Window win); diff --git a/.suckless/st/dwm/patch/bar_alpha.c b/.suckless/st/dwm/patch/bar_alpha.c new file mode 100644 index 0000000..465f6f2 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_alpha.c @@ -0,0 +1,43 @@ + +static int useargb = 0; +static Visual *visual; +static int depth; +static Colormap cmap; + +void +xinitvisual() +{ + XVisualInfo *infos; + XRenderPictFormat *fmt; + int nitems; + int i; + + XVisualInfo tpl = { + .screen = screen, + .depth = 32, + .class = TrueColor + }; + long masks = VisualScreenMask | VisualDepthMask | VisualClassMask; + + infos = XGetVisualInfo(dpy, masks, &tpl, &nitems); + visual = NULL; + for (i = 0; i < nitems; i ++) { + fmt = XRenderFindVisualFormat(dpy, infos[i].visual); + if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { + visual = infos[i].visual; + depth = infos[i].depth; + cmap = XCreateColormap(dpy, root, visual, AllocNone); + useargb = 1; + break; + } + } + + XFree(infos); + + if (!visual) { + visual = DefaultVisual(dpy, screen); + depth = DefaultDepth(dpy, screen); + cmap = DefaultColormap(dpy, screen); + } +} + diff --git a/.suckless/st/dwm/patch/bar_alpha.h b/.suckless/st/dwm/patch/bar_alpha.h new file mode 100644 index 0000000..1c2a012 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_alpha.h @@ -0,0 +1,4 @@ +#define OPAQUE 0xffU + +static void xinitvisual(); + diff --git a/.suckless/st/dwm/patch/bar_indicators.c b/.suckless/st/dwm/patch/bar_indicators.c new file mode 100644 index 0000000..b003fc8 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_indicators.c @@ -0,0 +1,104 @@ +/* Indicator properties, you can override these in your config.h if you want. */ +#ifndef TAGSINDICATOR +#define TAGSINDICATOR 1 // 0 = off, 1 = on if >1 client/view tag, 2 = always on +#endif +#ifndef TAGSPX +#define TAGSPX 5 // # pixels for tag grid boxes +#endif +#ifndef TAGSROWS +#define TAGSROWS 3 // # rows in tag grid (9 tags, e.g. 3x3) +#endif + +void +drawindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert, int type) +{ + int i, boxw, boxs, indn = 0; + if (!(occ & 1 << tag) || type == INDICATOR_NONE) + return; + + boxs = drw->fonts->h / 9; + boxw = drw->fonts->h / 6 + 2; + if (filled == -1) + filled = m == selmon && m->sel && m->sel->tags & 1 << tag; + + switch (type) { + default: + case INDICATOR_TOP_LEFT_SQUARE: + drw_rect(drw, x + boxs, y + boxs, boxw, boxw, filled, invert); + break; + case INDICATOR_TOP_LEFT_LARGER_SQUARE: + drw_rect(drw, x + boxs + 2, y + boxs+1, boxw+1, boxw+1, filled, invert); + break; + case INDICATOR_TOP_BAR: + drw_rect(drw, x + boxw, y, w - ( 2 * boxw + 1), boxw/2, filled, invert); + break; + case INDICATOR_TOP_BAR_SLIM: + drw_rect(drw, x + boxw, y, w - ( 2 * boxw + 1), 1, 0, invert); + break; + case INDICATOR_BOTTOM_BAR: + drw_rect(drw, x + boxw, y + h - boxw/2, w - ( 2 * boxw + 1), boxw/2, filled, invert); + break; + case INDICATOR_BOTTOM_BAR_SLIM: + drw_rect(drw, x + boxw, y + h - 1, w - ( 2 * boxw + 1), 1, 0, invert); + break; + case INDICATOR_BOX: + drw_rect(drw, x + boxw, y, w - 2 * boxw, h, 0, invert); + break; + case INDICATOR_BOX_WIDER: + drw_rect(drw, x + boxw/2, y, w - boxw, h, 0, invert); + break; + case INDICATOR_BOX_FULL: + drw_rect(drw, x, y, w - 2, h, 0, invert); + break; + case INDICATOR_CLIENT_DOTS: + for (c = m->clients; c; c = c->next) { + if (c->tags & (1 << tag)) { + drw_rect(drw, x, 1 + (indn * 2), m->sel == c ? 6 : 1, 1, 1, invert); + indn++; + } + if (h <= 1 + (indn * 2)) { + indn = 0; + x += 2; + } + } + break; + case INDICATOR_RIGHT_TAGS: + if (!c) + break; + for (i = 0; i < NUMTAGS; i++) { + drw_rect(drw, + ( x + w - 2 - ((NUMTAGS / TAGSROWS) * TAGSPX) + - (i % (NUMTAGS/TAGSROWS)) + ((i % (NUMTAGS / TAGSROWS)) * TAGSPX) + ), + ( y + 2 + ((i / (NUMTAGS/TAGSROWS)) * TAGSPX) + - ((i / (NUMTAGS/TAGSROWS))) + ), + TAGSPX, TAGSPX, (c->tags >> i) & 1, 0 + ); + } + break; + case INDICATOR_PLUS_AND_LARGER_SQUARE: + boxs += 2; + boxw += 2; + /* falls through */ + case INDICATOR_PLUS_AND_SQUARE: + drw_rect(drw, x + boxs, y + boxs, boxw % 2 ? boxw : boxw + 1, boxw % 2 ? boxw : boxw + 1, filled, invert); + /* falls through */ + case INDICATOR_PLUS: + if (!(boxw % 2)) + boxw += 1; + drw_rect(drw, x + boxs + boxw / 2, y + boxs, 1, boxw, filled, invert); // | + drw_rect(drw, x + boxs, y + boxs + boxw / 2, boxw + 1, 1, filled, invert); // ‒ + break; + } +} + +void +drawstateindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert) +{ + if (c->isfloating) + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, floatindicatortype); + else + drawindicator(m, c, occ, x, y, w, h, tag, filled, invert, tiledindicatortype); +} + diff --git a/.suckless/st/dwm/patch/bar_indicators.h b/.suckless/st/dwm/patch/bar_indicators.h new file mode 100644 index 0000000..c66e4f0 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_indicators.h @@ -0,0 +1,21 @@ +enum { + INDICATOR_NONE, + INDICATOR_TOP_LEFT_SQUARE, + INDICATOR_TOP_LEFT_LARGER_SQUARE, + INDICATOR_TOP_BAR, + INDICATOR_TOP_BAR_SLIM, + INDICATOR_BOTTOM_BAR, + INDICATOR_BOTTOM_BAR_SLIM, + INDICATOR_BOX, + INDICATOR_BOX_WIDER, + INDICATOR_BOX_FULL, + INDICATOR_CLIENT_DOTS, + INDICATOR_RIGHT_TAGS, + INDICATOR_PLUS, + INDICATOR_PLUS_AND_SQUARE, + INDICATOR_PLUS_AND_LARGER_SQUARE, +}; + +static void drawindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert, int type); +static void drawstateindicator(Monitor *m, Client *c, unsigned int occ, int x, int y, int w, int h, unsigned int tag, int filled, int invert); + diff --git a/.suckless/st/dwm/patch/bar_ltsymbol.c b/.suckless/st/dwm/patch/bar_ltsymbol.c new file mode 100644 index 0000000..1fbd1b8 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_ltsymbol.c @@ -0,0 +1,18 @@ +int +width_ltsymbol(Bar *bar, BarArg *a) +{ + return TEXTW(bar->mon->ltsymbol); +} + +int +draw_ltsymbol(Bar *bar, BarArg *a) +{ + return drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, bar->mon->ltsymbol, 0, False); +} + +int +click_ltsymbol(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkLtSymbol; +} + diff --git a/.suckless/st/dwm/patch/bar_ltsymbol.h b/.suckless/st/dwm/patch/bar_ltsymbol.h new file mode 100644 index 0000000..4de5720 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_ltsymbol.h @@ -0,0 +1,4 @@ +static int width_ltsymbol(Bar *bar, BarArg *a); +static int draw_ltsymbol(Bar *bar, BarArg *a); +static int click_ltsymbol(Bar *bar, Arg *arg, BarArg *a); + diff --git a/.suckless/st/dwm/patch/bar_status.c b/.suckless/st/dwm/patch/bar_status.c new file mode 100644 index 0000000..65595e0 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_status.c @@ -0,0 +1,20 @@ +int +width_status(Bar *bar, BarArg *a) +{ + return TEXTWM(stext); +} + + +int +draw_status(Bar *bar, BarArg *a) +{ + return drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, stext, 0, True); +} + + +int +click_status(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkStatusText; +} + diff --git a/.suckless/st/dwm/patch/bar_status.h b/.suckless/st/dwm/patch/bar_status.h new file mode 100644 index 0000000..c580597 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_status.h @@ -0,0 +1,4 @@ +static int width_status(Bar *bar, BarArg *a); +static int draw_status(Bar *bar, BarArg *a); +static int click_status(Bar *bar, Arg *arg, BarArg *a); + diff --git a/.suckless/st/dwm/patch/bar_tagicons.c b/.suckless/st/dwm/patch/bar_tagicons.c new file mode 100644 index 0000000..57d1629 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_tagicons.c @@ -0,0 +1,9 @@ +char * +tagicon(Monitor *m, int tag) +{ + int tagindex = tag + NUMTAGS * m->num; + if (tagindex >= LENGTH(tagicons[DEFAULT_TAGS])) + tagindex = tagindex % LENGTH(tagicons[DEFAULT_TAGS]); + return tagicons[DEFAULT_TAGS][tagindex]; +} + diff --git a/.suckless/st/dwm/patch/bar_tagicons.h b/.suckless/st/dwm/patch/bar_tagicons.h new file mode 100644 index 0000000..16fad2a --- /dev/null +++ b/.suckless/st/dwm/patch/bar_tagicons.h @@ -0,0 +1,8 @@ +enum { + DEFAULT_TAGS, + ALTERNATIVE_TAGS, + ALT_TAGS_DECORATION, +}; + +static char * tagicon(Monitor *m, int tag); + diff --git a/.suckless/st/dwm/patch/bar_tags.c b/.suckless/st/dwm/patch/bar_tags.c new file mode 100644 index 0000000..8aa37b2 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_tags.c @@ -0,0 +1,81 @@ +int +width_tags(Bar *bar, BarArg *a) +{ + int w, i; + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + + for (w = 0, i = 0; i < NUMTAGS; i++) { + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + w += TEXTW(tagicon(bar->mon, i)); + } + return w; +} + +int +draw_tags(Bar *bar, BarArg *a) +{ + int invert; + int w, x = a->x; + unsigned int i, occ = 0, urg = 0; + char *icon; + Client *c; + Monitor *m = bar->mon; + + for (c = m->clients; c; c = c->next) { + occ |= c->tags == 255 ? 0 : c->tags; + if (c->isurgent) + urg |= c->tags; + } + for (i = 0; i < NUMTAGS; i++) { + /* do not draw vacant tags */ + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; + + icon = tagicon(bar->mon, i); + invert = 0; + w = TEXTW(icon); + drw_setscheme(drw, scheme[ + m->tagset[m->seltags] & 1 << i + ? SchemeTagsSel + : urg & 1 << i + ? SchemeUrg + : SchemeTagsNorm + ]); + drw_text(drw, x, a->y, w, a->h, lrpad / 2, icon, invert, False); + drawindicator(m, NULL, occ, x, a->y, w, a->h, i, -1, invert, tagindicatortype); + x += w; + } + + return 1; +} + +int +click_tags(Bar *bar, Arg *arg, BarArg *a) +{ + int i = 0, x = 0; + Client *c; + unsigned int occ = 0; + for (c = bar->mon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + + do { + if (!(occ & 1 << i || bar->mon->tagset[bar->mon->seltags] & 1 << i)) + continue; + x += TEXTW(tagicon(bar->mon, i)); + } while (a->x >= x && ++i < NUMTAGS); + if (i < NUMTAGS) { + arg->ui = 1 << i; + } + return ClkTagBar; +} + +int +hover_tags(Bar *bar, BarArg *a, XMotionEvent *ev) +{ + + return 1; +} diff --git a/.suckless/st/dwm/patch/bar_tags.h b/.suckless/st/dwm/patch/bar_tags.h new file mode 100644 index 0000000..70040d2 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_tags.h @@ -0,0 +1,4 @@ +static int width_tags(Bar *bar, BarArg *a); +static int draw_tags(Bar *bar, BarArg *a); +static int click_tags(Bar *bar, Arg *arg, BarArg *a); +static int hover_tags(Bar *bar, BarArg *a, XMotionEvent *ev); diff --git a/.suckless/st/dwm/patch/bar_wintitle.c b/.suckless/st/dwm/patch/bar_wintitle.c new file mode 100644 index 0000000..d086736 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_wintitle.c @@ -0,0 +1,48 @@ +int +width_wintitle(Bar *bar, BarArg *a) +{ + return a->w; +} + +int +draw_wintitle(Bar *bar, BarArg *a) +{ + int x = a->x + lrpad / 2, w = a->w - lrpad / 2; + Monitor *m = bar->mon; + Client *c = m->sel; + + if (!c) { + drw_setscheme(drw, scheme[SchemeTitleNorm]); + drw_rect(drw, x, a->y, w, a->h, 1, 1); + return 0; + } + + int tpad = lrpad / 2; + int tx = x; + int tw = w; + + drw_setscheme(drw, scheme[m == selmon ? SchemeTitleSel : SchemeTitleNorm]); + + if (w <= TEXTW("A") - lrpad + tpad) // reduce text padding if wintitle is too small + tpad = (w - TEXTW("A") + lrpad < 0 ? 0 : (w - TEXTW("A") + lrpad) / 2); + + XSetForeground(drw->dpy, drw->gc, drw->scheme[ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, a->y, w, a->h); + + + tx += tpad; + tw -= lrpad; + + + drw_text(drw, tx, a->y, tw, a->h, 0, c->name, 0, False); + + drawstateindicator(m, c, 1, x, a->y, w, a->h, 0, 0, c->isfixed); + return 1; +} + +int +click_wintitle(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkWinTitle; +} + diff --git a/.suckless/st/dwm/patch/bar_wintitle.h b/.suckless/st/dwm/patch/bar_wintitle.h new file mode 100644 index 0000000..7e8cce5 --- /dev/null +++ b/.suckless/st/dwm/patch/bar_wintitle.h @@ -0,0 +1,4 @@ +static int width_wintitle(Bar *bar, BarArg *a); +static int draw_wintitle(Bar *bar, BarArg *a); +static int click_wintitle(Bar *bar, Arg *arg, BarArg *a); + diff --git a/.suckless/st/dwm/patch/cool_autostart.c b/.suckless/st/dwm/patch/cool_autostart.c new file mode 100644 index 0000000..ffd4ba3 --- /dev/null +++ b/.suckless/st/dwm/patch/cool_autostart.c @@ -0,0 +1,29 @@ +/* dwm will keep pid's of processes from autostart array and kill them at quit */ +static pid_t *autostart_pids; +static size_t autostart_len; + +/* execute command from autostart array */ +static void +autostart_exec() +{ + const char *const *p; + size_t i = 0; + + /* count entries */ + for (p = autostart; *p; autostart_len++, p++) + while (*++p); + + autostart_pids = malloc(autostart_len * sizeof(pid_t)); + for (p = autostart; *p; i++, p++) { + if ((autostart_pids[i] = fork()) == 0) { + setsid(); + execvp(*p, (char *const *)p); + fprintf(stderr, "dwm: execvp %s\n", *p); + perror(" failed"); + _exit(EXIT_FAILURE); + } + /* skip arguments */ + while (*++p); + } +} + diff --git a/.suckless/st/dwm/patch/cool_autostart.h b/.suckless/st/dwm/patch/cool_autostart.h new file mode 100644 index 0000000..5534d99 --- /dev/null +++ b/.suckless/st/dwm/patch/cool_autostart.h @@ -0,0 +1,2 @@ +static void autostart_exec(void); + diff --git a/.suckless/st/dwm/patch/dwm-alpha-20230401-348f655.diff b/.suckless/st/dwm/patch/dwm-alpha-20230401-348f655.diff new file mode 100644 index 0000000000000000000000000000000000000000..06d7405020018ddf3cacee90fd4af10487da3d20 GIT binary patch literal 1024 ScmZQz7zLvtFd70QH3R?z00031 literal 0 HcmV?d00001 diff --git a/.suckless/st/dwm/patch/dwmc b/.suckless/st/dwm/patch/dwmc new file mode 100644 index 0000000..3880428 --- /dev/null +++ b/.suckless/st/dwm/patch/dwmc @@ -0,0 +1,130 @@ +#!/usr/bin/env bash + +signal() { + xsetroot -name "fsignal:$*" +} + +case $# in +1) + case $1 in + focusurgent) ;& + mirrorlayout) ;& + mpdcontrol) ;& + pushdown) ;& + pushup) ;& + self_restart) ;& + setlayout) ;& + setcfact) ;& + switchcol) ;& + view) ;& + viewall) ;& + viewtoleft) ;& + viewtoright) ;& + tagtoleft) ;& + tagtoright) ;& + tagandviewtoleft) ;& + tagandviewtoright) ;& + transfer) ;& + transferall) ;& + togglealttag) ;& + togglebar) ;& + togglefloating) ;& + togglefullscreen) ;& + fullscreen) ;& + togglefakefullscreen) ;& + togglesticky) ;& + togglehorizontalmax) ;& + toggleverticalmax) ;& + togglemax) ;& + togglegaps) ;& + defaultgaps) ;& + unfloatvisible) ;& + winview) ;& + xrdb) ;& + zoom) ;& + killclient) ;& + quit) + signal $1 + ;; + *) + echo "Unknown command ($1) or missing one argument." + exit 1 + ;; + esac + ;; +2) + case $1 in + cyclelayout) ;& + explace) ;& + moveplace) ;& + mpdchange) ;& + setkeymode) ;& + switchtag) ;& + togglescratch) ;& + view) + signal $1 ui $2 + ;; + viewex) ;& + toggleviewex) ;& + tagallmon) ;& + tagswapmon) ;& + tagex) ;& + toggletagex) ;& + setborderpx) ;& + setgaps) ;& + setlayoutex) ;& + setlayoutaxisex) ;& + swapfocus) ;& + focusstack) ;& + pushstack) ;& + inplacerotate) ;& + rotatestack) ;& + rotatelayoutaxis) ;& + incnmaster) ;& + incnstack) ;& + incrgaps) ;& + incrigaps) ;& + incrogaps) ;& + incrihgaps) ;& + incrivgaps) ;& + incrohgaps) ;& + incrovgaps) ;& + movestack) ;& + shiftview) ;& + shiftviewclients) ;& + focusmon) ;& + tagmon) + signal $1 i $2 + ;; + setcfact) ;& + setmfact) + signal $1 f $2 + ;; + *) + echo "Unknown command ($1) or too many arguments" + exit 1 + ;; + esac + ;; +5) + case $1 in + setgaps) + # Expects "setgaps oh ov ih iv" where -1 means to keep existing values + [ $2 = -1 ] && oh=128 || oh=$2 + [ $3 = -1 ] && ov=128 || ov=$3 + [ $4 = -1 ] && ih=128 || ih=$4 + [ $5 = -1 ] && iv=128 || iv=$5 + signal $1 i $(((oh << 24) + (ov << 16) + (ih << 8) + iv)) + ;; + *) + echo "Unknown command ($1) or too many arguments" + exit 1 + ;; + esac + ;; +*) + echo "Unknown command ($1) or too many arguments" + exit 1 + ;; +esac + diff --git a/.suckless/st/dwm/patch/dwmc.c b/.suckless/st/dwm/patch/dwmc.c new file mode 100644 index 0000000..a892ff7 --- /dev/null +++ b/.suckless/st/dwm/patch/dwmc.c @@ -0,0 +1,85 @@ +void +setlayoutex(const Arg *arg) +{ + setlayout(&((Arg) { .v = &layouts[arg->i] })); +} + +void +viewex(const Arg *arg) +{ + view(&((Arg) { .ui = 1 << arg->ui })); +} + +void +viewallex(const Arg *arg) +{ + view(&((Arg){.ui = ~SPTAGMASK})); +} + +void +toggleviewex(const Arg *arg) +{ + toggleview(&((Arg) { .ui = 1 << arg->ui })); +} + +void +tagex(const Arg *arg) +{ + tag(&((Arg) { .ui = 1 << arg->ui })); +} + +void +toggletagex(const Arg *arg) +{ + toggletag(&((Arg) { .ui = 1 << arg->ui })); +} + +void +tagallex(const Arg *arg) +{ + tag(&((Arg){.ui = ~SPTAGMASK})); +} + +int +fake_signal(void) +{ + char fsignal[256]; + char indicator[9] = "fsignal:"; + char str_sig[50]; + char param[16]; + int i, len_str_sig, n, paramn; + size_t len_fsignal, len_indicator = strlen(indicator); + Arg arg; + + // Get root name property + if (gettextprop(root, XA_WM_NAME, fsignal, sizeof(fsignal))) { + len_fsignal = strlen(fsignal); + + // Check if this is indeed a fake signal + if (len_indicator > len_fsignal ? 0 : strncmp(indicator, fsignal, len_indicator) == 0) { + paramn = sscanf(fsignal+len_indicator, "%s%n%s%n", str_sig, &len_str_sig, param, &n); + + if (paramn == 1) arg = (Arg) {0}; + else if (paramn > 2) return 1; + else if (strncmp(param, "i", n - len_str_sig) == 0) + sscanf(fsignal + len_indicator + n, "%i", &(arg.i)); + else if (strncmp(param, "ui", n - len_str_sig) == 0) + sscanf(fsignal + len_indicator + n, "%u", &(arg.ui)); + else if (strncmp(param, "f", n - len_str_sig) == 0) + sscanf(fsignal + len_indicator + n, "%f", &(arg.f)); + else return 1; + + // Check if a signal was found, and if so handle it + for (i = 0; i < LENGTH(signals); i++) + if (strncmp(str_sig, signals[i].sig, len_str_sig) == 0 && signals[i].func) + signals[i].func(&(arg)); + + // A fake signal was sent + return 1; + } + } + + // No fake signal was sent, so proceed with update + return 0; +} + diff --git a/.suckless/st/dwm/patch/dwmc.h b/.suckless/st/dwm/patch/dwmc.h new file mode 100644 index 0000000..66e23a9 --- /dev/null +++ b/.suckless/st/dwm/patch/dwmc.h @@ -0,0 +1,14 @@ +typedef struct { + const char * sig; + void (*func)(const Arg *); +} Signal; + +static void setlayoutex(const Arg *arg); +static void viewex(const Arg *arg); +static void viewallex(const Arg *arg); +static void toggleviewex(const Arg *arg); +static void tagex(const Arg *arg); +static void toggletagex(const Arg *arg); +static void tagallex(const Arg *arg); +static int fake_signal(void); + diff --git a/.suckless/st/dwm/patch/fullscreen.c b/.suckless/st/dwm/patch/fullscreen.c new file mode 100644 index 0000000..5fb682a --- /dev/null +++ b/.suckless/st/dwm/patch/fullscreen.c @@ -0,0 +1,15 @@ +Layout *last_layout; + +void +fullscreen(const Arg *arg) +{ + int monocle_pos = 0; + if (selmon->showbar || last_layout == NULL) { + for (last_layout = (Layout *)layouts; last_layout != selmon->lt[selmon->sellt]; last_layout++); + setlayout(&((Arg) { .v = &layouts[monocle_pos] })); + } else { + setlayout(&((Arg) { .v = last_layout })); + } + togglebar(arg); +} + diff --git a/.suckless/st/dwm/patch/fullscreen.h b/.suckless/st/dwm/patch/fullscreen.h new file mode 100644 index 0000000..72983e1 --- /dev/null +++ b/.suckless/st/dwm/patch/fullscreen.h @@ -0,0 +1,2 @@ +static void fullscreen(const Arg *arg); + diff --git a/.suckless/st/dwm/patch/include.c b/.suckless/st/dwm/patch/include.c new file mode 100644 index 0000000..7130889 --- /dev/null +++ b/.suckless/st/dwm/patch/include.c @@ -0,0 +1,27 @@ +/* Bar functionality */ +#include "bar_indicators.c" +#include "bar_tagicons.c" +#include "bar.c" + +#include "bar_alpha.c" +#include "bar_ltsymbol.c" +#include "bar_status.c" +#include "bar_tags.c" +#include "bar_wintitle.c" + +/* Other patches */ +#include "attachx.c" +#include "cool_autostart.c" +#include "dwmc.c" +#include "fullscreen.c" +#include "moveresize.c" +#include "movestack.c" +#include "scratchpad.c" +#include "swallow.c" +#include "togglefullscreen.c" +#include "vanitygaps.c" +#include "xrdb.c" +/* Layouts */ +#include "layout_facts.c" +#include "layout_tile.c" + diff --git a/.suckless/st/dwm/patch/include.h b/.suckless/st/dwm/patch/include.h new file mode 100644 index 0000000..de7c6ec --- /dev/null +++ b/.suckless/st/dwm/patch/include.h @@ -0,0 +1,26 @@ +/* Bar functionality */ +#include "bar_indicators.h" +#include "bar_tagicons.h" +#include "bar.h" + +#include "bar_alpha.h" +#include "bar_ltsymbol.h" +#include "bar_status.h" +#include "bar_tags.h" +#include "bar_wintitle.h" + +/* Other patches */ +#include "attachx.h" +#include "cool_autostart.h" +#include "dwmc.h" +#include "fullscreen.h" +#include "moveresize.h" +#include "movestack.h" +#include "scratchpad.h" +#include "swallow.h" +#include "togglefullscreen.h" +#include "vanitygaps.h" +#include "xrdb.h" +/* Layouts */ +#include "layout_tile.h" + diff --git a/.suckless/st/dwm/patch/ipc/IPCClient.h b/.suckless/st/dwm/patch/ipc/IPCClient.h new file mode 100644 index 0000000..ee93030 --- /dev/null +++ b/.suckless/st/dwm/patch/ipc/IPCClient.h @@ -0,0 +1,62 @@ +#ifndef IPC_CLIENT_H_ +#define IPC_CLIENT_H_ + +#include +#include +#include + +typedef struct IPCClient IPCClient; +/** + * This structure contains the details of an IPC Client and pointers for a + * linked list + */ +struct IPCClient { + int fd; + int subscriptions; + + char *buffer; + uint32_t buffer_size; + + struct epoll_event event; + IPCClient *next; + IPCClient *prev; +}; + +typedef IPCClient *IPCClientList; + +/** + * Allocate memory for new IPCClient with the specified file descriptor and + * initialize struct. + * + * @param fd File descriptor of IPC client + * + * @return Address to allocated IPCClient struct + */ +IPCClient *ipc_client_new(int fd); + +/** + * Add an IPC Client to the specified list + * + * @param list Address of the list to add the client to + * @param nc Address of the IPCClient + */ +void ipc_list_add_client(IPCClientList *list, IPCClient *nc); + +/** + * Remove an IPCClient from the specified list + * + * @param list Address of the list to remove the client from + * @param c Address of the IPCClient + */ +void ipc_list_remove_client(IPCClientList *list, IPCClient *c); + +/** + * Get an IPCClient from the specified IPCClient list + * + * @param list List to remove the client from + * @param fd File descriptor of the IPCClient + */ +IPCClient *ipc_list_get_client(IPCClientList list, int fd); + +#endif // IPC_CLIENT_H_ + diff --git a/.suckless/st/dwm/patch/ipc/dwm-msg.c b/.suckless/st/dwm/patch/ipc/dwm-msg.c new file mode 100644 index 0000000..ca1e1a4 --- /dev/null +++ b/.suckless/st/dwm/patch/ipc/dwm-msg.c @@ -0,0 +1,549 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPC_MAGIC "DWM-IPC" +// clang-format off +#define IPC_MAGIC_ARR { 'D', 'W', 'M', '-', 'I', 'P', 'C' } +// clang-format on +#define IPC_MAGIC_LEN 7 // Not including null char + +#define IPC_EVENT_TAG_CHANGE "tag_change_event" +#define IPC_EVENT_CLIENT_FOCUS_CHANGE "client_focus_change_event" +#define IPC_EVENT_LAYOUT_CHANGE "layout_change_event" +#define IPC_EVENT_MONITOR_FOCUS_CHANGE "monitor_focus_change_event" +#define IPC_EVENT_FOCUSED_TITLE_CHANGE "focused_title_change_event" +#define IPC_EVENT_FOCUSED_STATE_CHANGE "focused_state_change_event" + +#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str)) +#define YINT(num) yajl_gen_integer(gen, num) +#define YDOUBLE(num) yajl_gen_double(gen, num) +#define YBOOL(v) yajl_gen_bool(gen, v) +#define YNULL() yajl_gen_null(gen) +#define YARR(body) \ + { \ + yajl_gen_array_open(gen); \ + body; \ + yajl_gen_array_close(gen); \ + } +#define YMAP(body) \ + { \ + yajl_gen_map_open(gen); \ + body; \ + yajl_gen_map_close(gen); \ + } + +typedef unsigned long Window; + +const char *DEFAULT_SOCKET_PATH = "/tmp/dwm.sock"; +static int sock_fd = -1; +static unsigned int ignore_reply = 0; + +typedef enum IPCMessageType { + IPC_TYPE_RUN_COMMAND = 0, + IPC_TYPE_GET_MONITORS = 1, + IPC_TYPE_GET_TAGS = 2, + IPC_TYPE_GET_LAYOUTS = 3, + IPC_TYPE_GET_DWM_CLIENT = 4, + IPC_TYPE_SUBSCRIBE = 5, + IPC_TYPE_EVENT = 6 +} IPCMessageType; + +// Every IPC message must begin with this +typedef struct dwm_ipc_header { + uint8_t magic[IPC_MAGIC_LEN]; + uint32_t size; + uint8_t type; +} __attribute((packed)) dwm_ipc_header_t; + +static int +recv_message(uint8_t *msg_type, uint32_t *reply_size, uint8_t **reply) +{ + uint32_t read_bytes = 0; + const int32_t to_read = sizeof(dwm_ipc_header_t); + char header[to_read]; + char *walk = header; + + // Try to read header + while (read_bytes < to_read) { + ssize_t n = read(sock_fd, header + read_bytes, to_read - read_bytes); + + if (n == 0) { + if (read_bytes == 0) { + fprintf(stderr, "Unexpectedly reached EOF while reading header."); + fprintf(stderr, + "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n", + read_bytes, to_read); + return -2; + } else { + fprintf(stderr, "Unexpectedly reached EOF while reading header."); + fprintf(stderr, + "Read %" PRIu32 " bytes, expected %" PRIu32 " total bytes.\n", + read_bytes, to_read); + return -3; + } + } else if (n == -1) { + return -1; + } + + read_bytes += n; + } + + // Check if magic string in header matches + if (memcmp(walk, IPC_MAGIC, IPC_MAGIC_LEN) != 0) { + fprintf(stderr, "Invalid magic string. Got '%.*s', expected '%s'\n", + IPC_MAGIC_LEN, walk, IPC_MAGIC); + return -3; + } + + walk += IPC_MAGIC_LEN; + + // Extract reply size + memcpy(reply_size, walk, sizeof(uint32_t)); + walk += sizeof(uint32_t); + + // Extract message type + memcpy(msg_type, walk, sizeof(uint8_t)); + walk += sizeof(uint8_t); + + (*reply) = malloc(*reply_size); + + // Extract payload + read_bytes = 0; + while (read_bytes < *reply_size) { + ssize_t n = read(sock_fd, *reply + read_bytes, *reply_size - read_bytes); + + if (n == 0) { + fprintf(stderr, "Unexpectedly reached EOF while reading payload."); + fprintf(stderr, "Read %" PRIu32 " bytes, expected %" PRIu32 " bytes.\n", + read_bytes, *reply_size); + free(*reply); + return -2; + } else if (n == -1) { + if (errno == EINTR || errno == EAGAIN) continue; + free(*reply); + return -1; + } + + read_bytes += n; + } + + return 0; +} + +static int +read_socket(IPCMessageType *msg_type, uint32_t *msg_size, char **msg) +{ + int ret = -1; + + while (ret != 0) { + ret = recv_message((uint8_t *)msg_type, msg_size, (uint8_t **)msg); + + if (ret < 0) { + // Try again (non-fatal error) + if (ret == -1 && (errno == EINTR || errno == EAGAIN)) continue; + + fprintf(stderr, "Error receiving response from socket. "); + fprintf(stderr, "The connection might have been lost.\n"); + exit(2); + } + } + + return 0; +} + +static ssize_t +write_socket(const void *buf, size_t count) +{ + size_t written = 0; + + while (written < count) { + const ssize_t n = + write(sock_fd, ((uint8_t *)buf) + written, count - written); + + if (n == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + continue; + else + return n; + } + written += n; + } + return written; +} + +static void +connect_to_socket() +{ + struct sockaddr_un addr; + + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + + // Initialize struct to 0 + memset(&addr, 0, sizeof(struct sockaddr_un)); + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, DEFAULT_SOCKET_PATH); + + connect(sock, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)); + + sock_fd = sock; +} + +static int +send_message(IPCMessageType msg_type, uint32_t msg_size, uint8_t *msg) +{ + dwm_ipc_header_t header = { + .magic = IPC_MAGIC_ARR, .size = msg_size, .type = msg_type}; + + size_t header_size = sizeof(dwm_ipc_header_t); + size_t total_size = header_size + msg_size; + + uint8_t buffer[total_size]; + + // Copy header to buffer + memcpy(buffer, &header, header_size); + // Copy message to buffer + memcpy(buffer + header_size, msg, header.size); + + write_socket(buffer, total_size); + + return 0; +} + +static int +is_float(const char *s) +{ + size_t len = strlen(s); + int is_dot_used = 0; + int is_minus_used = 0; + + // Floats can only have one decimal point in between or digits + // Optionally, floats can also be below zero (negative) + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else if (!is_dot_used && s[i] == '.' && i != 0 && i != len - 1) { + is_dot_used = 1; + continue; + } else if (!is_minus_used && s[i] == '-' && i == 0) { + is_minus_used = 1; + continue; + } else + return 0; + } + + return 1; +} + +static int +is_unsigned_int(const char *s) +{ + size_t len = strlen(s); + + // Unsigned int can only have digits + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else + return 0; + } + + return 1; +} + +static int +is_signed_int(const char *s) +{ + size_t len = strlen(s); + + // Signed int can only have digits and a negative sign at the start + for (int i = 0; i < len; i++) { + if (isdigit(s[i])) + continue; + else if (i == 0 && s[i] == '-') { + continue; + } else + return 0; + } + + return 1; +} + +static void +flush_socket_reply() +{ + IPCMessageType reply_type; + uint32_t reply_size; + char *reply; + + read_socket(&reply_type, &reply_size, &reply); + + free(reply); +} + +static void +print_socket_reply() +{ + IPCMessageType reply_type; + uint32_t reply_size; + char *reply; + + read_socket(&reply_type, &reply_size, &reply); + + printf("%.*s\n", reply_size, reply); + fflush(stdout); + free(reply); +} + +static int +run_command(const char *name, char *args[], int argc) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "command": "", + // "args": [ ... ] + // } + // clang-format off + YMAP( + YSTR("command"); YSTR(name); + YSTR("args"); YARR( + for (int i = 0; i < argc; i++) { + if (is_signed_int(args[i])) { + long long num = atoll(args[i]); + YINT(num); + } else if (is_float(args[i])) { + float num = atof(args[i]); + YDOUBLE(num); + } else { + YSTR(args[i]); + } + } + ) + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_RUN_COMMAND, msg_size, (uint8_t *)msg); + + if (!ignore_reply) + print_socket_reply(); + else + flush_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static int +get_monitors() +{ + send_message(IPC_TYPE_GET_MONITORS, 1, (uint8_t *)""); + print_socket_reply(); + return 0; +} + +static int +get_tags() +{ + send_message(IPC_TYPE_GET_TAGS, 1, (uint8_t *)""); + print_socket_reply(); + + return 0; +} + +static int +get_layouts() +{ + send_message(IPC_TYPE_GET_LAYOUTS, 1, (uint8_t *)""); + print_socket_reply(); + + return 0; +} + +static int +get_dwm_client(Window win) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "client_window_id": "" + // } + // clang-format off + YMAP( + YSTR("client_window_id"); YINT(win); + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_GET_DWM_CLIENT, msg_size, (uint8_t *)msg); + + print_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static int +subscribe(const char *event) +{ + const unsigned char *msg; + size_t msg_size; + + yajl_gen gen = yajl_gen_alloc(NULL); + + // Message format: + // { + // "event": "", + // "action": "subscribe" + // } + // clang-format off + YMAP( + YSTR("event"); YSTR(event); + YSTR("action"); YSTR("subscribe"); + ) + // clang-format on + + yajl_gen_get_buf(gen, &msg, &msg_size); + + send_message(IPC_TYPE_SUBSCRIBE, msg_size, (uint8_t *)msg); + + if (!ignore_reply) + print_socket_reply(); + else + flush_socket_reply(); + + yajl_gen_free(gen); + + return 0; +} + +static void +usage_error(const char *prog_name, const char *format, ...) +{ + va_list args; + va_start(args, format); + + fprintf(stderr, "Error: "); + vfprintf(stderr, format, args); + fprintf(stderr, "\nusage: %s [...]\n", prog_name); + fprintf(stderr, "Try '%s help'\n", prog_name); + + va_end(args); + exit(1); +} + +static void +print_usage(const char *name) +{ + printf("usage: %s [options] [...]\n", name); + puts(""); + puts("Commands:"); + puts(" run_command [args...] Run an IPC command"); + puts(""); + puts(" get_monitors Get monitor properties"); + puts(""); + puts(" get_tags Get list of tags"); + puts(""); + puts(" get_layouts Get list of layouts"); + puts(""); + puts(" get_dwm_client Get dwm client proprties"); + puts(""); + puts(" subscribe [events...] Subscribe to specified events"); + puts(" Options: " IPC_EVENT_TAG_CHANGE ","); + puts(" " IPC_EVENT_LAYOUT_CHANGE ","); + puts(" " IPC_EVENT_CLIENT_FOCUS_CHANGE ","); + puts(" " IPC_EVENT_MONITOR_FOCUS_CHANGE ","); + puts(" " IPC_EVENT_FOCUSED_TITLE_CHANGE ","); + puts(" " IPC_EVENT_FOCUSED_STATE_CHANGE); + puts(""); + puts(" help Display this message"); + puts(""); + puts("Options:"); + puts(" --ignore-reply Don't print reply messages from"); + puts(" run_command and subscribe."); + puts(""); +} + +int +main(int argc, char *argv[]) +{ + const char *prog_name = argv[0]; + + connect_to_socket(); + if (sock_fd == -1) { + fprintf(stderr, "Failed to connect to socket\n"); + return 1; + } + + int i = 1; + if (i < argc && strcmp(argv[i], "--ignore-reply") == 0) { + ignore_reply = 1; + i++; + } + + if (i >= argc) usage_error(prog_name, "Expected an argument, got none"); + + if (!argc || strcmp(argv[i], "help") == 0) + print_usage(prog_name); + else if (strcmp(argv[i], "run_command") == 0) { + if (++i >= argc) usage_error(prog_name, "No command specified"); + // Command name + char *command = argv[i]; + // Command arguments are everything after command name + char **command_args = argv + ++i; + // Number of command arguments + int command_argc = argc - i; + run_command(command, command_args, command_argc); + } else if (strcmp(argv[i], "get_monitors") == 0) { + get_monitors(); + } else if (strcmp(argv[i], "get_tags") == 0) { + get_tags(); + } else if (strcmp(argv[i], "get_layouts") == 0) { + get_layouts(); + } else if (strcmp(argv[i], "get_dwm_client") == 0) { + if (++i < argc) { + if (is_unsigned_int(argv[i])) { + Window win = atol(argv[i]); + get_dwm_client(win); + } else + usage_error(prog_name, "Expected unsigned integer argument"); + } else + usage_error(prog_name, "Expected the window id"); + } else if (strcmp(argv[i], "subscribe") == 0) { + if (++i < argc) { + for (int j = i; j < argc; j++) subscribe(argv[j]); + } else + usage_error(prog_name, "Expected event name"); + // Keep listening for events forever + while (1) { + print_socket_reply(); + } + } else + usage_error(prog_name, "Invalid argument '%s'", argv[i]); + + return 0; +} + diff --git a/.suckless/st/dwm/patch/ipc/yajl_dumps.h b/.suckless/st/dwm/patch/ipc/yajl_dumps.h new file mode 100644 index 0000000..bb57a17 --- /dev/null +++ b/.suckless/st/dwm/patch/ipc/yajl_dumps.h @@ -0,0 +1,66 @@ +#ifndef YAJL_DUMPS_H_ +#define YAJL_DUMPS_H_ + +#include +#include + +#define YSTR(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str)) +#define YINT(num) yajl_gen_integer(gen, num) +#define YDOUBLE(num) yajl_gen_double(gen, num) +#define YBOOL(v) yajl_gen_bool(gen, v) +#define YNULL() yajl_gen_null(gen) +#define YARR(body) \ + { \ + yajl_gen_array_open(gen); \ + body; \ + yajl_gen_array_close(gen); \ + } +#define YMAP(body) \ + { \ + yajl_gen_map_open(gen); \ + body; \ + yajl_gen_map_close(gen); \ + } + +int dump_tag(yajl_gen gen, const char *name, const int tag_mask); + +int dump_tags(yajl_gen gen, int tags_len); + +int dump_client(yajl_gen gen, Client *c); + +int dump_monitor(yajl_gen gen, Monitor *mon, int is_selected); + +int dump_monitors(yajl_gen gen, Monitor *mons, Monitor *selmon); + +int dump_layouts(yajl_gen gen, const Layout layouts[], const int layouts_len); + +int dump_tag_state(yajl_gen gen, TagState state); + +int dump_tag_event(yajl_gen gen, int mon_num, TagState old_state, + TagState new_state); + +int dump_client_focus_change_event(yajl_gen gen, Client *old_client, + Client *new_client, int mon_num); + +int dump_layout_change_event(yajl_gen gen, const int mon_num, + const char *old_symbol, const Layout *old_layout, + const char *new_symbol, const Layout *new_layout); + +int dump_monitor_focus_change_event(yajl_gen gen, const int last_mon_num, + const int new_mon_num); + +int dump_focused_title_change_event(yajl_gen gen, const int mon_num, + const Window client_id, + const char *old_name, const char *new_name); + +int dump_client_state(yajl_gen gen, const ClientState *state); + +int dump_focused_state_change_event(yajl_gen gen, const int mon_num, + const Window client_id, + const ClientState *old_state, + const ClientState *new_state); + +int dump_error_message(yajl_gen gen, const char *reason); + +#endif // YAJL_DUMPS_H_ + diff --git a/.suckless/st/dwm/patch/layout_facts.c b/.suckless/st/dwm/patch/layout_facts.c new file mode 100644 index 0000000..241d344 --- /dev/null +++ b/.suckless/st/dwm/patch/layout_facts.c @@ -0,0 +1,24 @@ +void +getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr) +{ + unsigned int n; + float mfacts, sfacts; + int mtotal = 0, stotal = 0; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + mfacts = MIN(n, m->nmaster); + sfacts = n - m->nmaster; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + if (n < m->nmaster) + mtotal += msize / mfacts; + else + stotal += ssize / sfacts; + + *mf = mfacts; // total factor of master area + *sf = sfacts; // total factor of stack area + *mr = msize - mtotal; // the remainder (rest) of pixels after an even master split + *sr = ssize - stotal; // the remainder (rest) of pixels after an even stack split +} + diff --git a/.suckless/st/dwm/patch/layout_tile.c b/.suckless/st/dwm/patch/layout_tile.c new file mode 100644 index 0000000..8d41d2a --- /dev/null +++ b/.suckless/st/dwm/patch/layout_tile.c @@ -0,0 +1,41 @@ +static void +tile(Monitor *m) +{ + unsigned int i, n; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + float mfacts, sfacts; + int mrest, srest; + Client *c; + + + int oh, ov, ih, iv; + getgaps(m, &oh, &ov, &ih, &iv, &n); + + if (n == 0) + return; + + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1); + sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); + sw = mw = m->ww - 2*ov; + + if (m->nmaster && n > m->nmaster) { + sw = (mw - iv) * (1 - m->mfact); + mw = (mw - iv) * m->mfact; + sx = mx + mw + iv; + } + + getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + my += HEIGHT(c) + ih; + } else { + resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + sy += HEIGHT(c) + ih; + } +} + diff --git a/.suckless/st/dwm/patch/layout_tile.h b/.suckless/st/dwm/patch/layout_tile.h new file mode 100644 index 0000000..78cafc8 --- /dev/null +++ b/.suckless/st/dwm/patch/layout_tile.h @@ -0,0 +1,2 @@ +static void tile(Monitor *); + diff --git a/.suckless/st/dwm/patch/moveresize.c b/.suckless/st/dwm/patch/moveresize.c new file mode 100644 index 0000000..75d58e2 --- /dev/null +++ b/.suckless/st/dwm/patch/moveresize.c @@ -0,0 +1,65 @@ +void +moveresize(const Arg *arg) { + /* only floating windows can be moved */ + Client *c; + c = selmon->sel; + int x, y, w, h, nx, ny, nw, nh, ox, oy, ow, oh; + char xAbs, yAbs, wAbs, hAbs; + int msx, msy, dx, dy, nmx, nmy; + unsigned int dui; + Window dummy; + + if (!c || !arg) + return; + if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) + return; + if (sscanf((char *)arg->v, "%d%c %d%c %d%c %d%c", &x, &xAbs, &y, &yAbs, &w, &wAbs, &h, &hAbs) != 8) + return; + + /* compute new window position; prevent window from be positioned outside the current monitor */ + nw = c->w + w; + if (wAbs == 'W') + nw = w < selmon->mw - 2 * c->bw ? w : selmon->mw - 2 * c->bw; + + nh = c->h + h; + if (hAbs == 'H') + nh = h < selmon->mh - 2 * c->bw ? h : selmon->mh - 2 * c->bw; + + nx = c->x + x; + if (xAbs == 'X') { + if (x < selmon->mx) + nx = selmon->mx; + else if (x > selmon->mx + selmon->mw) + nx = selmon->mx + selmon->mw - nw - 2 * c->bw; + else + nx = x; + } + + ny = c->y + y; + if (yAbs == 'Y') { + if (y < selmon->my) + ny = selmon->my; + else if (y > selmon->my + selmon->mh) + ny = selmon->my + selmon->mh - nh - 2 * c->bw; + else + ny = y; + } + + ox = c->x; + oy = c->y; + ow = c->w; + oh = c->h; + + XRaiseWindow(dpy, c->win); + Bool xqp = XQueryPointer(dpy, root, &dummy, &dummy, &msx, &msy, &dx, &dy, &dui); + resize(c, nx, ny, nw, nh, True); + + /* move cursor along with the window to avoid problems caused by the sloppy focus */ + if (xqp && ox <= msx && (ox + ow) >= msx && oy <= msy && (oy + oh) >= msy) + { + nmx = c->x - ox + c->w - ow; + nmy = c->y - oy + c->h - oh; + XWarpPointer(dpy, None, None, 0, 0, 0, 0, nmx, nmy); + } +} + diff --git a/.suckless/st/dwm/patch/moveresize.h b/.suckless/st/dwm/patch/moveresize.h new file mode 100644 index 0000000..919ebad --- /dev/null +++ b/.suckless/st/dwm/patch/moveresize.h @@ -0,0 +1,2 @@ +static void moveresize(const Arg *arg); + diff --git a/.suckless/st/dwm/patch/movestack.c b/.suckless/st/dwm/patch/movestack.c new file mode 100644 index 0000000..fe97f1d --- /dev/null +++ b/.suckless/st/dwm/patch/movestack.c @@ -0,0 +1,51 @@ +void +movestack(const Arg *arg) +{ + Client *c = NULL, *p = NULL, *pc = NULL, *i; + if (arg->i > 0) { + if (!selmon->sel) + return; + /* find the client after selmon->sel */ + for (c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + if (!c) + for (c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + } + else { + /* find the client before selmon->sel */ + for (i = selmon->clients; i != selmon->sel; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i) && !i->isfloating) + c = i; + } + + /* find the client before selmon->sel and c */ + for (i = selmon->clients; i && (!p || !pc); i = i->next) { + if (i->next == selmon->sel) + p = i; + if (i->next == c) + pc = i; + } + + /* swap c and selmon->sel selmon->clients in the selmon->clients list */ + if (c && c != selmon->sel) { + Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next; + selmon->sel->next = c->next==selmon->sel?c:c->next; + c->next = temp; + + if (p && p != c) + p->next = c; + if (pc && pc != selmon->sel) + pc->next = selmon->sel; + + if (selmon->sel == selmon->clients) + selmon->clients = c; + else if (c == selmon->clients) + selmon->clients = selmon->sel; + + arrange(selmon); + } +} + diff --git a/.suckless/st/dwm/patch/movestack.h b/.suckless/st/dwm/patch/movestack.h new file mode 100644 index 0000000..25f198f --- /dev/null +++ b/.suckless/st/dwm/patch/movestack.h @@ -0,0 +1,2 @@ +static void movestack(const Arg *arg); + diff --git a/.suckless/st/dwm/patch/scratchpad.c b/.suckless/st/dwm/patch/scratchpad.c new file mode 100644 index 0000000..9e24ff6 --- /dev/null +++ b/.suckless/st/dwm/patch/scratchpad.c @@ -0,0 +1,77 @@ +void +removescratch(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c) + return; + unsigned int scratchtag = SPTAG(arg->ui); + c->tags = c->mon->tagset[c->mon->seltags] ^ scratchtag; + arrange(c->mon); +} + +void +setscratch(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c) + return; + unsigned int scratchtag = SPTAG(arg->ui); + c->tags = scratchtag; + arrange(c->mon); +} + +void +togglescratch(const Arg *arg) +{ + Client *c = NULL, *next = NULL, *found = NULL; + Monitor *mon; + unsigned int scratchtag = SPTAG(arg->ui); + unsigned int newtagset = 0; + int nh = 0, nw = 0; + Arg sparg = {.v = scratchpads[arg->ui].cmd}; + + for (mon = mons; mon; mon = mon->next) { + for (c = mon->clients; c; c = next) { + next = c->next; + if (!(c->tags & scratchtag)) + continue; + + found = c; + + if (HIDDEN(c)) { + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); + newtagset = 0; + } else + newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; + + if (c->mon != selmon) { + if (c->mon->tagset[c->mon->seltags] & SPTAGMASK) + c->mon->tagset[c->mon->seltags] ^= scratchtag; + if (c->w > selmon->ww) + nw = selmon->ww - c->bw * 2; + if (c->h > selmon->wh) + nh = selmon->wh - c->bw * 2; + if (nw > 0 || nh > 0) + resizeclient(c, c->x, c->y, nw ? nw : c->w, nh ? nh : c->h); + sendmon(c, selmon); + } + } + } + + if (found) { + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } + if (ISVISIBLE(found)) { + focus(found); + restack(selmon); + } + } else { + selmon->tagset[selmon->seltags] |= scratchtag; + spawn(&sparg); + } +} + diff --git a/.suckless/st/dwm/patch/scratchpad.h b/.suckless/st/dwm/patch/scratchpad.h new file mode 100644 index 0000000..6230266 --- /dev/null +++ b/.suckless/st/dwm/patch/scratchpad.h @@ -0,0 +1,9 @@ +typedef struct { + const char *name; + const void *cmd; +} Sp; + +static void removescratch(const Arg *arg); +static void setscratch(const Arg *arg); +static void togglescratch(const Arg *arg); + diff --git a/.suckless/st/dwm/patch/swallow.c b/.suckless/st/dwm/patch/swallow.c new file mode 100644 index 0000000..0d65353 --- /dev/null +++ b/.suckless/st/dwm/patch/swallow.c @@ -0,0 +1,212 @@ +#include +#include +#ifdef __OpenBSD__ +#include +#include +#endif /* __OpenBSD__ */ + +static int scanner; +static xcb_connection_t *xcon; + +int +swallow(Client *p, Client *c) +{ + Client *s; + XWindowChanges wc; + + if (c->noswallow > 0 || c->isterminal) + return 0; + if (c->noswallow < 0 && !swallowfloating && c->isfloating) + return 0; + + XMapWindow(dpy, c->win); + + detach(c); + detachstack(c); + + setclientstate(c, WithdrawnState); + XUnmapWindow(dpy, p->win); + + p->swallowing = c; + c->mon = p->mon; + + Window w = p->win; + p->win = c->win; + c->win = w; + + XChangeProperty(dpy, c->win, netatom[NetClientList], XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(p->win), 1); + + updatetitle(p); + s = scanner ? c : p; + + wc.border_width = p->bw; + XConfigureWindow(dpy, p->win, CWBorderWidth, &wc); + XMoveResizeWindow(dpy, p->win, s->x, s->y, s->w, s->h); + XSetWindowBorder(dpy, p->win, scheme[SchemeNorm][ColBorder].pixel); + + arrange(p->mon); + configure(p); + updateclientlist(); + + return 1; +} + +void +unswallow(Client *c) +{ + XWindowChanges wc; + c->win = c->swallowing->win; + + free(c->swallowing); + c->swallowing = NULL; + + XDeleteProperty(dpy, c->win, netatom[NetClientList]); + + /* unfullscreen the client */ + setfullscreen(c, 0); + updatetitle(c); + arrange(c->mon); + XMapWindow(dpy, c->win); + + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + + setclientstate(c, NormalState); + focus(NULL); + arrange(c->mon); +} + +pid_t +winpid(Window w) +{ + pid_t result = 0; + + #ifdef __linux__ + xcb_res_client_id_spec_t spec = {0}; + spec.client = w; + spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; + + xcb_generic_error_t *e = NULL; + xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); + xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); + + if (!r) + return (pid_t)0; + + xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); + for (; i.rem; xcb_res_client_id_value_next(&i)) { + spec = i.data->spec; + if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { + uint32_t *t = xcb_res_client_id_value_value(i.data); + result = *t; + break; + } + } + + free(r); + + if (result == (pid_t)-1) + result = 0; + + #endif /* __linux__ */ + #ifdef __OpenBSD__ + Atom type; + int format; + unsigned long len, bytes; + unsigned char *prop; + pid_t ret; + + if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 1), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop) + return 0; + + ret = *(pid_t*)prop; + XFree(prop); + result = ret; + #endif /* __OpenBSD__ */ + + return result; +} + +pid_t +getparentprocess(pid_t p) +{ + unsigned int v = 0; + +#ifdef __linux__ + FILE *f; + char buf[256]; + snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); + + if (!(f = fopen(buf, "r"))) + return (pid_t)0; + + if (fscanf(f, "%*u %*s %*c %u", (unsigned *)&v) != 1) + v = (pid_t)0; + fclose(f); +#endif /* __linux__ */ +#ifdef __OpenBSD__ + int n; + kvm_t *kd; + struct kinfo_proc *kp; + + kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL); + if (!kd) + return 0; + + kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n); + v = kp->p_ppid; +#endif /* __OpenBSD__ */ + return (pid_t)v; +} + +int +isdescprocess(pid_t p, pid_t c) +{ + while (p != c && c != 0) + c = getparentprocess(c); + + return (int)c; +} + +Client * +termforwin(const Client *w) +{ + Client *c; + Monitor *m; + + if (!w->pid || w->isterminal) + return NULL; + + c = selmon->sel; + if (c && c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) + return c; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) + return c; + } + } + + return NULL; +} + +Client * +swallowingclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->swallowing && c->swallowing->win == w) + return c; + } + } + + return NULL; +} + diff --git a/.suckless/st/dwm/patch/swallow.h b/.suckless/st/dwm/patch/swallow.h new file mode 100644 index 0000000..529fea9 --- /dev/null +++ b/.suckless/st/dwm/patch/swallow.h @@ -0,0 +1,8 @@ +static pid_t getparentprocess(pid_t p); +static int isdescprocess(pid_t p, pid_t c); +static int swallow(Client *p, Client *c); +static Client *swallowingclient(Window w); +static Client *termforwin(const Client *c); +static void unswallow(Client *c); +static pid_t winpid(Window w); + diff --git a/.suckless/st/dwm/patch/togglefullscreen.c b/.suckless/st/dwm/patch/togglefullscreen.c new file mode 100644 index 0000000..a62edef --- /dev/null +++ b/.suckless/st/dwm/patch/togglefullscreen.c @@ -0,0 +1,10 @@ +void +togglefullscreen(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c) + return; + + setfullscreen(c, !c->isfullscreen); +} + diff --git a/.suckless/st/dwm/patch/togglefullscreen.h b/.suckless/st/dwm/patch/togglefullscreen.h new file mode 100644 index 0000000..96a6770 --- /dev/null +++ b/.suckless/st/dwm/patch/togglefullscreen.h @@ -0,0 +1,2 @@ +static void togglefullscreen(const Arg *arg); + diff --git a/.suckless/st/dwm/patch/vanitygaps.c b/.suckless/st/dwm/patch/vanitygaps.c new file mode 100644 index 0000000..d8d5e6e --- /dev/null +++ b/.suckless/st/dwm/patch/vanitygaps.c @@ -0,0 +1,177 @@ +/* Settings */ +static int enablegaps = 1; + +static void +setgaps(int oh, int ov, int ih, int iv) +{ + if (oh < 0) oh = 0; + if (ov < 0) ov = 0; + if (ih < 0) ih = 0; + if (iv < 0) iv = 0; + + selmon->gappoh = oh; + selmon->gappov = ov; + selmon->gappih = ih; + selmon->gappiv = iv; + + + arrange(selmon); +} + +/* External function that takes one integer and splits it + * into four gap values: + * - outer horizontal (oh) + * - outer vertical (ov) + * - inner horizontal (ih) + * - inner vertical (iv) + * + * Each value is represented as one byte with the uppermost + * bit of each byte indicating whether or not to keep the + * current value. + * + * Example: + * + * 10000000 10000000 00001111 00001111 + * | | | | + * + keep oh + keep ov + ih 15px + iv 15px + * + * This gives an int of: + * 10000000100000000000111100001111 = 2155876111 + * + * Thus this command should set inner gaps to 15: + * xsetroot -name "fsignal:setgaps i 2155876111" + */ +static void +setgapsex(const Arg *arg) +{ + int oh = selmon->gappoh; + int ov = selmon->gappov; + int ih = selmon->gappih; + int iv = selmon->gappiv; + + if (!(arg->i & (1 << 31))) + oh = (arg->i & 0x7f000000) >> 24; + if (!(arg->i & (1 << 23))) + ov = (arg->i & 0x7f0000) >> 16; + if (!(arg->i & (1 << 15))) + ih = (arg->i & 0x7f00) >> 8; + if (!(arg->i & (1 << 7))) + iv = (arg->i & 0x7f); + + /* Auto enable gaps if disabled */ + if (!enablegaps) + enablegaps = 1; + + setgaps(oh, ov, ih, iv); +} + +static void +togglegaps(const Arg *arg) +{ + enablegaps = !enablegaps; + + arrange(NULL); +} + +static void +defaultgaps(const Arg *arg) +{ + setgaps(gappoh, gappov, gappih, gappiv); +} + +static void +incrgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +static void +incrigaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +static void +incrogaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +static void +incrohgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov, + selmon->gappih, + selmon->gappiv + ); +} + +static void +incrovgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +static void +incrihgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + ); +} + +static void +incrivgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih, + selmon->gappiv + arg->i + ); +} + +static void +getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc) +{ + unsigned int n, oe, ie; + oe = ie = enablegaps; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 1) { + oe *= smartgaps_fact; // outer gaps disabled or multiplied when only one client + } + + *oh = m->gappoh*oe; // outer horizontal gap + *ov = m->gappov*oe; // outer vertical gap + *ih = m->gappih*ie; // inner horizontal gap + *iv = m->gappiv*ie; // inner vertical gap + *nc = n; // number of clients +} + diff --git a/.suckless/st/dwm/patch/vanitygaps.h b/.suckless/st/dwm/patch/vanitygaps.h new file mode 100644 index 0000000..8a3ed0b --- /dev/null +++ b/.suckless/st/dwm/patch/vanitygaps.h @@ -0,0 +1,16 @@ +/* Key binding functions */ +static void defaultgaps(const Arg *arg); +static void incrgaps(const Arg *arg); +static void incrigaps(const Arg *arg); +static void incrogaps(const Arg *arg); +static void incrohgaps(const Arg *arg); +static void incrovgaps(const Arg *arg); +static void incrihgaps(const Arg *arg); +static void incrivgaps(const Arg *arg); +static void togglegaps(const Arg *arg); + +/* Internals */ +static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc); +static void setgaps(int oh, int ov, int ih, int iv); +static void setgapsex(const Arg *arg); + diff --git a/.suckless/st/dwm/patch/xrdb.c b/.suckless/st/dwm/patch/xrdb.c new file mode 100644 index 0000000..f450023 --- /dev/null +++ b/.suckless/st/dwm/patch/xrdb.c @@ -0,0 +1,73 @@ +void +loadxrdb() +{ + Display *display; + char * resm; + XrmDatabase xrdb; + char *type; + XrmValue value; + + display = XOpenDisplay(NULL); + + if (display != NULL) { + resm = XResourceManagerString(display); + + if (resm != NULL) { + xrdb = XrmGetStringDatabase(resm); + + if (xrdb != NULL) { + XRDB_LOAD_COLOR("dwm.normfgcolor", normfgcolor); + XRDB_LOAD_COLOR("dwm.normbgcolor", normbgcolor); + XRDB_LOAD_COLOR("dwm.normbordercolor", normbordercolor); + XRDB_LOAD_COLOR("dwm.normfloatcolor", normfloatcolor); + XRDB_LOAD_COLOR("dwm.selfgcolor", selfgcolor); + XRDB_LOAD_COLOR("dwm.selbgcolor", selbgcolor); + XRDB_LOAD_COLOR("dwm.selbordercolor", selbordercolor); + XRDB_LOAD_COLOR("dwm.selfloatcolor", selfloatcolor); + XRDB_LOAD_COLOR("dwm.titlenormfgcolor", titlenormfgcolor); + XRDB_LOAD_COLOR("dwm.titlenormbgcolor", titlenormbgcolor); + XRDB_LOAD_COLOR("dwm.titlenormbordercolor", titlenormbordercolor); + XRDB_LOAD_COLOR("dwm.titlenormfloatcolor", titlenormfloatcolor); + XRDB_LOAD_COLOR("dwm.titleselfgcolor", titleselfgcolor); + XRDB_LOAD_COLOR("dwm.titleselbgcolor", titleselbgcolor); + XRDB_LOAD_COLOR("dwm.titleselbordercolor", titleselbordercolor); + XRDB_LOAD_COLOR("dwm.titleselfloatcolor", titleselfloatcolor); + XRDB_LOAD_COLOR("dwm.tagsnormfgcolor", tagsnormfgcolor); + XRDB_LOAD_COLOR("dwm.tagsnormbgcolor", tagsnormbgcolor); + XRDB_LOAD_COLOR("dwm.tagsnormbordercolor", tagsnormbordercolor); + XRDB_LOAD_COLOR("dwm.tagsnormfloatcolor", tagsnormfloatcolor); + XRDB_LOAD_COLOR("dwm.tagsselfgcolor", tagsselfgcolor); + XRDB_LOAD_COLOR("dwm.tagsselbgcolor", tagsselbgcolor); + XRDB_LOAD_COLOR("dwm.tagsselbordercolor", tagsselbordercolor); + XRDB_LOAD_COLOR("dwm.tagsselfloatcolor", tagsselfloatcolor); + XRDB_LOAD_COLOR("dwm.hidnormfgcolor", hidnormfgcolor); + XRDB_LOAD_COLOR("dwm.hidnormbgcolor", hidnormbgcolor); + XRDB_LOAD_COLOR("dwm.hidselfgcolor", hidselfgcolor); + XRDB_LOAD_COLOR("dwm.hidselbgcolor", hidselbgcolor); + XRDB_LOAD_COLOR("dwm.urgfgcolor", urgfgcolor); + XRDB_LOAD_COLOR("dwm.urgbgcolor", urgbgcolor); + XRDB_LOAD_COLOR("dwm.urgbordercolor", urgbordercolor); + XRDB_LOAD_COLOR("dwm.urgfloatcolor", urgfloatcolor); + + XrmDestroyDatabase(xrdb); + } + } + } + + XCloseDisplay(display); +} + +void +xrdb(const Arg *arg) +{ + loadxrdb(); + int i; + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], + alphas[i], + ColCount + ); + focus(NULL); + arrange(NULL); +} + diff --git a/.suckless/st/dwm/patch/xrdb.h b/.suckless/st/dwm/patch/xrdb.h new file mode 100644 index 0000000..3787bec --- /dev/null +++ b/.suckless/st/dwm/patch/xrdb.h @@ -0,0 +1,22 @@ +#include + +#define XRDB_LOAD_COLOR(R,V) if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) { \ + if (value.addr != NULL && strnlen(value.addr, 8) == 7 && value.addr[0] == '#') { \ + int i = 1; \ + for (; i <= 6; i++) { \ + if (value.addr[i] < 48) break; \ + if (value.addr[i] > 57 && value.addr[i] < 65) break; \ + if (value.addr[i] > 70 && value.addr[i] < 97) break; \ + if (value.addr[i] > 102) break; \ + } \ + if (i == 7) { \ + strncpy(V, value.addr, 7); \ + V[7] = '\0'; \ + } \ + } \ + } + +static void loadxrdb(void); +static void xrdb(const Arg *arg); + + diff --git a/.suckless/st/dwm/patches/dwm-attachaside-20160718-56a31dc.diff b/.suckless/st/dwm/patches/dwm-attachaside-20160718-56a31dc.diff new file mode 100644 index 0000000..b8471af --- /dev/null +++ b/.suckless/st/dwm/patches/dwm-attachaside-20160718-56a31dc.diff @@ -0,0 +1,92 @@ +diff --git a/dwm.c b/dwm.c +index b2bc9bd..58a86fa 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -49,7 +49,8 @@ + #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 ISVISIBLEONTAG(C, T) ((C->tags & T)) ++#define ISVISIBLE(C) ISVISIBLEONTAG(C, 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) +@@ -148,6 +149,7 @@ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interac + static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); ++static void attachaside(Client *c); + static void attachstack(Client *c); + static void buttonpress(XEvent *e); + static void checkotherwm(void); +@@ -185,6 +187,7 @@ static void maprequest(XEvent *e); + static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); ++static Client *nexttagged(Client *c); + static Client *nexttiled(Client *c); + static void pop(Client *); + static void propertynotify(XEvent *e); +@@ -408,6 +411,17 @@ attach(Client *c) + } + + void ++attachaside(Client *c) { ++ Client *at = nexttagged(c); ++ if(!at) { ++ attach(c); ++ return; ++ } ++ c->next = at->next; ++ at->next = c; ++} ++ ++void + attachstack(Client *c) + { + c->snext = c->mon->stack; +@@ -1079,7 +1093,7 @@ manage(Window w, XWindowAttributes *wa) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); +- attach(c); ++ attachaside(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +@@ -1213,6 +1227,16 @@ movemouse(const Arg *arg) + } + + Client * ++nexttagged(Client *c) { ++ Client *walked = c->mon->clients; ++ for(; ++ walked && (walked->isfloating || !ISVISIBLEONTAG(walked, c->tags)); ++ walked = walked->next ++ ); ++ return walked; ++} ++ ++Client * + nexttiled(Client *c) + { + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); +@@ -1437,7 +1461,7 @@ sendmon(Client *c, Monitor *m) + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ +- attach(c); ++ attachaside(c); + attachstack(c); + focus(NULL); + arrange(NULL); +@@ -1890,7 +1914,7 @@ updategeom(void) + m->clients = c->next; + detachstack(c); + c->mon = mons; +- attach(c); ++ attachaside(c); + attachstack(c); + } + if (m == selmon) diff --git a/.suckless/st/dwm/patches/dwm-bar-height-spacing-6.3.diff b/.suckless/st/dwm/patches/dwm-bar-height-spacing-6.3.diff new file mode 100644 index 0000000..cbdeb9a --- /dev/null +++ b/.suckless/st/dwm/patches/dwm-bar-height-spacing-6.3.diff @@ -0,0 +1,25 @@ +diff --git a/config.def.h b/config.def.h +index 1c0b587..9814500 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,7 @@ 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 user_bh = 2; /* 2 is the default spacing around the bar's font */ + 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 4465af1..2c27cb3 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1545,7 +1545,7 @@ setup(void) + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; +- bh = drw->fonts->h + 2; ++ bh = drw->fonts->h + user_bh; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); diff --git a/.suckless/st/dwm/patches/dwm-barpadding-20211020-a786211.diff b/.suckless/st/dwm/patches/dwm-barpadding-20211020-a786211.diff new file mode 100755 index 0000000..7842181 --- /dev/null +++ b/.suckless/st/dwm/patches/dwm-barpadding-20211020-a786211.diff @@ -0,0 +1,118 @@ +From a3cfb215f7f647d83d67e33df8f33a73e43bd65f Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Wed, 20 Oct 2021 09:14:07 +0200 +Subject: [PATCH] barpadding: adds space between the statusbar and the edge of + the screen + +--- + config.def.h | 2 ++ + dwm.c | 25 +++++++++++++++---------- + 2 files changed, 17 insertions(+), 10 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..f0b739f 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 vertpad = 10; /* vertical padding of bar */ ++static const int sidepad = 10; /* horizontal padding of bar */ + 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 5e4d494..df6d0d7 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -242,6 +242,8 @@ static int screen; + static int sw, sh; /* X display screen geometry width, height */ + static int bh, blw = 0; /* bar geometry */ + static int lrpad; /* sum of left and right padding for text */ ++static int vp; /* vertical padding for bar */ ++static int sp; /* side padding for bar */ + static int (*xerrorxlib)(Display *, XErrorEvent *); + static unsigned int numlockmask = 0; + static void (*handler[LASTEvent]) (XEvent *) = { +@@ -568,7 +570,7 @@ configurenotify(XEvent *e) + 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); ++ XMoveResizeWindow(dpy, m->barwin, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh); + } + focus(NULL); + arrange(NULL); +@@ -706,7 +708,7 @@ drawbar(Monitor *m) + 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); ++ drw_text(drw, m->ww - tw - 2 * sp, 0, tw, bh, 0, stext, 0); + } + + for (c = m->clients; c; c = c->next) { +@@ -732,12 +734,12 @@ drawbar(Monitor *m) + 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); ++ drw_text(drw, x, 0, w - 2 * sp, 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_rect(drw, x, 0, w - 2 * sp, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +@@ -1547,7 +1549,10 @@ setup(void) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; ++ sp = sidepad; ++ vp = (topbar == 1) ? vertpad : - vertpad; + updategeom(); ++ + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); +@@ -1704,7 +1709,7 @@ togglebar(const Arg *arg) + { + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); +- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); ++ XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, bh); + arrange(selmon); + } + +@@ -1814,7 +1819,7 @@ updatebars(void) + 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), ++ m->barwin = XCreateWindow(dpy, root, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); +@@ -1829,11 +1834,11 @@ 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; ++ m->wh = m->wh - vertpad - bh; ++ m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; ++ m->wy = m->topbar ? m->wy + bh + vp : m->wy; + } else +- m->by = -bh; ++ m->by = -bh - vp; + } + + void +-- +2.33.0 + diff --git a/.suckless/st/dwm/patches/dwm-fullgaps-6.4.diff b/.suckless/st/dwm/patches/dwm-fullgaps-6.4.diff new file mode 100755 index 0000000..dc52139 --- /dev/null +++ b/.suckless/st/dwm/patches/dwm-fullgaps-6.4.diff @@ -0,0 +1,94 @@ +diff -up a/config.def.h b/config.def.h +--- a/config.def.h ++++ b/config.def.h +@@ -2,6 +2,7 @@ + + /* appearance */ + static const unsigned int borderpx = 1; /* border pixel of windows */ ++static const unsigned int gappx = 5; /* gaps between 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 */ +@@ -85,6 +86,9 @@ static const Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY, XK_minus, setgaps, {.i = -1 } }, ++ { MODKEY, XK_equal, setgaps, {.i = +1 } }, ++ { MODKEY|ShiftMask, XK_equal, setgaps, {.i = 0 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff -up a/dwm.c b/dwm.c +--- a/dwm.c 2023-04-30 ++++ b/dwm.c 2023-04-30 +@@ -119,6 +119,7 @@ struct Monitor { + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ ++ int gappx; /* gaps between windows */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; +@@ -200,6 +201,7 @@ static void sendmon(Client *c, Monitor * + static void setclientstate(Client *c, long state); + static void setfocus(Client *c); + static void setfullscreen(Client *c, int fullscreen); ++static void setgaps(const Arg *arg); + static void setlayout(const Arg *arg); + static void setmfact(const Arg *arg); + static void setup(void); +@@ -641,6 +643,7 @@ createmon(void) + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; ++ m->gappx = gappx; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); +@@ -1508,6 +1511,16 @@ setfullscreen(Client *c, int fullscreen) + } + + void ++setgaps(const Arg *arg) ++{ ++ if ((arg->i == 0) || (selmon->gappx + arg->i < 0)) ++ selmon->gappx = 0; ++ else ++ selmon->gappx += arg->i; ++ arrange(selmon); ++} ++ ++void + setlayout(const Arg *arg) + { + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) +@@ -1697,18 +1710,18 @@ tile(Monitor *m) + 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); ++ mw = m->ww - m->gappx; ++ for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx; ++ resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0); ++ if (my + HEIGHT(c) + m->gappx < m->wh) ++ my += HEIGHT(c) + m->gappx; + } 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); ++ h = (m->wh - ty) / (n - i) - m->gappx; ++ resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0); ++ if (ty + HEIGHT(c) + m->gappx < m->wh) ++ ty += HEIGHT(c) + m->gappx; + } + } diff --git a/.suckless/st/dwm/patches/dwm-notitle-20210715-138b405.diff b/.suckless/st/dwm/patches/dwm-notitle-20210715-138b405.diff new file mode 100755 index 0000000..bc8a3e5 --- /dev/null +++ b/.suckless/st/dwm/patches/dwm-notitle-20210715-138b405.diff @@ -0,0 +1,81 @@ +From a3a7e94f59553689656871a65ea9ce90169a7c91 Mon Sep 17 00:00:00 2001 +From: birdalicous +Date: Thu, 15 Jul 2021 12:28:29 +0100 +Subject: [PATCH] notitle patch applied# + +--- + config.def.h | 1 - + dwm.c | 20 ++++---------------- + 2 files changed, 4 insertions(+), 17 deletions(-) + +diff --git a/config.def.h b/config.def.h +index a2ac963..eac20b4 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -103,7 +103,6 @@ static 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} }, +diff --git a/dwm.c b/dwm.c +index 5e4d494..6cd9fb7 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -64,8 +64,8 @@ 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 */ ++enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkClientWin, ++ ClkRootWin, ClkLast }; /* clicks */ + + typedef union { + int i; +@@ -440,10 +440,8 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + blw) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - (int)TEXTW(stext)) +- click = ClkStatusText; + else +- click = ClkWinTitle; ++ click = ClkStatusText; + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); +@@ -730,15 +728,8 @@ drawbar(Monitor *m) + 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); + } +@@ -1236,11 +1227,8 @@ propertynotify(XEvent *e) + drawbars(); + break; + } +- if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { ++ 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); + } +-- +2.32.0 + diff --git a/.suckless/st/dwm/patches/dwm-restartsig-20180523-6.2.diff b/.suckless/st/dwm/patches/dwm-restartsig-20180523-6.2.diff new file mode 100755 index 0000000..f1f8680 --- /dev/null +++ b/.suckless/st/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/.suckless/st/dwm/patches/dwm-statusallmons-6.2.diff b/.suckless/st/dwm/patches/dwm-statusallmons-6.2.diff new file mode 100755 index 0000000..9d9633d --- /dev/null +++ b/.suckless/st/dwm/patches/dwm-statusallmons-6.2.diff @@ -0,0 +1,25 @@ +diff -up a/dwm.c b/dwm.c +--- a/dwm.c 2020-07-09 16:49:10.023585649 +0200 ++++ b/dwm.c 2020-07-09 16:49:43.497542191 +0200 +@@ -702,7 +702,7 @@ drawbar(Monitor *m) + Client *c; + + /* draw status first so it can be overdrawn by tags later */ +- if (m == selmon) { /* status is only drawn on selected monitor */ ++ if (m == selmon || 1) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ + drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0); +@@ -1987,9 +1987,11 @@ updatesizehints(Client *c) + void + updatestatus(void) + { ++ Monitor* m; + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); +- drawbar(selmon); ++ for(m = mons; m; m = m->next) ++ drawbar(m); + } + + void diff --git a/.suckless/st/dwm/patches/dwm-warp-6.4.diff b/.suckless/st/dwm/patches/dwm-warp-6.4.diff new file mode 100755 index 0000000..02fcdba --- /dev/null +++ b/.suckless/st/dwm/patches/dwm-warp-6.4.diff @@ -0,0 +1,79 @@ +From a229c36f51ad6f8b40109ed53c643f242351962a Mon Sep 17 00:00:00 2001 +From: Jonas Dujava +Date: Fri, 26 May 2023 22:14:48 +0200 +Subject: [PATCH] Warp patch + +Warps the mouse cursor to the center of the currently focused +window or screen when the mouse cursor is + (a) on a different screen, or + (b) on top of a different window. + +This version properly handles warping to windows that have not been +mapped yet (before it resulted in a change of the stack order). +See the discussion in (thanks goes to Bakkeby): + https://github.com/bakkeby/patches/issues/60 +--- + dwm.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/dwm.c b/dwm.c +index e5efb6a..7ea6c14 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -228,6 +228,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); +@@ -834,6 +835,7 @@ focusmon(const Arg *arg) + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); ++ warp(selmon->sel); + } + + void +@@ -1366,6 +1368,8 @@ restack(Monitor *m) + wc.sibling = c->win; + } + } ++ if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle) ++ warp(m->sel); + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + } +@@ -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) + { +-- +2.40.1 + diff --git a/.suckless/st/dwm/patches/dwm-xrdb-6.4.diff b/.suckless/st/dwm/patches/dwm-xrdb-6.4.diff new file mode 100755 index 0000000..929b4e6 --- /dev/null +++ b/.suckless/st/dwm/patches/dwm-xrdb-6.4.diff @@ -0,0 +1,203 @@ +From e7c65d2ce902a19a20daa751b42f8ba0209fdb61 Mon Sep 17 00:00:00 2001 +From: NekoCWD +Date: Sun, 22 Jan 2023 23:42:57 +0300 +Subject: [PATCH] [dwm] xrdb update 6.4 + +--- + config.def.h | 22 ++++++++++--------- + drw.c | 2 +- + drw.h | 2 +- + dwm.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 76 insertions(+), 12 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 061ad66..686b947 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -7,15 +7,16 @@ static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; +-static const char col_gray1[] = "#222222"; +-static const char col_gray2[] = "#444444"; +-static const char col_gray3[] = "#bbbbbb"; +-static const char col_gray4[] = "#eeeeee"; +-static const char col_cyan[] = "#005577"; +-static const char *colors[][3] = { +- /* fg bg border */ +- [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, +- [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++static char normbgcolor[] = "#222222"; ++static char normbordercolor[] = "#444444"; ++static char normfgcolor[] = "#bbbbbb"; ++static char selfgcolor[] = "#eeeeee"; ++static char selbordercolor[] = "#005577"; ++static char selbgcolor[] = "#005577"; ++static char *colors[][3] = { ++ /* fg bg border */ ++ [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor }, ++ [SchemeSel] = { selfgcolor, selbgcolor, selbordercolor }, + }; + + /* tagging */ +@@ -56,7 +57,7 @@ static const Layout layouts[] = { + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + + /* commands */ +-static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; ++static const char *dmenucmd[] = { "dmenu_run", "-fn", dmenufont, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbordercolor, "-sf", selfgcolor, NULL }; + static const char *termcmd[] = { "st", NULL }; + + static const Key keys[] = { +@@ -84,6 +85,7 @@ static const Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY, XK_F5, xrdb, {.v = NULL } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff --git a/drw.c b/drw.c +index a58a2b4..f8a82f5 100644 +--- a/drw.c ++++ b/drw.c +@@ -195,7 +195,7 @@ drw_clr_create(Drw *drw, Clr *dest, const char *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) ++drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount) + { + size_t i; + Clr *ret; +diff --git a/drw.h b/drw.h +index 6471431..bdbf950 100644 +--- a/drw.h ++++ b/drw.h +@@ -40,7 +40,7 @@ void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned in + + /* 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); ++Clr *drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount); + + /* Cursor abstraction */ + Cur *drw_cur_create(Drw *drw, int shape); +diff --git a/dwm.c b/dwm.c +index e5efb6a..3fe76be 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -35,6 +35,7 @@ + #include + #include + #include ++#include + #include + #ifdef XINERAMA + #include +@@ -56,6 +57,21 @@ + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) + #define TAGMASK ((1 << LENGTH(tags)) - 1) + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) ++#define XRDB_LOAD_COLOR(R,V) if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) { \ ++ if (value.addr != NULL && strnlen(value.addr, 8) == 7 && value.addr[0] == '#') { \ ++ int i = 1; \ ++ for (; i <= 6; i++) { \ ++ if (value.addr[i] < 48) break; \ ++ if (value.addr[i] > 57 && value.addr[i] < 65) break; \ ++ if (value.addr[i] > 70 && value.addr[i] < 97) break; \ ++ if (value.addr[i] > 102) break; \ ++ } \ ++ if (i == 7) { \ ++ strncpy(V, value.addr, 7); \ ++ V[7] = '\0'; \ ++ } \ ++ } \ ++ } + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +@@ -178,6 +194,7 @@ static void grabkeys(void); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); ++static void loadxrdb(void); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); +@@ -233,6 +250,7 @@ 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 xrdb(const Arg *arg); + static void zoom(const Arg *arg); + + /* variables */ +@@ -1019,6 +1037,37 @@ killclient(const Arg *arg) + } + } + ++void ++loadxrdb() ++{ ++ Display *display; ++ char * resm; ++ XrmDatabase xrdb; ++ char *type; ++ XrmValue value; ++ ++ display = XOpenDisplay(NULL); ++ ++ if (display != NULL) { ++ resm = XResourceManagerString(display); ++ ++ if (resm != NULL) { ++ xrdb = XrmGetStringDatabase(resm); ++ ++ if (xrdb != NULL) { ++ XRDB_LOAD_COLOR("dwm.normbordercolor", normbordercolor); ++ XRDB_LOAD_COLOR("dwm.normbgcolor", normbgcolor); ++ XRDB_LOAD_COLOR("dwm.normfgcolor", normfgcolor); ++ XRDB_LOAD_COLOR("dwm.selbordercolor", selbordercolor); ++ XRDB_LOAD_COLOR("dwm.selbgcolor", selbgcolor); ++ XRDB_LOAD_COLOR("dwm.selfgcolor", selfgcolor); ++ } ++ } ++ } ++ ++ XCloseDisplay(display); ++} ++ + void + manage(Window w, XWindowAttributes *wa) + { +@@ -2110,6 +2159,17 @@ xerrorstart(Display *dpy, XErrorEvent *ee) + return -1; + } + ++void ++xrdb(const Arg *arg) ++{ ++ loadxrdb(); ++ int i; ++ for (i = 0; i < LENGTH(colors); i++) ++ scheme[i] = drw_scm_create(drw, colors[i], 3); ++ focus(NULL); ++ arrange(NULL); ++} ++ + void + zoom(const Arg *arg) + { +@@ -2134,6 +2194,8 @@ main(int argc, char *argv[]) + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + checkotherwm(); ++ XrmInitialize(); ++ loadxrdb(); + setup(); + #ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) +-- +2.38.2 + diff --git a/.suckless/st/dwm/readme.dwm.txt b/.suckless/st/dwm/readme.dwm.txt new file mode 100644 index 0000000..95d4fd0 --- /dev/null +++ b/.suckless/st/dwm/readme.dwm.txt @@ -0,0 +1,48 @@ +dwm - dynamic window manager +============================ +dwm is an extremely fast, small, and dynamic window manager for X. + + +Requirements +------------ +In order to build dwm you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dwm is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dwm (if +necessary as root): + + make clean install + + +Running dwm +----------- +Add the following line to your .xinitrc to start dwm using startx: + + exec dwm + +In order to connect dwm to a specific display, make sure that +the DISPLAY environment variable is set correctly, e.g.: + + DISPLAY=foo.bar:1 exec dwm + +(This will start dwm on display :1 of the host foo.bar.) + +In order to display status info in the bar, you can do something +like this in your .xinitrc: + + while xsetroot -name "`date` `uptime | sed 's/.*,//'`" + do + sleep 1 + done & + exec dwm + + +Configuration +------------- +The configuration of dwm is done by creating a custom config.h +and (re)compiling the source code. diff --git a/.suckless/st/dwm/transient.c b/.suckless/st/dwm/transient.c new file mode 100644 index 0000000..158460f --- /dev/null +++ b/.suckless/st/dwm/transient.c @@ -0,0 +1,43 @@ +/* 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/.suckless/st/dwm/util.c b/.suckless/st/dwm/util.c new file mode 100644 index 0000000..bcecb12 --- /dev/null +++ b/.suckless/st/dwm/util.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "util.h" + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} + +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); +} + diff --git a/.suckless/st/dwm/util.h b/.suckless/st/dwm/util.h new file mode 100644 index 0000000..1e3cf9a --- /dev/null +++ b/.suckless/st/dwm/util.h @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ + +#ifndef MAX +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#endif +#ifndef MIN +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#endif +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +#ifdef _DEBUG +#define DEBUG(...) fprintf(stderr, __VA_ARGS__) +#else +#define DEBUG(...) +#endif + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); + diff --git a/.suckless/st/dwm/util.o b/.suckless/st/dwm/util.o new file mode 100644 index 0000000000000000000000000000000000000000..04939521a548413d374b3f570d141b93a5cb2fc1 GIT binary patch literal 2216 zcmbtV&uddb5T4gWn`)ax5vkNegjY$xK9XRuv?{5Mb}K=uwuoLdOxV#W=HXw_P5|MF%!B^UXK2lbzk<(fQPc z4ow3o8k~h)Poe-n+fnk4@_TW^PbD3LW?an z>{EeAQlAV&Qu?hxWJ)gwBG2^&%n3x+^cAjO>8o77(O+}@PH&*jvL$bS7!8KidSeZ? zbOs&tHd*~MTVB++(cAFqK0)!Z<)j|kNb26GK!RV<0JbzhYGDNIepdg&v4w}8=5X~e ztAAe)VT@z3CRg8j2;C>qbvhCrxXeG<(?+$ME%R15aN~U@ozIst!vN!BW5c2F_@(Jc zC^l%sj94fhjSogo#s{$mcyWbr(QzUNIzVd>CeK_GeivdSha3gk{VSl=f?8j9r*8$x zM-it*LPr3j{@}BYQGf3f&qe>X#`8`Sj&g)PFn-PNBdev*{$BXeY!&qEbhjIIDk+yj zgnCdPRQS9%#17yJ3XZCJ+u+CA;Ndp7(FUhOZ?!KDiuWn|<7b4}NE<#>?Ofi-fRP%D zLN;eY#m<_R1@~slR<3BzqHpERBFvVnb_U8ADp_FGayFRG&A_@O6-VLRojdTm>BySE z=`QTWCGnR6ck|yW{7!}cLBVN8vd%{Zr@JNbO$Ar^TMACkK=OYoc(;Q85jgefQE-pA z*z~<<$vPplNr+ENNZ3HL?In=;TXIMjT7fON*Ab(4 zrruR_p)B& + + 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. +*/ + +//////////////////////////////////////////////////////////////////////////////// +// +// This file implements a subset of the kitty graphics protocol. +// +//////////////////////////////////////////////////////////////////////////////// + +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "graphics.h" +#include "khash.h" +#include "kvec.h" + +extern char **environ; + +#define MAX_FILENAME_SIZE 256 +#define MAX_INFO_LEN 256 +#define MAX_IMAGE_RECTS 20 + +/// The type used in this file to represent time. Used both for time differences +/// and absolute times (as milliseconds since an arbitrary point in time, see +/// `initialization_time`). +typedef int64_t Milliseconds; + +enum ScaleMode { + SCALE_MODE_UNSET = 0, + /// Stretch or shrink the image to fill the box, ignoring aspect ratio. + SCALE_MODE_FILL = 1, + /// Preserve aspect ratio and fit to width or to height so that the + /// whole image is visible. + SCALE_MODE_CONTAIN = 2, + /// Do not scale. The image may be cropped if the box is too small. + SCALE_MODE_NONE = 3, + /// Do not scale, unless the box is too small, in which case the image + /// will be shrunk like with `SCALE_MODE_CONTAIN`. + SCALE_MODE_NONE_OR_CONTAIN = 4, +}; + +enum AnimationState { + ANIMATION_STATE_UNSET = 0, + /// The animation is stopped. Display the current frame, but don't + /// advance to the next one. + ANIMATION_STATE_STOPPED = 1, + /// Run the animation to then end, then wait for the next frame. + ANIMATION_STATE_LOADING = 2, + /// Run the animation in a loop. + ANIMATION_STATE_LOOPING = 3, +}; + +/// The status of an image. Each image uploaded to the terminal is cached on +/// disk, then it is loaded to ram when needed. +enum ImageStatus { + STATUS_UNINITIALIZED = 0, + STATUS_UPLOADING = 1, + STATUS_UPLOADING_ERROR = 2, + STATUS_UPLOADING_SUCCESS = 3, + STATUS_RAM_LOADING_ERROR = 4, + STATUS_RAM_LOADING_IN_PROGRESS = 5, + STATUS_RAM_LOADING_SUCCESS = 6, +}; + +const char *image_status_strings[6] = { + "STATUS_UNINITIALIZED", + "STATUS_UPLOADING", + "STATUS_UPLOADING_ERROR", + "STATUS_UPLOADING_SUCCESS", + "STATUS_RAM_LOADING_ERROR", + "STATUS_RAM_LOADING_SUCCESS", +}; + +enum ImageUploadingFailure { + ERROR_OVER_SIZE_LIMIT = 1, + ERROR_CANNOT_OPEN_CACHED_FILE = 2, + ERROR_UNEXPECTED_SIZE = 3, + ERROR_CANNOT_COPY_FILE = 4, +}; + +const char *image_uploading_failure_strings[5] = { + "NO_ERROR", + "ERROR_OVER_SIZE_LIMIT", + "ERROR_CANNOT_OPEN_CACHED_FILE", + "ERROR_UNEXPECTED_SIZE", + "ERROR_CANNOT_COPY_FILE", +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// We use the following structures to represent images and placements: +// +// - Image: this is the main structure representing an image, usually created +// by actions 'a=t', 'a=T`. Each image has an id (image id aka client id, +// specified by 'i='). An image may have multiple frames (ImageFrame) and +// placements (ImagePlacement). +// +// - ImageFrame: represents a single frame of an image, usually created by +// the action 'a=f' (and the first frame is created with the image itself). +// Each frame has an index and also: +// - a file containing the frame data (considered to be "on disk", although +// it's probably in tmpfs), +// - an imlib object containing the fully composed frame (i.e. the frame +// data from the file composed onto the background frame or color). It is +// not ready for display yet, because it needs to be scaled and uploaded +// to the X server. +// +// - ImagePlacement: represents a placement of an image, created by 'a=p' and +// 'a=T'. Each placement has an id (placement id, specified by 'p='). Also +// each placement has an array of pixmaps: one for each frame of the image. +// Each pixmap is a scaled and uploaded image ready to be displayed. +// +// Images are store in the `images` hash table, mapping image ids to Image +// objects (allocated on the heap). +// +// Placements are stored in the `placements` hash table of each Image object, +// mapping placement ids to ImagePlacement objects (also allocated on the heap). +// +// ImageFrames are stored in the `first_frame` field and in the +// `frames_beyond_the_first` array of each Image object. They are stored by +// value, so ImageFrame pointer may be invalidated when frames are +// added/deleted, be careful. +// +//////////////////////////////////////////////////////////////////////////////// + +struct Image; +struct ImageFrame; +struct ImagePlacement; + +KHASH_MAP_INIT_INT(id2image, struct Image *) +KHASH_MAP_INIT_INT(id2placement, struct ImagePlacement *) + +typedef struct ImageFrame { + /// The image this frame belongs to. + struct Image *image; + /// The 1-based index of the frame. Zero if the frame isn't initialized. + int index; + /// The last time when the frame was displayed or otherwise touched. + Milliseconds atime; + /// The background color of the frame in the 0xRRGGBBAA format. + uint32_t background_color; + /// The index of the background frame. Zero to use the color instead. + int background_frame_index; + /// The duration of the frame in milliseconds. + int gap; + /// The expected size of the frame image file (specified with 'S='), + /// used to check if uploading succeeded. + unsigned expected_size; + /// Format specification (see the `f=` key). + int format; + /// Pixel width and height of the non-composed (on-disk) frame data. May + /// differ from the image (i.e. first frame) dimensions. + int data_pix_width, data_pix_height; + /// The offset of the frame relative to the first frame. + int x, y; + /// Compression mode (see the `o=` key). + char compression; + /// The status (see `ImageStatus`). + char status; + /// The reason of uploading failure (see `ImageUploadingFailure`). + char uploading_failure; + /// Whether failures and successes should be reported ('q='). + char quiet; + /// Whether to blend the frame with the background or replace it. + char blend; + /// The file corresponding to the on-disk cache, used when uploading. + FILE *open_file; + /// The size of the corresponding file cached on disk. + unsigned disk_size; + /// The imlib object containing the fully composed frame. It's not + /// scaled for screen display yet. + Imlib_Image imlib_object; +} ImageFrame; + +typedef struct Image { + /// The client id (the one specified with 'i='). Must be nonzero. + uint32_t image_id; + /// The client id specified in the query command (`a=q`). This one must + /// be used to create the response if it's non-zero. + uint32_t query_id; + /// The number specified in the transmission command (`I=`). If + /// non-zero, it may be used to identify the image instead of the + /// image_id, and it also should be mentioned in responses. + uint32_t image_number; + /// The last time when the image was displayed or otherwise touched. + Milliseconds atime; + /// The total duration of the animation in milliseconds. + int total_duration; + /// The total size of cached image files for all frames. + int total_disk_size; + /// The global index of the creation command. Used to decide which image + /// is newer if they have the same image number. + uint64_t global_command_index; + /// The 1-based index of the currently displayed frame. + int current_frame; + /// The state of the animation, see `AnimationState`. + char animation_state; + /// The absolute time that is assumed to be the start of the current + /// frame (in ms since initialization). + Milliseconds current_frame_time; + /// The absolute time of the last redraw (in ms since initialization). + /// Used to check whether it's the first time we draw the image in the + /// current redraw cycle. + Milliseconds last_redraw; + /// The absolute time of the next redraw (in ms since initialization). + /// 0 means no redraw is scheduled. + Milliseconds next_redraw; + /// The unscaled pixel width and height of the image. Usually inherited + /// from the first frame. + int pix_width, pix_height; + /// The first frame. + ImageFrame first_frame; + /// The array of frames beyond the first one. + kvec_t(ImageFrame) frames_beyond_the_first; + /// Image placements. + khash_t(id2placement) *placements; + /// The default placement. + uint32_t default_placement; + /// The initial placement id, specified with the transmission command, + /// used to report success or failure. + uint32_t initial_placement_id; +} Image; + +typedef struct ImagePlacement { + /// The image this placement belongs to. + Image *image; + /// The id of the placement. Must be nonzero. + uint32_t placement_id; + /// The last time when the placement was displayed or otherwise touched. + Milliseconds atime; + /// The 1-based index of the protected pixmap. We protect a pixmap in + /// gr_load_pixmap to avoid unloading it right after it was loaded. + int protected_frame; + /// Whether the placement is used only for Unicode placeholders. + char virtual; + /// The scaling mode (see `ScaleMode`). + char scale_mode; + /// Height and width in cells. + uint16_t rows, cols; + /// Top-left corner of the source rectangle ('x=' and 'y='). + int src_pix_x, src_pix_y; + /// Height and width of the source rectangle (zero if full image). + int src_pix_width, src_pix_height; + /// The image appropriately scaled and uploaded to the X server. This + /// pixmap is premultiplied by alpha. + Pixmap first_pixmap; + /// The array of pixmaps beyond the first one. + kvec_t(Pixmap) pixmaps_beyond_the_first; + /// The dimensions of the cell used to scale the image. If cell + /// dimensions are changed (font change), the image will be rescaled. + uint16_t scaled_cw, scaled_ch; + /// If true, do not move the cursor when displaying this placement + /// (non-virtual placements only). + char do_not_move_cursor; +} ImagePlacement; + +/// A rectangular piece of an image to be drawn. +typedef struct { + uint32_t image_id; + uint32_t placement_id; + /// The position of the rectangle in pixels. + int screen_x_pix, screen_y_pix; + /// The starting row on the screen. + int screen_y_row; + /// The part of the whole image to be drawn, in cells. Starts are + /// zero-based, ends are exclusive. + int img_start_col, img_end_col, img_start_row, img_end_row; + /// The current cell width and height in pixels. + int cw, ch; + /// Whether colors should be inverted. + int reverse; +} ImageRect; + +/// Executes `code` for each frame of an image. Example: +/// +/// foreach_frame(image, frame, { +/// printf("Frame %d\n", frame->index); +/// }); +/// +#define foreach_frame(image, framevar, code) { size_t __i; \ + for (__i = 0; __i <= kv_size((image).frames_beyond_the_first); ++__i) { \ + ImageFrame *framevar = \ + __i == 0 ? &(image).first_frame \ + : &kv_A((image).frames_beyond_the_first, __i - 1); \ + code; \ + } } + +/// Executes `code` for each pixmap of a placement. Example: +/// +/// foreach_pixmap(placement, pixmap, { +/// ... +/// }); +/// +#define foreach_pixmap(placement, pixmapvar, code) { size_t __i; \ + for (__i = 0; __i <= kv_size((placement).pixmaps_beyond_the_first); ++__i) { \ + Pixmap pixmapvar = \ + __i == 0 ? (placement).first_pixmap \ + : kv_A((placement).pixmaps_beyond_the_first, __i - 1); \ + code; \ + } } + + +static Image *gr_find_image(uint32_t image_id); +static void gr_get_frame_filename(ImageFrame *frame, char *out, size_t max_len); +static void gr_delete_image(Image *img); +static void gr_check_limits(); +static char *gr_base64dec(const char *src, size_t *size); +static void sanitize_str(char *str, size_t max_len); +static const char *sanitized_filename(const char *str); + +/// The array of image rectangles to draw. It is reset each frame. +static ImageRect image_rects[MAX_IMAGE_RECTS] = {{0}}; +/// The known images (including the ones being uploaded). +static khash_t(id2image) *images = NULL; +/// The total number of placements in all images. +static unsigned total_placement_count = 0; +/// The total size of all image files stored in the on-disk cache. +static int64_t images_disk_size = 0; +/// The total size of all images and placements loaded into ram. +static int64_t images_ram_size = 0; +/// The id of the last loaded image. +static uint32_t last_image_id = 0; +/// Current cell width and heigh in pixels. +static int current_cw = 0, current_ch = 0; +/// The id of the currently uploaded image (when using direct uploading). +static uint32_t current_upload_image_id = 0; +/// The index of the frame currently being uploaded. +static int current_upload_frame_index = 0; +/// The time when the graphics module was initialized. +static struct timespec initialization_time = {0}; +/// The time when the current frame drawing started, used for debugging fps and +/// to calculate the current frame for animations. +static Milliseconds drawing_start_time; +/// The global index of the current command. +static uint64_t global_command_counter = 0; +/// The next redraw times for each row of the terminal. Used for animations. +/// 0 means no redraw is scheduled. +static kvec_t(Milliseconds) next_redraw_times = {0, 0, NULL}; +/// The number of files loaded in the current redraw cycle. +static int this_redraw_cycle_loaded_files = 0; +/// The number of pixmaps loaded in the current redraw cycle. +static int this_redraw_cycle_loaded_pixmaps = 0; + +/// The directory where the cache files are stored. +static char cache_dir[MAX_FILENAME_SIZE - 16]; + +/// The table used for color inversion. +static unsigned char reverse_table[256]; + +// Declared in the header. +GraphicsDebugMode graphics_debug_mode = GRAPHICS_DEBUG_NONE; +char graphics_display_images = 1; +GraphicsCommandResult graphics_command_result = {0}; +int graphics_next_redraw_delay = INT_MAX; + +// Defined in config.h +extern const char graphics_cache_dir_template[]; +extern unsigned graphics_max_single_image_file_size; +extern unsigned graphics_total_file_cache_size; +extern unsigned graphics_max_single_image_ram_size; +extern unsigned graphics_max_total_ram_size; +extern unsigned graphics_max_total_placements; +extern double graphics_excess_tolerance_ratio; +extern unsigned graphics_animation_min_delay; + + +//////////////////////////////////////////////////////////////////////////////// +// Basic helpers. +//////////////////////////////////////////////////////////////////////////////// + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) + +/// Returns the difference between `end` and `start` in milliseconds. +static int64_t gr_timediff_ms(const struct timespec *end, + const struct timespec *start) { + return (end->tv_sec - start->tv_sec) * 1000 + + (end->tv_nsec - start->tv_nsec) / 1000000; +} + +/// Returns the current time in milliseconds since the initialization. +static Milliseconds gr_now_ms() { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return gr_timediff_ms(&now, &initialization_time); +} + +//////////////////////////////////////////////////////////////////////////////// +// Logging. +//////////////////////////////////////////////////////////////////////////////// + +#define GR_LOG(...) \ + do { if(graphics_debug_mode) fprintf(stderr, __VA_ARGS__); } while(0) + +//////////////////////////////////////////////////////////////////////////////// +// Basic image management functions (create, delete, find, etc). +//////////////////////////////////////////////////////////////////////////////// + +/// Returns the 1-based index of the last frame. Note that you may want to use +/// `gr_last_uploaded_frame_index` instead since the last frame may be not +/// fully uploaded yet. +static inline int gr_last_frame_index(Image *img) { + return kv_size(img->frames_beyond_the_first) + 1; +} + +/// Returns the frame with the given index. Returns NULL if the index is out of +/// bounds. The index is 1-based. +static ImageFrame *gr_get_frame(Image *img, int index) { + if (!img) + return NULL; + if (index == 1) + return &img->first_frame; + if (2 <= index && index <= gr_last_frame_index(img)) + return &kv_A(img->frames_beyond_the_first, index - 2); + return NULL; +} + +/// Returns the last frame of the image. Returns NULL if `img` is NULL. +static ImageFrame *gr_get_last_frame(Image *img) { + if (!img) + return NULL; + return gr_get_frame(img, gr_last_frame_index(img)); +} + +/// Returns the 1-based index of the last frame or the second-to-last frame if +/// the last frame is not fully uploaded yet. +static inline int gr_last_uploaded_frame_index(Image *img) { + int last_index = gr_last_frame_index(img); + if (last_index > 1 && + gr_get_frame(img, last_index)->status < STATUS_UPLOADING_SUCCESS) + return last_index - 1; + return last_index; +} + +/// Returns the pixmap for the frame with the given index. Returns 0 if the +/// index is out of bounds. The index is 1-based. +static Pixmap gr_get_frame_pixmap(ImagePlacement *placement, int index) { + if (index == 1) + return placement->first_pixmap; + if (2 <= index && + index <= kv_size(placement->pixmaps_beyond_the_first) + 1) + return kv_A(placement->pixmaps_beyond_the_first, index - 2); + return 0; +} + +/// Sets the pixmap for the frame with the given index. The index is 1-based. +/// The array of pixmaps is resized if needed. +static void gr_set_frame_pixmap(ImagePlacement *placement, int index, + Pixmap pixmap) { + if (index == 1) { + placement->first_pixmap = pixmap; + return; + } + // Resize the array if needed. + size_t old_size = kv_size(placement->pixmaps_beyond_the_first); + if (old_size < index - 1) { + kv_a(Pixmap, placement->pixmaps_beyond_the_first, index - 2); + for (size_t i = old_size; i < index - 1; i++) + kv_A(placement->pixmaps_beyond_the_first, i) = 0; + } + kv_A(placement->pixmaps_beyond_the_first, index - 2) = pixmap; +} + +/// Finds the image corresponding to the client id. Returns NULL if cannot find. +static Image *gr_find_image(uint32_t image_id) { + khiter_t k = kh_get(id2image, images, image_id); + if (k == kh_end(images)) + return NULL; + Image *res = kh_value(images, k); + return res; +} + +/// Finds the newest image corresponding to the image number. Returns NULL if +/// cannot find. +static Image *gr_find_image_by_number(uint32_t image_number) { + if (image_number == 0) + return NULL; + Image *newest_img = NULL; + Image *img = NULL; + kh_foreach_value(images, img, { + if (img->image_number == image_number && + (!newest_img || newest_img->global_command_index < + img->global_command_index)) + newest_img = img; + }); + if (!newest_img) + GR_LOG("Image number %u not found\n", image_number); + else + GR_LOG("Found image number %u, its id is %u\n", image_number, + img->image_id); + return newest_img; +} + +/// Finds the placement corresponding to the id. If the placement id is 0, +/// returns some default placement. +static ImagePlacement *gr_find_placement(Image *img, uint32_t placement_id) { + if (!img) + return NULL; + if (placement_id == 0) { + // Try to get the default placement. + ImagePlacement *dflt = NULL; + if (img->default_placement != 0) + dflt = gr_find_placement(img, img->default_placement); + if (dflt) + return dflt; + // If there is no default placement, return the first one and + // set it as the default. + kh_foreach_value(img->placements, dflt, { + img->default_placement = dflt->placement_id; + return dflt; + }); + // If there are no placements, return NULL. + return NULL; + } + khiter_t k = kh_get(id2placement, img->placements, placement_id); + if (k == kh_end(img->placements)) + return NULL; + ImagePlacement *res = kh_value(img->placements, k); + return res; +} + +/// Finds the placement by image id and placement id. +static ImagePlacement *gr_find_image_and_placement(uint32_t image_id, + uint32_t placement_id) { + return gr_find_placement(gr_find_image(image_id), placement_id); +} + +/// Writes the name of the on-disk cache file to `out`. `max_len` should be the +/// size of `out`. The name will be something like +/// "/tmp/st-images-xxx/img-ID-FRAME". +static void gr_get_frame_filename(ImageFrame *frame, char *out, + size_t max_len) { + snprintf(out, max_len, "%s/img-%.3u-%.3u", cache_dir, + frame->image->image_id, frame->index); +} + +/// Returns the (estimation) of the RAM size used by the frame right now. +static unsigned gr_frame_current_ram_size(ImageFrame *frame) { + if (!frame->imlib_object) + return 0; + return (unsigned)frame->image->pix_width * frame->image->pix_height * 4; +} + +/// Returns the (estimation) of the RAM size used by a single frame pixmap. +static unsigned gr_placement_single_frame_ram_size(ImagePlacement *placement) { + return (unsigned)placement->rows * placement->cols * + placement->scaled_ch * placement->scaled_cw * 4; +} + +/// Returns the (estimation) of the RAM size used by the placemenet right now. +static unsigned gr_placement_current_ram_size(ImagePlacement *placement) { + unsigned single_frame_size = + gr_placement_single_frame_ram_size(placement); + unsigned result = 0; + foreach_pixmap(*placement, pixmap, { + if (pixmap) + result += single_frame_size; + }); + return result; +} + +/// Unload the frame from RAM (i.e. delete the corresponding imlib object). +/// If the on-disk file of the frame is preserved, it can be reloaded later. +static void gr_unload_frame(ImageFrame *frame) { + if (!frame->imlib_object) + return; + + unsigned frame_ram_size = gr_frame_current_ram_size(frame); + images_ram_size -= frame_ram_size; + + imlib_context_set_image(frame->imlib_object); + imlib_free_image_and_decache(); + frame->imlib_object = NULL; + + GR_LOG("After unloading image %u frame %u (atime %ld ms ago) " + "ram: %ld KiB (- %u KiB)\n", + frame->image->image_id, frame->index, + drawing_start_time - frame->atime, images_ram_size / 1024, + frame_ram_size / 1024); +} + +/// Unload all frames of the image. +static void gr_unload_all_frames(Image *img) { + foreach_frame(*img, frame, { + gr_unload_frame(frame); + }); +} + +/// Unload the placement from RAM (i.e. free all of the corresponding pixmaps). +/// If the on-disk files or imlib objects of the corresponding image are +/// preserved, the placement can be reloaded later. +static void gr_unload_placement(ImagePlacement *placement) { + unsigned placement_ram_size = gr_placement_current_ram_size(placement); + images_ram_size -= placement_ram_size; + + Display *disp = imlib_context_get_display(); + foreach_pixmap(*placement, pixmap, { + if (pixmap) + XFreePixmap(disp, pixmap); + }); + + placement->first_pixmap = 0; + placement->pixmaps_beyond_the_first.n = 0; + placement->scaled_ch = placement->scaled_cw = 0; + + GR_LOG("After unloading placement %u/%u (atime %ld ms ago) " + "ram: %ld KiB (- %u KiB)\n", + placement->image->image_id, placement->placement_id, + drawing_start_time - placement->atime, images_ram_size / 1024, + placement_ram_size / 1024); +} + +/// Unload a single pixmap of the placement from RAM. +static void gr_unload_pixmap(ImagePlacement *placement, int frameidx) { + Pixmap pixmap = gr_get_frame_pixmap(placement, frameidx); + if (!pixmap) + return; + + Display *disp = imlib_context_get_display(); + XFreePixmap(disp, pixmap); + gr_set_frame_pixmap(placement, frameidx, 0); + images_ram_size -= gr_placement_single_frame_ram_size(placement); + + GR_LOG("After unloading pixmap %ld of " + "placement %u/%u (atime %ld ms ago) " + "frame %u (atime %ld ms ago) " + "ram: %ld KiB (- %u KiB)\n", + pixmap, placement->image->image_id, placement->placement_id, + drawing_start_time - placement->atime, frameidx, + drawing_start_time - + gr_get_frame(placement->image, frameidx)->atime, + images_ram_size / 1024, + gr_placement_single_frame_ram_size(placement) / 1024); +} + +/// Deletes the on-disk cache file corresponding to the frame. The in-ram image +/// object (if it exists) is not deleted, placements are not unloaded either. +static void gr_delete_imagefile(ImageFrame *frame) { + // It may still be being loaded. Close the file in this case. + if (frame->open_file) { + fclose(frame->open_file); + frame->open_file = NULL; + } + + if (frame->disk_size == 0) + return; + + char filename[MAX_FILENAME_SIZE]; + gr_get_frame_filename(frame, filename, MAX_FILENAME_SIZE); + remove(filename); + + unsigned disk_size = frame->disk_size; + images_disk_size -= disk_size; + frame->image->total_disk_size -= disk_size; + frame->disk_size = 0; + + GR_LOG("After deleting image file %u frame %u (atime %ld ms ago) " + "disk: %ld KiB (- %u KiB)\n", + frame->image->image_id, frame->index, + drawing_start_time - frame->atime, images_disk_size / 1024, + disk_size / 1024); +} + +/// Deletes all on-disk cache files of the image (for each frame). +static void gr_delete_imagefiles(Image *img) { + foreach_frame(*img, frame, { + gr_delete_imagefile(frame); + }); +} + +/// Deletes the given placement: unloads, frees the object, but doesn't change +/// the `placements` hash table. +static void gr_delete_placement_keep_id(ImagePlacement *placement) { + if (!placement) + return; + GR_LOG("Deleting placement %u/%u\n", placement->image->image_id, + placement->placement_id); + gr_unload_placement(placement); + kv_destroy(placement->pixmaps_beyond_the_first); + free(placement); + total_placement_count--; +} + +/// Deletes all placements of `img`. +static void gr_delete_all_placements(Image *img) { + ImagePlacement *placement = NULL; + kh_foreach_value(img->placements, placement, { + gr_delete_placement_keep_id(placement); + }); + kh_clear(id2placement, img->placements); +} + +/// Deletes the given image: unloads, deletes the file, frees the Image object, +/// but doesn't change the `images` hash table. +static void gr_delete_image_keep_id(Image *img) { + if (!img) + return; + GR_LOG("Deleting image %u\n", img->image_id); + foreach_frame(*img, frame, { + gr_delete_imagefile(frame); + gr_unload_frame(frame); + }); + kv_destroy(img->frames_beyond_the_first); + gr_delete_all_placements(img); + kh_destroy(id2placement, img->placements); + free(img); +} + +/// Deletes the given image: unloads, deletes the file, frees the Image object, +/// and also removes it from `images`. +static void gr_delete_image(Image *img) { + if (!img) + return; + uint32_t id = img->image_id; + gr_delete_image_keep_id(img); + khiter_t k = kh_get(id2image, images, id); + kh_del(id2image, images, k); +} + +/// Deletes the given placement: unloads, frees the object, and also removes it +/// from `placements`. +static void gr_delete_placement(ImagePlacement *placement) { + if (!placement) + return; + uint32_t id = placement->placement_id; + Image *img = placement->image; + gr_delete_placement_keep_id(placement); + khiter_t k = kh_get(id2placement, img->placements, id); + kh_del(id2placement, img->placements, k); +} + +/// Deletes all images and clears `images`. +static void gr_delete_all_images() { + Image *img = NULL; + kh_foreach_value(images, img, { + gr_delete_image_keep_id(img); + }); + kh_clear(id2image, images); +} + +/// Update the atime of the image. +static void gr_touch_image(Image *img) { + img->atime = gr_now_ms(); +} + +/// Update the atime of the frame. +static void gr_touch_frame(ImageFrame *frame) { + frame->image->atime = frame->atime = gr_now_ms(); +} + +/// Update the atime of the placement. Touches the images too. +static void gr_touch_placement(ImagePlacement *placement) { + placement->image->atime = placement->atime = gr_now_ms(); +} + +/// Creates a new image with the given id. If an image with that id already +/// exists, it is deleted first. If the provided id is 0, generates a +/// random id. +static Image *gr_new_image(uint32_t id) { + if (id == 0) { + do { + id = rand(); + // Avoid IDs that don't need full 32 bits. + } while ((id & 0xFF000000) == 0 || (id & 0x00FFFF00) == 0 || + gr_find_image(id)); + GR_LOG("Generated random image id %u\n", id); + } + Image *img = gr_find_image(id); + gr_delete_image_keep_id(img); + GR_LOG("Creating image %u\n", id); + img = malloc(sizeof(Image)); + memset(img, 0, sizeof(Image)); + img->placements = kh_init(id2placement); + int ret; + khiter_t k = kh_put(id2image, images, id, &ret); + kh_value(images, k) = img; + img->image_id = id; + gr_touch_image(img); + img->global_command_index = global_command_counter; + return img; +} + +/// Creates a new frame at the end of the frame array. It may be the first frame +/// if there are no frames yet. +static ImageFrame *gr_append_new_frame(Image *img) { + ImageFrame *frame = NULL; + if (img->first_frame.index == 0 && + kv_size(img->frames_beyond_the_first) == 0) { + frame = &img->first_frame; + frame->index = 1; + } else { + frame = kv_pushp(ImageFrame, img->frames_beyond_the_first); + memset(frame, 0, sizeof(ImageFrame)); + frame->index = kv_size(img->frames_beyond_the_first) + 1; + } + frame->image = img; + gr_touch_frame(frame); + GR_LOG("Appending frame %d to image %u\n", frame->index, img->image_id); + return frame; +} + +/// Creates a new placement with the given id. If a placement with that id +/// already exists, it is deleted first. If the provided id is 0, generates a +/// random id. +static ImagePlacement *gr_new_placement(Image *img, uint32_t id) { + if (id == 0) { + do { + // Currently we support only 24-bit IDs. + id = rand() & 0xFFFFFF; + // Avoid IDs that need only one byte. + } while ((id & 0x00FFFF00) == 0 || gr_find_placement(img, id)); + } + ImagePlacement *placement = gr_find_placement(img, id); + gr_delete_placement_keep_id(placement); + GR_LOG("Creating placement %u/%u\n", img->image_id, id); + placement = malloc(sizeof(ImagePlacement)); + memset(placement, 0, sizeof(ImagePlacement)); + total_placement_count++; + int ret; + khiter_t k = kh_put(id2placement, img->placements, id, &ret); + kh_value(img->placements, k) = placement; + placement->image = img; + placement->placement_id = id; + gr_touch_placement(placement); + if (img->default_placement == 0) + img->default_placement = id; + return placement; +} + +static int64_t ceil_div(int64_t a, int64_t b) { + return (a + b - 1) / b; +} + +/// Computes the best number of rows and columns for a placement if it's not +/// specified, and also adjusts the source rectangle size. +static void gr_infer_placement_size_maybe(ImagePlacement *placement) { + // The size of the image. + int image_pix_width = placement->image->pix_width; + int image_pix_height = placement->image->pix_height; + // Negative values are not allowed. Quietly set them to 0. + if (placement->src_pix_x < 0) + placement->src_pix_x = 0; + if (placement->src_pix_y < 0) + placement->src_pix_y = 0; + if (placement->src_pix_width < 0) + placement->src_pix_width = 0; + if (placement->src_pix_height < 0) + placement->src_pix_height = 0; + // If the source rectangle is outside the image, truncate it. + if (placement->src_pix_x > image_pix_width) + placement->src_pix_x = image_pix_width; + if (placement->src_pix_y > image_pix_height) + placement->src_pix_y = image_pix_height; + // If the source rectangle is not specified, use the whole image. If + // it's partially outside the image, truncate it. + if (placement->src_pix_width == 0 || + placement->src_pix_x + placement->src_pix_width > image_pix_width) + placement->src_pix_width = + image_pix_width - placement->src_pix_x; + if (placement->src_pix_height == 0 || + placement->src_pix_y + placement->src_pix_height > image_pix_height) + placement->src_pix_height = + image_pix_height - placement->src_pix_y; + + if (placement->cols != 0 && placement->rows != 0) + return; + if (placement->src_pix_width == 0 || placement->src_pix_height == 0) + return; + if (current_cw == 0 || current_ch == 0) + return; + + // If no size is specified, use the image size. + if (placement->cols == 0 && placement->rows == 0) { + placement->cols = + ceil_div(placement->src_pix_width, current_cw); + placement->rows = + ceil_div(placement->src_pix_height, current_ch); + return; + } + + // Some applications specify only one of the dimensions. + if (placement->scale_mode == SCALE_MODE_CONTAIN) { + // If we preserve aspect ratio and fit to width/height, the most + // logical thing is to find the minimum size of the + // non-specified dimension that allows the image to fit the + // specified dimension. + if (placement->cols == 0) { + placement->cols = ceil_div( + placement->src_pix_width * placement->rows * + current_ch, + placement->src_pix_height * current_cw); + return; + } + if (placement->rows == 0) { + placement->rows = + ceil_div(placement->src_pix_height * + placement->cols * current_cw, + placement->src_pix_width * current_ch); + return; + } + } else { + // Otherwise we stretch the image or preserve the original size. + // In both cases we compute the best number of columns from the + // pixel size and cell size. + // TODO: In the case of stretching it's not the most logical + // thing to do, may need to revisit in the future. + // Currently we switch to SCALE_MODE_CONTAIN when only one + // of the dimensions is specified, so this case shouldn't + // happen in practice. + if (!placement->cols) + placement->cols = + ceil_div(placement->src_pix_width, current_cw); + if (!placement->rows) + placement->rows = + ceil_div(placement->src_pix_height, current_ch); + } +} + +/// Adjusts the current frame index if enough time has passed since the display +/// of the current frame. Also computes the time of the next redraw of this +/// image (`img->next_redraw`). The current time is passed as an argument so +/// that all animations are in sync. +static void gr_update_frame_index(Image *img, Milliseconds now) { + if (img->current_frame == 0) { + img->current_frame_time = now; + img->current_frame = 1; + img->next_redraw = now + MAX(1, img->first_frame.gap); + return; + } + // If the animation is stopped, show the current frame. + if (!img->animation_state || + img->animation_state == ANIMATION_STATE_STOPPED || + img->animation_state == ANIMATION_STATE_UNSET) { + // The next redraw is never (unless the state is changed). + img->next_redraw = 0; + return; + } + int last_uploaded_frame_index = gr_last_uploaded_frame_index(img); + // If we are loading and we reached the last frame, show the last frame. + if (img->animation_state == ANIMATION_STATE_LOADING && + img->current_frame == last_uploaded_frame_index) { + // The next redraw is never (unless the state is changed or + // frames are added). + img->next_redraw = 0; + return; + } + + // Check how many milliseconds passed since the current frame was shown. + int passed_ms = now - img->current_frame_time; + // If the animation is looping and too much time has passes, we can + // make a shortcut. + if (img->animation_state == ANIMATION_STATE_LOOPING && + img->total_duration > 0 && passed_ms >= img->total_duration) { + passed_ms %= img->total_duration; + img->current_frame_time = now - passed_ms; + } + // Find the next frame. + int original_frame_index = img->current_frame; + while (1) { + ImageFrame *frame = gr_get_frame(img, img->current_frame); + if (!frame) { + // The frame doesn't exist, go to the first frame. + img->current_frame = 1; + img->current_frame_time = now; + img->next_redraw = now + MAX(1, img->first_frame.gap); + return; + } + if (frame->gap >= 0 && passed_ms < frame->gap) { + // Not enough time has passed, we are still in the same + // frame, and it's not a gapless frame. + img->next_redraw = + img->current_frame_time + MAX(1, frame->gap); + return; + } + // Otherwise go to the next frame. + passed_ms -= MAX(0, frame->gap); + if (img->current_frame >= last_uploaded_frame_index) { + // It's the last frame, if the animation is loading, + // remain on it. + if (img->animation_state == ANIMATION_STATE_LOADING) { + img->next_redraw = 0; + return; + } + // Otherwise the animation is looping. + img->current_frame = 1; + // TODO: Support finite number of loops. + } else { + img->current_frame++; + } + // Make sure we don't get stuck in an infinite loop. + if (img->current_frame == original_frame_index) { + // We looped through all frames, but haven't reached the + // next frame yet. This may happen if too much time has + // passed since the last redraw or all the frames are + // gapless. Just move on to the next frame. + img->current_frame++; + if (img->current_frame > + last_uploaded_frame_index) + img->current_frame = 1; + img->current_frame_time = now; + img->next_redraw = now + MAX( + 1, gr_get_frame(img, img->current_frame)->gap); + return; + } + // Adjust the start time of the frame. The next redraw time will + // be set in the next iteration. + img->current_frame_time += MAX(0, frame->gap); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Unloading and deleting images to save resources. +//////////////////////////////////////////////////////////////////////////////// + +/// A helper to compare frames by atime for qsort. +static int gr_cmp_frames_by_atime(const void *a, const void *b) { + ImageFrame *frame_a = *(ImageFrame *const *)a; + ImageFrame *frame_b = *(ImageFrame *const *)b; + if (frame_a->atime == frame_b->atime) + return frame_a->image->global_command_index - + frame_b->image->global_command_index; + return frame_a->atime - frame_b->atime; +} + +/// A helper to compare images by atime for qsort. +static int gr_cmp_images_by_atime(const void *a, const void *b) { + Image *img_a = *(Image *const *)a; + Image *img_b = *(Image *const *)b; + if (img_a->atime == img_b->atime) + return img_a->global_command_index - + img_b->global_command_index; + return img_a->atime - img_b->atime; +} + +/// A helper to compare placements by atime for qsort. +static int gr_cmp_placements_by_atime(const void *a, const void *b) { + ImagePlacement *p_a = *(ImagePlacement **)a; + ImagePlacement *p_b = *(ImagePlacement **)b; + if (p_a->atime == p_b->atime) + return p_a->image->global_command_index - + p_b->image->global_command_index; + return p_a->atime - p_b->atime; +} + +typedef kvec_t(Image *) ImageVec; +typedef kvec_t(ImagePlacement *) ImagePlacementVec; +typedef kvec_t(ImageFrame *) ImageFrameVec; + +/// Returns an array of pointers to all images sorted by atime. +static ImageVec gr_get_images_sorted_by_atime() { + ImageVec vec; + kv_init(vec); + if (kh_size(images) == 0) + return vec; + kv_resize(Image *, vec, kh_size(images)); + Image *img = NULL; + kh_foreach_value(images, img, { kv_push(Image *, vec, img); }); + qsort(vec.a, kv_size(vec), sizeof(Image *), gr_cmp_images_by_atime); + return vec; +} + +/// Returns an array of pointers to all placements sorted by atime. +static ImagePlacementVec gr_get_placements_sorted_by_atime() { + ImagePlacementVec vec; + kv_init(vec); + if (total_placement_count == 0) + return vec; + kv_resize(ImagePlacement *, vec, total_placement_count); + Image *img = NULL; + ImagePlacement *placement = NULL; + kh_foreach_value(images, img, { + kh_foreach_value(img->placements, placement, { + kv_push(ImagePlacement *, vec, placement); + }); + }); + qsort(vec.a, kv_size(vec), sizeof(ImagePlacement *), + gr_cmp_placements_by_atime); + return vec; +} + +/// Returns an array of pointers to all frames sorted by atime. +static ImageFrameVec gr_get_frames_sorted_by_atime() { + ImageFrameVec frames; + kv_init(frames); + Image *img = NULL; + kh_foreach_value(images, img, { + foreach_frame(*img, frame, { + kv_push(ImageFrame *, frames, frame); + }); + }); + qsort(frames.a, kv_size(frames), sizeof(ImageFrame *), + gr_cmp_frames_by_atime); + return frames; +} + +/// An object that can be unloaded from RAM. +typedef struct { + /// Some score, probably based on access time. The lower the score, the + /// more likely that the object should be unloaded. + int64_t score; + union { + ImagePlacement *placement; + ImageFrame *frame; + }; + /// If zero, the object is the imlib object of `frame`, if non-zero, + /// the object is a pixmap of `frameidx`-th frame of `placement`. + int frameidx; +} UnloadableObject; + +typedef kvec_t(UnloadableObject) UnloadableObjectVec; + +/// A helper to compare unloadable objects by score for qsort. +static int gr_cmp_unloadable_objects(const void *a, const void *b) { + UnloadableObject *obj_a = (UnloadableObject *)a; + UnloadableObject *obj_b = (UnloadableObject *)b; + return obj_a->score - obj_b->score; +} + +/// Unloads an unloadable object from RAM. +static void gr_unload_object(UnloadableObject *obj) { + if (obj->frameidx) { + if (obj->placement->protected_frame == obj->frameidx) + return; + gr_unload_pixmap(obj->placement, obj->frameidx); + } else { + gr_unload_frame(obj->frame); + } +} + +/// Returns the recency threshold for an image. Frames that were accessed within +/// this threshold from now are considered recent and may be handled +/// differently because we may need them again very soon. +static Milliseconds gr_recency_threshold(Image *img) { + return img->total_duration * 2 + 1000; +} + +/// Creates an unloadable object for the imlib object of a frame. +static UnloadableObject gr_unloadable_object_for_frame(Milliseconds now, + ImageFrame *frame) { + UnloadableObject obj = {0}; + obj.frameidx = 0; + obj.frame = frame; + Milliseconds atime = frame->atime; + obj.score = atime; + if (atime >= now - gr_recency_threshold(frame->image)) { + // This is a recent frame, probably from an active animation. + // Score it above `now` to prefer unloading non-active frames. + // Randomize the score because it's not very clear in which + // order we want to unload them: reloading a frame may require + // reloading other frames. + obj.score = now + 1000 + rand() % 1000; + } + return obj; +} + +/// Creates an unloadable object for a pixmap. +static UnloadableObject +gr_unloadable_object_for_pixmap(Milliseconds now, ImageFrame *frame, + ImagePlacement *placement) { + UnloadableObject obj = {0}; + obj.frameidx = frame->index; + obj.placement = placement; + obj.score = placement->atime; + // Since we don't store pixmap atimes, use the + // oldest atime of the frame and the placement. + Milliseconds atime = MIN(placement->atime, frame->atime); + obj.score = atime; + if (atime >= now - gr_recency_threshold(frame->image)) { + // This is a recent pixmap, probably from an active animation. + // Score it above `now` to prefer unloading non-active frames. + // Also assign higher scores to frames that are closer to the + // current frame (more likely to be used soon). + int num_frames = gr_last_frame_index(frame->image); + int dist = frame->index - frame->image->current_frame; + if (dist < 0) + dist += num_frames; + obj.score = + now + 1000 + (num_frames - dist) * 1000 / num_frames; + // If the pixmap is much larger than the imlib image, prefer to + // unload the pixmap by adding up to -1000 to the score. If the + // imlib image is larger, add up to +1000. + float imlib_size = gr_frame_current_ram_size(frame); + float pixmap_size = + gr_placement_single_frame_ram_size(placement); + obj.score += + 2000 * (imlib_size / (imlib_size + pixmap_size) - 0.5); + } + return obj; +} + +/// Returns an array of unloadable objects sorted by score. +static UnloadableObjectVec +gr_get_unloadable_objects_sorted_by_score(Milliseconds now) { + UnloadableObjectVec objects; + kv_init(objects); + Image *img = NULL; + ImagePlacement *placement = NULL; + kh_foreach_value(images, img, { + foreach_frame(*img, frame, { + if (!frame->imlib_object) + continue; + kv_push(UnloadableObject, objects, + gr_unloadable_object_for_frame(now, frame)); + int frameidx = frame->index; + kh_foreach_value(img->placements, placement, { + if (!gr_get_frame_pixmap(placement, frameidx)) + continue; + kv_push(UnloadableObject, objects, + gr_unloadable_object_for_pixmap( + now, frame, placement)); + }); + }); + }); + qsort(objects.a, kv_size(objects), sizeof(UnloadableObject), + gr_cmp_unloadable_objects); + return objects; +} + +/// Returns the limit adjusted by the excess tolerance ratio. +static inline unsigned apply_tolerance(unsigned limit) { + return limit + (unsigned)(limit * graphics_excess_tolerance_ratio); +} + +/// Checks RAM and disk cache limits and deletes/unloads some images. +static void gr_check_limits() { + Milliseconds now = gr_now_ms(); + ImageVec images_sorted = {0}; + ImagePlacementVec placements_sorted = {0}; + ImageFrameVec frames_sorted = {0}; + UnloadableObjectVec objects_sorted = {0}; + int images_begin = 0; + int placements_begin = 0; + char changed = 0; + // First reduce the number of images if there are too many. + if (kh_size(images) > apply_tolerance(graphics_max_total_placements)) { + GR_LOG("Too many images: %d\n", kh_size(images)); + changed = 1; + images_sorted = gr_get_images_sorted_by_atime(); + int to_delete = kv_size(images_sorted) - + graphics_max_total_placements; + for (; images_begin < to_delete; images_begin++) + gr_delete_image(images_sorted.a[images_begin]); + } + // Then reduce the number of placements if there are too many. + if (total_placement_count > + apply_tolerance(graphics_max_total_placements)) { + GR_LOG("Too many placements: %d\n", total_placement_count); + changed = 1; + placements_sorted = gr_get_placements_sorted_by_atime(); + int to_delete = kv_size(placements_sorted) - + graphics_max_total_placements; + for (; placements_begin < to_delete; placements_begin++) { + ImagePlacement *placement = + placements_sorted.a[placements_begin]; + if (placement->protected_frame) + break; + gr_delete_placement(placement); + } + } + // Then reduce the size of the image file cache. The files correspond to + // image frames. + if (images_disk_size > + apply_tolerance(graphics_total_file_cache_size)) { + GR_LOG("Too big disk cache: %ld KiB\n", + images_disk_size / 1024); + changed = 1; + frames_sorted = gr_get_frames_sorted_by_atime(); + for (int i = 0; i < kv_size(frames_sorted); i++) { + if (images_disk_size <= graphics_total_file_cache_size) + break; + gr_delete_imagefile(kv_A(frames_sorted, i)); + } + } + // Then unload images from RAM. + if (images_ram_size > apply_tolerance(graphics_max_total_ram_size)) { + changed = 1; + int frames_begin = 0; + GR_LOG("Too much ram: %ld KiB\n", images_ram_size / 1024); + objects_sorted = gr_get_unloadable_objects_sorted_by_score(now); + for (int i = 0; i < kv_size(objects_sorted); i++) { + if (images_ram_size <= graphics_max_total_ram_size) + break; + gr_unload_object(&kv_A(objects_sorted, i)); + } + } + if (changed) { + GR_LOG("After cleaning: ram: %ld KiB disk: %ld KiB " + "img count: %d placement count: %d\n", + images_ram_size / 1024, images_disk_size / 1024, + kh_size(images), total_placement_count); + } + kv_destroy(images_sorted); + kv_destroy(placements_sorted); + kv_destroy(frames_sorted); + kv_destroy(objects_sorted); +} + +/// Unloads all images by user request. +void gr_unload_images_to_reduce_ram() { + Image *img = NULL; + ImagePlacement *placement = NULL; + kh_foreach_value(images, img, { + kh_foreach_value(img->placements, placement, { + if (placement->protected_frame) + continue; + gr_unload_placement(placement); + }); + gr_unload_all_frames(img); + }); +} + +//////////////////////////////////////////////////////////////////////////////// +// Image loading. +//////////////////////////////////////////////////////////////////////////////// + +/// Copies `num_pixels` pixels (not bytes!) from a buffer `from` to an imlib2 +/// image data `to`. The format may be 24 (RGB) or 32 (RGBA), and it's converted +/// to imlib2's representation, which is 0xAARRGGBB (having BGRA memory layout +/// on little-endian architectures). +static inline void gr_copy_pixels(DATA32 *to, unsigned char *from, int format, + size_t num_pixels) { + size_t pixel_size = format == 24 ? 3 : 4; + if (format == 32) { + for (unsigned i = 0; i < num_pixels; ++i) { + unsigned byte_i = i * pixel_size; + to[i] = ((DATA32)from[byte_i + 2]) | + ((DATA32)from[byte_i + 1]) << 8 | + ((DATA32)from[byte_i]) << 16 | + ((DATA32)from[byte_i + 3]) << 24; + } + } else { + for (unsigned i = 0; i < num_pixels; ++i) { + unsigned byte_i = i * pixel_size; + to[i] = ((DATA32)from[byte_i + 2]) | + ((DATA32)from[byte_i + 1]) << 8 | + ((DATA32)from[byte_i]) << 16 | 0xFF000000; + } + } +} + +/// Loads uncompressed RGB or RGBA image data from a file. +static void gr_load_raw_pixel_data_uncompressed(DATA32 *data, FILE *file, + int format, + size_t total_pixels) { + unsigned char chunk[BUFSIZ]; + size_t pixel_size = format == 24 ? 3 : 4; + size_t chunk_size_pix = BUFSIZ / 4; + size_t chunk_size_bytes = chunk_size_pix * pixel_size; + size_t bytes = total_pixels * pixel_size; + for (size_t chunk_start_pix = 0; chunk_start_pix < total_pixels; + chunk_start_pix += chunk_size_pix) { + size_t read_size = fread(chunk, 1, chunk_size_bytes, file); + size_t read_pixels = read_size / pixel_size; + if (chunk_start_pix + read_pixels > total_pixels) + read_pixels = total_pixels - chunk_start_pix; + gr_copy_pixels(data + chunk_start_pix, chunk, format, + read_pixels); + } +} + +#define COMPRESSED_CHUNK_SIZE BUFSIZ +#define DECOMPRESSED_CHUNK_SIZE (BUFSIZ * 4) + +/// Loads compressed RGB or RGBA image data from a file. +static int gr_load_raw_pixel_data_compressed(DATA32 *data, FILE *file, + int format, size_t total_pixels) { + size_t pixel_size = format == 24 ? 3 : 4; + unsigned char compressed_chunk[COMPRESSED_CHUNK_SIZE]; + unsigned char decompressed_chunk[DECOMPRESSED_CHUNK_SIZE]; + + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.next_out = decompressed_chunk; + strm.avail_out = DECOMPRESSED_CHUNK_SIZE; + strm.avail_in = 0; + strm.next_in = Z_NULL; + int ret = inflateInit(&strm); + if (ret != Z_OK) + return 1; + + int error = 0; + int progress = 0; + size_t total_copied_pixels = 0; + while (1) { + // If we don't have enough data in the input buffer, try to read + // from the file. + if (strm.avail_in <= COMPRESSED_CHUNK_SIZE / 4) { + // Move the existing data to the beginning. + memmove(compressed_chunk, strm.next_in, strm.avail_in); + strm.next_in = compressed_chunk; + // Read more data. + size_t bytes_read = fread( + compressed_chunk + strm.avail_in, 1, + COMPRESSED_CHUNK_SIZE - strm.avail_in, file); + strm.avail_in += bytes_read; + if (bytes_read != 0) + progress = 1; + } + + // Try to inflate the data. + int ret = inflate(&strm, Z_SYNC_FLUSH); + if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR) { + error = 1; + fprintf(stderr, + "error: could not decompress the image, error " + "%s\n", + ret == Z_MEM_ERROR ? "Z_MEM_ERROR" + : "Z_DATA_ERROR"); + break; + } + + // Copy the data from the output buffer to the image. + size_t full_pixels = + (DECOMPRESSED_CHUNK_SIZE - strm.avail_out) / pixel_size; + // Make sure we don't overflow the image. + if (full_pixels > total_pixels - total_copied_pixels) + full_pixels = total_pixels - total_copied_pixels; + if (full_pixels > 0) { + // Copy pixels. + gr_copy_pixels(data, decompressed_chunk, format, + full_pixels); + data += full_pixels; + total_copied_pixels += full_pixels; + if (total_copied_pixels >= total_pixels) { + // We filled the whole image, there may be some + // data left, but we just truncate it. + break; + } + // Move the remaining data to the beginning. + size_t copied_bytes = full_pixels * pixel_size; + size_t leftover = + (DECOMPRESSED_CHUNK_SIZE - strm.avail_out) - + copied_bytes; + memmove(decompressed_chunk, + decompressed_chunk + copied_bytes, leftover); + strm.next_out -= copied_bytes; + strm.avail_out += copied_bytes; + progress = 1; + } + + // If we haven't made any progress, then we have reached the end + // of both the file and the inflated data. + if (!progress) + break; + progress = 0; + } + + inflateEnd(&strm); + return error; +} + +#undef COMPRESSED_CHUNK_SIZE +#undef DECOMPRESSED_CHUNK_SIZE + +/// Load the image from a file containing raw pixel data (RGB or RGBA), the data +/// may be compressed. +static Imlib_Image gr_load_raw_pixel_data(ImageFrame *frame, + const char *filename) { + size_t total_pixels = frame->data_pix_width * frame->data_pix_height; + if (total_pixels * 4 > graphics_max_single_image_ram_size) { + fprintf(stderr, + "error: image %u frame %u is too big too load: %zu > %u\n", + frame->image->image_id, frame->index, total_pixels * 4, + graphics_max_single_image_ram_size); + return NULL; + } + + FILE* file = fopen(filename, "rb"); + if (!file) { + fprintf(stderr, + "error: could not open image file: %s\n", + sanitized_filename(filename)); + return NULL; + } + + Imlib_Image image = imlib_create_image(frame->data_pix_width, + frame->data_pix_height); + if (!image) { + fprintf(stderr, + "error: could not create an image of size %d x %d\n", + frame->data_pix_width, frame->data_pix_height); + fclose(file); + return NULL; + } + + imlib_context_set_image(image); + imlib_image_set_has_alpha(1); + DATA32* data = imlib_image_get_data(); + + // The default format is 32. + int format = frame->format ? frame->format : 32; + + if (frame->compression == 0) { + gr_load_raw_pixel_data_uncompressed(data, file, format, + total_pixels); + } else { + int ret = gr_load_raw_pixel_data_compressed(data, file, format, + total_pixels); + if (ret != 0) { + imlib_image_put_back_data(data); + imlib_free_image(); + fclose(file); + return NULL; + } + } + + fclose(file); + imlib_image_put_back_data(data); + return image; +} + +/// Loads the unscaled frame into RAM as an imlib object. The frame imlib object +/// is fully composed on top of the background frame. If the frame is already +/// loaded, does nothing. Loading may fail, in which case the status of the +/// frame will be set to STATUS_RAM_LOADING_ERROR. +static void gr_load_imlib_object(ImageFrame *frame) { + if (frame->imlib_object) + return; + + // If the image is uninitialized or uploading has failed, or the file + // has been deleted, we cannot load the image. + if (frame->status < STATUS_UPLOADING_SUCCESS) + return; + if (frame->disk_size == 0) { + if (frame->status != STATUS_RAM_LOADING_ERROR) { + fprintf(stderr, + "error: cached image was deleted: %u frame %u\n", + frame->image->image_id, frame->index); + } + frame->status = STATUS_RAM_LOADING_ERROR; + return; + } + + // Prevent recursive dependences between frames. + if (frame->status == STATUS_RAM_LOADING_IN_PROGRESS) { + fprintf(stderr, + "error: recursive loading of image %u frame %u\n", + frame->image->image_id, frame->index); + frame->status = STATUS_RAM_LOADING_ERROR; + return; + } + frame->status = STATUS_RAM_LOADING_IN_PROGRESS; + + // Load the background frame if needed. Hopefully it's not recursive. + ImageFrame *bg_frame = NULL; + if (frame->background_frame_index) { + bg_frame = gr_get_frame(frame->image, + frame->background_frame_index); + if (!bg_frame) { + fprintf(stderr, + "error: could not find background " + "frame %d for image %u frame %d\n", + frame->background_frame_index, + frame->image->image_id, frame->index); + frame->status = STATUS_RAM_LOADING_ERROR; + return; + } + gr_load_imlib_object(bg_frame); + if (!bg_frame->imlib_object) { + fprintf(stderr, + "error: could not load background frame %d for " + "image %u frame %d\n", + frame->background_frame_index, + frame->image->image_id, frame->index); + frame->status = STATUS_RAM_LOADING_ERROR; + return; + } + } + + // Load the frame data image. + Imlib_Image frame_data_image = NULL; + char filename[MAX_FILENAME_SIZE]; + gr_get_frame_filename(frame, filename, MAX_FILENAME_SIZE); + GR_LOG("Loading image: %s\n", sanitized_filename(filename)); + if (frame->format == 100 || frame->format == 0) + frame_data_image = imlib_load_image(filename); + if (frame->format == 32 || frame->format == 24 || + (!frame_data_image && frame->format == 0)) + frame_data_image = gr_load_raw_pixel_data(frame, filename); + this_redraw_cycle_loaded_files++; + + if (!frame_data_image) { + if (frame->status != STATUS_RAM_LOADING_ERROR) { + fprintf(stderr, "error: could not load image: %s\n", + sanitized_filename(filename)); + } + frame->status = STATUS_RAM_LOADING_ERROR; + return; + } + + imlib_context_set_image(frame_data_image); + int frame_data_width = imlib_image_get_width(); + int frame_data_height = imlib_image_get_height(); + GR_LOG("Successfully loaded, size %d x %d\n", frame_data_width, + frame_data_height); + // If imlib loading succeeded, and it is the first frame, set the + // information about the original image size, unless it's already set. + if (frame->index == 1 && frame->image->pix_width == 0 && + frame->image->pix_height == 0) { + frame->image->pix_width = frame_data_width; + frame->image->pix_height = frame_data_height; + } + + int image_width = frame->image->pix_width; + int image_height = frame->image->pix_height; + + // Compose the image with the background color or frame. + if (frame->background_color != 0 || bg_frame || + image_width != frame_data_width || + image_height != frame_data_height) { + GR_LOG("Composing the frame bg = 0x%08X, bgframe = %d\n", + frame->background_color, frame->background_frame_index); + Imlib_Image composed_image = imlib_create_image( + image_width, image_height); + imlib_context_set_image(composed_image); + imlib_image_set_has_alpha(1); + imlib_context_set_anti_alias(0); + + // Start with the background frame or color. + imlib_context_set_blend(0); + if (bg_frame && bg_frame->imlib_object) { + imlib_blend_image_onto_image( + bg_frame->imlib_object, 1, 0, 0, + image_width, image_height, 0, 0, + image_width, image_height); + } else { + int r = (frame->background_color >> 24) & 0xFF; + int g = (frame->background_color >> 16) & 0xFF; + int b = (frame->background_color >> 8) & 0xFF; + int a = frame->background_color & 0xFF; + imlib_context_set_color(r, g, b, a); + imlib_image_fill_rectangle(0, 0, image_width, + image_height); + } + + // Blend the frame data image onto the background. + imlib_context_set_blend(1); + imlib_blend_image_onto_image( + frame_data_image, 1, 0, 0, frame->data_pix_width, + frame->data_pix_height, frame->x, frame->y, + frame->data_pix_width, frame->data_pix_height); + + // Free the frame data image. + imlib_context_set_image(frame_data_image); + imlib_free_image(); + + frame_data_image = composed_image; + } + + frame->imlib_object = frame_data_image; + + images_ram_size += gr_frame_current_ram_size(frame); + frame->status = STATUS_RAM_LOADING_SUCCESS; + + GR_LOG("After loading image %u frame %d ram: %ld KiB (+ %u KiB)\n", + frame->image->image_id, frame->index, + images_ram_size / 1024, gr_frame_current_ram_size(frame) / 1024); +} + +/// Premultiplies the alpha channel of the image data. The data is an array of +/// pixels such that each pixel is a 32-bit integer in the format 0xAARRGGBB. +static void gr_premultiply_alpha(DATA32 *data, size_t num_pixels) { + for (size_t i = 0; i < num_pixels; ++i) { + DATA32 pixel = data[i]; + unsigned char a = pixel >> 24; + if (a == 0) { + data[i] = 0; + } else if (a != 255) { + unsigned char b = (pixel & 0xFF) * a / 255; + unsigned char g = ((pixel >> 8) & 0xFF) * a / 255; + unsigned char r = ((pixel >> 16) & 0xFF) * a / 255; + data[i] = (a << 24) | (r << 16) | (g << 8) | b; + } + } +} + +/// Creates a pixmap for the frame of an image placement. The pixmap contain the +/// image data correctly scaled and fit to the box defined by the number of +/// rows/columns of the image placement and the provided cell dimensions in +/// pixels. If the placement is already loaded, it will be reloaded only if the +/// cell dimensions have changed. +Pixmap gr_load_pixmap(ImagePlacement *placement, int frameidx, int cw, int ch) { + Image *img = placement->image; + ImageFrame *frame = gr_get_frame(img, frameidx); + + // Update the atime uncoditionally. + gr_touch_placement(placement); + if (frame) + gr_touch_frame(frame); + + // If cw or ch are different, unload all the pixmaps. + if (placement->scaled_cw != cw || placement->scaled_ch != ch) { + gr_unload_placement(placement); + placement->scaled_cw = cw; + placement->scaled_ch = ch; + } + + // If it's already loaded, do nothing. + Pixmap pixmap = gr_get_frame_pixmap(placement, frameidx); + if (pixmap) + return pixmap; + + GR_LOG("Loading placement: %u/%u frame %u\n", img->image_id, + placement->placement_id, frameidx); + + // Load the imlib object for the frame. + if (!frame) { + fprintf(stderr, + "error: could not find frame %u for image %u\n", + frameidx, img->image_id); + return 0; + } + gr_load_imlib_object(frame); + if (!frame->imlib_object) + return 0; + + // Infer the placement size if needed. + gr_infer_placement_size_maybe(placement); + + // Create the scaled image. This is temporary, we will scale it + // appropriately, upload to the X server, and then delete immediately. + int scaled_w = (int)placement->cols * cw; + int scaled_h = (int)placement->rows * ch; + if (scaled_w * scaled_h * 4 > graphics_max_single_image_ram_size) { + fprintf(stderr, + "error: placement %u/%u would be too big to load: %d x " + "%d x 4 > %u\n", + img->image_id, placement->placement_id, scaled_w, + scaled_h, graphics_max_single_image_ram_size); + return 0; + } + Imlib_Image scaled_image = imlib_create_image(scaled_w, scaled_h); + if (!scaled_image) { + fprintf(stderr, + "error: imlib_create_image(%d, %d) returned " + "null\n", + scaled_w, scaled_h); + return 0; + } + imlib_context_set_image(scaled_image); + imlib_image_set_has_alpha(1); + + // First fill the scaled image with the transparent color. + imlib_context_set_blend(0); + imlib_context_set_color(0, 0, 0, 0); + imlib_image_fill_rectangle(0, 0, scaled_w, scaled_h); + imlib_context_set_anti_alias(1); + imlib_context_set_blend(1); + + // The source rectangle. + int src_x = placement->src_pix_x; + int src_y = placement->src_pix_y; + int src_w = placement->src_pix_width; + int src_h = placement->src_pix_height; + // Whether the box is too small to use the true size of the image. + char box_too_small = scaled_w < src_w || scaled_h < src_h; + char mode = placement->scale_mode; + + // Then blend the original image onto the transparent background. + if (src_w <= 0 || src_h <= 0) { + fprintf(stderr, "warning: image of zero size\n"); + } else if (mode == SCALE_MODE_FILL) { + imlib_blend_image_onto_image(frame->imlib_object, 1, src_x, + src_y, src_w, src_h, 0, 0, + scaled_w, scaled_h); + } else if (mode == SCALE_MODE_NONE || + (mode == SCALE_MODE_NONE_OR_CONTAIN && !box_too_small)) { + imlib_blend_image_onto_image(frame->imlib_object, 1, src_x, + src_y, src_w, src_h, 0, 0, src_w, + src_h); + } else { + if (mode != SCALE_MODE_CONTAIN && + mode != SCALE_MODE_NONE_OR_CONTAIN) { + fprintf(stderr, + "warning: unknown scale mode %u, using " + "'contain' instead\n", + mode); + } + int dest_x, dest_y; + int dest_w, dest_h; + if (scaled_w * src_h > src_w * scaled_h) { + // If the box is wider than the original image, fit to + // height. + dest_h = scaled_h; + dest_y = 0; + dest_w = src_w * scaled_h / src_h; + dest_x = (scaled_w - dest_w) / 2; + } else { + // Otherwise, fit to width. + dest_w = scaled_w; + dest_x = 0; + dest_h = src_h * scaled_w / src_w; + dest_y = (scaled_h - dest_h) / 2; + } + imlib_blend_image_onto_image(frame->imlib_object, 1, src_x, + src_y, src_w, src_h, dest_x, + dest_y, dest_w, dest_h); + } + + // XRender needs the alpha channel premultiplied. + DATA32 *data = imlib_image_get_data(); + gr_premultiply_alpha(data, scaled_w * scaled_h); + + // Upload the image to the X server. + Display *disp = imlib_context_get_display(); + Visual *vis = imlib_context_get_visual(); + Colormap cmap = imlib_context_get_colormap(); + Drawable drawable = imlib_context_get_drawable(); + if (!drawable) + drawable = DefaultRootWindow(disp); + pixmap = XCreatePixmap(disp, drawable, scaled_w, scaled_h, 32); + XVisualInfo visinfo; + XMatchVisualInfo(disp, DefaultScreen(disp), 32, TrueColor, &visinfo); + XImage *ximage = XCreateImage(disp, visinfo.visual, 32, ZPixmap, 0, + (char *)data, scaled_w, scaled_h, 32, 0); + GC gc = XCreateGC(disp, pixmap, 0, NULL); + XPutImage(disp, pixmap, gc, ximage, 0, 0, 0, 0, scaled_w, + scaled_h); + XFreeGC(disp, gc); + // XDestroyImage will free the data as well, but it is managed by imlib, + // so set it to NULL. + ximage->data = NULL; + XDestroyImage(ximage); + imlib_image_put_back_data(data); + imlib_free_image(); + + // Assign the pixmap to the frame and increase the ram size. + gr_set_frame_pixmap(placement, frameidx, pixmap); + images_ram_size += gr_placement_single_frame_ram_size(placement); + this_redraw_cycle_loaded_pixmaps++; + + GR_LOG("After loading placement %u/%u frame %d ram: %ld KiB (+ %u " + "KiB)\n", + frame->image->image_id, placement->placement_id, frame->index, + images_ram_size / 1024, + gr_placement_single_frame_ram_size(placement) / 1024); + + // Free up ram if needed, but keep the pixmap we've loaded no matter + // what. + placement->protected_frame = frameidx; + gr_check_limits(); + placement->protected_frame = 0; + + return pixmap; +} + +//////////////////////////////////////////////////////////////////////////////// +// Initialization and deinitialization. +//////////////////////////////////////////////////////////////////////////////// + +/// Creates a temporary directory. +static int gr_create_cache_dir() { + strncpy(cache_dir, graphics_cache_dir_template, sizeof(cache_dir)); + if (!mkdtemp(cache_dir)) { + fprintf(stderr, + "error: could not create temporary dir from template " + "%s\n", + sanitized_filename(cache_dir)); + return 0; + } + fprintf(stderr, "Graphics cache directory: %s\n", cache_dir); + return 1; +} + +/// Checks whether `tmp_dir` exists and recreates it if it doesn't. +static void gr_make_sure_tmpdir_exists() { + struct stat st; + if (stat(cache_dir, &st) == 0 && S_ISDIR(st.st_mode)) + return; + fprintf(stderr, + "error: %s is not a directory, will need to create a new " + "graphics cache directory\n", + sanitized_filename(cache_dir)); + gr_create_cache_dir(); +} + +/// Initialize the graphics module. +void gr_init(Display *disp, Visual *vis, Colormap cm) { + // Set the initialization time. + clock_gettime(CLOCK_MONOTONIC, &initialization_time); + + // Create the temporary dir. + if (!gr_create_cache_dir()) + abort(); + + // Initialize imlib. + imlib_context_set_display(disp); + imlib_context_set_visual(vis); + imlib_context_set_colormap(cm); + imlib_context_set_anti_alias(1); + imlib_context_set_blend(1); + // Imlib2 checks only the file name when caching, which is not enough + // for us since we reuse file names. Disable caching. + imlib_set_cache_size(0); + + // Prepare for color inversion. + for (size_t i = 0; i < 256; ++i) + reverse_table[i] = 255 - i; + + // Create data structures. + images = kh_init(id2image); + kv_init(next_redraw_times); + + atexit(gr_deinit); +} + +/// Deinitialize the graphics module. +void gr_deinit() { + // Remove the cache dir. + remove(cache_dir); + kv_destroy(next_redraw_times); + if (images) { + // Delete all images. + gr_delete_all_images(); + // Destroy the data structures. + kh_destroy(id2image, images); + images = NULL; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Dumping, debugging, and image preview. +//////////////////////////////////////////////////////////////////////////////// + +/// Returns a string containing a time difference in a human-readable format. +/// Uses a static buffer, so be careful. +static const char *gr_ago(Milliseconds diff) { + static char result[32]; + double seconds = (double)diff / 1000.0; + if (seconds < 1) + snprintf(result, sizeof(result), "%.2f sec ago", seconds); + else if (seconds < 60) + snprintf(result, sizeof(result), "%d sec ago", (int)seconds); + else if (seconds < 3600) + snprintf(result, sizeof(result), "%d min %d sec ago", + (int)(seconds / 60), (int)(seconds) % 60); + else { + snprintf(result, sizeof(result), "%d hr %d min %d sec ago", + (int)(seconds / 3600), (int)(seconds) % 3600 / 60, + (int)(seconds) % 60); + } + return result; +} + +/// Prints to `file` with an indentation of `ind` spaces. +static void fprintf_ind(FILE *file, int ind, const char *format, ...) { + fprintf(file, "%*s", ind, ""); + va_list args; + va_start(args, format); + vfprintf(file, format, args); + va_end(args); +} + +/// Dumps the image info to `file` with an indentation of `ind` spaces. +static void gr_dump_image_info(FILE *file, Image *img, int ind) { + if (!img) { + fprintf_ind(file, ind, "Image is NULL\n"); + return; + } + Milliseconds now = gr_now_ms(); + fprintf_ind(file, ind, "Image %u\n", img->image_id); + ind += 4; + fprintf_ind(file, ind, "number: %u\n", img->image_number); + fprintf_ind(file, ind, "global command index: %lu\n", + img->global_command_index); + fprintf_ind(file, ind, "accessed: %ld %s\n", img->atime, + gr_ago(now - img->atime)); + fprintf_ind(file, ind, "pix size: %ux%u\n", img->pix_width, + img->pix_height); + fprintf_ind(file, ind, "cur frame start time: %ld %s\n", + img->current_frame_time, + gr_ago(now - img->current_frame_time)); + if (img->next_redraw) + fprintf_ind(file, ind, "next redraw: %ld in %ld ms\n", + img->next_redraw, img->next_redraw - now); + fprintf_ind(file, ind, "total disk size: %u KiB\n", + img->total_disk_size / 1024); + fprintf_ind(file, ind, "total duration: %d\n", img->total_duration); + fprintf_ind(file, ind, "frames: %d\n", gr_last_frame_index(img)); + fprintf_ind(file, ind, "cur frame: %d\n", img->current_frame); + fprintf_ind(file, ind, "animation state: %d\n", img->animation_state); + fprintf_ind(file, ind, "default_placement: %u\n", + img->default_placement); +} + +/// Dumps the frame info to `file` with an indentation of `ind` spaces. +static void gr_dump_frame_info(FILE *file, ImageFrame *frame, int ind) { + if (!frame) { + fprintf_ind(file, ind, "Frame is NULL\n"); + return; + } + Milliseconds now = gr_now_ms(); + fprintf_ind(file, ind, "Frame %d\n", frame->index); + ind += 4; + if (frame->index == 0) { + fprintf_ind(file, ind, "NOT INITIALIZED\n"); + return; + } + if (frame->uploading_failure) + fprintf_ind(file, ind, "uploading failure: %s\n", + image_uploading_failure_strings + [frame->uploading_failure]); + fprintf_ind(file, ind, "gap: %d\n", frame->gap); + fprintf_ind(file, ind, "accessed: %ld %s\n", frame->atime, + gr_ago(now - frame->atime)); + fprintf_ind(file, ind, "data pix size: %ux%u\n", frame->data_pix_width, + frame->data_pix_height); + char filename[MAX_FILENAME_SIZE]; + gr_get_frame_filename(frame, filename, MAX_FILENAME_SIZE); + if (access(filename, F_OK) != -1) + fprintf_ind(file, ind, "file: %s\n", + sanitized_filename(filename)); + else + fprintf_ind(file, ind, "not on disk\n"); + fprintf_ind(file, ind, "disk size: %u KiB\n", frame->disk_size / 1024); + if (frame->imlib_object) { + unsigned ram_size = gr_frame_current_ram_size(frame); + fprintf_ind(file, ind, + "loaded into ram, size: %d " + "KiB\n", + ram_size / 1024); + } else { + fprintf_ind(file, ind, "not loaded into ram\n"); + } +} + +/// Dumps the placement info to `file` with an indentation of `ind` spaces. +static void gr_dump_placement_info(FILE *file, ImagePlacement *placement, + int ind) { + if (!placement) { + fprintf_ind(file, ind, "Placement is NULL\n"); + return; + } + Milliseconds now = gr_now_ms(); + fprintf_ind(file, ind, "Placement %u\n", placement->placement_id); + ind += 4; + fprintf_ind(file, ind, "accessed: %ld %s\n", placement->atime, + gr_ago(now - placement->atime)); + fprintf_ind(file, ind, "scale_mode: %u\n", placement->scale_mode); + fprintf_ind(file, ind, "size: %u cols x %u rows\n", placement->cols, + placement->rows); + fprintf_ind(file, ind, "cell size: %ux%u\n", placement->scaled_cw, + placement->scaled_ch); + fprintf_ind(file, ind, "ram per frame: %u KiB\n", + gr_placement_single_frame_ram_size(placement) / 1024); + unsigned ram_size = gr_placement_current_ram_size(placement); + fprintf_ind(file, ind, "ram size: %d KiB\n", ram_size / 1024); +} + +/// Dumps placement pixmaps to `file` with an indentation of `ind` spaces. +static void gr_dump_placement_pixmaps(FILE *file, ImagePlacement *placement, + int ind) { + if (!placement) + return; + int frameidx = 1; + foreach_pixmap(*placement, pixmap, { + fprintf_ind(file, ind, "Frame %d pixmap %lu\n", frameidx, + pixmap); + ++frameidx; + }); +} + +/// Dumps the internal state (images and placements) to stderr. +void gr_dump_state() { + FILE *file = stderr; + int ind = 0; + fprintf_ind(file, ind, "======= Graphics module state dump =======\n"); + fprintf_ind(file, ind, + "sizeof(Image) = %lu sizeof(ImageFrame) = %lu " + "sizeof(ImagePlacement) = %lu\n", + sizeof(Image), sizeof(ImageFrame), sizeof(ImagePlacement)); + fprintf_ind(file, ind, "Image count: %u\n", kh_size(images)); + fprintf_ind(file, ind, "Placement count: %u\n", total_placement_count); + fprintf_ind(file, ind, "Estimated RAM usage: %ld KiB\n", + images_ram_size / 1024); + fprintf_ind(file, ind, "Estimated Disk usage: %ld KiB\n", + images_disk_size / 1024); + + Milliseconds now = gr_now_ms(); + + int64_t images_ram_size_computed = 0; + int64_t images_disk_size_computed = 0; + + Image *img = NULL; + ImagePlacement *placement = NULL; + kh_foreach_value(images, img, { + fprintf_ind(file, ind, "----------------\n"); + gr_dump_image_info(file, img, 0); + int64_t total_disk_size_computed = 0; + int total_duration_computed = 0; + foreach_frame(*img, frame, { + gr_dump_frame_info(file, frame, 4); + if (frame->image != img) + fprintf_ind(file, 8, + "ERROR: WRONG IMAGE POINTER\n"); + total_duration_computed += frame->gap; + images_disk_size_computed += frame->disk_size; + total_disk_size_computed += frame->disk_size; + if (frame->imlib_object) + images_ram_size_computed += + gr_frame_current_ram_size(frame); + }); + if (img->total_disk_size != total_disk_size_computed) { + fprintf_ind(file, ind, + " ERROR: total_disk_size is %u, but " + "computed value is %ld\n", + img->total_disk_size, total_disk_size_computed); + } + if (img->total_duration != total_duration_computed) { + fprintf_ind(file, ind, + " ERROR: total_duration is %d, but computed " + "value is %d\n", + img->total_duration, total_duration_computed); + } + kh_foreach_value(img->placements, placement, { + gr_dump_placement_info(file, placement, 4); + if (placement->image != img) + fprintf_ind(file, 8, + "ERROR: WRONG IMAGE POINTER\n"); + fprintf_ind(file, 8, + "Pixmaps:\n"); + gr_dump_placement_pixmaps(file, placement, 12); + unsigned ram_size = + gr_placement_current_ram_size(placement); + images_ram_size_computed += ram_size; + }); + }); + if (images_ram_size != images_ram_size_computed) { + fprintf_ind(file, ind, + "ERROR: images_ram_size is %ld, but computed value " + "is %ld\n", + images_ram_size, images_ram_size_computed); + } + if (images_disk_size != images_disk_size_computed) { + fprintf_ind(file, ind, + "ERROR: images_disk_size is %ld, but computed value " + "is %ld\n", + images_disk_size, images_disk_size_computed); + } + fprintf_ind(file, ind, "===========================================\n"); +} + +/// Executes `command` with the name of the file corresponding to `image_id` as +/// the argument. Executes xmessage with an error message on failure. +// TODO: Currently we do this for the first frame only. Not sure what to do with +// animations. +void gr_preview_image(uint32_t image_id, const char *exec) { + char command[256]; + size_t len; + Image *img = gr_find_image(image_id); + if (img) { + ImageFrame *frame = &img->first_frame; + char filename[MAX_FILENAME_SIZE]; + gr_get_frame_filename(frame, filename, MAX_FILENAME_SIZE); + if (frame->disk_size == 0) { + len = snprintf(command, 255, + "xmessage 'Image with id=%u is not " + "fully copied to %s'", + image_id, sanitized_filename(filename)); + } else { + len = snprintf(command, 255, "%s %s &", exec, + sanitized_filename(filename)); + } + } else { + len = snprintf(command, 255, + "xmessage 'Cannot find image with id=%u'", + image_id); + } + if (len > 255) { + fprintf(stderr, "error: command too long: %s\n", command); + snprintf(command, 255, "xmessage 'error: command too long'"); + } + if (system(command) != 0) { + fprintf(stderr, "error: could not execute command %s\n", + command); + } +} + +/// Executes ` -e less ` where is the name of a temporary file +/// containing the information about an image and placement, and is +/// specified with `st_executable`. +void gr_show_image_info(uint32_t image_id, uint32_t placement_id, + uint32_t imgcol, uint32_t imgrow, + char is_classic_placeholder, int32_t diacritic_count, + char *st_executable) { + char filename[MAX_FILENAME_SIZE]; + snprintf(filename, sizeof(filename), "%s/info-%u", cache_dir, image_id); + FILE *file = fopen(filename, "w"); + if (!file) { + perror("fopen"); + return; + } + // Basic information about the cell. + fprintf(file, "image_id = %u = 0x%08X\n", image_id, image_id); + fprintf(file, "placement_id = %u = 0x%08X\n", placement_id, placement_id); + fprintf(file, "column = %d, row = %d\n", imgcol, imgrow); + fprintf(file, "classic/unicode placeholder = %s\n", + is_classic_placeholder ? "classic" : "unicode"); + fprintf(file, "original diacritic count = %d\n", diacritic_count); + // Information about the image and the placement. + Image *img = gr_find_image(image_id); + ImagePlacement *placement = gr_find_placement(img, placement_id); + gr_dump_image_info(file, img, 0); + gr_dump_placement_info(file, placement, 0); + if (img) { + fprintf(file, "Frames:\n"); + foreach_frame(*img, frame, { + gr_dump_frame_info(file, frame, 4); + }); + } + if (placement) { + fprintf(file, "Placement pixmaps:\n"); + gr_dump_placement_pixmaps(file, placement, 4); + } + fclose(file); + char *argv[] = {st_executable, "-e", "less", filename, NULL}; + if (posix_spawnp(NULL, st_executable, NULL, NULL, argv, environ) != 0) { + perror("posix_spawnp"); + return; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Appending and displaying image rectangles. +//////////////////////////////////////////////////////////////////////////////// + +/// Displays debug information in the rectangle using colors col1 and col2. +static void gr_displayinfo(Drawable buf, ImageRect *rect, int col1, int col2, + const char *message) { + int w_pix = (rect->img_end_col - rect->img_start_col) * rect->cw; + int h_pix = (rect->img_end_row - rect->img_start_row) * rect->ch; + Display *disp = imlib_context_get_display(); + GC gc = XCreateGC(disp, buf, 0, NULL); + char info[MAX_INFO_LEN]; + if (rect->placement_id) + snprintf(info, MAX_INFO_LEN, "%s%u/%u [%d:%d)x[%d:%d)", message, + rect->image_id, rect->placement_id, + rect->img_start_col, rect->img_end_col, + rect->img_start_row, rect->img_end_row); + else + snprintf(info, MAX_INFO_LEN, "%s%u [%d:%d)x[%d:%d)", message, + rect->image_id, rect->img_start_col, rect->img_end_col, + rect->img_start_row, rect->img_end_row); + XSetForeground(disp, gc, col1); + XDrawString(disp, buf, gc, rect->screen_x_pix + 4, + rect->screen_y_pix + h_pix - 3, info, strlen(info)); + XSetForeground(disp, gc, col2); + XDrawString(disp, buf, gc, rect->screen_x_pix + 2, + rect->screen_y_pix + h_pix - 5, info, strlen(info)); + XFreeGC(disp, gc); +} + +/// Draws a rectangle (bounding box) for debugging. +static void gr_showrect(Drawable buf, ImageRect *rect) { + int w_pix = (rect->img_end_col - rect->img_start_col) * rect->cw; + int h_pix = (rect->img_end_row - rect->img_start_row) * rect->ch; + Display *disp = imlib_context_get_display(); + GC gc = XCreateGC(disp, buf, 0, NULL); + XSetForeground(disp, gc, 0xFF00FF00); + XDrawRectangle(disp, buf, gc, rect->screen_x_pix, rect->screen_y_pix, + w_pix - 1, h_pix - 1); + XSetForeground(disp, gc, 0xFFFF0000); + XDrawRectangle(disp, buf, gc, rect->screen_x_pix + 1, + rect->screen_y_pix + 1, w_pix - 3, h_pix - 3); + XFreeGC(disp, gc); +} + +/// Updates the next redraw time for the given row. Resizes the +/// next_redraw_times array if needed. +static void gr_update_next_redraw_time(int row, Milliseconds next_redraw) { + if (next_redraw == 0) + return; + if (row >= kv_size(next_redraw_times)) { + size_t old_size = kv_size(next_redraw_times); + kv_a(Milliseconds, next_redraw_times, row); + for (size_t i = old_size; i <= row; ++i) + kv_A(next_redraw_times, i) = 0; + } + Milliseconds old_value = kv_A(next_redraw_times, row); + if (old_value == 0 || old_value > next_redraw) + kv_A(next_redraw_times, row) = next_redraw; +} + +/// Draws the given part of an image. +static void gr_drawimagerect(Drawable buf, ImageRect *rect) { + ImagePlacement *placement = + gr_find_image_and_placement(rect->image_id, rect->placement_id); + // If the image does not exist or image display is switched off, draw + // the bounding box. + if (!placement || !graphics_display_images) { + gr_showrect(buf, rect); + if (graphics_debug_mode == GRAPHICS_DEBUG_LOG_AND_BOXES) + gr_displayinfo(buf, rect, 0xFF000000, 0xFFFFFFFF, ""); + return; + } + + Image *img = placement->image; + + if (img->last_redraw < drawing_start_time) { + // This is the first time we draw this image in this redraw + // cycle. Update the frame index we are going to display. Note + // that currently all image placements are synchronized. + int old_frame = img->current_frame; + gr_update_frame_index(img, drawing_start_time); + img->last_redraw = drawing_start_time; + } + + // Adjust next redraw times for the rows of this image rect. + if (img->next_redraw) { + for (int row = rect->screen_y_row; + row <= rect->screen_y_row + rect->img_end_row - + rect->img_start_row - 1; ++row) { + gr_update_next_redraw_time( + row, img->next_redraw); + } + } + + // Load the frame. + Pixmap pixmap = gr_load_pixmap(placement, img->current_frame, rect->cw, + rect->ch); + + // If the image couldn't be loaded, display the bounding box. + if (!pixmap) { + gr_showrect(buf, rect); + if (graphics_debug_mode == GRAPHICS_DEBUG_LOG_AND_BOXES) + gr_displayinfo(buf, rect, 0xFF000000, 0xFFFFFFFF, ""); + return; + } + + int src_x = rect->img_start_col * rect->cw; + int src_y = rect->img_start_row * rect->ch; + int width = (rect->img_end_col - rect->img_start_col) * rect->cw; + int height = (rect->img_end_row - rect->img_start_row) * rect->ch; + int dst_x = rect->screen_x_pix; + int dst_y = rect->screen_y_pix; + + // Display the image. + Display *disp = imlib_context_get_display(); + Visual *vis = imlib_context_get_visual(); + + // Create an xrender picture for the window. + XRenderPictFormat *win_format = + XRenderFindVisualFormat(disp, vis); + Picture window_pic = + XRenderCreatePicture(disp, buf, win_format, 0, NULL); + + // If needed, invert the image pixmap. Note that this naive approach of + // inverting the pixmap is not entirely correct, because the pixmap is + // premultiplied. But the result is good enough to visually indicate + // selection. + if (rect->reverse) { + unsigned pixmap_w = + (unsigned)placement->cols * placement->scaled_cw; + unsigned pixmap_h = + (unsigned)placement->rows * placement->scaled_ch; + Pixmap invpixmap = + XCreatePixmap(disp, buf, pixmap_w, pixmap_h, 32); + XGCValues gcv = {.function = GXcopyInverted}; + GC gc = XCreateGC(disp, invpixmap, GCFunction, &gcv); + XCopyArea(disp, pixmap, invpixmap, gc, 0, 0, pixmap_w, + pixmap_h, 0, 0); + XFreeGC(disp, gc); + pixmap = invpixmap; + } + + // Create a picture for the image pixmap. + XRenderPictFormat *pic_format = + XRenderFindStandardFormat(disp, PictStandardARGB32); + Picture pixmap_pic = + XRenderCreatePicture(disp, pixmap, pic_format, 0, NULL); + + // Composite the image onto the window. In the reverse mode we ignore + // the alpha channel of the image because the naive inversion above + // seems to invert the alpha channel as well. + int pictop = rect->reverse ? PictOpSrc : PictOpOver; + XRenderComposite(disp, pictop, pixmap_pic, 0, window_pic, + src_x, src_y, src_x, src_y, dst_x, dst_y, width, + height); + + // Free resources + XRenderFreePicture(disp, pixmap_pic); + XRenderFreePicture(disp, window_pic); + if (rect->reverse) + XFreePixmap(disp, pixmap); + + // In debug mode always draw bounding boxes and print info. + if (graphics_debug_mode == GRAPHICS_DEBUG_LOG_AND_BOXES) { + gr_showrect(buf, rect); + gr_displayinfo(buf, rect, 0xFF000000, 0xFFFFFFFF, ""); + } +} + +/// Removes the given image rectangle. +static void gr_freerect(ImageRect *rect) { memset(rect, 0, sizeof(ImageRect)); } + +/// Returns the bottom coordinate of the rect. +static int gr_getrectbottom(ImageRect *rect) { + return rect->screen_y_pix + + (rect->img_end_row - rect->img_start_row) * rect->ch; +} + +/// Prepare for image drawing. `cw` and `ch` are dimensions of the cell. +void gr_start_drawing(Drawable buf, int cw, int ch) { + current_cw = cw; + current_ch = ch; + this_redraw_cycle_loaded_files = 0; + this_redraw_cycle_loaded_pixmaps = 0; + drawing_start_time = gr_now_ms(); + imlib_context_set_drawable(buf); +} + +/// Finish image drawing. This functions will draw all the rectangles left to +/// draw. +void gr_finish_drawing(Drawable buf) { + // Draw and then delete all known image rectangles. + for (size_t i = 0; i < MAX_IMAGE_RECTS; ++i) { + ImageRect *rect = &image_rects[i]; + if (!rect->image_id) + continue; + gr_drawimagerect(buf, rect); + gr_freerect(rect); + } + + // Compute the delay until the next redraw as the minimum of the next + // redraw delays for all rows. + Milliseconds drawing_end_time = gr_now_ms(); + graphics_next_redraw_delay = INT_MAX; + for (int row = 0; row < kv_size(next_redraw_times); ++row) { + Milliseconds row_next_redraw = kv_A(next_redraw_times, row); + if (row_next_redraw > 0) { + int delay = MAX(graphics_animation_min_delay, + row_next_redraw - drawing_end_time); + graphics_next_redraw_delay = + MIN(graphics_next_redraw_delay, delay); + } + } + + // In debug mode display additional info. + if (graphics_debug_mode) { + int milliseconds = drawing_end_time - drawing_start_time; + + Display *disp = imlib_context_get_display(); + GC gc = XCreateGC(disp, buf, 0, NULL); + const char *debug_mode_str = + graphics_debug_mode == GRAPHICS_DEBUG_LOG_AND_BOXES + ? "(boxes shown) " + : ""; + int redraw_delay = graphics_next_redraw_delay == INT_MAX + ? -1 + : graphics_next_redraw_delay; + char info[MAX_INFO_LEN]; + snprintf(info, MAX_INFO_LEN, + "%sRender time: %d ms ram %ld K disk %ld K count " + "%d cell %dx%d delay %d", + debug_mode_str, milliseconds, images_ram_size / 1024, + images_disk_size / 1024, kh_size(images), current_cw, + current_ch, redraw_delay); + XSetForeground(disp, gc, 0xFF000000); + XFillRectangle(disp, buf, gc, 0, 0, 600, 16); + XSetForeground(disp, gc, 0xFFFFFFFF); + XDrawString(disp, buf, gc, 0, 14, info, strlen(info)); + XFreeGC(disp, gc); + + if (milliseconds > 0) { + fprintf(stderr, "%s (loaded %d files, %d pixmaps)\n", + info, this_redraw_cycle_loaded_files, + this_redraw_cycle_loaded_pixmaps); + } + } + + // Check the limits in case we have used too much ram for placements. + gr_check_limits(); +} + +// Add an image rectangle to the list of rectangles to draw. +void gr_append_imagerect(Drawable buf, uint32_t image_id, uint32_t placement_id, + int img_start_col, int img_end_col, int img_start_row, + int img_end_row, int x_col, int y_row, int x_pix, + int y_pix, int cw, int ch, int reverse) { + current_cw = cw; + current_ch = ch; + + ImageRect new_rect; + new_rect.image_id = image_id; + new_rect.placement_id = placement_id; + new_rect.img_start_col = img_start_col; + new_rect.img_end_col = img_end_col; + new_rect.img_start_row = img_start_row; + new_rect.img_end_row = img_end_row; + new_rect.screen_y_row = y_row; + new_rect.screen_x_pix = x_pix; + new_rect.screen_y_pix = y_pix; + new_rect.ch = ch; + new_rect.cw = cw; + new_rect.reverse = reverse; + + // Display some red text in debug mode. + if (graphics_debug_mode == GRAPHICS_DEBUG_LOG_AND_BOXES) + gr_displayinfo(buf, &new_rect, 0xFF000000, 0xFFFF0000, "? "); + + // If it's the empty image (image_id=0) or an empty rectangle, do + // nothing. + if (image_id == 0 || img_end_col - img_start_col <= 0 || + img_end_row - img_start_row <= 0) + return; + // Try to find a rect to merge with. + ImageRect *free_rect = NULL; + for (size_t i = 0; i < MAX_IMAGE_RECTS; ++i) { + ImageRect *rect = &image_rects[i]; + if (rect->image_id == 0) { + if (!free_rect) + free_rect = rect; + continue; + } + if (rect->image_id != image_id || + rect->placement_id != placement_id || rect->cw != cw || + rect->ch != ch || rect->reverse != reverse) + continue; + // We only support the case when the new stripe is added to the + // bottom of an existing rectangle and they are perfectly + // aligned. + if (rect->img_end_row == img_start_row && + gr_getrectbottom(rect) == y_pix) { + if (rect->img_start_col == img_start_col && + rect->img_end_col == img_end_col && + rect->screen_x_pix == x_pix) { + rect->img_end_row = img_end_row; + return; + } + } + } + // If we haven't merged the new rect with any existing rect, and there + // is no free rect, we have to render one of the existing rects. + if (!free_rect) { + for (size_t i = 0; i < MAX_IMAGE_RECTS; ++i) { + ImageRect *rect = &image_rects[i]; + if (!free_rect || gr_getrectbottom(free_rect) > + gr_getrectbottom(rect)) + free_rect = rect; + } + gr_drawimagerect(buf, free_rect); + gr_freerect(free_rect); + } + // Start a new rectangle in `free_rect`. + *free_rect = new_rect; +} + +/// Mark rows containing animations as dirty if it's time to redraw them. Must +/// be called right after `gr_start_drawing`. +void gr_mark_dirty_animations(int *dirty, int rows) { + if (rows < kv_size(next_redraw_times)) + kv_size(next_redraw_times) = rows; + if (rows * 2 < kv_max(next_redraw_times)) + kv_resize(Milliseconds, next_redraw_times, rows); + for (int i = 0; i < MIN(rows, kv_size(next_redraw_times)); ++i) { + if (dirty[i]) { + kv_A(next_redraw_times, i) = 0; + continue; + } + Milliseconds next_update = kv_A(next_redraw_times, i); + if (next_update > 0 && next_update <= drawing_start_time) { + dirty[i] = 1; + kv_A(next_redraw_times, i) = 0; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Command parsing and handling. +//////////////////////////////////////////////////////////////////////////////// + +/// A parsed kitty graphics protocol command. +typedef struct { + /// The command itself, without the 'G'. + char *command; + /// The payload (after ';'). + char *payload; + /// 'a=', may be 't', 'q', 'f', 'T', 'p', 'd', 'a'. + char action; + /// 'q=', 1 to suppress OK response, 2 to suppress errors too. + int quiet; + /// 'f=', use 24 or 32 for raw pixel data, 100 to autodetect with + /// imlib2. If 'f=0', will try to load with imlib2, then fallback to + /// 32-bit pixel data. + int format; + /// 'o=', may be 'z' for RFC 1950 ZLIB. + int compression; + /// 't=', may be 'f', 't' or 'd'. + char transmission_medium; + /// 'd=' + char delete_specifier; + /// 's=', 'v=', if 'a=t' or 'a=T', used only when 'f=24' or 'f=32'. + /// When 'a=f', this is the size of the frame rectangle when composed on + /// top of another frame. + int frame_pix_width, frame_pix_height; + /// 'x=', 'y=' - top-left corner of the source rectangle. + int src_pix_x, src_pix_y; + /// 'w=', 'h=' - width and height of the source rectangle. + int src_pix_width, src_pix_height; + /// 'r=', 'c=' + int rows, columns; + /// 'i=' + uint32_t image_id; + /// 'I=' + uint32_t image_number; + /// 'p=' + uint32_t placement_id; + /// 'm=', may be 0 or 1. + int more; + /// True if either 'm=0' or 'm=1' is specified. + char is_data_transmission; + /// True if turns out that this command is a continuation of a data + /// transmission and not the first one for this image. Populated by + /// `gr_handle_transmit_command`. + char is_direct_transmission_continuation; + /// 'S=', used to check the size of uploaded data. + int size; + /// 'U=', whether it's a virtual placement for Unicode placeholders. + int virtual; + /// 'C=', if true, do not move the cursor when displaying this placement + /// (non-virtual placements only). + char do_not_move_cursor; + // --------------------------------------------------------------------- + // Animation-related fields. Their keys often overlap with keys of other + // commands, so these make sense only if the action is 'a=f' (frame + // transmission) or 'a=a' (animation control). + // + // 'x=' and 'y=', the relative position of the frame image when it's + // composed on top of another frame. + int frame_dst_pix_x, frame_dst_pix_y; + /// 'X=', 'X=1' to replace colors instead of alpha blending on top of + /// the background color or frame. + char replace_instead_of_blending; + /// 'Y=', the background color in the 0xRRGGBBAA format (still + /// transmitted as a decimal number). + uint32_t background_color; + /// (Only for 'a=f'). 'c=', the 1-based index of the background frame. + int background_frame; + /// (Only for 'a=a'). 'c=', sets the index of the current frame. + int current_frame; + /// 'r=', the 1-based index of the frame to edit. + int edit_frame; + /// 'z=', the duration of the frame. Zero if not specified, negative if + /// the frame is gapless (i.e. skipped). + int gap; + /// (Only for 'a=a'). 's=', if non-zero, sets the state of the + /// animation, 1 to stop, 2 to run in loading mode, 3 to loop. + int animation_state; + /// (Only for 'a=a'). 'v=', if non-zero, sets the number of times the + /// animation will loop. 1 to loop infinitely, N to loop N-1 times. + int loops; +} GraphicsCommand; + +/// Replaces all non-printed characters in `str` with '?' and truncates the +/// string to `max_size`, maybe inserting ellipsis at the end. +static void sanitize_str(char *str, size_t max_size) { + assert(max_size >= 4); + for (size_t i = 0; i < max_size; ++i) { + unsigned c = str[i]; + if (c == '\0') + return; + if (c >= 128 || !isprint(c)) + str[i] = '?'; + } + str[max_size - 1] = '\0'; + str[max_size - 2] = '.'; + str[max_size - 3] = '.'; + str[max_size - 4] = '.'; +} + +/// A non-destructive version of `sanitize_str`. Uses a static buffer, so be +/// careful. +static const char *sanitized_filename(const char *str) { + static char buf[MAX_FILENAME_SIZE]; + strncpy(buf, str, sizeof(buf)); + sanitize_str(buf, sizeof(buf)); + return buf; +} + +/// Creates a response to the current command in `graphics_command_result`. +static void gr_createresponse(uint32_t image_id, uint32_t image_number, + uint32_t placement_id, const char *msg) { + if (!image_id && !image_number && !placement_id) { + // Nobody expects the response in this case, so just print it to + // stderr. + fprintf(stderr, + "error: No image id or image number or placement_id, " + "but still there is a response: %s\n", + msg); + return; + } + char *buf = graphics_command_result.response; + size_t maxlen = MAX_GRAPHICS_RESPONSE_LEN; + size_t written; + written = snprintf(buf, maxlen, "\033_G"); + buf += written; + maxlen -= written; + if (image_id) { + written = snprintf(buf, maxlen, "i=%u,", image_id); + buf += written; + maxlen -= written; + } + if (image_number) { + written = snprintf(buf, maxlen, "I=%u,", image_number); + buf += written; + maxlen -= written; + } + if (placement_id) { + written = snprintf(buf, maxlen, "p=%u,", placement_id); + buf += written; + maxlen -= written; + } + buf[-1] = ';'; + written = snprintf(buf, maxlen, "%s\033\\", msg); + buf += written; + maxlen -= written; + buf[-2] = '\033'; + buf[-1] = '\\'; +} + +/// Creates the 'OK' response to the current command, unless suppressed or a +/// non-final data transmission. +static void gr_reportsuccess_cmd(GraphicsCommand *cmd) { + if (cmd->quiet < 1 && !cmd->more) + gr_createresponse(cmd->image_id, cmd->image_number, + cmd->placement_id, "OK"); +} + +/// Creates the 'OK' response to the current command (unless suppressed). +static void gr_reportsuccess_frame(ImageFrame *frame) { + uint32_t id = frame->image->query_id ? frame->image->query_id + : frame->image->image_id; + if (frame->quiet < 1) + gr_createresponse(id, frame->image->image_number, + frame->image->initial_placement_id, "OK"); +} + +/// Creates an error response to the current command (unless suppressed). +static void gr_reporterror_cmd(GraphicsCommand *cmd, const char *format, ...) { + char errmsg[MAX_GRAPHICS_RESPONSE_LEN]; + graphics_command_result.error = 1; + va_list args; + va_start(args, format); + vsnprintf(errmsg, MAX_GRAPHICS_RESPONSE_LEN, format, args); + va_end(args); + + fprintf(stderr, "%s in command: %s\n", errmsg, cmd->command); + if (cmd->quiet < 2) + gr_createresponse(cmd->image_id, cmd->image_number, + cmd->placement_id, errmsg); +} + +/// Creates an error response to the current command (unless suppressed). +static void gr_reporterror_frame(ImageFrame *frame, const char *format, ...) { + char errmsg[MAX_GRAPHICS_RESPONSE_LEN]; + graphics_command_result.error = 1; + va_list args; + va_start(args, format); + vsnprintf(errmsg, MAX_GRAPHICS_RESPONSE_LEN, format, args); + va_end(args); + + if (!frame) { + fprintf(stderr, "%s\n", errmsg); + gr_createresponse(0, 0, 0, errmsg); + } else { + uint32_t id = frame->image->query_id ? frame->image->query_id + : frame->image->image_id; + fprintf(stderr, "%s id=%u\n", errmsg, id); + if (frame->quiet < 2) + gr_createresponse(id, frame->image->image_number, + frame->image->initial_placement_id, + errmsg); + } +} + +/// Loads an image and creates a success/failure response. Returns `frame`, or +/// NULL if it's a query action and the image was deleted. +static ImageFrame *gr_loadimage_and_report(ImageFrame *frame) { + gr_load_imlib_object(frame); + if (!frame->imlib_object) { + gr_reporterror_frame(frame, "EBADF: could not load image"); + } else { + gr_reportsuccess_frame(frame); + } + // If it was a query action, discard the image. + if (frame->image->query_id) { + gr_delete_image(frame->image); + return NULL; + } + return frame; +} + +/// Creates an appropriate uploading failure response to the current command. +static void gr_reportuploaderror(ImageFrame *frame) { + switch (frame->uploading_failure) { + case 0: + return; + case ERROR_CANNOT_OPEN_CACHED_FILE: + gr_reporterror_frame(frame, + "EIO: could not create a file for image"); + break; + case ERROR_OVER_SIZE_LIMIT: + gr_reporterror_frame( + frame, + "EFBIG: the size of the uploaded image exceeded " + "the image size limit %u", + graphics_max_single_image_file_size); + break; + case ERROR_UNEXPECTED_SIZE: + gr_reporterror_frame(frame, + "EINVAL: the size of the uploaded image %u " + "doesn't match the expected size %u", + frame->disk_size, frame->expected_size); + break; + }; +} + +/// Displays a non-virtual placement. This functions records the information in +/// `graphics_command_result`, the placeholder itself is created by the terminal +/// after handling the current command in the graphics module. +static void gr_display_nonvirtual_placement(ImagePlacement *placement) { + if (placement->virtual) + return; + if (placement->image->first_frame.status < STATUS_RAM_LOADING_SUCCESS) + return; + // Infer the placement size if needed. + gr_infer_placement_size_maybe(placement); + // Populate the information about the placeholder which will be created + // by the terminal. + graphics_command_result.create_placeholder = 1; + graphics_command_result.placeholder.image_id = placement->image->image_id; + graphics_command_result.placeholder.placement_id = placement->placement_id; + graphics_command_result.placeholder.columns = placement->cols; + graphics_command_result.placeholder.rows = placement->rows; + graphics_command_result.placeholder.do_not_move_cursor = + placement->do_not_move_cursor; + GR_LOG("Creating a placeholder for %u/%u %d x %d\n", + placement->image->image_id, placement->placement_id, + placement->cols, placement->rows); +} + +/// Marks the rows that are occupied by the image as dirty. +static void gr_schedule_image_redraw(Image *img) { + if (!img) + return; + gr_schedule_image_redraw_by_id(img->image_id); +} + +/// Appends data from `payload` to the frame `frame` when using direct +/// transmission. Note that we report errors only for the final command +/// (`!more`) to avoid spamming the client. If the frame is not specified, use +/// the image id and frame index we are currently uploading. +static void gr_append_data(ImageFrame *frame, const char *payload, int more) { + if (!frame) { + Image *img = gr_find_image(current_upload_image_id); + frame = gr_get_frame(img, current_upload_frame_index); + GR_LOG("Appending data to image %u frame %d\n", + current_upload_image_id, current_upload_frame_index); + if (!img) + GR_LOG("ERROR: this image doesn't exist\n"); + if (!frame) + GR_LOG("ERROR: this frame doesn't exist\n"); + } + if (!more) { + current_upload_image_id = 0; + current_upload_frame_index = 0; + } + if (!frame) { + if (!more) + gr_reporterror_frame(NULL, "ENOENT: could not find the " + "image to append data to"); + return; + } + if (frame->status != STATUS_UPLOADING) { + if (!more) + gr_reportuploaderror(frame); + return; + } + + // Decode the data. + size_t data_size = 0; + char *data = gr_base64dec(payload, &data_size); + + GR_LOG("appending %u + %zu = %zu bytes\n", frame->disk_size, data_size, + frame->disk_size + data_size); + + // Do not append this data if the image exceeds the size limit. + if (frame->disk_size + data_size > + graphics_max_single_image_file_size || + frame->expected_size > graphics_max_single_image_file_size) { + free(data); + gr_delete_imagefile(frame); + frame->uploading_failure = ERROR_OVER_SIZE_LIMIT; + if (!more) + gr_reportuploaderror(frame); + return; + } + + // If there is no open file corresponding to the image, create it. + if (!frame->open_file) { + gr_make_sure_tmpdir_exists(); + char filename[MAX_FILENAME_SIZE]; + gr_get_frame_filename(frame, filename, MAX_FILENAME_SIZE); + FILE *file = fopen(filename, frame->disk_size ? "a" : "w"); + if (!file) { + frame->status = STATUS_UPLOADING_ERROR; + frame->uploading_failure = ERROR_CANNOT_OPEN_CACHED_FILE; + if (!more) + gr_reportuploaderror(frame); + return; + } + frame->open_file = file; + } + + // Write data to the file and update disk size variables. + fwrite(data, 1, data_size, frame->open_file); + free(data); + frame->disk_size += data_size; + frame->image->total_disk_size += data_size; + images_disk_size += data_size; + gr_touch_frame(frame); + + if (more) { + current_upload_image_id = frame->image->image_id; + current_upload_frame_index = frame->index; + } else { + current_upload_image_id = 0; + current_upload_frame_index = 0; + // Close the file. + if (frame->open_file) { + fclose(frame->open_file); + frame->open_file = NULL; + } + frame->status = STATUS_UPLOADING_SUCCESS; + uint32_t placement_id = frame->image->default_placement; + if (frame->expected_size && + frame->expected_size != frame->disk_size) { + // Report failure if the uploaded image size doesn't + // match the expected size. + frame->status = STATUS_UPLOADING_ERROR; + frame->uploading_failure = ERROR_UNEXPECTED_SIZE; + gr_reportuploaderror(frame); + } else { + // Make sure to redraw all existing image instances. + gr_schedule_image_redraw(frame->image); + // Try to load the image into ram and report the result. + frame = gr_loadimage_and_report(frame); + // If there is a non-virtual image placement, we may + // need to display it. + if (frame && frame->index == 1) { + Image *img = frame->image; + ImagePlacement *placement = NULL; + kh_foreach_value(img->placements, placement, { + gr_display_nonvirtual_placement(placement); + }); + } + } + } + + // Check whether we need to delete old images. + gr_check_limits(); +} + +/// Finds the image either by id or by number specified in the command and sets +/// the image_id of `cmd` if the image was found. +static Image *gr_find_image_for_command(GraphicsCommand *cmd) { + if (cmd->image_id) + return gr_find_image(cmd->image_id); + Image *img = NULL; + // If the image number is not specified, we can't find the image, unless + // it's a put command, in which case we will try the last image. + if (cmd->image_number == 0 && cmd->action == 'p') + img = gr_find_image(last_image_id); + else + img = gr_find_image_by_number(cmd->image_number); + if (img) + cmd->image_id = img->image_id; + return img; +} + +/// Creates a new image or a new frame in an existing image (depending on the +/// command's action) and initializes its parameters from the command. +static ImageFrame *gr_new_image_or_frame_from_command(GraphicsCommand *cmd) { + if (cmd->format != 0 && cmd->format != 32 && cmd->format != 24 && + cmd->compression != 0) { + gr_reporterror_cmd(cmd, "EINVAL: compression is supported only " + "for raw pixel data (f=32 or f=24)"); + // Even though we report an error, we still create an image. + } + + Image *img = NULL; + if (cmd->action == 'f') { + // If it's a frame transmission action, there must be an + // existing image. + img = gr_find_image_for_command(cmd); + if (!img) { + gr_reporterror_cmd(cmd, "ENOENT: image not found"); + return NULL; + } + } else { + // Otherwise create a new image object. If the action is `q`, + // we'll use random id instead of the one specified in the + // command. + uint32_t image_id = cmd->action == 'q' ? 0 : cmd->image_id; + img = gr_new_image(image_id); + if (!img) + return NULL; + if (cmd->action == 'q') + img->query_id = cmd->image_id; + else if (!cmd->image_id) + cmd->image_id = img->image_id; + // Set the image number. + img->image_number = cmd->image_number; + } + + ImageFrame *frame = gr_append_new_frame(img); + // Initialize the frame. + frame->expected_size = cmd->size; + frame->format = cmd->format; + frame->compression = cmd->compression; + frame->background_color = cmd->background_color; + frame->background_frame_index = cmd->background_frame; + frame->gap = cmd->gap; + img->total_duration += frame->gap; + frame->blend = !cmd->replace_instead_of_blending; + frame->data_pix_width = cmd->frame_pix_width; + frame->data_pix_height = cmd->frame_pix_height; + if (cmd->action == 'f') { + frame->x = cmd->frame_dst_pix_x; + frame->y = cmd->frame_dst_pix_y; + } + // We save the quietness information in the frame because for direct + // transmission subsequent transmission command won't contain this info. + frame->quiet = cmd->quiet; + return frame; +} + +/// Removes a file if it actually looks like a temporary file. +static void gr_delete_tmp_file(const char *filename) { + if (strstr(filename, "tty-graphics-protocol") == NULL) + return; + if (strstr(filename, "/tmp/") != filename) { + const char *tmpdir = getenv("TMPDIR"); + if (!tmpdir || !tmpdir[0] || + strstr(filename, tmpdir) != filename) + return; + } + unlink(filename); +} + +/// Handles a data transmission command. +static ImageFrame *gr_handle_transmit_command(GraphicsCommand *cmd) { + // The default is direct transmission. + if (!cmd->transmission_medium) + cmd->transmission_medium = 'd'; + + // If neither id, nor image number is specified, and the transmission + // medium is 'd' (or unspecified), and there is an active direct upload, + // this is a continuation of the upload. + if (current_upload_image_id != 0 && cmd->image_id == 0 && + cmd->image_number == 0 && cmd->transmission_medium == 'd') { + cmd->image_id = current_upload_image_id; + GR_LOG("No images id is specified, continuing uploading %u\n", + cmd->image_id); + } + + ImageFrame *frame = NULL; + if (cmd->transmission_medium == 'f' || + cmd->transmission_medium == 't') { + // File transmission. + // Create a new image or a new frame of an existing image. + frame = gr_new_image_or_frame_from_command(cmd); + if (!frame) + return NULL; + last_image_id = frame->image->image_id; + // Decode the filename. + char *original_filename = gr_base64dec(cmd->payload, NULL); + GR_LOG("Copying image %s\n", + sanitized_filename(original_filename)); + // Stat the file and check that it's a regular file and not too + // big. + struct stat st; + int stat_res = stat(original_filename, &st); + const char *stat_error = NULL; + if (stat_res) + stat_error = strerror(errno); + else if (!S_ISREG(st.st_mode)) + stat_error = "Not a regular file"; + else if (st.st_size == 0) + stat_error = "The size of the file is zero"; + else if (st.st_size > graphics_max_single_image_file_size) + stat_error = "The file is too large"; + if (stat_error) { + gr_reporterror_cmd(cmd, + "EBADF: %s", stat_error); + fprintf(stderr, "Could not load the file %s\n", + sanitized_filename(original_filename)); + frame->status = STATUS_UPLOADING_ERROR; + frame->uploading_failure = ERROR_CANNOT_COPY_FILE; + } else { + gr_make_sure_tmpdir_exists(); + // Build the filename for the cached copy of the file. + char cache_filename[MAX_FILENAME_SIZE]; + gr_get_frame_filename(frame, cache_filename, + MAX_FILENAME_SIZE); + // We will create a symlink to the original file, and + // then copy the file to the temporary cache dir. We do + // this symlink trick mostly to be able to use cp for + // copying, and avoid escaping file name characters when + // calling system at the same time. + char tmp_filename_symlink[MAX_FILENAME_SIZE + 4] = {0}; + strcat(tmp_filename_symlink, cache_filename); + strcat(tmp_filename_symlink, ".sym"); + char command[MAX_FILENAME_SIZE + 256]; + size_t len = + snprintf(command, MAX_FILENAME_SIZE + 255, + "cp '%s' '%s'", tmp_filename_symlink, + cache_filename); + if (len > MAX_FILENAME_SIZE + 255 || + symlink(original_filename, tmp_filename_symlink) || + system(command) != 0) { + gr_reporterror_cmd(cmd, + "EBADF: could not copy the " + "image to the cache dir"); + fprintf(stderr, + "Could not copy the image " + "%s (symlink %s) to %s", + sanitized_filename(original_filename), + tmp_filename_symlink, cache_filename); + frame->status = STATUS_UPLOADING_ERROR; + frame->uploading_failure = ERROR_CANNOT_COPY_FILE; + } else { + // Get the file size of the copied file. + frame->status = STATUS_UPLOADING_SUCCESS; + frame->disk_size = st.st_size; + frame->image->total_disk_size += st.st_size; + images_disk_size += frame->disk_size; + if (frame->expected_size && + frame->expected_size != frame->disk_size) { + // The file has unexpected size. + frame->status = STATUS_UPLOADING_ERROR; + frame->uploading_failure = + ERROR_UNEXPECTED_SIZE; + gr_reportuploaderror(frame); + } else { + // Everything seems fine, try to load + // and redraw existing instances. + gr_schedule_image_redraw(frame->image); + frame = gr_loadimage_and_report(frame); + } + } + // Delete the symlink. + unlink(tmp_filename_symlink); + // Delete the original file if it's temporary. + if (cmd->transmission_medium == 't') + gr_delete_tmp_file(original_filename); + } + free(original_filename); + gr_check_limits(); + } else if (cmd->transmission_medium == 'd') { + // Direct transmission (default if 't' is not specified). + frame = gr_get_last_frame(gr_find_image_for_command(cmd)); + if (frame && frame->status == STATUS_UPLOADING) { + // This is a continuation of the previous transmission. + cmd->is_direct_transmission_continuation = 1; + gr_append_data(frame, cmd->payload, cmd->more); + return frame; + } + // If no action is specified, it's not the first transmission + // command. If we couldn't find the image, something went wrong + // and we should just drop this command. + if (cmd->action == 0) + return NULL; + // Otherwise create a new image or frame structure. + frame = gr_new_image_or_frame_from_command(cmd); + if (!frame) + return NULL; + last_image_id = frame->image->image_id; + frame->status = STATUS_UPLOADING; + // Start appending data. + gr_append_data(frame, cmd->payload, cmd->more); + } else { + gr_reporterror_cmd( + cmd, + "EINVAL: transmission medium '%c' is not supported", + cmd->transmission_medium); + return NULL; + } + + return frame; +} + +/// Handles the 'put' command by creating a placement. +static void gr_handle_put_command(GraphicsCommand *cmd) { + if (cmd->image_id == 0 && cmd->image_number == 0) { + gr_reporterror_cmd(cmd, + "EINVAL: neither image id nor image number " + "are specified or both are zero"); + return; + } + + // Find the image with the id or number. + Image *img = gr_find_image_for_command(cmd); + if (!img) { + gr_reporterror_cmd(cmd, "ENOENT: image not found"); + return; + } + + // Create a placement. If a placement with the same id already exists, + // it will be deleted. If the id is zero, a random id will be generated. + ImagePlacement *placement = gr_new_placement(img, cmd->placement_id); + placement->virtual = cmd->virtual; + placement->src_pix_x = cmd->src_pix_x; + placement->src_pix_y = cmd->src_pix_y; + placement->src_pix_width = cmd->src_pix_width; + placement->src_pix_height = cmd->src_pix_height; + placement->cols = cmd->columns; + placement->rows = cmd->rows; + placement->do_not_move_cursor = cmd->do_not_move_cursor; + + if (placement->virtual) { + placement->scale_mode = SCALE_MODE_CONTAIN; + } else if (placement->cols && placement->rows) { + // For classic placements the default is to stretch the image if + // both cols and rows are specified. + placement->scale_mode = SCALE_MODE_FILL; + } else if (placement->cols || placement->rows) { + // But if only one of them is specified, the default is to + // contain. + placement->scale_mode = SCALE_MODE_CONTAIN; + } else { + // If none of them are specified, the default is to use the + // original size. + placement->scale_mode = SCALE_MODE_NONE; + } + + // Display the placement unless it's virtual. + gr_display_nonvirtual_placement(placement); + + // Report success. + gr_reportsuccess_cmd(cmd); +} + +/// Information about what to delete. +typedef struct DeletionData { + uint32_t image_id; + uint32_t placement_id; + /// If true, delete the image object if there are no more placements. + char delete_image_if_no_ref; +} DeletionData; + +/// The callback called for each cell to perform deletion. +static int gr_deletion_callback(void *data, uint32_t image_id, + uint32_t placement_id, int col, + int row, char is_classic) { + DeletionData *del_data = data; + // Leave unicode placeholders alone. + if (!is_classic) + return 0; + if (del_data->image_id && del_data->image_id != image_id) + return 0; + if (del_data->placement_id && del_data->placement_id != placement_id) + return 0; + Image *img = gr_find_image(image_id); + // If the image is already deleted, just erase the placeholder. + if (!img) + return 1; + // Delete the placement. + if (placement_id) + gr_delete_placement(gr_find_placement(img, placement_id)); + // Delete the image if image deletion is requested (uppercase delete + // specifier) and there are no more placements. + if (del_data->delete_image_if_no_ref && kh_size(img->placements) == 0) + gr_delete_image(img); + return 1; +} + +/// Handles the delete command. +static void gr_handle_delete_command(GraphicsCommand *cmd) { + DeletionData del_data = {0}; + del_data.delete_image_if_no_ref = isupper(cmd->delete_specifier) != 0; + char d = tolower(cmd->delete_specifier); + + if (d == 'n') { + d = 'i'; + Image *img = gr_find_image_by_number(cmd->image_number); + if (!img) + return; + del_data.image_id = img->image_id; + } + + if (!d || d == 'a') { + // Delete all visible placements. + gr_for_each_image_cell(gr_deletion_callback, &del_data); + } else if (d == 'i') { + // Delete the specified image by image id and maybe placement + // id. + if (!del_data.image_id) + del_data.image_id = cmd->image_id; + if (!del_data.image_id) { + fprintf(stderr, + "ERROR: image id is not specified in the " + "delete command\n"); + return; + } + del_data.placement_id = cmd->placement_id; + // NOTE: It's not very clear whether we should delete the image + // even if there are no _visible_ placements to delete. We do + // this because otherwise there is no way to delete an image + // with virtual placements in one command. + if (!del_data.placement_id && del_data.delete_image_if_no_ref) + gr_delete_image(gr_find_image(cmd->image_id)); + gr_for_each_image_cell(gr_deletion_callback, &del_data); + } else { + fprintf(stderr, + "WARNING: unsupported value of the d key: '%c'. The " + "command is ignored.\n", + cmd->delete_specifier); + } +} + +static void gr_handle_animation_control_command(GraphicsCommand *cmd) { + if (cmd->image_id == 0 && cmd->image_number == 0) { + gr_reporterror_cmd(cmd, + "EINVAL: neither image id nor image number " + "are specified or both are zero"); + return; + } + + // Find the image with the id or number. + Image *img = gr_find_image_for_command(cmd); + if (!img) { + gr_reporterror_cmd(cmd, "ENOENT: image not found"); + return; + } + + // Find the frame to edit, if requested. + ImageFrame *frame = NULL; + if (cmd->edit_frame) + frame = gr_get_frame(img, cmd->edit_frame); + if (cmd->edit_frame || cmd->gap) { + if (!frame) { + gr_reporterror_cmd(cmd, "ENOENT: frame %d not found", + cmd->edit_frame); + return; + } + if (cmd->gap) { + img->total_duration -= frame->gap; + frame->gap = cmd->gap; + img->total_duration += frame->gap; + } + } + + // Set animation-related parameters of the image. + if (cmd->current_frame) + img->current_frame = cmd->current_frame; + if (cmd->animation_state) { + if (cmd->animation_state == 1) { + img->animation_state = ANIMATION_STATE_STOPPED; + } else if (cmd->animation_state == 2) { + img->animation_state = ANIMATION_STATE_LOADING; + } else if (cmd->animation_state == 3) { + img->animation_state = ANIMATION_STATE_LOOPING; + } else { + gr_reporterror_cmd( + cmd, "EINVAL: invalid animation state: %d", + cmd->animation_state); + } + } + // TODO: Set the number of loops to cmd->loops + + // Make sure we redraw all instances of the image. + gr_schedule_image_redraw(img); +} + +/// Handles a command. +static void gr_handle_command(GraphicsCommand *cmd) { + if (!cmd->image_id && !cmd->image_number) { + // If there is no image id or image number, nobody expects a + // response, so set quiet to 2. + cmd->quiet = 2; + } + ImageFrame *frame = NULL; + switch (cmd->action) { + case 0: + // If no action is specified, it may be a data transmission + // command if 'm=' is specified. + if (cmd->is_data_transmission) { + gr_handle_transmit_command(cmd); + break; + } + gr_reporterror_cmd(cmd, "EINVAL: no action specified"); + break; + case 't': + case 'q': + case 'f': + // Transmit data. 'q' means query, which is basically the same + // as transmit, but the image is discarded, and the id is fake. + // 'f' appends a frame to an existing image. + gr_handle_transmit_command(cmd); + break; + case 'p': + // Display (put) the image. + gr_handle_put_command(cmd); + break; + case 'T': + // Transmit and display. + frame = gr_handle_transmit_command(cmd); + if (frame && !cmd->is_direct_transmission_continuation) { + gr_handle_put_command(cmd); + if (cmd->placement_id) + frame->image->initial_placement_id = + cmd->placement_id; + } + break; + case 'd': + gr_handle_delete_command(cmd); + break; + case 'a': + gr_handle_animation_control_command(cmd); + break; + default: + gr_reporterror_cmd(cmd, "EINVAL: unsupported action: %c", + cmd->action); + return; + } +} + +/// A partially parsed key-value pair. +typedef struct KeyAndValue { + char *key_start; + char *val_start; + unsigned key_len, val_len; +} KeyAndValue; + +/// Parses the value of a key and assigns it to the appropriate field of `cmd`. +static void gr_set_keyvalue(GraphicsCommand *cmd, KeyAndValue *kv) { + char *key_start = kv->key_start; + char *key_end = key_start + kv->key_len; + char *value_start = kv->val_start; + char *value_end = value_start + kv->val_len; + // Currently all keys are one-character. + if (key_end - key_start != 1) { + gr_reporterror_cmd(cmd, "EINVAL: unknown key of length %ld: %s", + key_end - key_start, key_start); + return; + } + long num = 0; + if (*key_start == 'a' || *key_start == 't' || *key_start == 'd' || + *key_start == 'o') { + // Some keys have one-character values. + if (value_end - value_start != 1) { + gr_reporterror_cmd( + cmd, + "EINVAL: value of 'a', 't' or 'd' must be a " + "single char: %s", + key_start); + return; + } + } else { + // All the other keys have integer values. + char *num_end = NULL; + num = strtol(value_start, &num_end, 10); + if (num_end != value_end) { + gr_reporterror_cmd( + cmd, "EINVAL: could not parse number value: %s", + key_start); + return; + } + } + switch (*key_start) { + case 'a': + cmd->action = *value_start; + break; + case 't': + cmd->transmission_medium = *value_start; + break; + case 'd': + cmd->delete_specifier = *value_start; + break; + case 'q': + cmd->quiet = num; + break; + case 'f': + cmd->format = num; + if (num != 0 && num != 24 && num != 32 && num != 100) { + gr_reporterror_cmd( + cmd, + "EINVAL: unsupported format specification: %s", + key_start); + } + break; + case 'o': + cmd->compression = *value_start; + if (cmd->compression != 'z') { + gr_reporterror_cmd(cmd, + "EINVAL: unsupported compression " + "specification: %s", + key_start); + } + break; + case 's': + if (cmd->action == 'a') + cmd->animation_state = num; + else + cmd->frame_pix_width = num; + break; + case 'v': + if (cmd->action == 'a') + cmd->loops = num; + else + cmd->frame_pix_height = num; + break; + case 'i': + cmd->image_id = num; + break; + case 'I': + cmd->image_number = num; + break; + case 'p': + cmd->placement_id = num; + break; + case 'x': + cmd->src_pix_x = num; + cmd->frame_dst_pix_x = num; + break; + case 'y': + if (cmd->action == 'f') + cmd->frame_dst_pix_y = num; + else + cmd->src_pix_y = num; + break; + case 'w': + cmd->src_pix_width = num; + break; + case 'h': + cmd->src_pix_height = num; + break; + case 'c': + if (cmd->action == 'f') + cmd->background_frame = num; + else if (cmd->action == 'a') + cmd->current_frame = num; + else + cmd->columns = num; + break; + case 'r': + if (cmd->action == 'f' || cmd->action == 'a') + cmd->edit_frame = num; + else + cmd->rows = num; + break; + case 'm': + cmd->is_data_transmission = 1; + cmd->more = num; + break; + case 'S': + cmd->size = num; + break; + case 'U': + cmd->virtual = num; + break; + case 'X': + if (cmd->action == 'f') + cmd->replace_instead_of_blending = num; + else + break; /*ignore*/ + break; + case 'Y': + if (cmd->action == 'f') + cmd->background_color = num; + else + break; /*ignore*/ + break; + case 'z': + if (cmd->action == 'f' || cmd->action == 'a') + cmd->gap = num; + else + break; /*ignore*/ + break; + case 'C': + cmd->do_not_move_cursor = num; + break; + default: + gr_reporterror_cmd(cmd, "EINVAL: unsupported key: %s", + key_start); + return; + } +} + +/// Parse and execute a graphics command. `buf` must start with 'G' and contain +/// at least `len + 1` characters. Returns 1 on success. +int gr_parse_command(char *buf, size_t len) { + if (buf[0] != 'G') + return 0; + + memset(&graphics_command_result, 0, sizeof(GraphicsCommandResult)); + + global_command_counter++; + GR_LOG("### Command %lu: %.80s\n", global_command_counter, buf); + + // Eat the 'G'. + ++buf; + --len; + + GraphicsCommand cmd = {.command = buf}; + // The state of parsing. 'k' to parse key, 'v' to parse value, 'p' to + // parse the payload. + char state = 'k'; + // An array of partially parsed key-value pairs. + KeyAndValue key_vals[32]; + unsigned key_vals_count = 0; + char *key_start = buf; + char *key_end = NULL; + char *val_start = NULL; + char *val_end = NULL; + char *c = buf; + while (c - buf < len + 1) { + if (state == 'k') { + switch (*c) { + case ',': + case ';': + case '\0': + state = *c == ',' ? 'k' : 'p'; + key_end = c; + gr_reporterror_cmd( + &cmd, "EINVAL: key without value: %s ", + key_start); + break; + case '=': + key_end = c; + state = 'v'; + val_start = c + 1; + break; + default: + break; + } + } else if (state == 'v') { + switch (*c) { + case ',': + case ';': + case '\0': + state = *c == ',' ? 'k' : 'p'; + val_end = c; + if (key_vals_count >= + sizeof(key_vals) / sizeof(*key_vals)) { + gr_reporterror_cmd(&cmd, + "EINVAL: too many " + "key-value pairs"); + break; + } + key_vals[key_vals_count].key_start = key_start; + key_vals[key_vals_count].val_start = val_start; + key_vals[key_vals_count].key_len = + key_end - key_start; + key_vals[key_vals_count].val_len = + val_end - val_start; + ++key_vals_count; + key_start = c + 1; + break; + default: + break; + } + } else if (state == 'p') { + cmd.payload = c; + // break out of the loop, we don't check the payload + break; + } + ++c; + } + + // Set the action key ('a=') first because we need it to disambiguate + // some keys. Also set 'i=' and 'I=' for better error reporting. + for (unsigned i = 0; i < key_vals_count; ++i) { + if (key_vals[i].key_len == 1) { + char *start = key_vals[i].key_start; + if (*start == 'a' || *start == 'i' || *start == 'I') { + gr_set_keyvalue(&cmd, &key_vals[i]); + break; + } + } + } + // Set the rest of the keys. + for (unsigned i = 0; i < key_vals_count; ++i) + gr_set_keyvalue(&cmd, &key_vals[i]); + + if (!cmd.payload) + cmd.payload = buf + len; + + if (cmd.payload && cmd.payload[0]) + GR_LOG(" payload size: %ld\n", strlen(cmd.payload)); + + if (!graphics_command_result.error) + gr_handle_command(&cmd); + + if (graphics_debug_mode) { + fprintf(stderr, "Response: "); + for (const char *resp = graphics_command_result.response; + *resp != '\0'; ++resp) { + if (isprint(*resp)) + fprintf(stderr, "%c", *resp); + else + fprintf(stderr, "(0x%x)", *resp); + } + fprintf(stderr, "\n"); + } + + // Make sure that we suppress response if needed. Usually cmd.quiet is + // taken into account when creating the response, but it's not very + // reliable in the current implementation. + if (cmd.quiet) { + if (!graphics_command_result.error || cmd.quiet >= 2) + graphics_command_result.response[0] = '\0'; + } + + return 1; +} + +//////////////////////////////////////////////////////////////////////////////// +// base64 decoding part is basically copied from st.c +//////////////////////////////////////////////////////////////////////////////// + +static const char gr_base64_digits[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static char gr_base64_getc(const char **src) { + while (**src && !isprint(**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char *gr_base64dec(const char *src, size_t *size) { + size_t in_len = strlen(src); + char *result, *dst; + + result = dst = malloc((in_len + 3) / 4 * 3 + 1); + while (*src) { + int a = gr_base64_digits[(unsigned char)gr_base64_getc(&src)]; + int b = gr_base64_digits[(unsigned char)gr_base64_getc(&src)]; + int c = gr_base64_digits[(unsigned char)gr_base64_getc(&src)]; + int d = gr_base64_digits[(unsigned char)gr_base64_getc(&src)]; + + if (a == -1 || b == -1) + break; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + if (size) { + *size = dst - result; + } + return result; +} diff --git a/.suckless/st/graphics.h b/.suckless/st/graphics.h new file mode 100644 index 0000000..2e75dea --- /dev/null +++ b/.suckless/st/graphics.h @@ -0,0 +1,107 @@ + +#include +#include +#include + +/// Initialize the graphics module. +void gr_init(Display *disp, Visual *vis, Colormap cm); +/// Deinitialize the graphics module. +void gr_deinit(); + +/// Add an image rectangle to a list if rectangles to draw. This function may +/// actually draw some rectangles, or it may wait till more rectangles are +/// appended. Must be called between `gr_start_drawing` and `gr_finish_drawing`. +/// - `img_start_col..img_end_col` and `img_start_row..img_end_row` define the +/// part of the image to draw (row/col indices are zero-based, ends are +/// excluded). +/// - `x_col` and `y_row` are the coordinates of the top-left corner of the +/// image in the terminal grid. +/// - `x_pix` and `y_pix` are the same but in pixels. +/// - `reverse` indicates whether colors should be inverted. +void gr_append_imagerect(Drawable buf, uint32_t image_id, uint32_t placement_id, + int img_start_col, int img_end_col, int img_start_row, + int img_end_row, int x_col, int y_row, int x_pix, + int y_pix, int cw, int ch, int reverse); +/// Prepare for image drawing. `cw` and `ch` are dimensions of the cell. +void gr_start_drawing(Drawable buf, int cw, int ch); +/// Finish image drawing. This functions will draw all the rectangles left to +/// draw. +void gr_finish_drawing(Drawable buf); +/// Mark rows containing animations as dirty if it's time to redraw them. Must +/// be called right after `gr_start_drawing`. +void gr_mark_dirty_animations(int *dirty, int rows); + +/// Parse and execute a graphics command. `buf` must start with 'G' and contain +/// at least `len + 1` characters (including '\0'). Returns 1 on success. +/// Additional informations is returned through `graphics_command_result`. +int gr_parse_command(char *buf, size_t len); + +/// Executes `command` with the name of the file corresponding to `image_id` as +/// the argument. Executes xmessage with an error message on failure. +void gr_preview_image(uint32_t image_id, const char *command); + +/// Executes ` -e less ` where is the name of a temporary file +/// containing the information about an image and placement, and is +/// specified with `st_executable`. +void gr_show_image_info(uint32_t image_id, uint32_t placement_id, + uint32_t imgcol, uint32_t imgrow, + char is_classic_placeholder, int32_t diacritic_count, + char *st_executable); + +/// Dumps the internal state (images and placements) to stderr. +void gr_dump_state(); + +/// Unloads images to reduce RAM usage. +void gr_unload_images_to_reduce_ram(); + +/// Executes `callback` for each image cell. `callback` may return 1 to erase +/// the cell or 0 to keep it. This function is implemented in `st.c`. +void gr_for_each_image_cell(int (*callback)(void *data, uint32_t image_id, + uint32_t placement_id, int col, + int row, char is_classic), + void *data); + +/// Marks all the rows containing the image with `image_id` as dirty. +void gr_schedule_image_redraw_by_id(uint32_t image_id); + +typedef enum { + GRAPHICS_DEBUG_NONE = 0, + GRAPHICS_DEBUG_LOG = 1, + GRAPHICS_DEBUG_LOG_AND_BOXES = 2, +} GraphicsDebugMode; + +/// Print additional information, draw bounding bounding boxes, etc. +extern GraphicsDebugMode graphics_debug_mode; + +/// Whether to display images or just draw bounding boxes. +extern char graphics_display_images; + +/// The time in milliseconds until the next redraw to update animations. +/// INT_MAX means no redraw is needed. Populated by `gr_finish_drawing`. +extern int graphics_next_redraw_delay; + +#define MAX_GRAPHICS_RESPONSE_LEN 256 + +/// A structure representing the result of a graphics command. +typedef struct { + /// Indicates if the terminal needs to be redrawn. + char redraw; + /// The response of the command that should be sent back to the client + /// (may be empty if the quiet flag is set). + char response[MAX_GRAPHICS_RESPONSE_LEN]; + /// Whether there was an error executing this command (not very useful, + /// the response must be sent back anyway). + char error; + /// Whether the terminal has to create a placeholder for a non-virtual + /// placement. + char create_placeholder; + /// The placeholder that needs to be created. + struct { + uint32_t rows, columns; + uint32_t image_id, placement_id; + char do_not_move_cursor; + } placeholder; +} GraphicsCommandResult; + +/// The result of a graphics command. +extern GraphicsCommandResult graphics_command_result; diff --git a/.suckless/st/graphics.o b/.suckless/st/graphics.o new file mode 100644 index 0000000000000000000000000000000000000000..73551f99d237c95f40e06dc7a8f04b8dc01c0ae8 GIT binary patch literal 82328 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%0D@@0&KR`i0`cv^$^RF2;i7Uq8I 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 exit 1" INT + +cols="" +rows="" +file="" +tty="/dev/tty" +uploading_method="auto" +cell_size="" +scale=1 +max_cols="" +max_rows="" +speed="" + +# Parse the command line. +while [ $# -gt 0 ]; do + case "$1" in + -c|--columns|--cols) + cols="$2" + shift 2 + ;; + -r|--rows|-l|--lines) + rows="$2" + shift 2 + ;; + -s|--scale) + scale="$2" + shift 2 + ;; + -h|--help) + echo "$short_help" + exit 0 + ;; + -m|--upload-method|--uploading-method) + uploading_method="$2" + shift 2 + ;; + --cell-size) + cell_size="$2" + shift 2 + ;; + --max-cols) + max_cols="$2" + shift 2 + ;; + --max-rows) + max_rows="$2" + shift 2 + ;; + --speed) + speed="$2" + shift 2 + ;; + --) + file="$2" + shift 2 + ;; + -*) + echo "Unknown option: $1" >&2 + exit 1 + ;; + *) + if [ -n "$file" ]; then + echo "Multiple image files are not supported: $file and $1" >&2 + exit 1 + fi + file="$1" + shift + ;; + esac +done + +file="$(realpath "$file")" + +##################################################################### +# Adjust the terminal state +##################################################################### + +stty_orig="$(stty -g < "$tty")" +stty -echo < "$tty" +# Disable ctrl-z. Pressing ctrl-z during image uploading may cause some +# horrible issues otherwise. +stty susp undef < "$tty" +stty -icanon < "$tty" + +restore_echo() { + [ -n "$stty_orig" ] || return + stty $stty_orig < "$tty" +} + +trap restore_echo EXIT TERM + +##################################################################### +# Detect imagemagick +##################################################################### + +# If there is the 'magick' command, use it instead of separate 'convert' and +# 'identify' commands. +if command -v magick > /dev/null; then + identify="magick identify" + convert="magick" +else + identify="identify" + convert="convert" +fi + +##################################################################### +# Detect tmux +##################################################################### + +# Check if we are inside tmux. +inside_tmux="" +if [ -n "$TMUX" ]; then + case "$TERM" in + *tmux*|*screen*) + inside_tmux=1 + ;; + esac +fi + +##################################################################### +# Compute the number of rows and columns +##################################################################### + +is_pos_int() { + if [ -z "$1" ]; then + return 1 # false + fi + if [ -z "$(printf '%s' "$1" | tr -d '[:digit:]')" ]; then + if [ "$1" -gt 0 ]; then + return 0 # true + fi + fi + return 1 # false +} + +if [ -n "$cols" ] || [ -n "$rows" ]; then + if [ -n "$max_cols" ] || [ -n "$max_rows" ]; then + echo "You can't specify both max-cols/rows and cols/rows" >&2 + exit 1 + fi +fi +# Get the max number of cols and rows. +[ -n "$max_cols" ] || max_cols="$(tput cols)" +[ -n "$max_rows" ] || max_rows="$(tput lines)" +if [ "$max_rows" -gt 255 ]; then + max_rows=255 +fi + +python_ioctl_command="import array, fcntl, termios +buf = array.array('H', [0, 0, 0, 0]) +fcntl.ioctl(0, termios.TIOCGWINSZ, buf) +print(int(buf[2]/buf[1]), int(buf[3]/buf[0]))" + +# Get the cell size in pixels if either cols or rows are not specified. +if [ -z "$cols" ] || [ -z "$rows" ]; then + cell_width="" + cell_height="" + # If the cell size is specified, use it. + if [ -n "$cell_size" ]; then + cell_width="${cell_size%x*}" + cell_height="${cell_size#*x}" + if ! is_pos_int "$cell_height" || ! is_pos_int "$cell_width"; then + echo "Invalid cell size: $cell_size" >&2 + exit 1 + fi + fi + # Otherwise try to use TIOCGWINSZ ioctl via python. + if [ -z "$cell_width" ] || [ -z "$cell_height" ]; then + cell_size_ioctl="$(python3 -c "$python_ioctl_command" < "$tty" 2> /dev/null)" + cell_width="${cell_size_ioctl% *}" + cell_height="${cell_size_ioctl#* }" + if ! is_pos_int "$cell_height" || ! is_pos_int "$cell_width"; then + cell_width="" + cell_height="" + fi + fi + # If it didn't work, try to use csi XTWINOPS. + if [ -z "$cell_width" ] || [ -z "$cell_height" ]; then + if [ -n "$inside_tmux" ]; then + printf '\ePtmux;\e\e[16t\e\\' >> "$tty" + else + printf '\e[16t' >> "$tty" + fi + # The expected response will look like ^[[6;;t + term_response="" + while true; do + char=$(dd bs=1 count=1 <"$tty" 2>/dev/null) + if [ "$char" = "t" ]; then + break + fi + term_response="$term_response$char" + done + cell_height="$(printf '%s' "$term_response" | cut -d ';' -f 2)" + cell_width="$(printf '%s' "$term_response" | cut -d ';' -f 3)" + if ! is_pos_int "$cell_height" || ! is_pos_int "$cell_width"; then + cell_width=8 + cell_height=16 + fi + fi +fi + +# Compute a formula with bc and round to the nearest integer. +bc_round() { + LC_NUMERIC=C printf '%.0f' "$(printf '%s\n' "scale=2;($1) + 0.5" | bc)" +} + +# Compute the number of rows and columns of the image. +if [ -z "$cols" ] || [ -z "$rows" ]; then + # Get the size of the image and its resolution. If it's an animation, use + # the first frame. + format_output="$($identify -format '%w %h\n' "$file" | head -1)" + img_width="$(printf '%s' "$format_output" | cut -d ' ' -f 1)" + img_height="$(printf '%s' "$format_output" | cut -d ' ' -f 2)" + if ! is_pos_int "$img_width" || ! is_pos_int "$img_height"; then + echo "Couldn't get image size from identify: $format_output" >&2 + echo >&2 + exit 1 + fi + opt_cols_expr="(${scale}*${img_width}/${cell_width})" + opt_rows_expr="(${scale}*${img_height}/${cell_height})" + if [ -z "$cols" ] && [ -z "$rows" ]; then + # If columns and rows are not specified, compute the optimal values + # using the information about rows and columns per inch. + cols="$(bc_round "$opt_cols_expr")" + rows="$(bc_round "$opt_rows_expr")" + # Make sure that automatically computed rows and columns are within some + # sane limits + if [ "$cols" -gt "$max_cols" ]; then + rows="$(bc_round "$rows * $max_cols / $cols")" + cols="$max_cols" + fi + if [ "$rows" -gt "$max_rows" ]; then + cols="$(bc_round "$cols * $max_rows / $rows")" + rows="$max_rows" + fi + elif [ -z "$cols" ]; then + # If only one dimension is specified, compute the other one to match the + # aspect ratio as close as possible. + cols="$(bc_round "${opt_cols_expr}*${rows}/${opt_rows_expr}")" + elif [ -z "$rows" ]; then + rows="$(bc_round "${opt_rows_expr}*${cols}/${opt_cols_expr}")" + fi + + if [ "$cols" -lt 1 ]; then + cols=1 + fi + if [ "$rows" -lt 1 ]; then + rows=1 + fi +fi + +##################################################################### +# Generate an image id +##################################################################### + +image_id="" +while [ -z "$image_id" ]; do + image_id="$(shuf -i 16777217-4294967295 -n 1)" + # Check that the id requires 24-bit fg colors. + if [ "$(expr \( "$image_id" / 256 \) % 65536)" -eq 0 ]; then + image_id="" + fi +done + +##################################################################### +# Uploading the image +##################################################################### + +# Choose the uploading method +if [ "$uploading_method" = "auto" ]; then + if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ] || [ -n "$SSH_CONNECTION" ]; then + uploading_method="direct" + else + uploading_method="file" + fi +fi + +# Functions to emit the start and the end of a graphics command. +if [ -n "$inside_tmux" ]; then + # If we are in tmux we have to wrap the command in Ptmux. + graphics_command_start='\ePtmux;\e\e_G' + graphics_command_end='\e\e\\\e\\' +else + graphics_command_start='\e_G' + graphics_command_end='\e\\' +fi + +start_gr_command() { + printf "$graphics_command_start" >> "$tty" +} +end_gr_command() { + printf "$graphics_command_end" >> "$tty" +} + +# Send a graphics command with the correct start and end +gr_command() { + start_gr_command + printf '%s' "$1" >> "$tty" + end_gr_command +} + +# Send an uploading command. Usage: gr_upload +# Where is a part of command that specifies the action, it will be +# repeated for every chunk (if the method is direct), and is the rest +# of the command that specifies the image parameters. and +# must not include the transmission method or ';'. +# Example: +# gr_upload "a=T,q=2" "U=1,i=${image_id},f=100,c=${cols},r=${rows}" "$file" +gr_upload() { + arg_action="$1" + arg_command="$2" + arg_file="$3" + if [ "$uploading_method" = "file" ]; then + # base64-encode the filename + encoded_filename=$(printf '%s' "$arg_file" | base64 -w0) + gr_command "${arg_action},${arg_command},t=f;${encoded_filename}" + fi + if [ "$uploading_method" = "direct" ]; then + # Create a temporary directory to store the chunked image. + chunkdir="$(mktemp -d)" + if [ ! "$chunkdir" ] || [ ! -d "$chunkdir" ]; then + echo "Can't create a temp dir" >&2 + exit 1 + fi + # base64-encode the file and split it into chunks. The size of each + # graphics command shouldn't be more than 4096, so we set the size of an + # encoded chunk to be 3968, slightly less than that. + chunk_size=3968 + cat "$arg_file" | base64 -w0 | split -b "$chunk_size" - "$chunkdir/chunk_" + + # Issue a command indicating that we want to start data transmission for + # a new image. + gr_command "${arg_action},${arg_command},t=d,m=1" + + # Transmit chunks. + for chunk in "$chunkdir/chunk_"*; do + start_gr_command + printf '%s' "${arg_action},i=${image_id},m=1;" >> "$tty" + cat "$chunk" >> "$tty" + end_gr_command + rm "$chunk" + done + + # Tell the terminal that we are done. + gr_command "${arg_action},i=$image_id,m=0" + + # Remove the temporary directory. + rmdir "$chunkdir" + fi +} + +delayed_frame_dir_cleanup() { + arg_frame_dir="$1" + sleep 2 + if [ -n "$arg_frame_dir" ]; then + for frame in "$arg_frame_dir"/frame_*.png; do + rm "$frame" + done + rmdir "$arg_frame_dir" + fi +} + +upload_image_and_print_placeholder() { + # Check if the file is an animation. + frame_count=$($identify -format '%n\n' "$file" | head -n 1) + if [ "$frame_count" -gt 1 ]; then + # The file is an animation, decompose into frames and upload each frame. + frame_dir="$(mktemp -d)" + frame_dir="$HOME/temp/frames${frame_dir}" + mkdir -p "$frame_dir" + if [ ! "$frame_dir" ] || [ ! -d "$frame_dir" ]; then + echo "Can't create a temp dir for frames" >&2 + exit 1 + fi + + # Decompose the animation into separate frames. + $convert "$file" -coalesce "$frame_dir/frame_%06d.png" + + # Get all frame delays at once, in centiseconds, as a space-separated + # string. + delays=$($identify -format "%T " "$file") + + frame_number=1 + for frame in "$frame_dir"/frame_*.png; do + # Read the delay for the current frame and convert it from + # centiseconds to milliseconds. + delay=$(printf '%s' "$delays" | cut -d ' ' -f "$frame_number") + delay=$((delay * 10)) + # If the delay is 0, set it to 100ms. + if [ "$delay" -eq 0 ]; then + delay=100 + fi + + if [ -n "$speed" ]; then + delay=$(bc_round "$delay / $speed") + fi + + if [ "$frame_number" -eq 1 ]; then + # Upload the first frame with a=T + gr_upload "q=2,a=T" "f=100,U=1,i=${image_id},c=${cols},r=${rows}" "$frame" + # Set the delay for the first frame and also play the animation + # in loading mode (s=2). + gr_command "a=a,v=1,s=2,r=${frame_number},z=${delay},i=${image_id}" + # Print the placeholder after the first frame to reduce the wait + # time. + print_placeholder + else + # Upload subsequent frames with a=f + gr_upload "q=2,a=f" "f=100,i=${image_id},z=${delay}" "$frame" + fi + + frame_number=$((frame_number + 1)) + done + + # Play the animation in loop mode (s=3). + gr_command "a=a,v=1,s=3,i=${image_id}" + + # Remove the temporary directory, but do it in the background with a + # delay to avoid removing files before they are loaded by the terminal. + delayed_frame_dir_cleanup "$frame_dir" 2> /dev/null & + else + # The file is not an animation, upload it directly + gr_upload "q=2,a=T" "U=1,i=${image_id},f=100,c=${cols},r=${rows}" "$file" + # Print the placeholder + print_placeholder + fi +} + +##################################################################### +# Printing the image placeholder +##################################################################### + +print_placeholder() { + # Each line starts with the escape sequence to set the foreground color to + # the image id. + blue="$(expr "$image_id" % 256 )" + green="$(expr \( "$image_id" / 256 \) % 256 )" + red="$(expr \( "$image_id" / 65536 \) % 256 )" + line_start="$(printf "\e[38;2;%d;%d;%dm" "$red" "$green" "$blue")" + line_end="$(printf "\e[39;m")" + + id4th="$(expr \( "$image_id" / 16777216 \) % 256 )" + eval "id_diacritic=\$d${id4th}" + + # Reset the brush state, mostly to reset the underline color. + printf "\e[0m" + + # Fill the output with characters representing the image + for y in $(seq 0 "$(expr "$rows" - 1)"); do + eval "row_diacritic=\$d${y}" + printf '%s' "$line_start" + for x in $(seq 0 "$(expr "$cols" - 1)"); do + eval "col_diacritic=\$d${x}" + # Note that when $x is out of bounds, the column diacritic will + # be empty, meaning that the column should be guessed by the + # terminal. + if [ "$x" -ge "$num_diacritics" ]; then + printf '%s' "${placeholder}${row_diacritic}" + else + printf '%s' "${placeholder}${row_diacritic}${col_diacritic}${id_diacritic}" + fi + done + printf '%s\n' "$line_end" + done + + printf "\e[0m" +} + +d0="̅" +d1="̍" +d2="̎" +d3="̐" +d4="̒" +d5="̽" +d6="̾" +d7="̿" +d8="͆" +d9="͊" +d10="͋" +d11="͌" +d12="͐" +d13="͑" +d14="͒" +d15="͗" +d16="͛" +d17="ͣ" +d18="ͤ" +d19="ͥ" +d20="ͦ" +d21="ͧ" +d22="ͨ" +d23="ͩ" +d24="ͪ" +d25="ͫ" +d26="ͬ" +d27="ͭ" +d28="ͮ" +d29="ͯ" +d30="҃" +d31="҄" +d32="҅" +d33="҆" +d34="҇" +d35="֒" +d36="֓" +d37="֔" +d38="֕" +d39="֗" +d40="֘" +d41="֙" +d42="֜" +d43="֝" +d44="֞" +d45="֟" +d46="֠" +d47="֡" +d48="֨" +d49="֩" +d50="֫" +d51="֬" +d52="֯" +d53="ׄ" +d54="ؐ" +d55="ؑ" +d56="ؒ" +d57="ؓ" +d58="ؔ" +d59="ؕ" +d60="ؖ" +d61="ؗ" +d62="ٗ" +d63="٘" +d64="ٙ" +d65="ٚ" +d66="ٛ" +d67="ٝ" +d68="ٞ" +d69="ۖ" +d70="ۗ" +d71="ۘ" +d72="ۙ" +d73="ۚ" +d74="ۛ" +d75="ۜ" +d76="۟" +d77="۠" +d78="ۡ" +d79="ۢ" +d80="ۤ" +d81="ۧ" +d82="ۨ" +d83="۫" +d84="۬" +d85="ܰ" +d86="ܲ" +d87="ܳ" +d88="ܵ" +d89="ܶ" +d90="ܺ" +d91="ܽ" +d92="ܿ" +d93="݀" +d94="݁" +d95="݃" +d96="݅" +d97="݇" +d98="݉" +d99="݊" +d100="߫" +d101="߬" +d102="߭" +d103="߮" +d104="߯" +d105="߰" +d106="߱" +d107="߳" +d108="ࠖ" +d109="ࠗ" +d110="࠘" +d111="࠙" +d112="ࠛ" +d113="ࠜ" +d114="ࠝ" +d115="ࠞ" +d116="ࠟ" +d117="ࠠ" +d118="ࠡ" +d119="ࠢ" +d120="ࠣ" +d121="ࠥ" +d122="ࠦ" +d123="ࠧ" +d124="ࠩ" +d125="ࠪ" +d126="ࠫ" +d127="ࠬ" +d128="࠭" +d129="॑" +d130="॓" +d131="॔" +d132="ྂ" +d133="ྃ" +d134="྆" +d135="྇" +d136="፝" +d137="፞" +d138="፟" +d139="៝" +d140="᤺" +d141="ᨗ" +d142="᩵" +d143="᩶" +d144="᩷" +d145="᩸" +d146="᩹" +d147="᩺" +d148="᩻" +d149="᩼" +d150="᭫" +d151="᭭" +d152="᭮" +d153="᭯" +d154="᭰" +d155="᭱" +d156="᭲" +d157="᭳" +d158="᳐" +d159="᳑" +d160="᳒" +d161="᳚" +d162="᳛" +d163="᳠" +d164="᷀" +d165="᷁" +d166="᷃" +d167="᷄" +d168="᷅" +d169="᷆" +d170="᷇" +d171="᷈" +d172="᷉" +d173="᷋" +d174="᷌" +d175="᷑" +d176="᷒" +d177="ᷓ" +d178="ᷔ" +d179="ᷕ" +d180="ᷖ" +d181="ᷗ" +d182="ᷘ" +d183="ᷙ" +d184="ᷚ" +d185="ᷛ" +d186="ᷜ" +d187="ᷝ" +d188="ᷞ" +d189="ᷟ" +d190="ᷠ" +d191="ᷡ" +d192="ᷢ" +d193="ᷣ" +d194="ᷤ" +d195="ᷥ" +d196="ᷦ" +d197="᷾" +d198="⃐" +d199="⃑" +d200="⃔" +d201="⃕" +d202="⃖" +d203="⃗" +d204="⃛" +d205="⃜" +d206="⃡" +d207="⃧" +d208="⃩" +d209="⃰" +d210="⳯" +d211="⳰" +d212="⳱" +d213="ⷠ" +d214="ⷡ" +d215="ⷢ" +d216="ⷣ" +d217="ⷤ" +d218="ⷥ" +d219="ⷦ" +d220="ⷧ" +d221="ⷨ" +d222="ⷩ" +d223="ⷪ" +d224="ⷫ" +d225="ⷬ" +d226="ⷭ" +d227="ⷮ" +d228="ⷯ" +d229="ⷰ" +d230="ⷱ" +d231="ⷲ" +d232="ⷳ" +d233="ⷴ" +d234="ⷵ" +d235="ⷶ" +d236="ⷷ" +d237="ⷸ" +d238="ⷹ" +d239="ⷺ" +d240="ⷻ" +d241="ⷼ" +d242="ⷽ" +d243="ⷾ" +d244="ⷿ" +d245="꙯" +d246="꙼" +d247="꙽" +d248="꛰" +d249="꛱" +d250="꣠" +d251="꣡" +d252="꣢" +d253="꣣" +d254="꣤" +d255="꣥" +d256="꣦" +d257="꣧" +d258="꣨" +d259="꣩" +d260="꣪" +d261="꣫" +d262="꣬" +d263="꣭" +d264="꣮" +d265="꣯" +d266="꣰" +d267="꣱" +d268="ꪰ" +d269="ꪲ" +d270="ꪳ" +d271="ꪷ" +d272="ꪸ" +d273="ꪾ" +d274="꪿" +d275="꫁" +d276="︠" +d277="︡" +d278="︢" +d279="︣" +d280="︤" +d281="︥" +d282="︦" +d283="𐨏" +d284="𐨸" +d285="𝆅" +d286="𝆆" +d287="𝆇" +d288="𝆈" +d289="𝆉" +d290="𝆪" +d291="𝆫" +d292="𝆬" +d293="𝆭" +d294="𝉂" +d295="𝉃" +d296="𝉄" + +num_diacritics="297" + +placeholder="􎻮" + +##################################################################### +# Upload the image and print the placeholder +##################################################################### + +upload_image_and_print_placeholder diff --git a/.suckless/st/khash.h b/.suckless/st/khash.h new file mode 100644 index 0000000..f75f347 --- /dev/null +++ b/.suckless/st/khash.h @@ -0,0 +1,627 @@ +/* The MIT License + + Copyright (c) 2008, 2009, 2011 by Attractive Chaos + + 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. +*/ + +/* + An example: + +#include "khash.h" +KHASH_MAP_INIT_INT(32, char) +int main() { + int ret, is_missing; + khiter_t k; + khash_t(32) *h = kh_init(32); + k = kh_put(32, h, 5, &ret); + kh_value(h, k) = 10; + k = kh_get(32, h, 10); + is_missing = (k == kh_end(h)); + k = kh_get(32, h, 5); + kh_del(32, h, k); + for (k = kh_begin(h); k != kh_end(h); ++k) + if (kh_exist(h, k)) kh_value(h, k) = 1; + kh_destroy(32, h); + return 0; +} +*/ + +/* + 2013-05-02 (0.2.8): + + * Use quadratic probing. When the capacity is power of 2, stepping function + i*(i+1)/2 guarantees to traverse each bucket. It is better than double + hashing on cache performance and is more robust than linear probing. + + In theory, double hashing should be more robust than quadratic probing. + However, my implementation is probably not for large hash tables, because + the second hash function is closely tied to the first hash function, + which reduce the effectiveness of double hashing. + + Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php + + 2011-12-29 (0.2.7): + + * Minor code clean up; no actual effect. + + 2011-09-16 (0.2.6): + + * The capacity is a power of 2. This seems to dramatically improve the + speed for simple keys. Thank Zilong Tan for the suggestion. Reference: + + - http://code.google.com/p/ulib/ + - http://nothings.org/computer/judy/ + + * Allow to optionally use linear probing which usually has better + performance for random input. Double hashing is still the default as it + is more robust to certain non-random input. + + * Added Wang's integer hash function (not used by default). This hash + function is more robust to certain non-random input. + + 2011-02-14 (0.2.5): + + * Allow to declare global functions. + + 2009-09-26 (0.2.4): + + * Improve portability + + 2008-09-19 (0.2.3): + + * Corrected the example + * Improved interfaces + + 2008-09-11 (0.2.2): + + * Improved speed a little in kh_put() + + 2008-09-10 (0.2.1): + + * Added kh_clear() + * Fixed a compiling error + + 2008-09-02 (0.2.0): + + * Changed to token concatenation which increases flexibility. + + 2008-08-31 (0.1.2): + + * Fixed a bug in kh_get(), which has not been tested previously. + + 2008-08-31 (0.1.1): + + * Added destructor +*/ + + +#ifndef __AC_KHASH_H +#define __AC_KHASH_H + +/*! + @header + + Generic hash table library. + */ + +#define AC_VERSION_KHASH_H "0.2.8" + +#include +#include +#include + +/* compiler specific configuration */ + +#if UINT_MAX == 0xffffffffu +typedef unsigned int khint32_t; +#elif ULONG_MAX == 0xffffffffu +typedef unsigned long khint32_t; +#endif + +#if ULONG_MAX == ULLONG_MAX +typedef unsigned long khint64_t; +#else +typedef unsigned long long khint64_t; +#endif + +#ifndef kh_inline +#ifdef _MSC_VER +#define kh_inline __inline +#else +#define kh_inline inline +#endif +#endif /* kh_inline */ + +#ifndef klib_unused +#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3) +#define klib_unused __attribute__ ((__unused__)) +#else +#define klib_unused +#endif +#endif /* klib_unused */ + +typedef khint32_t khint_t; +typedef khint_t khiter_t; + +#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) +#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) +#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) +#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) +#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) +#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) +#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) + +#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) + +#ifndef kroundup32 +#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#endif + +#ifndef kcalloc +#define kcalloc(N,Z) calloc(N,Z) +#endif +#ifndef kmalloc +#define kmalloc(Z) malloc(Z) +#endif +#ifndef krealloc +#define krealloc(P,Z) realloc(P,Z) +#endif +#ifndef kfree +#define kfree(P) free(P) +#endif + +static const double __ac_HASH_UPPER = 0.77; + +#define __KHASH_TYPE(name, khkey_t, khval_t) \ + typedef struct kh_##name##_s { \ + khint_t n_buckets, size, n_occupied, upper_bound; \ + khint32_t *flags; \ + khkey_t *keys; \ + khval_t *vals; \ + } kh_##name##_t; + +#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ + extern kh_##name##_t *kh_init_##name(void); \ + extern void kh_destroy_##name(kh_##name##_t *h); \ + extern void kh_clear_##name(kh_##name##_t *h); \ + extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ + extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ + extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ + extern void kh_del_##name(kh_##name##_t *h, khint_t x); + +#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + SCOPE kh_##name##_t *kh_init_##name(void) { \ + return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \ + } \ + SCOPE void kh_destroy_##name(kh_##name##_t *h) \ + { \ + if (h) { \ + kfree((void *)h->keys); kfree(h->flags); \ + kfree((void *)h->vals); \ + kfree(h); \ + } \ + } \ + SCOPE void kh_clear_##name(kh_##name##_t *h) \ + { \ + if (h && h->flags) { \ + memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ + h->size = h->n_occupied = 0; \ + } \ + } \ + SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ + { \ + if (h->n_buckets) { \ + khint_t k, i, last, mask, step = 0; \ + mask = h->n_buckets - 1; \ + k = __hash_func(key); i = k & mask; \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + i = (i + (++step)) & mask; \ + if (i == last) return h->n_buckets; \ + } \ + return __ac_iseither(h->flags, i)? h->n_buckets : i; \ + } else return 0; \ + } \ + SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ + { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ + khint32_t *new_flags = 0; \ + khint_t j = 1; \ + { \ + kroundup32(new_n_buckets); \ + if (new_n_buckets < 4) new_n_buckets = 4; \ + if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \ + else { /* hash table size to be changed (shrink or expand); rehash */ \ + new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (!new_flags) return -1; \ + memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (h->n_buckets < new_n_buckets) { /* expand */ \ + khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (!new_keys) { kfree(new_flags); return -1; } \ + h->keys = new_keys; \ + if (kh_is_map) { \ + khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + if (!new_vals) { kfree(new_flags); return -1; } \ + h->vals = new_vals; \ + } \ + } /* otherwise shrink */ \ + } \ + } \ + if (j) { /* rehashing is needed */ \ + for (j = 0; j != h->n_buckets; ++j) { \ + if (__ac_iseither(h->flags, j) == 0) { \ + khkey_t key = h->keys[j]; \ + khval_t val; \ + khint_t new_mask; \ + new_mask = new_n_buckets - 1; \ + if (kh_is_map) val = h->vals[j]; \ + __ac_set_isdel_true(h->flags, j); \ + while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ + khint_t k, i, step = 0; \ + k = __hash_func(key); \ + i = k & new_mask; \ + while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ + __ac_set_isempty_false(new_flags, i); \ + if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ + { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ + if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ + __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ + } else { /* write the element and jump out of the loop */ \ + h->keys[i] = key; \ + if (kh_is_map) h->vals[i] = val; \ + break; \ + } \ + } \ + } \ + } \ + if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ + h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + kfree(h->flags); /* free the working space */ \ + h->flags = new_flags; \ + h->n_buckets = new_n_buckets; \ + h->n_occupied = h->size; \ + h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ + } \ + return 0; \ + } \ + SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ + { \ + khint_t x; \ + if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ + if (h->n_buckets > (h->size<<1)) { \ + if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ + *ret = -1; return h->n_buckets; \ + } \ + } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ + *ret = -1; return h->n_buckets; \ + } \ + } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ + { \ + khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ + x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ + if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \ + else { \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (__ac_isdel(h->flags, i)) site = i; \ + i = (i + (++step)) & mask; \ + if (i == last) { x = site; break; } \ + } \ + if (x == h->n_buckets) { \ + if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ + else x = i; \ + } \ + } \ + } \ + if (__ac_isempty(h->flags, x)) { /* not present at all */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; ++h->n_occupied; \ + *ret = 1; \ + } else if (__ac_isdel(h->flags, x)) { /* deleted */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + *ret = 2; \ + } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ + return x; \ + } \ + SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ + { \ + if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ + __ac_set_isdel_true(h->flags, x); \ + --h->size; \ + } \ + } + +#define KHASH_DECLARE(name, khkey_t, khval_t) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_PROTOTYPES(name, khkey_t, khval_t) + +#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +/* --- BEGIN OF HASH FUNCTIONS --- */ + +/*! @function + @abstract Integer hash function + @param key The integer [khint32_t] + @return The hash value [khint_t] + */ +#define kh_int_hash_func(key) (khint32_t)(key) +/*! @function + @abstract Integer comparison function + */ +#define kh_int_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract 64-bit integer hash function + @param key The integer [khint64_t] + @return The hash value [khint_t] + */ +#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) +/*! @function + @abstract 64-bit integer comparison function + */ +#define kh_int64_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract const char* hash function + @param s Pointer to a null terminated string + @return The hash value + */ +static kh_inline khint_t __ac_X31_hash_string(const char *s) +{ + khint_t h = (khint_t)*s; + if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; + return h; +} +/*! @function + @abstract Another interface to const char* hash function + @param key Pointer to a null terminated string [const char*] + @return The hash value [khint_t] + */ +#define kh_str_hash_func(key) __ac_X31_hash_string(key) +/*! @function + @abstract Const char* comparison function + */ +#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) + +static kh_inline khint_t __ac_Wang_hash(khint_t key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} +#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key) + +/* --- END OF HASH FUNCTIONS --- */ + +/* Other convenient macros... */ + +/*! + @abstract Type of the hash table. + @param name Name of the hash table [symbol] + */ +#define khash_t(name) kh_##name##_t + +/*! @function + @abstract Initiate a hash table. + @param name Name of the hash table [symbol] + @return Pointer to the hash table [khash_t(name)*] + */ +#define kh_init(name) kh_init_##name() + +/*! @function + @abstract Destroy a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_destroy(name, h) kh_destroy_##name(h) + +/*! @function + @abstract Reset a hash table without deallocating memory. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_clear(name, h) kh_clear_##name(h) + +/*! @function + @abstract Resize a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param s New size [khint_t] + */ +#define kh_resize(name, h, s) kh_resize_##name(h, s) + +/*! @function + @abstract Insert a key to the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @param r Extra return code: -1 if the operation failed; + 0 if the key is present in the hash table; + 1 if the bucket is empty (never used); 2 if the element in + the bucket has been deleted [int*] + @return Iterator to the inserted element [khint_t] + */ +#define kh_put(name, h, k, r) kh_put_##name(h, k, r) + +/*! @function + @abstract Retrieve a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t] + */ +#define kh_get(name, h, k) kh_get_##name(h, k) + +/*! @function + @abstract Remove a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Iterator to the element to be deleted [khint_t] + */ +#define kh_del(name, h, k) kh_del_##name(h, k) + +/*! @function + @abstract Test whether a bucket contains data. + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return 1 if containing data; 0 otherwise [int] + */ +#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) + +/*! @function + @abstract Get key given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Key [type of keys] + */ +#define kh_key(h, x) ((h)->keys[x]) + +/*! @function + @abstract Get value given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Value [type of values] + @discussion For hash sets, calling this results in segfault. + */ +#define kh_val(h, x) ((h)->vals[x]) + +/*! @function + @abstract Alias of kh_val() + */ +#define kh_value(h, x) ((h)->vals[x]) + +/*! @function + @abstract Get the start iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The start iterator [khint_t] + */ +#define kh_begin(h) (khint_t)(0) + +/*! @function + @abstract Get the end iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The end iterator [khint_t] + */ +#define kh_end(h) ((h)->n_buckets) + +/*! @function + @abstract Get the number of elements in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of elements in the hash table [khint_t] + */ +#define kh_size(h) ((h)->size) + +/*! @function + @abstract Get the number of buckets in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of buckets in the hash table [khint_t] + */ +#define kh_n_buckets(h) ((h)->n_buckets) + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach_value(h, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/* More convenient interfaces */ + +/*! @function + @abstract Instantiate a hash set containing integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT(name) \ + KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT(name, khval_t) \ + KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash set containing 64-bit integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT64(name) \ + KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT64(name, khval_t) \ + KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +typedef const char *kh_cstr_t; +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_STR(name) \ + KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_STR(name, khval_t) \ + KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#endif /* __AC_KHASH_H */ diff --git a/.suckless/st/kvec.h b/.suckless/st/kvec.h new file mode 100644 index 0000000..10f1c5b --- /dev/null +++ b/.suckless/st/kvec.h @@ -0,0 +1,90 @@ +/* The MIT License + + Copyright (c) 2008, by Attractive Chaos + + 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. +*/ + +/* + An example: + +#include "kvec.h" +int main() { + kvec_t(int) array; + kv_init(array); + kv_push(int, array, 10); // append + kv_a(int, array, 20) = 5; // dynamic + kv_A(array, 20) = 4; // static + kv_destroy(array); + return 0; +} +*/ + +/* + 2008-09-22 (0.1.0): + + * The initial version. + +*/ + +#ifndef AC_KVEC_H +#define AC_KVEC_H + +#include + +#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) + +#define kvec_t(type) struct { size_t n, m; type *a; } +#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0) +#define kv_destroy(v) free((v).a) +#define kv_A(v, i) ((v).a[(i)]) +#define kv_pop(v) ((v).a[--(v).n]) +#define kv_size(v) ((v).n) +#define kv_max(v) ((v).m) + +#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m)) + +#define kv_copy(type, v1, v0) do { \ + if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \ + (v1).n = (v0).n; \ + memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \ + } while (0) \ + +#define kv_push(type, v, x) do { \ + if ((v).n == (v).m) { \ + (v).m = (v).m? (v).m<<1 : 2; \ + (v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \ + } \ + (v).a[(v).n++] = (x); \ + } while (0) + +#define kv_pushp(type, v) ((((v).n == (v).m)? \ + ((v).m = ((v).m? (v).m<<1 : 2), \ + (v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \ + : 0), ((v).a + ((v).n++))) + +#define kv_a(type, v, i) (((v).m <= (size_t)(i)? \ + ((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \ + (v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \ + : (v).n <= (size_t)(i)? (v).n = (i) + 1 \ + : 0), (v).a[(i)]) + +#endif diff --git a/.suckless/st/rowcolumn_diacritics_helpers.c b/.suckless/st/rowcolumn_diacritics_helpers.c new file mode 100644 index 0000000..829c0fc --- /dev/null +++ b/.suckless/st/rowcolumn_diacritics_helpers.c @@ -0,0 +1,391 @@ +#include + +uint16_t diacritic_to_num(uint32_t code) +{ + switch (code) { + case 0x305: + return code - 0x305 + 1; + case 0x30d: + case 0x30e: + return code - 0x30d + 2; + case 0x310: + return code - 0x310 + 4; + case 0x312: + return code - 0x312 + 5; + case 0x33d: + case 0x33e: + case 0x33f: + return code - 0x33d + 6; + case 0x346: + return code - 0x346 + 9; + case 0x34a: + case 0x34b: + case 0x34c: + return code - 0x34a + 10; + case 0x350: + case 0x351: + case 0x352: + return code - 0x350 + 13; + case 0x357: + return code - 0x357 + 16; + case 0x35b: + return code - 0x35b + 17; + case 0x363: + case 0x364: + case 0x365: + case 0x366: + case 0x367: + case 0x368: + case 0x369: + case 0x36a: + case 0x36b: + case 0x36c: + case 0x36d: + case 0x36e: + case 0x36f: + return code - 0x363 + 18; + case 0x483: + case 0x484: + case 0x485: + case 0x486: + case 0x487: + return code - 0x483 + 31; + case 0x592: + case 0x593: + case 0x594: + case 0x595: + return code - 0x592 + 36; + case 0x597: + case 0x598: + case 0x599: + return code - 0x597 + 40; + case 0x59c: + case 0x59d: + case 0x59e: + case 0x59f: + case 0x5a0: + case 0x5a1: + return code - 0x59c + 43; + case 0x5a8: + case 0x5a9: + return code - 0x5a8 + 49; + case 0x5ab: + case 0x5ac: + return code - 0x5ab + 51; + case 0x5af: + return code - 0x5af + 53; + case 0x5c4: + return code - 0x5c4 + 54; + case 0x610: + case 0x611: + case 0x612: + case 0x613: + case 0x614: + case 0x615: + case 0x616: + case 0x617: + return code - 0x610 + 55; + case 0x657: + case 0x658: + case 0x659: + case 0x65a: + case 0x65b: + return code - 0x657 + 63; + case 0x65d: + case 0x65e: + return code - 0x65d + 68; + case 0x6d6: + case 0x6d7: + case 0x6d8: + case 0x6d9: + case 0x6da: + case 0x6db: + case 0x6dc: + return code - 0x6d6 + 70; + case 0x6df: + case 0x6e0: + case 0x6e1: + case 0x6e2: + return code - 0x6df + 77; + case 0x6e4: + return code - 0x6e4 + 81; + case 0x6e7: + case 0x6e8: + return code - 0x6e7 + 82; + case 0x6eb: + case 0x6ec: + return code - 0x6eb + 84; + case 0x730: + return code - 0x730 + 86; + case 0x732: + case 0x733: + return code - 0x732 + 87; + case 0x735: + case 0x736: + return code - 0x735 + 89; + case 0x73a: + return code - 0x73a + 91; + case 0x73d: + return code - 0x73d + 92; + case 0x73f: + case 0x740: + case 0x741: + return code - 0x73f + 93; + case 0x743: + return code - 0x743 + 96; + case 0x745: + return code - 0x745 + 97; + case 0x747: + return code - 0x747 + 98; + case 0x749: + case 0x74a: + return code - 0x749 + 99; + case 0x7eb: + case 0x7ec: + case 0x7ed: + case 0x7ee: + case 0x7ef: + case 0x7f0: + case 0x7f1: + return code - 0x7eb + 101; + case 0x7f3: + return code - 0x7f3 + 108; + case 0x816: + case 0x817: + case 0x818: + case 0x819: + return code - 0x816 + 109; + case 0x81b: + case 0x81c: + case 0x81d: + case 0x81e: + case 0x81f: + case 0x820: + case 0x821: + case 0x822: + case 0x823: + return code - 0x81b + 113; + case 0x825: + case 0x826: + case 0x827: + return code - 0x825 + 122; + case 0x829: + case 0x82a: + case 0x82b: + case 0x82c: + case 0x82d: + return code - 0x829 + 125; + case 0x951: + return code - 0x951 + 130; + case 0x953: + case 0x954: + return code - 0x953 + 131; + case 0xf82: + case 0xf83: + return code - 0xf82 + 133; + case 0xf86: + case 0xf87: + return code - 0xf86 + 135; + case 0x135d: + case 0x135e: + case 0x135f: + return code - 0x135d + 137; + case 0x17dd: + return code - 0x17dd + 140; + case 0x193a: + return code - 0x193a + 141; + case 0x1a17: + return code - 0x1a17 + 142; + case 0x1a75: + case 0x1a76: + case 0x1a77: + case 0x1a78: + case 0x1a79: + case 0x1a7a: + case 0x1a7b: + case 0x1a7c: + return code - 0x1a75 + 143; + case 0x1b6b: + return code - 0x1b6b + 151; + case 0x1b6d: + case 0x1b6e: + case 0x1b6f: + case 0x1b70: + case 0x1b71: + case 0x1b72: + case 0x1b73: + return code - 0x1b6d + 152; + case 0x1cd0: + case 0x1cd1: + case 0x1cd2: + return code - 0x1cd0 + 159; + case 0x1cda: + case 0x1cdb: + return code - 0x1cda + 162; + case 0x1ce0: + return code - 0x1ce0 + 164; + case 0x1dc0: + case 0x1dc1: + return code - 0x1dc0 + 165; + case 0x1dc3: + case 0x1dc4: + case 0x1dc5: + case 0x1dc6: + case 0x1dc7: + case 0x1dc8: + case 0x1dc9: + return code - 0x1dc3 + 167; + case 0x1dcb: + case 0x1dcc: + return code - 0x1dcb + 174; + case 0x1dd1: + case 0x1dd2: + case 0x1dd3: + case 0x1dd4: + case 0x1dd5: + case 0x1dd6: + case 0x1dd7: + case 0x1dd8: + case 0x1dd9: + case 0x1dda: + case 0x1ddb: + case 0x1ddc: + case 0x1ddd: + case 0x1dde: + case 0x1ddf: + case 0x1de0: + case 0x1de1: + case 0x1de2: + case 0x1de3: + case 0x1de4: + case 0x1de5: + case 0x1de6: + return code - 0x1dd1 + 176; + case 0x1dfe: + return code - 0x1dfe + 198; + case 0x20d0: + case 0x20d1: + return code - 0x20d0 + 199; + case 0x20d4: + case 0x20d5: + case 0x20d6: + case 0x20d7: + return code - 0x20d4 + 201; + case 0x20db: + case 0x20dc: + return code - 0x20db + 205; + case 0x20e1: + return code - 0x20e1 + 207; + case 0x20e7: + return code - 0x20e7 + 208; + case 0x20e9: + return code - 0x20e9 + 209; + case 0x20f0: + return code - 0x20f0 + 210; + case 0x2cef: + case 0x2cf0: + case 0x2cf1: + return code - 0x2cef + 211; + case 0x2de0: + case 0x2de1: + case 0x2de2: + case 0x2de3: + case 0x2de4: + case 0x2de5: + case 0x2de6: + case 0x2de7: + case 0x2de8: + case 0x2de9: + case 0x2dea: + case 0x2deb: + case 0x2dec: + case 0x2ded: + case 0x2dee: + case 0x2def: + case 0x2df0: + case 0x2df1: + case 0x2df2: + case 0x2df3: + case 0x2df4: + case 0x2df5: + case 0x2df6: + case 0x2df7: + case 0x2df8: + case 0x2df9: + case 0x2dfa: + case 0x2dfb: + case 0x2dfc: + case 0x2dfd: + case 0x2dfe: + case 0x2dff: + return code - 0x2de0 + 214; + case 0xa66f: + return code - 0xa66f + 246; + case 0xa67c: + case 0xa67d: + return code - 0xa67c + 247; + case 0xa6f0: + case 0xa6f1: + return code - 0xa6f0 + 249; + case 0xa8e0: + case 0xa8e1: + case 0xa8e2: + case 0xa8e3: + case 0xa8e4: + case 0xa8e5: + case 0xa8e6: + case 0xa8e7: + case 0xa8e8: + case 0xa8e9: + case 0xa8ea: + case 0xa8eb: + case 0xa8ec: + case 0xa8ed: + case 0xa8ee: + case 0xa8ef: + case 0xa8f0: + case 0xa8f1: + return code - 0xa8e0 + 251; + case 0xaab0: + return code - 0xaab0 + 269; + case 0xaab2: + case 0xaab3: + return code - 0xaab2 + 270; + case 0xaab7: + case 0xaab8: + return code - 0xaab7 + 272; + case 0xaabe: + case 0xaabf: + return code - 0xaabe + 274; + case 0xaac1: + return code - 0xaac1 + 276; + case 0xfe20: + case 0xfe21: + case 0xfe22: + case 0xfe23: + case 0xfe24: + case 0xfe25: + case 0xfe26: + return code - 0xfe20 + 277; + case 0x10a0f: + return code - 0x10a0f + 284; + case 0x10a38: + return code - 0x10a38 + 285; + case 0x1d185: + case 0x1d186: + case 0x1d187: + case 0x1d188: + case 0x1d189: + return code - 0x1d185 + 286; + case 0x1d1aa: + case 0x1d1ab: + case 0x1d1ac: + case 0x1d1ad: + return code - 0x1d1aa + 291; + case 0x1d242: + case 0x1d243: + case 0x1d244: + return code - 0x1d242 + 295; + } + return 0; +} diff --git a/.suckless/st/rowcolumn_diacritics_helpers.o b/.suckless/st/rowcolumn_diacritics_helpers.o new file mode 100644 index 0000000000000000000000000000000000000000..e3b6b55cea7236ea80ec14b046ddc12c36f8b5c3 GIT binary patch literal 16264 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 da{Q<-&u{d`=Wn*zdAj + +Copyright 2016 Roy Freytag +Copyright 2016 Vincent Loupmon +Copyright 2016 Daniel Walter +Copyright 2016-2018 Ali H. Fardan +Copyright 2016 Jody Leonard +Copyright 2016-2018 Quentin Rameau +Copyright 2016 Mike Coddington +Copyright 2016-2018 Ivan J. +Copyright 2017 Tobias Stoeckmann +Copyright 2017-2018 Laslo Hunhold +Copyright 2018 Darron Anderson +Copyright 2018 Josuah Demangeon +Copyright 2018 Tobias Tschinkowitz +Copyright 2018 David Demelier +Copyright 2018-2012 Michael Buch +Copyright 2018 Ian Remmler +Copyright 2016-2019 Joerg Jung +Copyright 2019 Ryan Kes +Copyright 2019 Cem Keylan +Copyright 2019 Dimitris Papastamos +Copyright 2019-2022 Ingo Feinerer +Copyright 2020 Alexandre Ratchov +Copyright 2020 Mart Lubbers +Copyright 2020 Daniel Moch +Copyright 2022 Nickolas Raymond Kaczynski +Copyright 2022 Patrick Iacob +Copyright 2021-2022 Steven Ward +Copyright 2025 Joakim Sindholt +Copyright 2025 Al +Copyright 2025 sewn + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/.suckless/st/slstatus/Makefile b/.suckless/st/slstatus/Makefile new file mode 100644 index 0000000..7a18274 --- /dev/null +++ b/.suckless/st/slstatus/Makefile @@ -0,0 +1,69 @@ +# See LICENSE file for copyright and license details +# slstatus - suckless status monitor +.POSIX: + +include config.mk + +REQ = util +COM =\ + components/battery\ + components/cat\ + components/cpu\ + components/datetime\ + components/disk\ + components/entropy\ + components/hostname\ + components/ip\ + components/kernel_release\ + components/keyboard_indicators\ + components/keymap\ + components/load_avg\ + components/netspeeds\ + components/num_files\ + components/ram\ + components/run_command\ + components/swap\ + components/temperature\ + components/uptime\ + components/user\ + components/volume\ + components/wifi + +all: slstatus + +$(COM:=.o): config.mk $(REQ:=.h) slstatus.h +slstatus.o: slstatus.c slstatus.h arg.h config.h config.mk $(REQ:=.h) + +.c.o: + $(CC) -o $@ -c $(CPPFLAGS) $(CFLAGS) $< + +config.h: + cp config.def.h $@ + +slstatus: slstatus.o $(COM:=.o) $(REQ:=.o) + $(CC) -o $@ $(LDFLAGS) $(COM:=.o) $(REQ:=.o) slstatus.o $(LDLIBS) + +clean: + rm -f slstatus slstatus.o $(COM:=.o) $(REQ:=.o) slstatus-${VERSION}.tar.gz + +dist: + rm -rf "slstatus-$(VERSION)" + mkdir -p "slstatus-$(VERSION)/components" + cp -R LICENSE Makefile README config.mk config.def.h \ + arg.h slstatus.h slstatus.c $(REQ:=.c) $(REQ:=.h) \ + slstatus.1 "slstatus-$(VERSION)" + cp -R $(COM:=.c) "slstatus-$(VERSION)/components" + tar -cf - "slstatus-$(VERSION)" | gzip -c > "slstatus-$(VERSION).tar.gz" + rm -rf "slstatus-$(VERSION)" + +install: all + mkdir -p "$(DESTDIR)$(PREFIX)/bin" + cp -f slstatus "$(DESTDIR)$(PREFIX)/bin" + chmod 755 "$(DESTDIR)$(PREFIX)/bin/slstatus" + mkdir -p "$(DESTDIR)$(MANPREFIX)/man1" + cp -f slstatus.1 "$(DESTDIR)$(MANPREFIX)/man1" + chmod 644 "$(DESTDIR)$(MANPREFIX)/man1/slstatus.1" + +uninstall: + rm -f "$(DESTDIR)$(PREFIX)/bin/slstatus" + rm -f "$(DESTDIR)$(MANPREFIX)/man1/slstatus.1" diff --git a/.suckless/st/slstatus/README b/.suckless/st/slstatus/README new file mode 100644 index 0000000..4d592bb --- /dev/null +++ b/.suckless/st/slstatus/README @@ -0,0 +1,65 @@ +slstatus - suckless status +========================== +slstatus is a small tool for providing system status information to other +programs over the EWMH property of the root window (used by dwm(1)) or +standard input/output. It is designed to be as efficient as possible by +only issuing the minimum of system calls required. + + +Features +-------- +- Battery percentage/state/time left +- Cat (read file) +- CPU usage +- CPU frequency +- Custom shell commands +- Date and time +- Disk status (free storage, percentage, total storage and used storage) +- Available entropy +- Username/GID/UID +- Hostname +- IP address (IPv4 and IPv6), interface status +- Kernel version +- Keyboard indicators +- Keymap +- Load average +- Network speeds (RX and TX) +- Number of files in a directory (hint: Maildir) +- Memory status (free memory, percentage, total memory and used memory) +- Swap status (free swap, percentage, total swap and used swap) +- Temperature +- Uptime +- Volume percentage +- WiFi signal percentage and ESSID + + +Requirements +------------ +Currently slstatus works on FreeBSD, Linux and OpenBSD. +In order to build slstatus you need the Xlib header files. + +- For volume percentage on Linux the kernel module `snd-mixer-oss` must be + loaded. +- For volume percentage on FreeBSD, `sndio` must be installed. + + +Installation +------------ +Edit config.mk to match your local setup (slstatus is installed into the +/usr/local namespace by default). + +Afterwards enter the following command to build and install slstatus (if +necessary as root): + + make clean install + + +Running slstatus +---------------- +See the man page for details. + + +Configuration +------------- +slstatus can be customized by creating a custom config.h and (re)compiling the +source code. This keeps it fast, secure and simple. diff --git a/.suckless/st/slstatus/arg.h b/.suckless/st/slstatus/arg.h new file mode 100644 index 0000000..5f1f408 --- /dev/null +++ b/.suckless/st/slstatus/arg.h @@ -0,0 +1,33 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef ARG_H +#define ARG_H + +extern char *argv0; + +/* int main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, *argv ? (argc--, argv++) : ((void *)0); \ + *argv && (*argv)[0] == '-' && (*argv)[1]; argc--, argv++) { \ + int i_, argused_; \ + if ((*argv)[1] == '-' && !(*argv)[2]) { \ + argc--, argv++; \ + break; \ + } \ + for (i_ = 1, argused_ = 0; (*argv)[i_]; i_++) { \ + switch ((*argv)[i_]) +#define ARGEND if (argused_) { \ + if ((*argv)[i_ + 1]) { \ + break; \ + } else { \ + argc--, argv++; \ + break; \ + } \ + } \ + } \ + } +#define ARGC() ((*argv)[i_]) +#define ARGF_(x) (((*argv)[i_ + 1]) ? (argused_ = 1, &((*argv)[i_ + 1])) : \ + (*(argv + 1)) ? (argused_ = 1, *(argv + 1)) : (x)) +#define EARGF(x) ARGF_(((x), exit(1), (char *)0)) +#define ARGF() ARGF_((char *)0) + +#endif diff --git a/.suckless/st/slstatus/components/battery.c b/.suckless/st/slstatus/components/battery.c new file mode 100644 index 0000000..1c753f9 --- /dev/null +++ b/.suckless/st/slstatus/components/battery.c @@ -0,0 +1,247 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +#if defined(__linux__) +/* + * https://www.kernel.org/doc/html/latest/power/power_supply_class.html + */ + #include + #include + #include + + #define POWER_SUPPLY_CAPACITY "/sys/class/power_supply/%s/capacity" + #define POWER_SUPPLY_STATUS "/sys/class/power_supply/%s/status" + #define POWER_SUPPLY_CHARGE "/sys/class/power_supply/%s/charge_now" + #define POWER_SUPPLY_ENERGY "/sys/class/power_supply/%s/energy_now" + #define POWER_SUPPLY_CURRENT "/sys/class/power_supply/%s/current_now" + #define POWER_SUPPLY_POWER "/sys/class/power_supply/%s/power_now" + + static const char * + pick(const char *bat, const char *f1, const char *f2, char *path, + size_t length) + { + if (esnprintf(path, length, f1, bat) > 0 && + access(path, R_OK) == 0) + return f1; + + if (esnprintf(path, length, f2, bat) > 0 && + access(path, R_OK) == 0) + return f2; + + return NULL; + } + + const char * + battery_perc(const char *bat) + { + int cap_perc; + char path[PATH_MAX]; + + if (esnprintf(path, sizeof(path), POWER_SUPPLY_CAPACITY, bat) < 0) + return NULL; + if (pscanf(path, "%d", &cap_perc) != 1) + return NULL; + + return bprintf("%d", cap_perc); + } + + const char * + battery_state(const char *bat) + { + static struct { + char *state; + char *symbol; + } map[] = { + { "Charging", "+" }, + { "Discharging", "-" }, + { "Full", "o" }, + { "Not charging", "o" }, + }; + size_t i; + char path[PATH_MAX], state[12]; + + if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0) + return NULL; + if (pscanf(path, "%12[a-zA-Z ]", state) != 1) + return NULL; + + for (i = 0; i < LEN(map); i++) + if (!strcmp(map[i].state, state)) + break; + + return (i == LEN(map)) ? "?" : map[i].symbol; + } + + const char * + battery_remaining(const char *bat) + { + uintmax_t charge_now, current_now, m, h; + double timeleft; + char path[PATH_MAX], state[12]; + + if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0) + return NULL; + if (pscanf(path, "%12[a-zA-Z ]", state) != 1) + return NULL; + + if (!pick(bat, POWER_SUPPLY_CHARGE, POWER_SUPPLY_ENERGY, path, + sizeof(path)) || + pscanf(path, "%ju", &charge_now) < 0) + return NULL; + + if (!strcmp(state, "Discharging")) { + if (!pick(bat, POWER_SUPPLY_CURRENT, POWER_SUPPLY_POWER, path, + sizeof(path)) || + pscanf(path, "%ju", ¤t_now) < 0) + return NULL; + + if (current_now == 0) + return NULL; + + timeleft = (double)charge_now / (double)current_now; + h = timeleft; + m = (timeleft - (double)h) * 60; + + return bprintf("%juh %jum", h, m); + } + + return ""; + } +#elif defined(__OpenBSD__) + #include + #include + #include + #include + + static int + load_apm_power_info(struct apm_power_info *apm_info) + { + int fd; + + fd = open("/dev/apm", O_RDONLY); + if (fd < 0) { + warn("open '/dev/apm':"); + return 0; + } + + memset(apm_info, 0, sizeof(struct apm_power_info)); + if (ioctl(fd, APM_IOC_GETPOWER, apm_info) < 0) { + warn("ioctl 'APM_IOC_GETPOWER':"); + close(fd); + return 0; + } + return close(fd), 1; + } + + const char * + battery_perc(const char *unused) + { + struct apm_power_info apm_info; + + if (load_apm_power_info(&apm_info)) + return bprintf("%d", apm_info.battery_life); + + return NULL; + } + + const char * + battery_state(const char *unused) + { + struct { + unsigned int state; + char *symbol; + } map[] = { + { APM_AC_ON, "+" }, + { APM_AC_OFF, "-" }, + }; + struct apm_power_info apm_info; + size_t i; + + if (load_apm_power_info(&apm_info)) { + for (i = 0; i < LEN(map); i++) + if (map[i].state == apm_info.ac_state) + break; + + return (i == LEN(map)) ? "?" : map[i].symbol; + } + + return NULL; + } + + const char * + battery_remaining(const char *unused) + { + struct apm_power_info apm_info; + unsigned int h, m; + + if (load_apm_power_info(&apm_info)) { + if (apm_info.ac_state != APM_AC_ON) { + h = apm_info.minutes_left / 60; + m = apm_info.minutes_left % 60; + return bprintf("%uh %02um", h, m); + } else { + return ""; + } + } + + return NULL; + } +#elif defined(__FreeBSD__) + #include + + #define BATTERY_LIFE "hw.acpi.battery.life" + #define BATTERY_STATE "hw.acpi.battery.state" + #define BATTERY_TIME "hw.acpi.battery.time" + + const char * + battery_perc(const char *unused) + { + int cap_perc; + size_t len; + + len = sizeof(cap_perc); + if (sysctlbyname(BATTERY_LIFE, &cap_perc, &len, NULL, 0) < 0 || !len) + return NULL; + + return bprintf("%d", cap_perc); + } + + const char * + battery_state(const char *unused) + { + int state; + size_t len; + + len = sizeof(state); + if (sysctlbyname(BATTERY_STATE, &state, &len, NULL, 0) < 0 || !len) + return NULL; + + switch (state) { + case 0: /* FALLTHROUGH */ + case 2: + return "+"; + case 1: + return "-"; + default: + return "?"; + } + } + + const char * + battery_remaining(const char *unused) + { + int rem; + size_t len; + + len = sizeof(rem); + if (sysctlbyname(BATTERY_TIME, &rem, &len, NULL, 0) < 0 || !len + || rem < 0) + return NULL; + + return bprintf("%uh %02um", rem / 60, rem % 60); + } +#endif diff --git a/.suckless/st/slstatus/components/battery.o b/.suckless/st/slstatus/components/battery.o new file mode 100644 index 0000000000000000000000000000000000000000..3deff79222a4a3a127f6674b2a4a2b757e868717 GIT binary patch literal 5152 zcmbtYZERat89sI#lCW~^j%-Nn0w;=U*t&AZxfpR8 zQ04N8n?F4mrj4#BxQ$p@^S33cj z8ee>}t6hrB#xk{)G_aAbm}uaOyPzT3*Dh9;7Xmcg2P{()C|`I+C>`T>Z4uHzYlxqE z4Lu&dkfVX$2!jX%%8eX&gr9mlWyX`N$CK3KIT*&X3GpP&c=%$kh~>(@>Rje%z~^31@VS4+Du3pSt)Q-S@K@i8?OWKhqaC56(v8H_4y=R04U z`Jp7EtyA_X#~GjRq<%Nhc0%p==_6}vYoUIkiGKV$_fb1v*wdAuNaCmaL~W$2ozMSn zrfrqSKXra(E3aOfyBgt_uJevdfL>jVKUO1pNVo{X`MG5x{DpU%2jNX3{AxA+H6kX& zpvU8V=Y7caytA@7p1QaiAAJ+GcX|95|K82#{}D?4b|uNH@6bu&m#(45jwPrO8)~DZ zc2F#ELv3lrq6YNYvqVzw?Owlg!%etu=VEEG{1^KCS-&q|aD2b7TzbOw#{5dTT$t_a zCe$fA`Ke%*bsuB9*)52E-~<()b!P?+JH01zy@%{0>{fiY=z0^gW5v=Fw_@cd9dE)F ztnRN@*q$k0AXCMOTX}_p_+Qc$&vT1`(w?-bgK0L14k3RR>tzpC3I$eT`%8gc?-r-) z|F`AAP-B0V4Gj+7Yo~_xAGp)b>`G_S8GE3AU|0Xn0dXfWT#Gh6Sal+?D2vp9sXIC% z>m7u_wuBjrocIch%p@Y)H?_4tP2`<~cgnUWkw0K17NbK}@=VK+Wj_@gvQl%cU$Xk6 z`>oU-%g$LzB0ONV+e94!pKJk1&PvQhziOSeqn?$>S?vVfL(dG_$ZAN0_QTN5K|2TS zA#2Hwe&0$FQWqyG;(8H2WH&J=@;wuM$VxudLSf8NTuXM#m56Ynt|=lx-7tYfjJ>hY|LHI+(4O7;DjR1%E;m#FBIK1cZNw32GQ#oi|D=?(bLZ-D<` z1N>)%L%&UKts2!~=jdq`&+CMP|Ayp?vjB&qP7t`>Y&y@%Q~BfRe5vRMWv^6D_p@mS zX?l22BpH-Gjbta10VH=L`3#bKkbD-&E+m=IMfhX{H6vUzJDKYj%idHm7-vpC@A|%q zV65zV6oj95isS64q>PQx#h*VumY+O6HttLn>SVkRT;>N}e!5%-J$KrfD&l%&-547g z?y_z4{EObYu=@d$nfSvYEbxy|S@B6q82CDl^@T<0X`@H+e+rPy)PEy{1s{7(@qaCG z@IR{IS0oO7oqtv0;D3xBrSqPKZ`bfGa=oF0J6!QUFL5)@M>YNjHU6l^*W;Yf__(!{ z{t1o0L&LwP@o_gQ{!1EPkN>j9*W*|F*NlHV21Rp~zCJ%EG(OI;;-As*%^LokhTpE?Ki6evRJlz7U(cSyY7z&|c=e1pJHMVyLnQyGSNlT$0|>b<3K^&U{U!qxr#vhZr? zsC$~F{n_cjIZAoph54i^d2Yc;2kuP3(#M>@Vd8NrRizJSxE_6|hl1|p*tq9RyE5wcpV{P! zIcb$Bp<*#z=Ao#0ur+D9%{+$Ytxf-|1i-fDPPJKc8>ln%= z_gjSF{jHx}@XktqGcFZ3II!pFfu`xdB>k^}q?zgeU21DK|I2dzAD6zU;{Bxj)&6bP zH(Xwp2kM#(4DTL%2bunOr +#include + +#include "../slstatus.h" +#include "../util.h" + +const char * +cat(const char *path) +{ + char *f; + FILE *fp; + + if (!(fp = fopen(path, "r"))) { + warn("fopen '%s':", path); + return NULL; + } + + f = fgets(buf, sizeof(buf) - 1, fp); + if (fclose(fp) < 0) { + warn("fclose '%s':", path); + return NULL; + } + if (!f) + return NULL; + + if ((f = strrchr(buf, '\n'))) + f[0] = '\0'; + + return buf[0] ? buf : NULL; +} + diff --git a/.suckless/st/slstatus/components/cat.o b/.suckless/st/slstatus/components/cat.o new file mode 100644 index 0000000000000000000000000000000000000000..6c3820dc5ae4cc9120cf66df1d1d8e5f838b4edb GIT binary patch literal 2104 zcmbu9&ubG=5XWbeYFn*qDk{Z_>_M%HE=jOj3YMfryM-#E77-QM#7zq}N!{IItA`4u zV!(p<*LW(yUw?q$)sqLIhrO|o4!7ae$cGoSaJdHa54_OGAn#|y*KM_3-CLsTdwZ02e-MwGD_ui#S zeZi?2LnpkUQL2>Z>z<lUdhRvLrGxFK|aQ1vYSOQ95nFcr1V;!YP zYl6w1XtDSpkHNI3H%ME`XoG#-J+E1Omf6u@gy;?WaXs@YW$6Pi(nZ}^?Mkic14TVE zuJn`JVmC$jj-;U^Cm-U2EuR;V)T_@256^u%^+=gEeFDCuj4sAdCFq}R1RQNGKHs~=X9P5FB<4A>H1Z0D@mz(f$KsIn(Z{gPg*-!`H zCj2%a8}xg*37-dKi_iNw#DN}XFZvb5MZcxi+lX<|8xfA2mYLpi)wfGz@?M7hoOrG? zZ=1feL)oFdfe>nzc<8AGZRVk=g?z;>_8?`|Wa2ACqgpW(aW~c<56hv*Gk*4rsHqk-h9F|9T#NLxmy|!EzW-TE89MR&zbk3> A3jhEB literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/cpu.c b/.suckless/st/slstatus/components/cpu.c new file mode 100644 index 0000000..d0d03c7 --- /dev/null +++ b/.suckless/st/slstatus/components/cpu.c @@ -0,0 +1,157 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +#if defined(__linux__) + #define CPU_FREQ "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" + + const char * + cpu_freq(const char *unused) + { + uintmax_t freq; + + /* in kHz */ + if (pscanf(CPU_FREQ, "%ju", &freq) != 1) + return NULL; + + return fmt_human(freq * 1000, 1000); + } + + const char * + cpu_perc(const char *unused) + { + static long double a[7]; + long double b[7], sum; + + memcpy(b, a, sizeof(b)); + /* cpu user nice system idle iowait irq softirq */ + if (pscanf("/proc/stat", "%*s %Lf %Lf %Lf %Lf %Lf %Lf %Lf", + &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6]) + != 7) + return NULL; + + if (b[0] == 0) + return NULL; + + sum = (b[0] + b[1] + b[2] + b[3] + b[4] + b[5] + b[6]) - + (a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + a[6]); + + if (sum == 0) + return NULL; + + return bprintf("%d", (int)(100 * + ((b[0] + b[1] + b[2] + b[5] + b[6]) - + (a[0] + a[1] + a[2] + a[5] + a[6])) / sum)); + } +#elif defined(__OpenBSD__) + #include + #include + #include + + const char * + cpu_freq(const char *unused) + { + int freq, mib[2]; + size_t size; + + mib[0] = CTL_HW; + mib[1] = HW_CPUSPEED; + + size = sizeof(freq); + + /* in MHz */ + if (sysctl(mib, 2, &freq, &size, NULL, 0) < 0) { + warn("sysctl 'HW_CPUSPEED':"); + return NULL; + } + + return fmt_human(freq * 1E6, 1000); + } + + const char * + cpu_perc(const char *unused) + { + int mib[2]; + static uintmax_t a[CPUSTATES]; + uintmax_t b[CPUSTATES], sum; + size_t size; + + mib[0] = CTL_KERN; + mib[1] = KERN_CPTIME; + + size = sizeof(a); + + memcpy(b, a, sizeof(b)); + if (sysctl(mib, 2, &a, &size, NULL, 0) < 0) { + warn("sysctl 'KERN_CPTIME':"); + return NULL; + } + if (b[0] == 0) + return NULL; + + sum = (a[CP_USER] + a[CP_NICE] + a[CP_SYS] + a[CP_INTR] + a[CP_IDLE]) - + (b[CP_USER] + b[CP_NICE] + b[CP_SYS] + b[CP_INTR] + b[CP_IDLE]); + + if (sum == 0) + return NULL; + + return bprintf("%d", 100 * + ((a[CP_USER] + a[CP_NICE] + a[CP_SYS] + + a[CP_INTR]) - + (b[CP_USER] + b[CP_NICE] + b[CP_SYS] + + b[CP_INTR])) / sum); + } +#elif defined(__FreeBSD__) + #include + #include + #include + + const char * + cpu_freq(const char *unused) + { + int freq; + size_t size; + + size = sizeof(freq); + /* in MHz */ + if (sysctlbyname("hw.clockrate", &freq, &size, NULL, 0) < 0 || !size) { + warn("sysctlbyname 'hw.clockrate':"); + return NULL; + } + + return fmt_human(freq * 1E6, 1000); + } + + const char * + cpu_perc(const char *unused) + { + size_t size; + static long a[CPUSTATES]; + long b[CPUSTATES], sum; + + size = sizeof(a); + memcpy(b, a, sizeof(b)); + if (sysctlbyname("kern.cp_time", &a, &size, NULL, 0) < 0 || !size) { + warn("sysctlbyname 'kern.cp_time':"); + return NULL; + } + if (b[0] == 0) + return NULL; + + sum = (a[CP_USER] + a[CP_NICE] + a[CP_SYS] + a[CP_INTR] + a[CP_IDLE]) - + (b[CP_USER] + b[CP_NICE] + b[CP_SYS] + b[CP_INTR] + b[CP_IDLE]); + + if (sum == 0) + return NULL; + + return bprintf("%d", 100 * + ((a[CP_USER] + a[CP_NICE] + a[CP_SYS] + + a[CP_INTR]) - + (b[CP_USER] + b[CP_NICE] + b[CP_SYS] + + b[CP_INTR])) / sum); + } +#endif diff --git a/.suckless/st/slstatus/components/cpu.o b/.suckless/st/slstatus/components/cpu.o new file mode 100644 index 0000000000000000000000000000000000000000..8e1bfc1b5a22cb194778ba99799cfd3246b44910 GIT binary patch literal 3112 zcma)7UuauZ7(X{{ZQN=aOU2?Q2E3I{*PEnkWsWJKf81q8DYDH!=+ZPzmbxU3$&IX| zbdFkBsewL-41Aey;!_1*9m(2N7Dq8Cf*Wz8lys_y?b)R79*ll_=LG|zsD>O0Wr&Of=!vrt-XEFmqvAO zxMD8-y$yVKqttCqpVqfqgt(2#M(=sZWBHu!oibnRw!^G8+h8uN_pF)M@2?(u)lWq86YpY=z}XG78Qa6qnJpVQNh14$n?C9^4=Y)XAisi!G<$IbHa__(>S?H>-BLrgM(J8wBsbwpH7z-RrXF z4$S3G= zMDMG`KyNg4&=A3VVI~nQT5(JCPSHvm!{W@y$f-cznfJ~g3xtP^un`Unga(E}g9AgN zl{^A8+Gvo2@CdB}jC)p-I`x-L-I8lp-xt~yzjmaf-Md20z>iAXMetEy!1IyMdz{2U zi7z7Q8Q)FKlkwfs#(Y1}Sk>O~t!SP<4*9%ehvPo4bbW*&v{gLZeZ)XOOS9Vj))sqr z`>}RXH5Z~gmk&~ zWuGZw12mkP_J_Q206ua6evWYD=e+i}A?(uw`~~%+kq~hsB#h{YJ%u3+U^;>6bxcF> zbNVz-otsOERMv{6OWAl%#A4KxiOaD>`f@B4pU&8>`D7s>Ci8{qoRy-b1-=i+Ljzyq zzg>rX0EvL(v>1N{AQ5`Dg9hWL0TKbnon`zyKqBDC$#@zd5%Qzg8J_`21RS+7UIIv@ z`hV@fHjZXy|8HG5@}pMJEqmU}!*}RM1;;%C{p_&jy1Y+3nINtw>ZoEva>hTAdP8#c z+@6;gpZ&T17ZsfA?^STFzhA+*o)Zer^;}SJ-tR>P=lx!D;ZM_ExX$lg_#qemi^Q>C z+#mK2kPO0pF_=zDfpNYk!YIyWt@tEytKjf7ONC@6Zdl1HmN2H`R$Lg9#iB3@Gf?18 z4{uXV6sAg3DNC*%2D=M(`JBo7luPQ0gX;`p>1B$?M|9+6m=;{Dq z5xGKcCz8LB@qCZi%RCT~ PK>S@<{|zK_ksAL$CYjDx literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/datetime.c b/.suckless/st/slstatus/components/datetime.c new file mode 100644 index 0000000..5b10daf --- /dev/null +++ b/.suckless/st/slstatus/components/datetime.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +const char * +datetime(const char *fmt) +{ + time_t t; + + t = time(NULL); + if (!strftime(buf, sizeof(buf), fmt, localtime(&t))) { + warn("strftime: Result string exceeds buffer size"); + return NULL; + } + + return buf; +} diff --git a/.suckless/st/slstatus/components/datetime.o b/.suckless/st/slstatus/components/datetime.o new file mode 100644 index 0000000000000000000000000000000000000000..225a048c326f9c5bfa616825b817db148b645f64 GIT binary patch literal 1944 zcmb_c&ubG=5S~q28?}bCRH(LxJ)}}mmu#?7ib_h=u2K}L9;!mvCYv@iKkDuV(|S-S z9zrR2^k4BJ5&bK?i-KM}E7bXR-z4jrT*MD1Z|0jH@9oTcd2w^~RzlNApwV^eS;7)c z4fphXpynw>r|7=5|2(f$7_0roY7b`u{T*=y>+o90zqi_- z!jM;;=7iZ%j({>d-$t#&ks$EkK_*$UE=&g1{gE(b|6}^`2hVrQex>Ft>T8bItok|= zmHMXcw2F>X^7M^nx$L;QSJ`ujR+g6+^|_V156|o7ykQ!qp3COuvzK%8*ag7n=`7fm z<0wH|2b}ami`kRx1GK$0(pqV4dTccLnuBMUg8;kuSxTk%6E{+N!cC=@Qb|z#fPIGP z$5B7uQ1=u)?^vJ)P3n=Xjk8Qd;)63VcbX;5631C4`tg|o{M-QE9Kc^0z*EWg9n8-t zlCip+?b=jr6zwWJ#3LjbGd;82Iu#0@Zx^=<#jWi^*{)QH&TxGIvqCq19fg<$$pC+h zMR=SONU)5u2!9nM1MwJ=@OMBm5I@8s{5_Bi#2;r7{t-wj_dDoDhfJVg|cgN+y4u6u7W#_-bayFhTKc>$vYE!nicdmFN9hk-z#V{a5Tdd+|R0xRY!rRnhplr2kU%DJuw#=P#)I1qoDZJfEL- z-}%2(1Ccpn{&;_qFMPkfvDJ>+U%q3EIeGK&OFnAGXAgf%eb5Sw2vpfFGtOGS{5Ohx K-i-{Ec>Zr@lGcC# literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/disk.c b/.suckless/st/slstatus/components/disk.c new file mode 100644 index 0000000..e19a693 --- /dev/null +++ b/.suckless/st/slstatus/components/disk.c @@ -0,0 +1,59 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +const char * +disk_free(const char *path) +{ + struct statvfs fs; + + if (statvfs(path, &fs) < 0) { + warn("statvfs '%s':", path); + return NULL; + } + + return fmt_human(fs.f_frsize * fs.f_bavail, 1024); +} + +const char * +disk_perc(const char *path) +{ + struct statvfs fs; + + if (statvfs(path, &fs) < 0) { + warn("statvfs '%s':", path); + return NULL; + } + + return bprintf("%d", (int)(100 * + (1 - ((double)fs.f_bavail / (double)fs.f_blocks)))); +} + +const char * +disk_total(const char *path) +{ + struct statvfs fs; + + if (statvfs(path, &fs) < 0) { + warn("statvfs '%s':", path); + return NULL; + } + + return fmt_human(fs.f_frsize * fs.f_blocks, 1024); +} + +const char * +disk_used(const char *path) +{ + struct statvfs fs; + + if (statvfs(path, &fs) < 0) { + warn("statvfs '%s':", path); + return NULL; + } + + return fmt_human(fs.f_frsize * (fs.f_blocks - fs.f_bfree), 1024); +} diff --git a/.suckless/st/slstatus/components/disk.o b/.suckless/st/slstatus/components/disk.o new file mode 100644 index 0000000000000000000000000000000000000000..f3bd7bdfae597027242b24c22f34990038cf73f0 GIT binary patch literal 3120 zcmcIl&2Jk;6rXjQCTWY;RH=w)4|{N^OUo*Dh=eKut`g{|Dn;T;t*CG|_PUNzf5^Kj z4hj${ij*j=6d`dz`~lp!azZBM5GrxtA3z){%7Oa81yFgv*?HdS)&Ug3lis|U-}{<* z`)2mzS1!GpvMj-5iI+sYVpNFMNIWm9X;IiBE#8!C->z2V*2#$+p>p$Dwj(!xm$$?0 z$UT^?cbAXnyK?hKdHY4I6`1edKQ6acvKeWAxsuI&Ee>jh+9Dq1+CtmWD- ztMA^~Z=#!WYc`vc>)p<9PukBS^XAUlPnP`QXF0k9Xs2gi>B4HfZ?*fl)m^gsu4ljZ zP0!9efs^P1<-0xm+HTLjs=%Io_Cd7i|NbOK$;SWJl0A)YGcM1pWF3`9eWlDl9V-aE z@Mbk|PE7=-&WVW%YQ*&G%hBv=QCygrIp^dqTz>nslb?3;Zr&+O7N#ekFH9>F>IC5s z4-u1*NC~S8jC<13>rui$W04Tn2X6?gld(=59Uj>xV~6mQTDJgsKAl-h&8D}l)OV-Ng{w+%awVD1R4c1O7w}3~`+690%|q!nqEL2M-774)jkQYq9UBIx&Ws z>OOJkQIJ;06f#6N^-+3CEf0w(UaUjSjv0B?nF)J_I1`)oXYL;O)E+nPsp>78sKbH41jVJT| zX4pf=@&7b%+*QVp=(^)v=tjoJG>#bPRmM*nxVit=4cx3-$%t>(tzy`lb^Fl3&ANSJ z;AY;h4BX7StMTOb`rfcN^WHabGp{Nu&P^}636`$jYL zU3#aVQx`<9uQ!(5R=Y{}BU~m&ME7fS!MwW9$J)1a$P=YazH~*xK!?rZ(OQ27rNeN7 z7_~1Fn>2?eCdz65qcn#vdH^&Y3H1&FNAH<*F_D*b$^5wYh`C>Xybp+3AU28fKWTzA z=lm>-8jJo5vGJVVfAVeS^Rs+S`%lKMYSz;J{NAye^Rj$L`?H?qt$$g+Dme@jC(XAt zklYvVoAZ1`6YM4F-_^huY^Y_@f0^(>{j-5h5zVPl2=ou`J^OS249X3cnr?_yT`0Vf l2$}T98 + #include + + #include "../util.h" + + #define ENTROPY_AVAIL "/proc/sys/kernel/random/entropy_avail" + + const char * + entropy(const char *unused) + { + uintmax_t num; + + if (pscanf(ENTROPY_AVAIL, "%ju", &num) != 1) + return NULL; + + return bprintf("%ju", num); + } +#elif defined(__OpenBSD__) | defined(__FreeBSD__) + const char * + entropy(const char *unused) + { + // https://www.unicode.org/charts/PDF/U2200.pdf + /* Unicode Character 'INFINITY' (U+221E) */ + return "\u221E"; + } +#endif diff --git a/.suckless/st/slstatus/components/entropy.o b/.suckless/st/slstatus/components/entropy.o new file mode 100644 index 0000000000000000000000000000000000000000..53d9b826a654b98a1b893261bafd83408dcde303 GIT binary patch literal 1800 zcmbtUO=}Zj5S~pF8?lBe6#`Y*Ln`&_%?Hh;Pz|Ko6^aL`pa+SY&BoLuo4C7?Mh_l( zv4leZfd9i|K|z0lXT9{`LGMDG*~}!{ZHk}+Z)fJ2d1l_cGu^j$H}AzX4Ma4!4HHjL zfOj(!yA+ruNWpn9c7LRe{URa8{u-&8F^DfMlfSo~N#+lYy>CZ^55IgM)tfg4D;drW z0>)q~ldKsx76O)^7=gQ=(jS*z^dQ@HJ66`~d)cP#I(94Tnoh0L&f1RecDntF`O4gG z0c@1ZtNQZBgGbl&d{O09UN7Vd#oS6ExEeqlTkCidcrXiMpdAt>zg3I)3m7BOUOxnF zXI@*Jn@t{IGU1nl2*6X)>nT0-)lA<6RrNejZigI7$87^;b=qxwD^O0yw-v6~t>7`( zf9~`YE^^m){XSBupxvm{U9)YEZ}4A+j%?aBdb6;U*pa=EnC!jyi)hfBmjqz_j34QM zecDgr%EG4^r@0Xx`b-3o_Z*L^Xn)#2s`;<{JaVaK0Z&+@|Mf74w)7WWVk{TQ`^>)l z&r4W%qVF?b;-bHc5z)dE_z4=czsGhG{Uc6@-b>z}!|)Ofsu|^{xzXSF_z9gl|DRk? z=1lWHi%0qkKCL&Vfx-R1l7ljbhrVu6KGlrQ9wE@V{~9?V;j&}o0$S7fNe17HGe<_0 F{|A)uwEh49 literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/hostname.c b/.suckless/st/slstatus/components/hostname.c new file mode 100644 index 0000000..dab8b63 --- /dev/null +++ b/.suckless/st/slstatus/components/hostname.c @@ -0,0 +1,17 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +const char * +hostname(const char *unused) +{ + if (gethostname(buf, sizeof(buf)) < 0) { + warn("gethostbyname:"); + return NULL; + } + + return buf; +} diff --git a/.suckless/st/slstatus/components/hostname.o b/.suckless/st/slstatus/components/hostname.o new file mode 100644 index 0000000000000000000000000000000000000000..e678e250ff6529f528eae90f3cf2e086aa8f44d0 GIT binary patch literal 1656 zcmb_cL2DCH5S~q|skFwlSP<%Ay@;YdvcX&mmX)BbPy_`JdeKeXHqd6b?!IDT4|?gb z;1BTNFOdcRfIp+)$y*OXop19ddA>#jap2|6eDlrBd-G=V{=w$MiszAtM{6{(1SLAD zO!Tl+L#ok5dh&&4i8hYz0XxReVKH0^3L6QE@70Z?b8x=+x*h9Yn(NLmiTd#>(R#bR z>fcy@{OqP5v{ax1zu9QE8h4v5@R6K+UWjHARmdxV$r(MzzRW&|_wFfqgSxk}P@Vh8 zWZ+jz4^e>qeyyI?<_vOnL;hL%aeCH6G&8O~@h+5VdB+LJ+7(_)7Qd{`*dnhwFIScs zn1)~b15RirTB*9EHroyA>~E3jF<^QdWeLY#Fop1pIOWgNk<<*1o}Rbx6@!C6W8uER z!LM5QErYxHeG9inh)CtbzK%M)>uj|5M9bpcNa=W>NxhDAM5>eLq_PwS=sYB(x@o^3 zCrn9F9V;GzXSRgKkardzxVE6b z=wWDstREZVz~J+|1CmL_JVCDW0*<$_<&AAvx?BG?`;hZr<8eRaZ1S>2>R+-!)}_Ae zp}`ss?}M@BTNSs&$^M8Pq6-K6C)==em;0Q3nBU)KlI_pDAnv)O|06FE?undk{5M1R zTl_BbroI0wlTh9n?~iX+>I*)tc3Ik7v?vWC{4b%78;_js+ashf@t7}L3_OMwS(_F= NFy}uQGe?RW{|ibfj{^Vz literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/ip.c b/.suckless/st/slstatus/components/ip.c new file mode 100644 index 0000000..2cdad46 --- /dev/null +++ b/.suckless/st/slstatus/components/ip.c @@ -0,0 +1,87 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#if defined(__OpenBSD__) + #include + #include +#elif defined(__FreeBSD__) + #include + #include +#endif + +#include "../slstatus.h" +#include "../util.h" + +static const char * +ip(const char *interface, unsigned short sa_family) +{ + struct ifaddrs *ifaddr, *ifa; + int s; + char host[NI_MAXHOST]; + + if (getifaddrs(&ifaddr) < 0) { + warn("getifaddrs:"); + return NULL; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + + s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), + host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (!strcmp(ifa->ifa_name, interface) && + (ifa->ifa_addr->sa_family == sa_family)) { + freeifaddrs(ifaddr); + if (s != 0) { + warn("getnameinfo: %s", gai_strerror(s)); + return NULL; + } + return bprintf("%s", host); + } + } + + freeifaddrs(ifaddr); + + return NULL; +} + +const char * +ipv4(const char *interface) +{ + return ip(interface, AF_INET); +} + +const char * +ipv6(const char *interface) +{ + return ip(interface, AF_INET6); +} + +const char * +up(const char *interface) +{ + struct ifaddrs *ifaddr, *ifa; + + if (getifaddrs(&ifaddr) < 0) { + warn("getifaddrs:"); + return NULL; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + + if (!strcmp(ifa->ifa_name, interface)) { + freeifaddrs(ifaddr); + return ifa->ifa_flags & IFF_UP ? "up" : "down"; + } + } + + freeifaddrs(ifaddr); + + return NULL; +} diff --git a/.suckless/st/slstatus/components/ip.o b/.suckless/st/slstatus/components/ip.o new file mode 100644 index 0000000000000000000000000000000000000000..ef0faf7a7f5a69354f45e82315f4c609542a187f GIT binary patch literal 3104 zcmbuA-D@0G6u{4Hl4jI&vf6@+v38KHMr?=Omz<4K=HP@#y_k1Z8n`V@`l%-)k+$6XLT?B0*x zJ?GqW&;6KJ6UnDSng%Qy9EP19Q-JsMopzt!>;nUKgZ-R6a>V@7F~9i1*pDjO~{je{OB9-$||Su;nLb`DPe16P1}sNA)A#rjP+)D#C%ukBaia zsPL+o4!2h(cH)5z_8z!2d zX}_VPA9RCj@P#&qgxHGT0$>)y{yUOgrWzew4B1BHLK8Y%ZXPsZp|=eafh|2th+@WTywq5*%l0YBY%!MsCbNuZn`$|3c*~;$*b20O0F|8Q-rLO$*|63DmR8W<2VHe#*0oa z?`B~PQEKAM*u>PCv1}?gjT64oO~vE>?ZYtQ6blO7DLNCITQ#u>qR{h0W6T9X6k@bS z=4T0_5Wfrm%sqlA#OX{i=R6SKt?(<@CLvB|k@Y_$h(htusbzkZAPVWL@qFpinm*kD z*8f^^(r;Jz4+>Y~*;F{4PxfQW{UkqX-jb3B^LAX(SL1XQuIA^w!qs?QSNyjs@mx|k zK26(kUT)y$V}+~hGSQZV;-^20D;v!D{J#<4eE#1Ja6bQFl@?~))Hv3zQ>&-A5?D@wI;4)D2e2jzGc%bys^kl9#==j+A^(WW{$zgEDmsN?#&H+W zlpJQJ#l|I?|Oy-rx0$u390Xj2%tRU+v@+l6>q|+TDTiQ#K z+B*yI5Vnao5mcmA-7JL%=Rb&J0MTZ=YLp**yj1U?vpGQ(6eOx!S4&( zaeYMbsk(UjU#|Zo8qvK8st-#LjEnQnbG?iWiX~`&UV_hAQPx5GY2@|xzd}Kwyy%y; zH`fWyA}h)|X#W=r CGU(9& literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/kernel_release.c b/.suckless/st/slstatus/components/kernel_release.c new file mode 100644 index 0000000..36a6a44 --- /dev/null +++ b/.suckless/st/slstatus/components/kernel_release.c @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +const char * +kernel_release(const char *unused) +{ + struct utsname udata; + + if (uname(&udata) < 0) { + warn("uname:"); + return NULL; + } + + return bprintf("%s", udata.release); +} diff --git a/.suckless/st/slstatus/components/kernel_release.o b/.suckless/st/slstatus/components/kernel_release.o new file mode 100644 index 0000000000000000000000000000000000000000..207785ebcd55da6f075368e6c40429fd2881a951 GIT binary patch literal 1832 zcmbtU&ui3B5T0yXTeX&2iV79UVJnsDW3#v-MN44OMkxw{2Tx0Oo3*9gY)w*ZRnUTh zQVKnI@aWN#Cl4MxEa;!$!9PLJgWiQY-`h9aY}k6JA57lNH@{xqo8;-W`Rh5$B7sG- zv}XxR^saADceU!$2puDD^XD6mU+}iBOinTHz50}Y$%ME4B%k|^z!$BJF{kBiUxlF= zr}g86xBVgioC(7_E$#8PUhC`yZ+cvpO9tBf<`;DNycYV6V1_1RqPcQ;#-5tHb@!a@ zl$4_!yI3fe3Kxqd^r3zhn`MB7gD6K<3mjL`url_z@Q?10wLWT13=akw zvkssRqyrUiG!nl`Do&#Ix+of-g|CueJt4K=CqAia9FvNgFc8keNvhUtG=h*r!e$aE z9?1*(D2#nSTvMx2b2W&P4NgVDV6jq^L_f7bA+--!0SH~#FB?SGyFQ1hVqn19qPvZNvXkEBV~rN6AM z;bi_K{G(CiA0fQt$$G=&i(l5SqGVRd6Z#%2nD?%kWc!;5$nHzd-?-@kE2x>xA2N+D zOQ2b^`TVK&y#G;iA#%^Se|%4pFMO}wXyuyzzhE$iS@0Lk=A&lz?wO*3d7;}dQZQvZ T%yHIw +#include +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +/* + * fmt consists of uppercase or lowercase 'c' for caps lock and/or 'n' for num + * lock, each optionally followed by '?', in the order of indicators desired. + * If followed by '?', the letter with case preserved is included in the output + * if the corresponding indicator is on. Otherwise, the letter is always + * included, lowercase when off and uppercase when on. + */ +const char * +keyboard_indicators(const char *fmt) +{ + Display *dpy; + XKeyboardState state; + size_t fmtlen, i, n; + int togglecase, isset; + char key; + + if (!(dpy = XOpenDisplay(NULL))) { + warn("XOpenDisplay: Failed to open display"); + return NULL; + } + XGetKeyboardControl(dpy, &state); + XCloseDisplay(dpy); + + fmtlen = strnlen(fmt, 4); + for (i = n = 0; i < fmtlen; i++) { + key = tolower(fmt[i]); + if (key != 'c' && key != 'n') + continue; + + togglecase = (i + 1 >= fmtlen || fmt[i + 1] != '?'); + isset = (state.led_mask & (1 << (key == 'n'))); + + if (togglecase) + buf[n++] = isset ? toupper(key) : key; + else if (isset) + buf[n++] = fmt[i]; + } + + buf[n] = 0; + return buf; +} diff --git a/.suckless/st/slstatus/components/keyboard_indicators.o b/.suckless/st/slstatus/components/keyboard_indicators.o new file mode 100644 index 0000000000000000000000000000000000000000..5542fb2daf7e3f2776feeafcd9c54b37e2d2c403 GIT binary patch literal 2416 zcmbuA&2Jk;6u{pm&PO3#2Z$i3AhyUwu1Z#RM5MF|;I*A*qgphArkIb`+SnT$#o0CM zv04KXsnVhvW5@*waq6}I0D?Oj|2s{H?ps%d^H6s90cQaLs(d`;i8eEe2zVlmmt zY%eUlY(zirpv^aYGlbS?_S@{?dy7{(>@c>+n`dGZy?ICXP{p*Vj~8~0s~^n3-2>=w z$+*g9!0dj1hjqJMv;8!tfFB~qz-(n>`I31o;x7Ul=_T3uu531cee0a@wsFolZxn9i zPgiXxyHTrFti}oLq_t78OB!=E7qM3Aae$>v=7bhsdS&&fmYma*dQzKB%+4iVn4QCF zv?v-j+7@(yAy7KR#NH{P@f6xfl=of*<>I7rXku)97oBs+p7JdKa}=CbCqElXtJ9wj zr&X;TNvrWsMhrFa(Ws%$?~G;DYs&DGiaMWF6NVZ`7j(stv^vg{4Cp9z1peywGYA*u_~iloivb)q?Z%pG zd8NXJQ`#t6%=Kz|5%gRp(a!|((Si$>=RkhRW~X?PjO#GZt-xA+9rBrqTeJBogc|dl zitRw5P-Km&U0`m-yTCE1U=hA@O@d*IE z=x%M<4!Srlvvs`sGyX>K@jFgkuX^~7c&ve;l+Z30);(*>4rcJb6b+;4lWDJHN%4WW zkHW^6M-1T*YP43Vfb^6Vf+*uWfcAt`@vKaf5#>2-#OVylQV$p6#xQ;!IpzGX@pO(U z=PWi^MExT&L|xPueTp-BuVnd>JBYVf*hHM@m(d_};Y0mb{a_zox<7QE{&gmC?Ee>H zAD)+3e>C~>F8)%faQtmvzua>Re#7zfQ1sjXJb&uNKGXiwJr?nT_p43CR``Tg_&n(D lQk`%- +#include +#include +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +static int +valid_layout_or_variant(char *sym) +{ + size_t i; + /* invalid symbols from xkb rules config */ + static const char *invalid[] = { "evdev", "inet", "pc", "base" }; + + for (i = 0; i < LEN(invalid); i++) + if (!strncmp(sym, invalid[i], strlen(invalid[i]))) + return 0; + + return 1; +} + +static char * +get_layout(char *syms, int grp_num) +{ + char *tok, *layout; + int grp; + + layout = NULL; + tok = strtok(syms, "+:_"); + for (grp = 0; tok && grp <= grp_num; tok = strtok(NULL, "+:_")) { + if (!valid_layout_or_variant(tok)) { + continue; + } else if (strlen(tok) == 1 && isdigit(tok[0])) { + /* ignore :2, :3, :4 (additional layout groups) */ + continue; + } + layout = tok; + grp++; + } + + return layout; +} + +const char * +keymap(const char *unused) +{ + Display *dpy; + XkbDescRec *desc; + XkbStateRec state; + char *symbols; + const char *layout; + + layout = NULL; + + if (!(dpy = XOpenDisplay(NULL))) { + warn("XOpenDisplay: Failed to open display"); + return NULL; + } + if (!(desc = XkbAllocKeyboard())) { + warn("XkbAllocKeyboard: Failed to allocate keyboard"); + goto end; + } + if (XkbGetNames(dpy, XkbSymbolsNameMask, desc)) { + warn("XkbGetNames: Failed to retrieve key symbols"); + goto end; + } + if (XkbGetState(dpy, XkbUseCoreKbd, &state)) { + warn("XkbGetState: Failed to retrieve keyboard state"); + goto end; + } + if (!(symbols = XGetAtomName(dpy, desc->names->symbols))) { + warn("XGetAtomName: Failed to get atom name"); + goto end; + } + layout = bprintf("%s", get_layout(symbols, state.group)); + XFree(symbols); +end: + XkbFreeKeyboard(desc, XkbSymbolsNameMask, 1); + if (XCloseDisplay(dpy)) + warn("XCloseDisplay: Failed to close display"); + + return layout; +} diff --git a/.suckless/st/slstatus/components/keymap.o b/.suckless/st/slstatus/components/keymap.o new file mode 100644 index 0000000000000000000000000000000000000000..d565c9a015393abd11347c930643464e8c40e098 GIT binary patch literal 4016 zcmb`JUuYaf9LMMKr-_xg ziHQwXOe-ws)nf6%H+>L&5ETRk!H_ns2qIMRK`2svYe^yaVqe7b_nV!$Ox9Zj@dvvz z^O^7OH?uQ4zul9g@v){rfJh6FC&@-jQbInvZKFQKt3xD2I*D<_c+QwK4o^Geez#_f0cIG3>=>RecE;cn}^`}7Li zUU!XNah+ShnL6vVdN|y>j1yKP8O}u*@5k}VXaqHtQb_F`cfNt?u+KZch=qG;bpYoS zcW#bGYfdcO;VPX~diqM|sn?+^wl@>*SUf?iTTw_WQR>9Q-PE~B9TpaWD^R1(RhT(< zhv);e0;+hml{+n+Z;$UiKYIZ*Zv5J}v-57PbGeIF$RDvofp%tz+0@r<_4;hBRuc~& zcm5Jf2eYNlD))~&>tEq^@fC9;I&#@&TdDKQT6cZzc=y_xQM)+qTwOayE3xokhVE?P z8^&5)@1={BI@g{*YP?_^HI5m{)xQ8j zS4=-`Eu@O3ol%7*c9_i4W<)2d4_NG=nYYTS(6*SJwdS}`D=*|z#avlvCm00q)AA9u zGD=7S0voKD$Ka~|xWzORxK@A;vU6XO?10!KxnkLB@N8-1^@p?OGS(c)7A!_eX_7L_ z7H;al*@2OfeOljvgOd+y(E&ZGN45S)|3GASKY!r}zTp~N_=13lG?BmxGTcnGfTw+j zi%H;>XGvf_6zJ(_Yd;I(IPiN#-*4zV91OkHG#uxs1sx8C8zBgNqVC0Q0MsRSc zEf#z&(0nNn9E=4cMz9YgAO)AiHY?6xk+TX|6w>(;NhZ^5p=2dfNl23KA5rw`{HGLshZ3ix=rNbdd7e=CJqoWXyhq{hC>)Sd|v!> zlKf*uukQPI3cpX$LzNqLH^bE=bnoNL{5_yb{P zis*Q3c{wd>wv{t=X3aCAXG~@iJyk9f-7aDSOd`Cm+fD7Xe2Utl-vkXsP>eG*Nsm31s} z@A2Ipf=ji_m<+%!i$%me$8qF7$@T9Q9QRVS2RAMLMvvIOI(P(X81ujSN5L0k_QR!` z9DloKK-rY#C^%u=JN}>mni>@9-u4i%M(g(;CMmEN`Q1G;xJTagO8~?1BSI&Ogcx7u z70KniWobf(tV<1+%= z>&KHh^vU-O-#^Tc(qHmMV}p!@FesROe}hoR%j=Kdo<>Xct2j_MpkFob`r)^ORx literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/load_avg.c b/.suckless/st/slstatus/components/load_avg.c new file mode 100644 index 0000000..f278a40 --- /dev/null +++ b/.suckless/st/slstatus/components/load_avg.c @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +const char * +load_avg(const char *unused) +{ + double avgs[3]; + + if (getloadavg(avgs, 3) < 0) { + warn("getloadavg: Failed to obtain load average"); + return NULL; + } + + return bprintf("%.2f %.2f %.2f", avgs[0], avgs[1], avgs[2]); +} diff --git a/.suckless/st/slstatus/components/load_avg.o b/.suckless/st/slstatus/components/load_avg.o new file mode 100644 index 0000000000000000000000000000000000000000..3be39fcdf04358fb32782d336d478c9fba2c01d2 GIT binary patch literal 1864 zcmbW1-D(q25XWbe)<&!~mLi1;-HTM>N7rnys6{2v+OAMUq=H_BO|to*$!^`OwN=53 z;zcP!AHgROpFje7;hR*@3-wy4^WU7=q}vpv11EFlHy`KZ%-Qqy{>oB9(?~?4d(^Xp z5`9bd=7n%tpfNg2*7ncrXZR}C?wy$|Ppq9cnMrGB|2v}Ib+*RMj`jX7jF6a}AJc!% zrB^d){$0tWl$#z2W$F&7m11U)7@@qUD8!c|E)Q@X2M}oHIOJphIK?gx%lXh8rYnarU0{2>uTHopX45>yHr~D8*WggVzC|A<+WnDzE-SqgK3KEL(q#;JpLYZB@4)s;DPHW zd=`)e@gqEhF9Nb49zNlZ09nA$B;gJq3pjc%T<19ibuc0^+FPx_E|Jj={I0Ic%y*i$ z5jdLx85KLQ$tbnkWcVHog!64AqwKX>j>{pg7dQrA!P)R6lwEUQ80)^b?)bqLr=p-! zFIIiKB^!-4atEvl)I4dv$+@VR=g}{+{_TFqb6H<>L9tvcZeQ8*CWu?|L_bjZ;uoEi z00BzJ@iXSQ?}zRr+bIT3O8Fj(;Z;q{B>qh`GKjOeNQLl%raQDq~7_j6FbrQqu*vO9_A{{uYM!N&jq literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/netspeeds.c b/.suckless/st/slstatus/components/netspeeds.c new file mode 100644 index 0000000..cde6fa9 --- /dev/null +++ b/.suckless/st/slstatus/components/netspeeds.c @@ -0,0 +1,129 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +#if defined(__linux__) + #include + + #define NET_RX_BYTES "/sys/class/net/%s/statistics/rx_bytes" + #define NET_TX_BYTES "/sys/class/net/%s/statistics/tx_bytes" + + const char * + netspeed_rx(const char *interface) + { + uintmax_t oldrxbytes; + static uintmax_t rxbytes; + extern const unsigned int interval; + char path[PATH_MAX]; + + oldrxbytes = rxbytes; + + if (esnprintf(path, sizeof(path), NET_RX_BYTES, interface) < 0) + return NULL; + if (pscanf(path, "%ju", &rxbytes) != 1) + return NULL; + if (oldrxbytes == 0) + return NULL; + + return fmt_human((rxbytes - oldrxbytes) * 1000 / interval, + 1024); + } + + const char * + netspeed_tx(const char *interface) + { + uintmax_t oldtxbytes; + static uintmax_t txbytes; + extern const unsigned int interval; + char path[PATH_MAX]; + + oldtxbytes = txbytes; + + if (esnprintf(path, sizeof(path), NET_TX_BYTES, interface) < 0) + return NULL; + if (pscanf(path, "%ju", &txbytes) != 1) + return NULL; + if (oldtxbytes == 0) + return NULL; + + return fmt_human((txbytes - oldtxbytes) * 1000 / interval, + 1024); + } +#elif defined(__OpenBSD__) | defined(__FreeBSD__) + #include + #include + #include + #include + #include + + const char * + netspeed_rx(const char *interface) + { + struct ifaddrs *ifal, *ifa; + struct if_data *ifd; + uintmax_t oldrxbytes; + static uintmax_t rxbytes; + extern const unsigned int interval; + int if_ok = 0; + + oldrxbytes = rxbytes; + + if (getifaddrs(&ifal) < 0) { + warn("getifaddrs failed"); + return NULL; + } + rxbytes = 0; + for (ifa = ifal; ifa; ifa = ifa->ifa_next) + if (!strcmp(ifa->ifa_name, interface) && + (ifd = (struct if_data *)ifa->ifa_data)) + rxbytes += ifd->ifi_ibytes, if_ok = 1; + + freeifaddrs(ifal); + if (!if_ok) { + warn("reading 'if_data' failed"); + return NULL; + } + if (oldrxbytes == 0) + return NULL; + + return fmt_human((rxbytes - oldrxbytes) * 1000 / interval, + 1024); + } + + const char * + netspeed_tx(const char *interface) + { + struct ifaddrs *ifal, *ifa; + struct if_data *ifd; + uintmax_t oldtxbytes; + static uintmax_t txbytes; + extern const unsigned int interval; + int if_ok = 0; + + oldtxbytes = txbytes; + + if (getifaddrs(&ifal) < 0) { + warn("getifaddrs failed"); + return NULL; + } + txbytes = 0; + for (ifa = ifal; ifa; ifa = ifa->ifa_next) + if (!strcmp(ifa->ifa_name, interface) && + (ifd = (struct if_data *)ifa->ifa_data)) + txbytes += ifd->ifi_obytes, if_ok = 1; + + freeifaddrs(ifal); + if (!if_ok) { + warn("reading 'if_data' failed"); + return NULL; + } + if (oldtxbytes == 0) + return NULL; + + return fmt_human((txbytes - oldtxbytes) * 1000 / interval, + 1024); + } +#endif diff --git a/.suckless/st/slstatus/components/netspeeds.o b/.suckless/st/slstatus/components/netspeeds.o new file mode 100644 index 0000000000000000000000000000000000000000..9d8e32c627254f56110abe3624a37ddd09b511f5 GIT binary patch literal 2760 zcmdUw!E4+_6vy9hleV$8OO%KUt+GNQt<~S=w-^vBvL)Dz5~WxwN|E)K&2P;%yPNel zld@ZpTC_q~0vdkW%%g$YY)((8jo_uCCrTKW*JyzpeWF z!25^s8~u2L#@XrkE!7y@8Ds62(UalfjWg2PQscXWaeK%tG`v z`cwJIPjYASg^$J-@?RBm*YX7>r}7hQg0F%P%fI;!y>|X0zj3;|wbMd0ww?X`*0F52 z_cPR4z#Mr&q<@rka1r58WFV_iF=hRemJj=p>4fD^jZmh|^6$e=p4mAtdx0C5?+!V= z2Y+=B{>C2sojv$_%#n}eAw6*w>Q0g@>##;?H!Hs|B4f2#3K!?Ip9orc+r650Dbejt z+Gv|Pb#yIm*NOEcy%;yC-ZIsd^;X=bYL)A|wpy*NtXAuBqdA(ubU7uG=P1Yeer|tl z4dn=oEa3bwY~R#LKo;EGL2lx|2*~35pB>_iV=eJNZ#m|9(BZ3=`}5%ZAmE3%$vm5Y zESLwiCj4_i7Vtbb;oktVU>?+*@E-tKWL@FE4e^fs%7@|m@3$W|xVzuH<^Fyjcl>eA zvhK9Q-ThXadEEWJ>2P;sSX*ngk~X`v*Gv-fllM$kfOh!DJr2c$$x5}J#;rs$ z`)_@b!*LX-WmhV8P`BqKHg8C%d)!e)9Bh9bkmY9^fSQFW?^Xdu7Q9d3xJy^Q@E5X1 zA3x0;bNcaLT1WrwlJ#Y+h!wb&1=wEV&v;zcm%hSw=y*R}`NKNMe~{QDPWqSGAiDUV zpK^=OG5Ef3u?^pi?_RLL&&%IG-U-a<$14juY;(nrZ?bW>`@d!jO4i5yKfq1m<^FeD t8|z)S>wjoBjBk3kcrgF&5&xX6zYj2Q#6Q1>*tT2zO`HGwPApVE{%>*Pl_vlI literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/num_files.c b/.suckless/st/slstatus/components/num_files.c new file mode 100644 index 0000000..df0acd1 --- /dev/null +++ b/.suckless/st/slstatus/components/num_files.c @@ -0,0 +1,32 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +const char * +num_files(const char *path) +{ + struct dirent *dp; + DIR *dir; + int num; + + if (!(dir = opendir(path))) { + warn("opendir '%s':", path); + return NULL; + } + + num = 0; + while ((dp = readdir(dir))) { + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; /* skip self and parent */ + + num++; + } + + closedir(dir); + + return bprintf("%d", num); +} diff --git a/.suckless/st/slstatus/components/num_files.o b/.suckless/st/slstatus/components/num_files.o new file mode 100644 index 0000000000000000000000000000000000000000..b18b49e3d23ac3f744f063faa057ec8eee6a785b GIT binary patch literal 2112 zcmbu9TTc^F5P)Y3NaeDrV6+h*)&~S{C*1-s7)?t>iV|bEL<|tJw2L(M=I#;!AABH~ zG?B!g)I>@A58fWt7)^YVL=&9Zo+;at_C+T-otbYgXV1>rKA)Vq9nv&l(cmT=drSeU z?ZyLdsXkjOy#rhlOxjmi~Z^SF^Z=vpL~w^y@GZR zwcQU#-wwERsq!Pee}jrv%xZG^YpwjFS(Vi9vt(9RlFP44WxJToImXbSJ2Vaggn+>; zz*H(VZVXS|TexJHW5N`s5s$^kVxw_<7CMfBs{PM}3+(Hk5!n@7YHz9>X~D0z z;1ezQ-4=YV1&88hVRb#1w_T9|F_SXsCr&>j^b0TFS$1P zYh@=_^w#02O)gxz8T;r0(?hu>Q8rw^K${-6X1sA)Sp&egu7kvtThn$ z8f$ukEyvDV!n3zL5LwH!K&-hg2&Y62rQ;U~B2y|9>>{QVOP($8Q@rMSR_3wv9~C!6 z*};i9-Zqv}LwjR&-LVRG{R#fdFtD1wIeIriNl1t9JI5$DC@#Ka@4B*kpKVy literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/pixVol.sh b/.suckless/st/slstatus/components/pixVol.sh new file mode 100644 index 0000000..8f59505 --- /dev/null +++ b/.suckless/st/slstatus/components/pixVol.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +volume=$(pactl get-sink-volume @DEFAULT_SINK@ | grep -Po '\d+%' | head -n 1 | tr -d '%') +muted=$(pactl get-sink-mute @DEFAULT_SINK@ | awk '{print $2}') + +if [ "$muted" = "yes" ]; then + echo "Muted" +else + echo "${volume}" +fi diff --git a/.suckless/st/slstatus/components/ram.c b/.suckless/st/slstatus/components/ram.c new file mode 100644 index 0000000..15c4b74 --- /dev/null +++ b/.suckless/st/slstatus/components/ram.c @@ -0,0 +1,212 @@ +/* See LICENSE file for copyright and license details. */ +#include + +#include "../slstatus.h" +#include "../util.h" + +#if defined(__linux__) + #include + + const char * + ram_free(const char *unused) + { + uintmax_t free; + + if (pscanf("/proc/meminfo", + "MemTotal: %ju kB\n" + "MemFree: %ju kB\n" + "MemAvailable: %ju kB\n", + &free, &free, &free) != 3) + return NULL; + + return fmt_human(free * 1024, 1024); + } + + const char * + ram_perc(const char *unused) + { + uintmax_t total, free, buffers, cached; + int percent; + + if (pscanf("/proc/meminfo", + "MemTotal: %ju kB\n" + "MemFree: %ju kB\n" + "MemAvailable: %ju kB\n" + "Buffers: %ju kB\n" + "Cached: %ju kB\n", + &total, &free, &buffers, &buffers, &cached) != 5) + return NULL; + + if (total == 0) + return NULL; + + percent = 100 * ((total - free) - (buffers + cached)) / total; + return bprintf("%d", percent); + } + + const char * + ram_total(const char *unused) + { + uintmax_t total; + + if (pscanf("/proc/meminfo", "MemTotal: %ju kB\n", &total) + != 1) + return NULL; + + return fmt_human(total * 1024, 1024); + } + + const char * + ram_used(const char *unused) + { + uintmax_t total, free, buffers, cached, used; + + if (pscanf("/proc/meminfo", + "MemTotal: %ju kB\n" + "MemFree: %ju kB\n" + "MemAvailable: %ju kB\n" + "Buffers: %ju kB\n" + "Cached: %ju kB\n", + &total, &free, &buffers, &buffers, &cached) != 5) + return NULL; + + used = (total - free - buffers - cached); + return fmt_human(used * 1024, 1024); + } +#elif defined(__OpenBSD__) + #include + #include + #include + #include + + #define LOG1024 10 + #define pagetok(size, pageshift) (size_t)(size << (pageshift - LOG1024)) + + inline int + load_uvmexp(struct uvmexp *uvmexp) + { + int uvmexp_mib[] = {CTL_VM, VM_UVMEXP}; + size_t size; + + size = sizeof(*uvmexp); + + if (sysctl(uvmexp_mib, 2, uvmexp, &size, NULL, 0) >= 0) + return 1; + + return 0; + } + + const char * + ram_free(const char *unused) + { + struct uvmexp uvmexp; + int free_pages; + + if (!load_uvmexp(&uvmexp)) + return NULL; + + free_pages = uvmexp.npages - uvmexp.active; + return fmt_human(pagetok(free_pages, uvmexp.pageshift) * + 1024, 1024); + } + + const char * + ram_perc(const char *unused) + { + struct uvmexp uvmexp; + int percent; + + if (!load_uvmexp(&uvmexp)) + return NULL; + + percent = uvmexp.active * 100 / uvmexp.npages; + return bprintf("%d", percent); + } + + const char * + ram_total(const char *unused) + { + struct uvmexp uvmexp; + + if (!load_uvmexp(&uvmexp)) + return NULL; + + return fmt_human(pagetok(uvmexp.npages, + uvmexp.pageshift) * 1024, 1024); + } + + const char * + ram_used(const char *unused) + { + struct uvmexp uvmexp; + + if (!load_uvmexp(&uvmexp)) + return NULL; + + return fmt_human(pagetok(uvmexp.active, + uvmexp.pageshift) * 1024, 1024); + } +#elif defined(__FreeBSD__) + #include + #include + #include + #include + + const char * + ram_free(const char *unused) { + struct vmtotal vm_stats; + int mib[] = {CTL_VM, VM_TOTAL}; + size_t len; + + len = sizeof(struct vmtotal); + if (sysctl(mib, 2, &vm_stats, &len, NULL, 0) < 0 + || !len) + return NULL; + + return fmt_human(vm_stats.t_free * getpagesize(), 1024); + } + + const char * + ram_total(const char *unused) { + unsigned int npages; + size_t len; + + len = sizeof(npages); + if (sysctlbyname("vm.stats.vm.v_page_count", + &npages, &len, NULL, 0) < 0 || !len) + return NULL; + + return fmt_human(npages * getpagesize(), 1024); + } + + const char * + ram_perc(const char *unused) { + unsigned int npages; + unsigned int active; + size_t len; + + len = sizeof(npages); + if (sysctlbyname("vm.stats.vm.v_page_count", + &npages, &len, NULL, 0) < 0 || !len) + return NULL; + + if (sysctlbyname("vm.stats.vm.v_active_count", + &active, &len, NULL, 0) < 0 || !len) + return NULL; + + return bprintf("%d", active * 100 / npages); + } + + const char * + ram_used(const char *unused) { + unsigned int active; + size_t len; + + len = sizeof(active); + if (sysctlbyname("vm.stats.vm.v_active_count", + &active, &len, NULL, 0) < 0 || !len) + return NULL; + + return fmt_human(active * getpagesize(), 1024); + } +#endif diff --git a/.suckless/st/slstatus/components/ram.o b/.suckless/st/slstatus/components/ram.o new file mode 100644 index 0000000000000000000000000000000000000000..55c1c637e9f16b91dd80fb65329852440ab55c41 GIT binary patch literal 3072 zcmc&#O>7%g5FR@TF(F+A6ro7vum`6&3T$JOh$t$^t<&UHby0*;sX?gj#$MwPuWjr# zYM~O9A`Zri)H6s3ZX7xGgj|72x%R{j=!Hsfsf5%VkivZHH_7YP6(|=*dO!2M`Fk_- zc0W0rUx~+JBow1J>5vnY=tkmjpAENJvgjD8oj<44?g9|C`v#Vh>JCq*z;~9DiF8kO z?w{-Ysye?t!0FE3PgJOP_32N+tm@oV zvryz|B7F~wt?!_=dnNVgVdqY|*N=ijOAQEZ5uyG2{X-l3cmJo0w_jG>>wM=f-?g9h zA-=Gh`BBYY=Nm^$F!-JqJb%3&xYZ?l`u(=OeP)b-m8R#J{OJ$ft*X0OJp}1wqggMW z^gVy8R<6_0jGSqg%U-i};3?-8D_-ehKW{lxg|Tb`qsr6JH6nfowS{q%Ph>z zXTn}Z^O4PCga->zoMJs-()+OpKgnmH*hglD=?hHDR<>%X6>IIhrA}Mf_$4a=-Ag!Qx{L|B8-3jdc&5)VMRixL1mHo0_=oA? z{-Cp=gWDaRj)q%g;c*%d*%5mEahRUh_}K{lqQ=9!8fV8f{-uFoYx8))h$qY)V>3J^ zctmNAfkF7vQ}FYQ!@k&+xYnN{|E`vokivQ}R@V4s-56m#7^9}^J4JHxxj9T}Oc_kC zVVcKrq0H}t8m*#RD^uAI3YE6+)~HZu1#WS>P^@eh%KTtL(T3M7(q^N%RSU`?5a6+e zyLQVf5%udt+^0lCJpSJAlZ9BAfTMo}e+3{DuxEJ)o&(4Pe2jN>y9RoN0{$k*!Ut8w^_r(xe)EmL2&!vunOFst@dFf|z zTG#x*-Q+cB_ScH+nqJj)0`F=-PRR{iayDBna+-B85a*97ImNo~do>oR)dSDrPwA^+ zdu#r^THE1Y0JnE=ja6aLtMJ2ceF<1Dbi~zWJ){mjWl~l%4|HDTEW#4LPpDf~1oZk1 zip;d96;Kmw<-^ +#include + +#include "../slstatus.h" +#include "../util.h" + +const char * +run_command(const char *cmd) +{ + char *p; + FILE *fp; + + if (!(fp = popen(cmd, "r"))) { + warn("popen '%s':", cmd); + return NULL; + } + + p = fgets(buf, sizeof(buf) - 1, fp); + if (pclose(fp) < 0) { + warn("pclose '%s':", cmd); + return NULL; + } + if (!p) + return NULL; + + if ((p = strrchr(buf, '\n'))) + p[0] = '\0'; + + return buf[0] ? buf : NULL; +} diff --git a/.suckless/st/slstatus/components/run_command.o b/.suckless/st/slstatus/components/run_command.o new file mode 100644 index 0000000000000000000000000000000000000000..14993236eef25ebc1dd7e5d36d46ba914e2c970b GIT binary patch literal 2120 zcmbtUO=}ZT6upzQw$&J;C{!vk8?A~Cnb4vXl%&OW3blw@L{!Q&owi`};mibET~sI) z0~W*|RDxf)7TmgWArui;E-KV}X5LLEL$c8WZ{EG-j7Ot5Hi?^{>|URk*uZtNuaRR{ibHev&BE)qhUc z-v{PaWBo)c!fXVj+1NF1Y<=^8?;J$dFq>5HlOKtm2L;IgVr}=L)cuEr2k%^}R;o@} zA3oy^k5RR_TJap^(A31ln0{_*cJ91xjvA(6>bY!gG&_A{jFiS$s@$3Zo-d)WPrzY z7yc$l2J&ET!rukSfc_9S;a5R2;&mT~bf8bME&2_`i+)e7w;tm~uSa;qG+}t_CEs2k zgZDD*SHyCiRon2LHJ^+{+qcPB@H{fy3LMD!9g=}FbjmC#SA56dr+3No?cyWlFPCdZ z)#Z2X`s*BugwD$Rl53Zo<_!MJ&^a1!?LaI`UiotWWfn?ZFiFWjEaxXOZJ~70q{N`jmPIzycNot-*`OUU8nQU zf@H`!o2s0Gc!i9n@mGDq3!6mO~dKWi#O HBOd=3SeX5} literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/swap.c b/.suckless/st/slstatus/components/swap.c new file mode 100644 index 0000000..f270d93 --- /dev/null +++ b/.suckless/st/slstatus/components/swap.c @@ -0,0 +1,274 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +#if defined(__linux__) + static int + get_swap_info(long *s_total, long *s_free, long *s_cached) + { + FILE *fp; + struct { + const char *name; + const size_t len; + long *var; + } ent[] = { + { "SwapTotal", sizeof("SwapTotal") - 1, s_total }, + { "SwapFree", sizeof("SwapFree") - 1, s_free }, + { "SwapCached", sizeof("SwapCached") - 1, s_cached }, + }; + size_t line_len = 0, i, left; + char *line = NULL; + + /* get number of fields we want to extract */ + for (i = 0, left = 0; i < LEN(ent); i++) + if (ent[i].var) + left++; + + if (!(fp = fopen("/proc/meminfo", "r"))) { + warn("fopen '/proc/meminfo':"); + return 1; + } + + /* read file line by line and extract field information */ + while (left > 0 && getline(&line, &line_len, fp) >= 0) { + for (i = 0; i < LEN(ent); i++) { + if (ent[i].var && + !strncmp(line, ent[i].name, ent[i].len)) { + sscanf(line + ent[i].len + 1, + "%ld kB\n", ent[i].var); + left--; + break; + } + } + } + free(line); + if (ferror(fp)) { + warn("getline '/proc/meminfo':"); + return 1; + } + + fclose(fp); + return 0; + } + + const char * + swap_free(const char *unused) + { + long free; + + if (get_swap_info(NULL, &free, NULL)) + return NULL; + + return fmt_human(free * 1024, 1024); + } + + const char * + swap_perc(const char *unused) + { + long total, free, cached; + + if (get_swap_info(&total, &free, &cached) || total == 0) + return NULL; + + return bprintf("%d", 100 * (total - free - cached) / total); + } + + const char * + swap_total(const char *unused) + { + long total; + + if (get_swap_info(&total, NULL, NULL)) + return NULL; + + return fmt_human(total * 1024, 1024); + } + + const char * + swap_used(const char *unused) + { + long total, free, cached; + + if (get_swap_info(&total, &free, &cached)) + return NULL; + + return fmt_human((total - free - cached) * 1024, 1024); + } +#elif defined(__OpenBSD__) + #include + #include + #include + #include + + static int + getstats(int *total, int *used) + { + struct swapent *sep, *fsep; + int rnswap, nswap, i; + + if ((nswap = swapctl(SWAP_NSWAP, 0, 0)) < 1) { + warn("swaptctl 'SWAP_NSWAP':"); + return 1; + } + if (!(fsep = sep = calloc(nswap, sizeof(*sep)))) { + warn("calloc 'nswap':"); + return 1; + } + if ((rnswap = swapctl(SWAP_STATS, (void *)sep, nswap)) < 0) { + warn("swapctl 'SWAP_STATA':"); + return 1; + } + if (nswap != rnswap) { + warn("getstats: SWAP_STATS != SWAP_NSWAP"); + return 1; + } + + *total = 0; + *used = 0; + + for (i = 0; i < rnswap; i++) { + *total += sep->se_nblks >> 1; + *used += sep->se_inuse >> 1; + } + + free(fsep); + + return 0; + } + + const char * + swap_free(const char *unused) + { + int total, used; + + if (getstats(&total, &used)) + return NULL; + + return fmt_human((total - used) * 1024, 1024); + } + + const char * + swap_perc(const char *unused) + { + int total, used; + + if (getstats(&total, &used)) + return NULL; + + if (total == 0) + return NULL; + + return bprintf("%d", 100 * used / total); + } + + const char * + swap_total(const char *unused) + { + int total, used; + + if (getstats(&total, &used)) + return NULL; + + return fmt_human(total * 1024, 1024); + } + + const char * + swap_used(const char *unused) + { + int total, used; + + if (getstats(&total, &used)) + return NULL; + + return fmt_human(used * 1024, 1024); + } +#elif defined(__FreeBSD__) + #include + #include + #include + #include + #include + + static int getswapinfo(struct kvm_swap *swap_info, size_t size) + { + kvm_t *kd; + + kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, NULL); + if (kd == NULL) { + warn("kvm_openfiles '/dev/null':"); + return 0; + } + + if (kvm_getswapinfo(kd, swap_info, size, 0 /* Unused flags */) < 0) { + warn("kvm_getswapinfo:"); + kvm_close(kd); + return 0; + } + + kvm_close(kd); + return 1; + } + + const char * + swap_free(const char *unused) + { + struct kvm_swap swap_info[1]; + long used, total; + + if (!getswapinfo(swap_info, 1)) + return NULL; + + total = swap_info[0].ksw_total; + used = swap_info[0].ksw_used; + + return fmt_human((total - used) * getpagesize(), 1024); + } + + const char * + swap_perc(const char *unused) + { + struct kvm_swap swap_info[1]; + long used, total; + + if (!getswapinfo(swap_info, 1)) + return NULL; + + total = swap_info[0].ksw_total; + used = swap_info[0].ksw_used; + + return bprintf("%d", used * 100 / total); + } + + const char * + swap_total(const char *unused) + { + struct kvm_swap swap_info[1]; + long total; + + if (!getswapinfo(swap_info, 1)) + return NULL; + + total = swap_info[0].ksw_total; + + return fmt_human(total * getpagesize(), 1024); + } + + const char * + swap_used(const char *unused) + { + struct kvm_swap swap_info[1]; + long used; + + if (!getswapinfo(swap_info, 1)) + return NULL; + + used = swap_info[0].ksw_used; + + return fmt_human(used * getpagesize(), 1024); + } +#endif diff --git a/.suckless/st/slstatus/components/swap.o b/.suckless/st/slstatus/components/swap.o new file mode 100644 index 0000000000000000000000000000000000000000..b5cf1d73b6d4f1234c0583bf3b80ef16075db861 GIT binary patch literal 3880 zcmb`JZERCj7{^cd0u{!kB4`bU)TBreE$!H3BS_Y5z`+zlV3Qfe(zUmAS>J4L!;mFv z$~4zn-x4DxnrPw&qhE;l!Ni2rfP(ssPei`>lDO%JMocgop+5g}&)KfmHZjpBx#v9R zcb=E?a?iQ<$(_+%Z7vs6;$l14VoOoR-d(<^4+wRDX>0|H9E>~^8I6pL9tdCNl`B(8 zUhP;PAOmg=`_sJE30rPX`$OC`{G6X3_6KePB4xlWm9b^QXvBXQUcLpdVSk@Z-xsd) znFg=jPsVKJt7m!Th70x{Uh4(omCFrY`9m--8gqhL_I#$k^2GZD_L%4R%w=Oauffyo z@Xv4M)oH(17)Q-nQJZIB{v3i@?(?N+wd~nDbFCcWl~29V{_EwRc(upRYvdX=zqL}$ z)Td5Is~NwS*D`)zl={;1=XrJ1&8uY=HGk&jh~I6QR!N(wmp|B3n_lCR6Ns8udE-mb z|HD|L(FoT+##RV0?^a{uRa)32-()Sb(P_o2oSQN)J#r}WXyj1ju}F*-=ZZIcG2Ed2 z=#l$j_}$$6e(r%AjRs|2&8yq}XIKYK@srtQR*p7WVGN9-uNF>=(Y=TmplDVHWD4S$0hwz2h@q;GO?89SmInTed<#30Go z?xCS=zQFDWM>qPyy}@uW?CTD7_lA19#fhM;p#5b&dT-AELI$!O(oA1>e(x?`y%wTJXay zc%cO^x8Tz)c#Zff+DEm2;yohvS_}KPrTvd`>&5#>44;r@>vLM#$J6FA*5s4;OB1fxSCMnxD!H;Ntx!R?v$HHeM)B3~{ZA5tmDJnca!GkYIYaSN^2$7UUox2{=xP;t4T*E; zvGS2WwjO8BE(5Z$My|K&ldp+titifsVqk$-iui{owIDYG?>KzbM;PB|$X)b+JJPUG zCx9`DGygrr5YuV?Z~enEW;fM#QT`KlNV>|e>H*0@4!bjQKF>d&l3cK=h0^Jd^VMwsPstyhDd~y* z$Gxlk)&4ij4VOzY|66ikxO;G)JN*%}*-}kmQAnKQcdj4q44CA&RV|cG|Gxl} C2eDHC literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/temperature.c b/.suckless/st/slstatus/components/temperature.c new file mode 100644 index 0000000..7cf1394 --- /dev/null +++ b/.suckless/st/slstatus/components/temperature.c @@ -0,0 +1,73 @@ +/* See LICENSE file for copyright and license details. */ +#include + +#include "../slstatus.h" +#include "../util.h" + + +#if defined(__linux__) + #include + + const char * + temp(const char *file) + { + uintmax_t temp; + + if (pscanf(file, "%ju", &temp) != 1) + return NULL; + + return bprintf("%ju", temp / 1000); + } +#elif defined(__OpenBSD__) + #include + #include /* before for struct timeval */ + #include + #include + + const char * + temp(const char *unused) + { + int mib[5]; + size_t size; + struct sensor temp; + + mib[0] = CTL_HW; + mib[1] = HW_SENSORS; + mib[2] = 0; /* cpu0 */ + mib[3] = SENSOR_TEMP; + mib[4] = 0; /* temp0 */ + + size = sizeof(temp); + + if (sysctl(mib, 5, &temp, &size, NULL, 0) < 0) { + warn("sysctl 'SENSOR_TEMP':"); + return NULL; + } + + /* kelvin to celsius */ + return bprintf("%d", (int)((float)(temp.value-273150000) / 1E6)); + } +#elif defined(__FreeBSD__) + #include + #include + #include + + #define ACPI_TEMP "hw.acpi.thermal.%s.temperature" + + const char * + temp(const char *zone) + { + char buf[256]; + int temp; + size_t len; + + len = sizeof(temp); + snprintf(buf, sizeof(buf), ACPI_TEMP, zone); + if (sysctlbyname(buf, &temp, &len, NULL, 0) < 0 + || !len) + return NULL; + + /* kelvin to decimal celcius */ + return bprintf("%d.%d", (temp - 2731) / 10, abs((temp - 2731) % 10)); + } +#endif diff --git a/.suckless/st/slstatus/components/temperature.o b/.suckless/st/slstatus/components/temperature.o new file mode 100644 index 0000000000000000000000000000000000000000..0a95a28258fde584bdc827d60a5e1fcaf41a6240 GIT binary patch literal 1720 zcmbtTPiqrV5T8v`8?nYJJwz(FLMjpUkqrh^P!?0|3dMuyK|Cy*WMgWZKkmLjss|4} zl~V8{c<|sy&|APFJ^K+n1@zLB9)vja@+R4ATPQm4c4mI_o0<1#_U-+Z2MNOf76b0W z*kcsn=G54>m1#p7PJ^@gGvn+O38^~U$;BKR&el>kW%Zn`?|s4td!0)d%6{%+4eOh; zbB9pp*S@p$)#@qAj|9@UpJY>2=jyzwA*WTq+1$;1S$x?6ST2=ro4MsjPp_C(QCPw< z3;9AZf1{v!LlR4xxTbL-2@+uR2;;LDF@GLoB*yE_1z;#3ERo2`}ea)?TtLv&6erB|hKux3YXE2nD8mGIa zQ!ze#6~K3x);H5z^sDb1Gj1&+*hb#U#!Xm&(9XyJ{?4NRqie%-AFUc(>l;J*wL zvuSqd4x(afhu@!LJjd)Wph0(TGoW=&Us6~5r%+Qa4{%1~mo*;wtyScMj>lEJ|1~5~ z&410eFqdi;a7Bgt)7hfJ{aM=>Bs(7^q5V<&{10=O^H@K`0NczVy&H8RcAWbRV>G7| zGl~A5&WZ1f?_Y=E6&h4Cp8xxp`SfGb9od@C>b)+=?@aH15*PPp{IK3sBc~7ABL`&` m7xjtfQ_c9>BLsb&e~X-mXn2I2MeDHqPkR4vM$DlR&;JYM7^Tht literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/uptime.c b/.suckless/st/slstatus/components/uptime.c new file mode 100644 index 0000000..6227f73 --- /dev/null +++ b/.suckless/st/slstatus/components/uptime.c @@ -0,0 +1,34 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +#if defined(CLOCK_BOOTTIME) + #define UPTIME_FLAG CLOCK_BOOTTIME +#elif defined(CLOCK_UPTIME) + #define UPTIME_FLAG CLOCK_UPTIME +#else + #define UPTIME_FLAG CLOCK_MONOTONIC +#endif + +const char * +uptime(const char *unused) +{ + char warn_buf[256]; + uintmax_t h, m; + struct timespec uptime; + + if (clock_gettime(UPTIME_FLAG, &uptime) < 0) { + snprintf(warn_buf, sizeof(warn_buf), "clock_gettime %d", UPTIME_FLAG); + warn(warn_buf); + return NULL; + } + + h = uptime.tv_sec / 3600; + m = uptime.tv_sec % 3600 / 60; + + return bprintf("%juh %jum", h, m); +} diff --git a/.suckless/st/slstatus/components/uptime.o b/.suckless/st/slstatus/components/uptime.o new file mode 100644 index 0000000000000000000000000000000000000000..61f6704ecd75afaadd9ff0922e3ef007d33cb294 GIT binary patch literal 1968 zcmbtUOKTHR6h4zSHfoJgDirEsgj8CzLngEc;v=Qjj!?u0g5n}fl1WpWmy;>BDoBwo zLMga#Q4ztVxaz`9HyTu2`!hsc=t=~kp6}+~WHKZh{ou|y=R2=E_uQFBv-1~2nnns5 zoujs;SfWIKTTcl!MNv9H*WV4WWvzYJIV)#vOpcDhuD^*7GhuCd@yHj1uQygFc0}mr z5!i3h#cWuc597lkpi}>rur@BlBRjDuikwA;_00P9d*=+a`X{UYS=d(nz1a}?*2YuO zGH0C_5_|B%O@8eows!kNwpht7r57B}TP!*HXpTnjEEjZsl!)eLW=`v4bC++7>*kbU z8m691B&QOS$tiRO@cKHhFfWdy5NQo?Tv|X2*h7lHe~q-2m^Lyv5P8DEapsRo-)qR} zXzX!lKDr$WJ&(qwqY>%rCD5;*e(rYj_qBG@_U;oZcHN1C!VJ@)PMWaq!@fFVkIaqm ziUnd}j`!e`J@`{S`13t@TCQSVMwX2EnFJoDq^M({R4Z5A#j=;Dd$wDqj5N~enrHLa zvxTK}-d-$<^XFD#o_kpS*==GHBqPMDkC)7BU>qzoHB=$E1qNURh|$hB<+RrvQc#_Rmb&KITZz+LOSo-C8xE6|59|$#vR7l z`;n4{x^K#-zAt6t9^$N0ijeY$Jfv6B-p>-b>U$cN{Bg-+#(sSD8$LbQ|0MfR^I!8# z&P7dpAAYF*ct1Z>effT=Do@Ev>`=OjfjriY>=Jv6IeZ5lcarTf znIGJjI)4rkxXVw32J>&q{FI1lKZ5y1c6FQomK=zhGv<%)Oyw)STW@sRlKmr64l;+o kW-uQ$gR^IfUdk7`0>cMcuEpSg+bw@vk`G#uO(mHB7h@pVmjD0& literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/user.c b/.suckless/st/slstatus/components/user.c new file mode 100644 index 0000000..3517495 --- /dev/null +++ b/.suckless/st/slstatus/components/user.c @@ -0,0 +1,33 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +const char * +gid(const char *unused) +{ + return bprintf("%d", getgid()); +} + +const char * +username(const char *unused) +{ + struct passwd *pw; + + if (!(pw = getpwuid(geteuid()))) { + warn("getpwuid '%d':", geteuid()); + return NULL; + } + + return bprintf("%s", pw->pw_name); +} + +const char * +uid(const char *unused) +{ + return bprintf("%d", geteuid()); +} diff --git a/.suckless/st/slstatus/components/user.o b/.suckless/st/slstatus/components/user.o new file mode 100644 index 0000000000000000000000000000000000000000..7e2240e249a2b50cb4bcaf4e25a2270b1ddc743e GIT binary patch literal 2192 zcmb`HOHUL*5XWl}1bJ*$)cC-dxgeV8xU)heM9rGb%9@}dMhTFp8Q5VVun%X40DAD` zffy6MhhJh76ZJ!Q@Tf=cCRqQTuHD{-y%;O$sj6Q+x~Hdm-WBE^_BalSICP)do=~E6 zU)#Jq4}S1iXZPmE$@Hvo4;T!D7^nQiN0^fqs0#yo#i)|RE}ARLgo9-<9y6aDQzURByOm-?WnVrHqsF#P!1IyLI5IChzE2{#lMeoHtL&*-blR!~FvXoD6_;oXYk14;p4!RH%hcp zk2b3Dno6n$l@L7SbJRLev=c;CwCTZv1!tt6$DhMQP64tYj=d9p5s(G>gFJ-0Y(v0@ zcnHTnK+p#<;dcR9>^}Jft9+bc$)D34`NIZZFu1v%7nBysYR^ z?rM(y7$Vc!5H9Cp(cE_`iDS6;<{3u%4?I}xb;7*))TEuqSki(7W3o2e-V(Sm?-s6!9Pjr<@+BuH%fN({PSAlpMf)K*Q2NX f?HSQG{XzvehUj)Ja+JRk#-?lOn!><1efSK`$ literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/components/volume.c b/.suckless/st/slstatus/components/volume.c new file mode 100644 index 0000000..6cec556 --- /dev/null +++ b/.suckless/st/slstatus/components/volume.c @@ -0,0 +1,219 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +#if defined(__OpenBSD__) | defined(__FreeBSD__) + #include + #include + #include + #include + + struct control { + LIST_ENTRY(control) next; + unsigned int addr; + #define CTRL_NONE 0 + #define CTRL_LEVEL 1 + #define CTRL_MUTE 2 + unsigned int type; + unsigned int maxval; + unsigned int val; + }; + + static LIST_HEAD(, control) controls = LIST_HEAD_INITIALIZER(controls); + static struct pollfd *pfds; + static struct sioctl_hdl *hdl; + static int initialized; + + /* + * Call-back to obtain the description of all audio controls. + */ + static void + ondesc(void *unused, struct sioctl_desc *desc, int val) + { + struct control *c, *ctmp; + unsigned int type = CTRL_NONE; + + if (desc == NULL) + return; + + /* Delete existing audio control with the same address. */ + LIST_FOREACH_SAFE(c, &controls, next, ctmp) { + if (desc->addr == c->addr) { + LIST_REMOVE(c, next); + free(c); + break; + } + } + + /* Only match output.level and output.mute audio controls. */ + if (desc->group[0] != 0 || + strcmp(desc->node0.name, "output") != 0) + return; + if (desc->type == SIOCTL_NUM && + strcmp(desc->func, "level") == 0) + type = CTRL_LEVEL; + else if (desc->type == SIOCTL_SW && + strcmp(desc->func, "mute") == 0) + type = CTRL_MUTE; + else + return; + + c = malloc(sizeof(struct control)); + if (c == NULL) { + warn("sndio: failed to allocate audio control\n"); + return; + } + + c->addr = desc->addr; + c->type = type; + c->maxval = desc->maxval; + c->val = val; + LIST_INSERT_HEAD(&controls, c, next); + } + + /* + * Call-back invoked whenever an audio control changes. + */ + static void + onval(void *unused, unsigned int addr, unsigned int val) + { + struct control *c; + + LIST_FOREACH(c, &controls, next) { + if (c->addr == addr) + break; + } + c->val = val; + } + + static void + cleanup(void) + { + struct control *c; + + if (hdl) { + sioctl_close(hdl); + hdl = NULL; + } + + free(pfds); + pfds = NULL; + + while (!LIST_EMPTY(&controls)) { + c = LIST_FIRST(&controls); + LIST_REMOVE(c, next); + free(c); + } + } + + static int + init(void) + { + hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0); + if (hdl == NULL) { + warn("sndio: cannot open device"); + goto failed; + } + + if (!sioctl_ondesc(hdl, ondesc, NULL)) { + warn("sndio: cannot set control description call-back"); + goto failed; + } + + if (!sioctl_onval(hdl, onval, NULL)) { + warn("sndio: cannot set control values call-back"); + goto failed; + } + + pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd)); + if (pfds == NULL) { + warn("sndio: cannot allocate pollfd structures"); + goto failed; + } + + return 1; + failed: + cleanup(); + return 0; + } + + const char * + vol_perc(const char *unused) + { + struct control *c; + int n, v, value; + + if (!initialized) + initialized = init(); + + if (hdl == NULL) + return NULL; + + n = sioctl_pollfd(hdl, pfds, POLLIN); + if (n > 0) { + n = poll(pfds, n, 0); + if (n > 0) { + if (sioctl_revents(hdl, pfds) & POLLHUP) { + warn("sndio: disconnected"); + cleanup(); + initialized = 0; + return NULL; + } + } + } + + value = 100; + LIST_FOREACH(c, &controls, next) { + if (c->type == CTRL_MUTE && c->val == 1) + value = 0; + else if (c->type == CTRL_LEVEL) { + v = (c->val * 100 + c->maxval / 2) / c->maxval; + /* For multiple channels return the minimum. */ + if (v < value) + value = v; + } + } + + return bprintf("%d", value); + } +#else + #include + + const char * + vol_perc(const char *card) + { + size_t i; + int v, afd, devmask; + char *vnames[] = SOUND_DEVICE_NAMES; + + if ((afd = open(card, O_RDONLY | O_NONBLOCK)) < 0) { + warn("open '%s':", card); + return NULL; + } + + if (ioctl(afd, (int)SOUND_MIXER_READ_DEVMASK, &devmask) < 0) { + warn("ioctl 'SOUND_MIXER_READ_DEVMASK':"); + close(afd); + return NULL; + } + for (i = 0; i < LEN(vnames); i++) { + if (devmask & (1 << i) && !strcmp("vol", vnames[i])) { + if (ioctl(afd, MIXER_READ(i), &v) < 0) { + warn("ioctl 'MIXER_READ(%ld)':", i); + close(afd); + return NULL; + } + } + } + + close(afd); + + return bprintf("%d", v & 0xff); + } +#endif diff --git a/.suckless/st/slstatus/components/volume.o b/.suckless/st/slstatus/components/volume.o new file mode 100644 index 0000000000000000000000000000000000000000..9328f1173fbff1e456c2bed594545fa26d53df04 GIT binary patch literal 3584 zcmds(O>7%g5P-*V+NKa3Q!13IJuE08siLlIC!r-0U^hv-ZIcis4JzWtIO|Pp#rB%@ zCQbMesag;wMCt*d9ynA~h#WwaOI0LPSrjNI#09AmsPqO>kt)<1MTD94OtOBpFB}=^ z?VI`D%)Gbz{`Sq`kwZ;^0Eh(ODcH1x0>tn0>7E*gHV8rDxUt$#_LnhyOidc)yT++M zZlazxR+>AzQ8CJAI<8|Ktr)AXcZBbJd<9yF{`xyYc&oDXkWs#2tUgIrwBo^A`^(qL z-x{mOJ6erv-~BV%yd15Jtfo7{(Vrz~S8YW3w$~7?+?rb%>S%4daJ}^Ok(It**o&j) zd#(T`qr@%c+s5`AlC91VJ~P&@8mI5>n*W@0w_S2OT55YWR^InsEp7Dd^xoCo;$<{W zeRC7v=J^*BFI=V9`u~Vsvtm&KurKN(Kfufm*QqfAySq|i~1qV`&*hUg~ zbpis5;lS?JZ7pZfxewVMuLa;y3O*PLzt=PxYJWSJ2x+IAkA{|;g26yYON81Jq42>_ z3p+QcC`B{;xn8e1xG}og)*u9H0b4=XdJnH|_j`HR^V|yeD-jPf->2~1%%ci_g!xg0 zccHz*H-7J1A&X$C_FWr5vs8}{G~n?D{7?fv+JH|r;I!uF^OkOcJ~9~F?>!IDGvcXF z(6cu}Udf{6m_YjpPNtkfb)$fpvrD)cT*u58VY=vK3+@aglO;E0&Lz#vTyiFr&0*iP z9!Z+8(5(KwUKQ^YMVrRfZH;SMpHwzPFD>QW z)HLXLW>o8pXpWUj>8`ctg6^s5l$!#58t+`)vB{tqyzxLc?R?%UpkKjuEgkQO<|H?nPp1 z|B;$WMh6ximYDE3YQ$n2q^d~#j|!J~sfV$h^oZgo1CoChX_h#t$2eZvrQV~y5I0Di z_`QS*eJb^JHI7%uCErit7f_*`YWz6IkBcMMYWyq6>&@R9T>>bwPH6t +#include +#include +#include +#include +#include + +#include "../slstatus.h" +#include "../util.h" + +#define RSSI_TO_PERC(rssi) \ + rssi >= -50 ? 100 : \ + (rssi <= -100 ? 0 : \ + (2 * (rssi + 100))) + +#if defined(__linux__) + #include + #include + #include + #include + #include + + static int nlsock = -1; + static uint32_t seq = 1; + static char resp[4096]; + + static char * + findattr(int attr, const char *p, const char *e, size_t *len) + { + while (p < e) { + struct nlattr nla; + memcpy(&nla, p, sizeof(nla)); + if (nla.nla_type == attr) { + *len = nla.nla_len - NLA_HDRLEN; + return (char *)(p + NLA_HDRLEN); + } + p += NLA_ALIGN(nla.nla_len); + } + return NULL; + } + + static uint16_t + nl80211fam(void) + { + static const char family[] = "nl80211"; + static uint16_t id; + ssize_t r; + size_t len; + char ctrl[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(sizeof(family))] = {0}, *p = ctrl; + + if (id) + return id; + + memcpy(p, &(struct nlmsghdr){ + .nlmsg_len = sizeof(ctrl), + .nlmsg_type = GENL_ID_CTRL, + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_seq = seq++, + .nlmsg_pid = 0, + }, sizeof(struct nlmsghdr)); + p += NLMSG_HDRLEN; + memcpy(p, &(struct genlmsghdr){ + .cmd = CTRL_CMD_GETFAMILY, + .version = 1, + }, sizeof(struct genlmsghdr)); + p += GENL_HDRLEN; + memcpy(p, &(struct nlattr){ + .nla_len = NLA_HDRLEN+sizeof(family), + .nla_type = CTRL_ATTR_FAMILY_NAME, + }, sizeof(struct nlattr)); + p += NLA_HDRLEN; + memcpy(p, family, sizeof(family)); + + if (nlsock < 0) + nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (nlsock < 0) { + warn("socket 'AF_NETLINK':"); + return 0; + } + if (send(nlsock, ctrl, sizeof(ctrl), 0) != sizeof(ctrl)) { + warn("send 'AF_NETLINK':"); + return 0; + } + r = recv(nlsock, resp, sizeof(resp), 0); + if (r < 0) { + warn("recv 'AF_NETLINK':"); + return 0; + } + if ((size_t)r <= sizeof(ctrl)) + return 0; + p = findattr(CTRL_ATTR_FAMILY_ID, resp + sizeof(ctrl), resp + r, &len); + if (p && len == 2) + memcpy(&id, p, 2); + + return id; + } + + static int + ifindex(const char *interface) + { + static struct ifreq ifr; + static int ifsock = -1; + + if (ifsock < 0) + ifsock = socket(AF_UNIX, SOCK_DGRAM, 0); + if (ifsock < 0) { + warn("socket 'AF_UNIX':"); + return -1; + } + if (strcmp(ifr.ifr_name, interface) != 0) { + strcpy(ifr.ifr_name, interface); + if (ioctl(ifsock, SIOCGIFINDEX, &ifr) != 0) { + warn("ioctl 'SIOCGIFINDEX':"); + return -1; + } + } + return ifr.ifr_ifindex; + } + + const char * + wifi_essid(const char *interface) + { + uint16_t fam = nl80211fam(); + ssize_t r; + size_t len; + char req[NLMSG_HDRLEN+GENL_HDRLEN+NLA_HDRLEN+NLA_ALIGN(4)] = {0}, *p = req; + int idx = ifindex(interface); + if (!fam) { + fprintf(stderr, "nl80211 family not found\n"); + return NULL; + } + if (idx < 0) { + fprintf(stderr, "interface %s not found\n", interface); + return NULL; + } + + memcpy(p, &(struct nlmsghdr){ + .nlmsg_len = sizeof(req), + .nlmsg_type = fam, + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_seq = seq++, + .nlmsg_pid = 0, + }, sizeof(struct nlmsghdr)); + p += NLMSG_HDRLEN; + memcpy(p, &(struct genlmsghdr){ + .cmd = NL80211_CMD_GET_INTERFACE, + .version = 1, + }, sizeof(struct genlmsghdr)); + p += GENL_HDRLEN; + memcpy(p, &(struct nlattr){ + .nla_len = NLA_HDRLEN+4, + .nla_type = NL80211_ATTR_IFINDEX, + }, sizeof(struct nlattr)); + p += NLA_HDRLEN; + memcpy(p, &(uint32_t){idx}, 4); + + if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) { + warn("send 'AF_NETLINK':"); + return NULL; + } + r = recv(nlsock, resp, sizeof(resp), 0); + if (r < 0) { + warn("recv 'AF_NETLINK':"); + return NULL; + } + + if ((size_t)r <= NLMSG_HDRLEN + GENL_HDRLEN) + return NULL; + p = findattr(NL80211_ATTR_SSID, resp + NLMSG_HDRLEN + GENL_HDRLEN, resp + r, &len); + if (p) + p[len] = 0; + + return p; + } + + const char * + wifi_perc(const char *interface) + { + static char strength[4]; + struct nlmsghdr hdr; + uint16_t fam = nl80211fam(); + ssize_t r; + size_t len; + char req[NLMSG_HDRLEN + GENL_HDRLEN + NLA_HDRLEN + NLA_ALIGN(4)] = {0}, *p = req, *e; + int idx = ifindex(interface); + + if (idx < 0) { + fprintf(stderr, "interface %s not found\n", interface); + return NULL; + } + + memcpy(p, &(struct nlmsghdr){ + .nlmsg_len = sizeof(req), + .nlmsg_type = fam, + .nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP, + .nlmsg_seq = seq++, + .nlmsg_pid = 0, + }, sizeof(struct nlmsghdr)); + p += NLMSG_HDRLEN; + memcpy(p, &(struct genlmsghdr){ + .cmd = NL80211_CMD_GET_STATION, + .version = 1, + }, sizeof(struct genlmsghdr)); + p += GENL_HDRLEN; + memcpy(p, &(struct nlattr){ + .nla_len = NLA_HDRLEN + 4, + .nla_type = NL80211_ATTR_IFINDEX, + }, sizeof(struct nlattr)); + p += NLA_HDRLEN; + memcpy(p, &idx, 4); + + if (send(nlsock, req, sizeof(req), 0) != sizeof(req)) { + warn("send 'AF_NETLINK':"); + return NULL; + } + + *strength = 0; + while (1) { + r = recv(nlsock, resp, sizeof(resp), 0); + if (r < 0) { + warn("recv 'AF_NETLINK':"); + return NULL; + } + if ((size_t)r < sizeof(hdr)) + return NULL; + + for (p = resp; p != resp + r && (size_t)(resp + r-p) >= sizeof(hdr); p = e) { + memcpy(&hdr, p, sizeof(hdr)); + e = resp + r - p < hdr.nlmsg_len ? resp + r : p + hdr.nlmsg_len; + + if (!*strength && hdr.nlmsg_len > NLMSG_HDRLEN+GENL_HDRLEN) { + p += NLMSG_HDRLEN+GENL_HDRLEN; + p = findattr(NL80211_ATTR_STA_INFO, p, e, &len); + if (p) + p = findattr(NL80211_STA_INFO_SIGNAL_AVG, p, e, &len); + if (p && len == 1) + snprintf(strength, sizeof(strength), "%d", RSSI_TO_PERC(*p)); + } + if (hdr.nlmsg_type == NLMSG_DONE) + return *strength ? strength : NULL; + } + } + } +#elif defined(__OpenBSD__) + #include + #include + #include + #include /* before for NBBY */ + #include + #include + #include + + static int + load_ieee80211_nodereq(const char *interface, struct ieee80211_nodereq *nr) + { + struct ieee80211_bssid bssid; + int sockfd; + uint8_t zero_bssid[IEEE80211_ADDR_LEN]; + + memset(&bssid, 0, sizeof(bssid)); + memset(nr, 0, sizeof(struct ieee80211_nodereq)); + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + warn("socket 'AF_INET':"); + return 0; + } + strlcpy(bssid.i_name, interface, sizeof(bssid.i_name)); + if ((ioctl(sockfd, SIOCG80211BSSID, &bssid)) < 0) { + warn("ioctl 'SIOCG80211BSSID':"); + close(sockfd); + return 0; + } + memset(&zero_bssid, 0, sizeof(zero_bssid)); + if (memcmp(bssid.i_bssid, zero_bssid, + IEEE80211_ADDR_LEN) == 0) { + close(sockfd); + return 0; + } + strlcpy(nr->nr_ifname, interface, sizeof(nr->nr_ifname)); + memcpy(&nr->nr_macaddr, bssid.i_bssid, sizeof(nr->nr_macaddr)); + if ((ioctl(sockfd, SIOCG80211NODE, nr)) < 0 && nr->nr_rssi) { + warn("ioctl 'SIOCG80211NODE':"); + close(sockfd); + return 0; + } + + return close(sockfd), 1; + } + + const char * + wifi_perc(const char *interface) + { + struct ieee80211_nodereq nr; + int q; + + if (load_ieee80211_nodereq(interface, &nr)) { + if (nr.nr_max_rssi) + q = IEEE80211_NODEREQ_RSSI(&nr); + else + q = RSSI_TO_PERC(nr.nr_rssi); + + return bprintf("%d", q); + } + + return NULL; + } + + const char * + wifi_essid(const char *interface) + { + struct ieee80211_nodereq nr; + + if (load_ieee80211_nodereq(interface, &nr)) + return bprintf("%s", nr.nr_nwid); + + return NULL; + } +#elif defined(__FreeBSD__) + #include + #include + + int + load_ieee80211req(int sock, const char *interface, void *data, int type, size_t *len) + { + char warn_buf[256]; + struct ieee80211req ireq; + memset(&ireq, 0, sizeof(ireq)); + ireq.i_type = type; + ireq.i_data = (caddr_t) data; + ireq.i_len = *len; + + strlcpy(ireq.i_name, interface, sizeof(ireq.i_name)); + if (ioctl(sock, SIOCG80211, &ireq) < 0) { + snprintf(warn_buf, sizeof(warn_buf), + "ioctl: 'SIOCG80211': %d", type); + warn(warn_buf); + return 0; + } + + *len = ireq.i_len; + return 1; + } + + const char * + wifi_perc(const char *interface) + { + union { + struct ieee80211req_sta_req sta; + uint8_t buf[24 * 1024]; + } info; + uint8_t bssid[IEEE80211_ADDR_LEN]; + int rssi_dbm; + int sockfd; + size_t len; + const char *fmt; + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + warn("socket 'AF_INET':"); + return NULL; + } + + /* Retreive MAC address of interface */ + len = IEEE80211_ADDR_LEN; + fmt = NULL; + if (load_ieee80211req(sockfd, interface, &bssid, IEEE80211_IOC_BSSID, &len)) + { + /* Retrieve info on station with above BSSID */ + memset(&info, 0, sizeof(info)); + memcpy(info.sta.is_u.macaddr, bssid, sizeof(bssid)); + + len = sizeof(info); + if (load_ieee80211req(sockfd, interface, &info, IEEE80211_IOC_STA_INFO, &len)) { + rssi_dbm = info.sta.info[0].isi_noise + + info.sta.info[0].isi_rssi / 2; + + fmt = bprintf("%d", RSSI_TO_PERC(rssi_dbm)); + } + } + + close(sockfd); + return fmt; + } + + const char * + wifi_essid(const char *interface) + { + char ssid[IEEE80211_NWID_LEN + 1]; + size_t len; + int sockfd; + const char *fmt; + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + warn("socket 'AF_INET':"); + return NULL; + } + + fmt = NULL; + len = sizeof(ssid); + memset(&ssid, 0, len); + if (load_ieee80211req(sockfd, interface, &ssid, IEEE80211_IOC_SSID, &len)) { + if (len < sizeof(ssid)) + len += 1; + else + len = sizeof(ssid); + + ssid[len - 1] = '\0'; + fmt = bprintf("%s", ssid); + } + + close(sockfd); + return fmt; + } +#endif diff --git a/.suckless/st/slstatus/components/wifi.o b/.suckless/st/slstatus/components/wifi.o new file mode 100644 index 0000000000000000000000000000000000000000..4d633356ead3d228187323022d3eea797a9b4c52 GIT binary patch literal 5920 zcmb_gZ){vg5#RIKty7%4OMD5YwZn6Dt{ssaeohQ=nj)X=Bx}{UKw>q8M)u|Y#3uIH zzI*4|ZK@&;8y=Sv@S&nYAeADaBB2T-M2r#kIS#J#C6rJ>X{3+}sXfP`ZB!u{sB32S z?ViUkexG2ZeY5-9nc3Ny*?sTsxuadjN?b0c$i+U#Hbx3%?8VBBeyixVG7sC%Lgp$r z|H4~7O4Vm~doNy^zvb&_xs=|XDU|XTxas1#W2Fn3yu0qhbr<8V#g*6f{6dfS@%#y% zxqT>}twCE+o-M6wfDSi10u6fJyQ3pej*qJN15v6!81n!CF%R( zSr)hakzwOmh*p$;%8H#)CVjbl$b91~rx$v-xpv}FCyEJ=6y2FgMkKc zukgXz`a0Btxq!*85E7&4#U#j_%H)Tfm_CT8Q4T;=f=&j}$JcRjy+8{G3= zT+pwX=K}#gixsoJ4zZuVsaZ;$)3^gfd1k4aoAbQ(4)@M1H*#~yDl{yQ3O-#1i701o zRa4cZl4h#GL|5%R)DTufg;hv?{mQ2NW>%}y4Lm2R+*-R>2sbQ2*o!T8%PqpORlj%Y z)7Znp@Vj{~<)1qUYZlB8t-2`ZS$|-$)W>-Cq^JYG2rj7uhOQ$kx{;>qlEc!83=9_bPJ_>*Tx0NM0hHr9naSrdV!c_^j;*oZx zTfWVH69xRp)o1UlsLhtb5D%VM&?=kxe`xW{nmfHydckvI z@9Bz_a&F#Oegp~!G@J_~^knGt0+*c&TK$R6$J&o~9_#GxIEtvembG|zXkc(cOAH%Yd^nwm z-p2+KMl2Z*M`BuCisE$;q~q=FhqZ>|-939Xy*a1{b*-tfsk!lBlQ{egL!?2&D1Z>W z2V8l?aBoO1Io@UsnO}0((_dn)ai8mfihIky3&ME)7DIy}k8i4^ z!&8-UcX+Jf8}eu+8BY}eAc{7>A?2Pf+6*b1qhQk# z1ert7ej#OR@cM4^_;Mv}o~jFOusKy4@-$@1Iy`;uiV{x)AYcU~+Jrw+?JkB(ma;o= zE#6J;V$?Rzx#1?jYASrww^wCZqf=HN9ARQX|^8@LU$PHjE(9n(|Wv5NxrA(6(m@g#s5A7>by%Pcu zXHZQEeoErIHld82mbkL3rq4+HVFgHjR^p$x;e!&7*zi$_KWoFsB>ub&e+I@fRGVhL z?tp*O0mry*W#@Yi_)8A>&m3?(2V2?yHQ+lTGY^$O2S}VL;PcS7l3#MbuR7p=1|03b zC`(10L%?o2$g`&g;se15iw`8CVZ%tOTMsThHV_RSggbQ}nSe9eYjqIDn6X_-Mw*XYlb^d4-Ehn>`gyCcu7VjP>@W zjBw;!Z>0ZRZ#+COh?XNmBZ7@gSXZ@hx;K_e4MYKt#*#@EA4wZ2cqZX)j$891v1Ej$ z5)y*_BZVvmXUsZ_9di6tf^VEM#(wI6|H_7|^L1U~IQ}>^_;eBUmV^Ae1osko^-~A! zR1w_cvJ}vNtQTc}m&8&269lgz^0+VMXMo^{sdvEvV&{Hh=P;4SyHTw>N^rE(0S#%~ z0nZWp4}hGq^AeFqKNWtF$kTYv68W7({xZR-o!1GDeyVrpUx|Ih)Vl9D$loUNHAMcO z4)QK}&T-8>M7~1e$nYntTDOwO(>(4W@@Na+A4p+>YcN)J`U#HT5DFh6_G<}#p2$-> zQv}C%gR+w&b{-`7FNr*@w?El%P!aUL173;BK)6?0|MyEA)oA_K6C8C^J?|$tc5x2r zh{SPB>zosLTIY`ud778!2~O+z2C<{T8Y*rDg41}uN9;UE$kRAqBJwoOYeXKq%AY>@ zDTDFZ4Nb*4L2#5;cuL}k1qhz8$t(NTe*^eUX2o9Fd4<@adDlP=3FWaV!e7Kl3U9WC ztWR=Lr}K;Jv4qu%o@n3@g9YIoA7;TbsT2z) zhjD=7@FRrOu>^>~@0?f=ewyqT?_oJUn@9&olEd&yH6}n64aWL=r&W%BC*!TA<#nru40C`vZ7m{JhgVp`3qtN0xQ~=(`Xw$CmllpzOk2_5%O0EOP^_y(C(tp8*tNkn93iH@+w8_0J*H`a5 zHLdg%@09v#Uh&IPUyT)4dTRUxbnbf8NN@mI2=&_SDa{Z*-7P=b=%^#=jp zD*rFz#{tl|#Te!ve_JbkybF=GS{w13xGA70TSnxek8KDdS zpU5ztp2rDN`DIJ!Pm={sFzG!h(#>>)kdst;NR;$qU0X--6iF2u=SeS#ZdLlirLyln z`qvE3OKQ|(`IQQJ6aD-$j!SCPqu4ao@$|;k{b|cU&iBXy3P0lkliv0fJbn9J{poQk z2rfxwJjqA;l!|zoX&%z(%46S(V%FOv^vu*Q>A#jS>W+KIM(_K@!U^ej`~H@5`_!#} z$f~PdcH_jXx~h!2+InA0MoZ3(88=SM^fY8nV0tCcC3~#gaf+(MT^3K+EPUu1H*({M zTR;EB>f94S+4^{b;oA-aqG=mYMJbMf|0M>Vj|P1)JEvmE??P@DliwG^&O0%1m{-(( zo^$4Pkc-*BD~6r=7=BKRVP{?pd20;&mKgH4#jyWG418$}``?d&zZ)apO)>C?V&G?E z_>&Vu{;3%Fy)o<@iy?1|QLdgCc77Z~zB-2eKgEz=9Ru%;fiI7N|1n0st{Cz!#gJbT z178ut&fjCi=Z`Vu=fsfr#*iNgc@-b?bts0Pb7I&jkAeGQ*ts``{9j_=zlDPr8wWV>NJKZtl?~Gx8Z4CMUieZ0d4E(_u_Wd#JpNb*BI);2h40%rsfA+_)b0mhH*)inD zLte$ld~E=5vHF?-f5w`H!Cx7}&n+?X4aC61z%Nyj)l@~mbhnoTJxcksI-jQo zq{$66Wb1Jo^awq#2Bmy&!wQ}QvoX)>@Qfg{Ly|<~MPN`n*_IebLx2dvml~V7j zZ}8N)-Hi&+ng)-zo}6rSH#Idhp?tNThRWRBisouhrK`SLsf4e$S3uHR3n`S_ixh}e zdRH~NE4&T9Mrc+*2pCc!TUm2^MYXFIxl$?IEwx_7Q@h+%>8)+3CvzTmRc#YF(^Og0 zq*U{KtE*YbfqENmSE?HjF$Ip??)H+GjVpb%Rish}SmwQq;L5N@;ReHbbnc z!N=6F=5mud6|lCxx`8ZIvvL5eYj9P$nwKk$%q3XGQms-{>l(ESpAF4rI0*?l-wzRaEk7o?&f6 zr57=3^m!|l=4z1zu@qfV2_9YtWQ8=ByK3QJ6OWjyveND0mAax)@v%xoPPNs@rHO1n ztiFL_4$TR48&glA3RxFYuLGX4vMe6k653EXDFrJYn0zCy^WCcH)9=_dS<$=si86YdxC zc_#e#-{A6^3IDE;FE`;&upX%>OHKGsgnW$&e^}s+CR~=c#e_d5v4nsB$k zTTFPJz*|jtlfXBa@MeK;GU0a%e6tDn3w(P5Au+-)6$UBk=7e{09OLoA4hA zyu*b5OyHd+{MQ2SGU5L%@E#NXsK9j-{wIMSH{nkU{Im(*C2+;xzuxxF@qH-PHU>PwfTtSpL<6oGaEgVz(p&i**%yz<8IWzj z<=9FIIR<<%0piMQ_@xAhtEH9SkbUF+w$*@N&PCYw1_M6SfNwJ3!wmRl13uhXB%*_lxL|q z2K+jMe4YWn-hgWce7pfKHQ*Tryxf3`B@s(qYQVD%@-+tBVZa*=_yhyqV!*{xlBKp9 z@QDWb4F-IY0pDc6Z#3YW4fxj$_!a}6W5Bl>@LU7F&47y~HA~%Yz;80hhYk1?1Kwf4 zod&$qfQuzKOYJh?Hyh-840xUa*9~~S0Y7fQ3k>*a13uM&D`K5P?Z41~CmZl018y_m z(+zm40WUV-ssW#2z|#%*OauPa?f;tyoV1QQqxp{}Yr&-duE6xTF6@nuc541T$?Yt| z=%nWWN7Md>Z|mqh+!0?xX_1~NuC(36#}VJj_@{}FCqB&h$BCyUL1Y`_e@A>G@mm=G zE8=PS5!uA}9}`bYf=DalzfU|h!$>3J?}pYLE0VzJPd|(j%RW zpG-VW<&iMsClF6lcw`&nuOoga@mm-_mUx=7Bbyk174bAxM_L(w8SyklM;aMFn0T6+ zBTE?{PdrV@ky6H=!K=d>nu;TNj6XpQvnX8hyC(-a!n#`xb6Pg7@P3*&!9JWZLA zO^p9B@ibLNS{eU+;%SPEG&25v;%RD(EM@$?#M6`*DP{Z}#M4w5$zyyI@iYZSvKjv^ z;%VxOsEl7mJWY8K8{-!dPfP8H!uYww)6^F^{yD3E;%Ul@^f11Fc$(@Wos6GMJWX+t zFyki>Pg7fD8{@Ado~E?O7RHYyo~E+MCdOYyJWXMdR>ogOJWXAZM#c{&o~EqGQpU#< zPg7N-l<{YlfTt-clE?TH#M9Ii$!7c!;%Q2XsEmJ`cqj2T#=k-QH;7jle}MR#i9h}s ztAFD2i0@(iZsPNa?_~Vb#1{}BX8hyC)6^2##`xb6Pg6={3*&!9JWVB$P2lqvD9 zn?En`nYQL=C%U;798jeN$EQ(^Yk^1X9fSow)&fbcMm}B*h(53R_a+xP-qnIdG=f>z z(j?NFli|DT{?fR$2fWSsf29Gc^-PBEiq@8R#dm4+`Ciu)kMD~7XDFdKx|dW7?oUi# z8}=RUy=h9b*EYrB8?ir0_455s??1GV^C?ubBdiZV>+OAB3r;%J01raWM>u>8jlB0s z?o8y{2-Ui7?=VR1;8N?k)U%Lsgm+K}&_b*2IgSG-f3JnS_G~SbWI-W!(a@;{3hmi? z9^&CRz!G=S0HkLb02<-eY_x9&!DZ7gM_SRkcYUKBQT;F~4gp%~VFB``z{dq>@%k*} ztp(oOMP=6Gp|CEz%7Vm1?ph|2_pP;ekU=dpk{%5>-Y*ILzKi(aown%ZTA&T_^$pZQ zL%N}}lYT~|1?MF@!dmE=@uaAY`#=xC&bn}mMcb_T+f%i;y~Vk2`F6rNZ!W}`eSP8? zxEOTah051Akh?+Wx9NU7aLhsF@aisfGDLeb#*$tQ>ECN2w;o{nTIGE`lv`WRKrP`u zoo<7THvQbEePt@*+KZqK-TBbfHzA*3(p=o4jjVfzln{6u568*#svo1bBjeFb)oV}H zf@O9T(Rxc*3zpbz+Vv@RE%1q!`>OS)VQt)NCp7DIuYtQO`GnP$^n2tBI^+pZZCr=r z*%Q`1foSljx-it>z_IGDcB?Nv?0R0t)v%&D#DTsqgoKn1a)7fU7yH$rZ-71KmfHhd#KMk!i$&W-nd}EGCjP zgGlaa>Ox5aAcRtT2St0VqgzR?T~Nr6Ve*r%A~Nor7C5f8eQ>kZb~-_e+o!#J&N~za z=&=tONRIYYTSsH`k^1Cct8*lD72gf>HNTU+2+#um@(w`=u33n+&#)~})+^!czNC*9 zVK`XQe%{~q>ht%sJ|cUO<&e#vUmQ5=27U0P41@?a6uMm~Ex^@ZLDYtKv_J2qM_cQy}4NjzM!9omqG^u7W4nv`~?m){x8mxBr4$}fJ``?b! zUXEykUM7f!x-i7KKnDrz_V+;Gh&G6Z!0Voa0LH}IAZQy_k9AqKHEXwr%#A+H24fe*Vd zb>?QY+{;lq3}NAsyyfync)sP{^AW3ls#>c4c2s_7(t>nUI(pP+`W9*(nEq0{G_2d9*Jz*bKE6!g4QoLArBt3{b<`K4P)C-Q$)D&{A$3$!B)P*!lVVKpQL-Rxb z7^>AyvFq??Cw=LEMX7t^AwkobvDI&Yli2a4wxd~ZV%`i(qwz8{X)!YB&PZiU!BQl(^bn)15I80=O&h&KSb)wC;?{`CU$T#+Cdoy*pzxzf0}TAp6ir zF4aN>rKOr*x0Rw^E`#52M14$o{e!^n?mGL@?uhj#VQW#_T1?n&aLs>apmp6&+#s>^ zU>m;k@SQ<#wYoD}@lD(YLANoK-rPVgyK4`=Ri_n^pcVF;;4hqxQ+t$$0Y#;m>C5k9!*_2&+@ z;a{X{$Tk%|eE52ueRcN;JILL^iFVA28{l%J81?>E*IQk^m$B~@`j-DtEnEtNxBsXO zwK_s9d2dCB3w(xEor9_=a99f@+VzKzN25EHMAiycxk0cn3%sHKoJ7z@XnQ^tE*`(@mMjA=|w*3(2gruIcG`5`i`anqQf5k<}V zC`LEtlRggxrHopPt!ymI=xFN~XY|Yo-eF`$*_6>CY=2RPW*Upw>W7U!;R^?s-E8!M zRO*)$N17R+LFc?gDkS8BnlwRPQmi-=7P8cNjLTe>5T`cmLXk>lBK#bJTElOB-h-z1-d zcP5tvKFG&l-U@cnLUgacXu+we)rGm)zQ-uO$@*}#2-@F_EDRjhANl}VA?IF-USJV7_hDjlZykNW5Ccgm`5lu$2;vj^Y7sKQ}1kStFe*kML4{ZHUFKd ziZ_#&_C5WFC<6*QiWZ)^6opVy5>!u3wZ(Zc0VSQKxx|WC_F~*QrVob*+PQgb$s+Il zdu)8_T61)B9BWBa#K`uHeFFemdBp2$AqpRPJNOl>>qqd+44rLbr<>q|r55q`f2|K8kt%Yrc zV~!VLMKFd|wAlQm=yx1}E7qO3h1tCOr)1Yukbhf#MgO2_=<~9s=s87H-$e1naz{ig0eu9u zQE(X|wi;#8!{k}WX#-x&#JpG2nh0$d#XqKhk5w`Db0O!|Ox4k?Cy{a@lp&yD-*L@O zdfTk8=ay86q2nRH1I582EVYiYUH%60Bjj|jeD=W|?-SOi3hmcm!7>$CN#GMy8K&kG z-6HE)boj#DDtpT6JMs8pPZ6~thZZQITK+@};6Z?1LD`}vZK!H^t~W%eQFe?z|jza;mU2F4^mbH-DmqUQ}oP2HGP&Rw>RM z7&l7-=McIaG&XpPcB9*2sU&dhX=)QJV)|GX?PL1yKoNl?E$|kWz#(T9TRrN}W0Bh1 zn;+d%95~eb1FGO0V+B76*HOWm7SQFufeJ=nNZ*OIAdTne>lOhxtWRZS6&-LA^3ZG9 zhNgEk7V2eo8@@}h5(zn{B5hj&SS&+UqWY-Ury9FqF3&o>9a|;ZhK_*(+X2$HiFRCC zaHO+@`M}$waXVT^s~zlrFYVir&cMJD8kPbDMD!F!O-k1hIU2VPA^6?c-@iW-PdbLo zq=EhbpI^pxX|-Wm_iCSfh;FR!Vy-@L!EQCEBT#GhkY9_JNS7Z#6`5VW7g-pl!L@xm zXyNh?U;!f-ZKAo$bD%&?Qn)-DgsQV7AnUOH|ZqJ`e?P=QDnex0y-q1hXMId$NAri_UV)ddbxbvPe_p0437$zn&Cc1mK!@B=aza<+pQLRBdCCB=arE$~l$Jc{K%oq+90 zaEhH*R@>ph`Y2TSMYkuB_gGWL_QZcQ2R?gvCbv;0CE|%j3A%fFNr)DH*iU1o!xjg7 z3xL>H%nG#We}?r%3;m~)_4Sl^gmR&KAXFiBg>!3U2Dez=pyCUC%_4|FY8zEG{p6y1wa0;{sq5-)>R>=o2-B7 zg71WNR&d|7Y_UNj1z&S&3AD2xx1X&JHL8N2pp4R!e&wv@p5*WyaUn{{cf|G75-~W5 z9dSN9>lHiVcgo?c+!5bMapF7TnJ|TJB6h@ALjwchxl4$ccErDg1^rga8I6_ih(mz* zj(9EZ*s|jMMDOTE1HqP~?;%&Ay#`B_y{!MO#shYY@tU?DZGzqw(oG(oRkE9Mwh71d zx|BVLh1Nq%3xg`|9~Ad!N?0wT2HwE}#QH;2`nfogM{}E$je=y}_;Rnv>r$9z2O@?oaI zqet`$eKRs*YcrgjG@R{Ai`UyPNS{L39hZO`Vh==s(-q(umoy@v7>(C+Ra9Th< zq zWIjmZ&c?dwNAOpMs^84J>z^!C+-KBj2l{@${ve(Y{SGc-92$eJV&6e?B)w&F{+Pyh znk;G0@-wRPi#;q_%=1WXpSNouHP1CiRkRU9En`OpfIl|)c@OLC89SJ0XxMkL?_euD znnciW^;xhc?>~hQ1_4wZCX5|6OfGQBMNUyw?U+Kj1@>uyPEmEc;pQoHW!ef*mc~Z& zCuB_fCcul^OZ5R1`jB%JvR_2Si|mBn0zC`4V`)%M!Wd&N(}LNMx)P(bBfK#3W60-5RdT?*3hqd-D0KC|? zlMnN^XqcB3c!{C$a{h?rdl-yS!gmIsBEBBjPmdXC&A)%&xmk&=cwrE#!gSTG-^oLK zC0v;USD^72G_XXv7YPXG?TBWI_19gVn~Q@JG37gFK}x6ipbuWZQ-LuyTVik+bWqRW z)`Sb!aDz+9g+H*z>7QNcdla^>ftd9v>UVgkiRm1<)?qBAnxLW?YvTGt2-BK96aqe8 z2Tp4Nye>QExi&c5`sdwkM{I?;yM06adLrHz1~3#&OKkUt6Vg79#wmD%)BB1e+&&-g z&FHyUPIYkP+=70=#Bh#RbC;sY&{t|T_V(+BqS}K)X63=L1G_MeMYZiJ^0#4cfexh7 zn4PR`Pk>Yke#9F3wivOu*VpA_PjEPtz@$r|8+Z{9x3w{!2Au=J@rhy3hk<8l&tO{@ zT{UhO+NSa}eX%vQo=wdu>5ZGAx*zp82*GJZaC%44>n;TAX%(XSbqC>(|Fr7G$mR6$ z;GM%Bsn->B{v5h^sc|0~kmK2>=_L`K8R%QzWG(Gh$kGV^5@|QzfTa{FE(!8JmA}IL zB?eY%$CqmUDfW2Z;Od}K9Y`7rV+h;TAk?#HUQd+U!0)_8zU;}Nk(<`OXXnicj=U63 ztm|H7XGXC$rH!awh8l;zz33Z(q+{Q}H)g1o0{%$jM|qG~hH@eEn>e(Vpqt@1HS=N3 z-~xws-Vgxz9P)%5?YSRiJx!&-iPOElV-3OI%{ED{|55v9mQ&6G+yUrQBvRtMn*T!`9-8-G&rhY zKL2yO<3M1w-LCDV788v^w5>-CItQb$#cWm~RP%R?pogtv==DSdYl@EGkawMA7XE14 zlhCG;l?2(QMg%79MdY)GTq?LqBCa+zbNhg#QX(H%96p$S^ZGj!KP-0+mnOH^PgW(U%N2+J{UsuS?qZgVC#5HZ%3t|y^lnM zUtpo22q$p=>QJ`GuMw5=VGZnOv8L!!yo1iI7}_ru;hk}`eQSF!n?go`z7v8X(3f5i z=-ZI^Zvy=&#V_PkXem#Dp0F?JFK}2X38f81EeD;WU_->a1S^%S*J(pV4?I}NyD=pp z< z@vfoPot;yD%GzaU<7FKHt+PAO)h0uaJwTw2=X?tV(sdL=YkOa<_Mc7gUDoQ{4-msf zQW(^i(u=js!H5$L2O8!L&I(dx4TIk2(-3h&3!J0f<{Z@ap_^d+Icht>jKAZ)^(Mu8 z4ec<8L#=gnLUgk43JeFkROF|RMer9nqKlVAYM@aLeDN+?b}xw}Q?;x)N;3y764BAi zXtiEq&!KHyUJ35BwPEkqt>X&qrPgsJ_NCTwW%fqvxS!fvp<^BQ8~Y|kAF*#?^hx_R zMxU{VK@Br6CI|}4T6+(0tVQ=H+SwL>O$b|yw`8RG{7f>3IrB)brNo}_WC~%QncrT^ zV90p}t5{4SR;{vA%YCEiQa~pFp@SXAc!K%hf+cwmDdHCd-vAHUhLz|e;d7uXK(&b z;03=Uj92eY1}5N^A`Gq5KZyzAX-Aq_RcQ(0KJvUSYA| z5S}>{2R@QZY2J?iZ;uZX#h%hjd&)^w7!zm|+eYn;4gbF3d`zM?scnl_#sR&gJoBHL`OkJUD}+ zZdFzqP*lN4l=plK{S)qX)xxlQoRi-oiCzR#W z-$^+}QFhWwU&-9fsCETs!gz7$3&l+w0b|r?SmwpqFDj*|u4Z?WYq?uV%bb9UmtV%_ z3{%wY$X^^4R9orEYHF!iw#tj6d<^~XLMgA55+}Qe`p?W%X1Q0CHF#ZhsJ?Ic)Y}UN z1DJ-BTa0`@H3K@0s1Fp)Z-sjWJLcv;Gg83XrKiv6R99tb1Pf2%phQ)9R|Iz^r0WNf!#BHqReLq^4DoBs&=hxmLjd zJ5fiycHTGV&n~Q(Ra{;)w_AS)q6u-7fS-9E*dqgjUp6DwS0rEtEEa&Kpv2v=I&qYGB_aE`c0n~XBDa5CSN^`2vth;3U5V?Z-uLVvRYiuo+n}qSL@64^qDm!G0oqU9%(m;3q+^fmT z{L(UJbhWBXEuAlKJ>!rx)$cqxBG<`^6Rr)%7eI%yE@^fSMb0iml8UnQ99qO@o&+n7pwNX&S;d*q0N0c z8qEg&Cs6vIe|ZO@(T$)Ama`q8XR+$VGckp>L)oD3)1C&j>9uII1+*7*GiVuh*3W>B z!qbBu(EC6Wu}%3HG#zv?62AaB4f-K|V`9fk1`B>|wiL7m^a0RO_zm4Q&^FKx z(1-9F%A=qg@uSUT)RX@s!~yi4kD&)j7v6-hEAEcDN?eOA?&_3*$#nK7;Un^&V$=nJW_Yz`4K!L>; zil1&t-Iy@lqOMPzZb|ni&9Y?2&$gr&TGV_?Dg+8F$tpx2z^4d$K~N>%V)Mr@uxwG| zn=H0`3yQDg;Ikc{M<5dh<+kUL?R>JGPqwF9I@S1}S<->@8HXtO{t-UeXtQTgPSV{N zUusERp8z-f@T*fz=#FDb$Qm9&2G@A{K>lO2?e7!7^fb~#4*tY=za`ZuKNKRjLjD%? zf!jzP*5emilF5!2_|Jf^qx`2^Hpj)+S+>L#S+?S{EpDb|Q(XM3gDuHLm$)p+R0qF- z+zIrdRk-KzAY(inDxf_`pNYOzYeu|&2jp9F`~wS7@d;PPS#ppU$^j8WImo3Fe0~Fa zD=;Sh^UK(4jZ4`31@@>OK7ze-7)zP|RLELOaysx7^ye~+sZZdZ>SsOm3qShnHZ^`T zYu>#7k!%iRyT63&M95Y_b{WRxb!7K^jHj}GQ#KgGhs9P%?R~B#b*iXC^7E(AdlY(Z z(xY~?l#dTj0{VB9zMJr(lDf$so*|HX8t^Zq#$ zbHZrM6n*D?)M#pfg79|G?poYnITQhZ*PgY2fF(&)#4 zCrO6c?PYe^_=mLBSgU-()3(LMzsYKPHYBJ%%aA?~YnRU{o%MIrff!R~SdPcVziUya z_Ma}<_`DH%S75C}bxT(sK3jl&)Q0sB#w@ZW<^~Lu#<`)`(t26KlL?mWsph!=S_-;6 zvrpAWXX(>L7=6CFzKXzC5%?+sUq#@n2z(WRuOjeO1pa3tAkQb4=aF0I@Oof`7(@?-TsXg35EK<+;;u3Y^xobe*LA1}@3d+4FRz zaKab}pxfDXstMlIb6xi#8C?*5{zuTC$6s zw1zuuvG^qx+wyr#F|Fru;U_WdqCE#)^bzrEe*S_5HwX7 zlz14w^`lGrP0v~B!sAhP$^1Wy^6~&=68ISkD_^P5j|yJK8IN9B`s4UbAYGpdp6$mu z^uOwR`&}1z=SG#|(*#{0Xtkitg02_zK|y~j=u?936ZBO<-xKtdpaaGz>>4g89e+aC zje<@Sbb+ANf;J1fUeE^x{jH!+3A#_vR|S1f&{KjA7|Zh;F6cFa-YDoaK^F*GEoif# z>jiyK(BBIBl%V?reO1u+1U)6_fUk-C1-(Yl8wH&v=mJ5j1#K2|y`T>Y`ddMt5_F%S zuL}B}pr-_-Gt1~2F6cFa-YDoaK^F*GEoif#>jmXjF8tQ+y$~Zro^vnHwU_7E%X91H zIrZ{fdU+1LJa=B6GcV7Tm*>dKbK~VX@$y`Fc@Dfh_g$XzF3)wB=eWyr+vPd!@?3U# z4!b;eU7oX!{*V8Ufd4jw%UDu*F1b8kU7n{d&rg@SQ$?PeI11j!Bt#3#v}Yo-iqU;siWI%kwTH{(Fpzo4}SD>9$MzB#h)C+A9l%PFLfIgCRpS0)F_!%tLpKC z3{JgGJOw{=@X%l9VPb-*Kw6W#&P591uCdNb7V!stGQI8=eB)2v)FQ2+%H?$_neH0= zRtCRGsi>)fuH=MU`31!pUe|K2U*T$M!rww-`tqJ^P=+waoWuL+N;uSjZ19!I^5*$0 z!+ZM7%7zsy@Vgje1^kEiG`|hRCtm26Ch!|c^8_x5oS!5=3O7dmO+sH%b9YRXih|O+ z7HY3D|0jjMq;fu&Y9wpC=zZ@g%xO};Q|L>|-l0H7&iiKnry`AFAocqmx(P?wiLOYe z>74SG`f@&=35>Fo`f^<%sa!`ugk5I;=K)1m?ekyG(~|yN=t=)&K9Vj)I;{aDFV_K* zHVS>S{{mKMaX>|t`f?p2sa%K1{LSU3cYdT_fDc`AT_dSn2a!I-%&hMRMltDIhpAxb zQrWj6*L$41@R{h{NHFTFLQhim4p=IK%4fHd22A>!>|9vV(c=ut27QTs7dOWI<+@c; zxt^8sqFG5+`JvF4@ndSpVOC4D| z-`+TPo|pRFNTB#h{Z_G$XchW7lxVnA!#&;V_)y!G`f~j$|1L89?u0JczGc3${e6fu zWByykK4L3%OkC-l^nFWxNlzigs4w>+y3p5T!w?pvo}{!$FzV;s%LVhAINd7z7wU>C zZv^H4k?ef=<=^knJzSvFv|%#q4~BipB3sy#MCzOSzjVh)W$dLc(F8F$UM>YiqKQ@h E5AS&XSO5S3 literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/slstatus.1 b/.suckless/st/slstatus/slstatus.1 new file mode 100644 index 0000000..73e7a60 --- /dev/null +++ b/.suckless/st/slstatus/slstatus.1 @@ -0,0 +1,47 @@ +.Dd 2023-04-23 +.Dt SLSTATUS 1 +.Os +.Sh NAME +.Nm slstatus +.Nd suckless status +.Sh SYNOPSIS +.Nm +.Op Fl s +.Op Fl 1 +.Sh DESCRIPTION +.Nm +is a small tool for providing system status information to other programs +over the EWMH +.Em WM_NAME +property of the root window (used by +.Xr dwm 1 ) or standard input/output. It is designed to be as efficient as possible by +only issuing the minimum of system calls required. +.P +By default, +.Nm +outputs to WM_NAME. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl v +Print version information to stderr, then exit. +.It Fl s +Write to stdout instead of WM_NAME. +.It Fl 1 +Write once to stdout and quit. +.El +.Sh CUSTOMIZATION +.Nm +can be customized by creating a custom config.h and (re)compiling the source +code. This keeps it fast, secure and simple. +.Sh SIGNALS +.Nm +responds to the following signals: +.Pp +.Bl -tag -width TERM -compact +.It USR1 +Triggers an instant redraw. +.El +.Sh AUTHORS +See the LICENSE file for the authors. +.Sh SEE ALSO +.Xr dwm 1 diff --git a/.suckless/st/slstatus/slstatus.c b/.suckless/st/slstatus/slstatus.c new file mode 100644 index 0000000..16d88fe --- /dev/null +++ b/.suckless/st/slstatus/slstatus.c @@ -0,0 +1,135 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include + +#include "arg.h" +#include "slstatus.h" +#include "util.h" + +struct arg { + const char *(*func)(const char *); + const char *fmt; + const char *args; +}; + +char buf[1024]; +static volatile sig_atomic_t done; +static Display *dpy; + +#include "config.h" + +static void +terminate(const int signo) +{ + if (signo != SIGUSR1) + done = 1; +} + +static void +difftimespec(struct timespec *res, struct timespec *a, struct timespec *b) +{ + res->tv_sec = a->tv_sec - b->tv_sec - (a->tv_nsec < b->tv_nsec); + res->tv_nsec = a->tv_nsec - b->tv_nsec + + (a->tv_nsec < b->tv_nsec) * 1E9; +} + +static void +usage(void) +{ + die("usage: %s [-v] [-s] [-1]", argv0); +} + +int +main(int argc, char *argv[]) +{ + struct sigaction act; + struct timespec start, current, diff, intspec, wait; + size_t i, len; + int sflag, ret; + char status[MAXLEN]; + const char *res; + + sflag = 0; + ARGBEGIN { + case 'v': + die("slstatus-"VERSION); + break; + case '1': + done = 1; + /* FALLTHROUGH */ + case 's': + sflag = 1; + break; + default: + usage(); + } ARGEND + + if (argc) + usage(); + + memset(&act, 0, sizeof(act)); + act.sa_handler = terminate; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + act.sa_flags |= SA_RESTART; + sigaction(SIGUSR1, &act, NULL); + + if (!sflag && !(dpy = XOpenDisplay(NULL))) + die("XOpenDisplay: Failed to open display"); + + do { + if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) + die("clock_gettime:"); + + status[0] = '\0'; + for (i = len = 0; i < LEN(args); i++) { + if (!(res = args[i].func(args[i].args))) + res = unknown_str; + + if ((ret = esnprintf(status + len, sizeof(status) - len, + args[i].fmt, res)) < 0) + break; + + len += ret; + } + + if (sflag) { + puts(status); + fflush(stdout); + if (ferror(stdout)) + die("puts:"); + } else { + if (XStoreName(dpy, DefaultRootWindow(dpy), status) < 0) + die("XStoreName: Allocation failed"); + XFlush(dpy); + } + + if (!done) { + if (clock_gettime(CLOCK_MONOTONIC, ¤t) < 0) + die("clock_gettime:"); + difftimespec(&diff, ¤t, &start); + + intspec.tv_sec = interval / 1000; + intspec.tv_nsec = (interval % 1000) * 1E6; + difftimespec(&wait, &intspec, &diff); + + if (wait.tv_sec >= 0 && + nanosleep(&wait, NULL) < 0 && + errno != EINTR) + die("nanosleep:"); + } + } while (!done); + + if (!sflag) { + XStoreName(dpy, DefaultRootWindow(dpy), NULL); + if (XCloseDisplay(dpy) < 0) + die("XCloseDisplay: Failed to close display"); + } + + return 0; +} diff --git a/.suckless/st/slstatus/slstatus.h b/.suckless/st/slstatus/slstatus.h new file mode 100644 index 0000000..394281c --- /dev/null +++ b/.suckless/st/slstatus/slstatus.h @@ -0,0 +1,85 @@ +/* See LICENSE file for copyright and license details. */ + +/* battery */ +const char *battery_perc(const char *); +const char *battery_remaining(const char *); +const char *battery_state(const char *); + +/* cat */ +const char *cat(const char *path); + +/* cpu */ +const char *cpu_freq(const char *unused); +const char *cpu_perc(const char *unused); + +/* datetime */ +const char *datetime(const char *fmt); + +/* disk */ +const char *disk_free(const char *path); +const char *disk_perc(const char *path); +const char *disk_total(const char *path); +const char *disk_used(const char *path); + +/* entropy */ +const char *entropy(const char *unused); + +/* hostname */ +const char *hostname(const char *unused); + +/* ip */ +const char *ipv4(const char *interface); +const char *ipv6(const char *interface); +const char *up(const char *interface); + +/* kernel_release */ +const char *kernel_release(const char *unused); + +/* keyboard_indicators */ +const char *keyboard_indicators(const char *fmt); + +/* keymap */ +const char *keymap(const char *unused); + +/* load_avg */ +const char *load_avg(const char *unused); + +/* netspeeds */ +const char *netspeed_rx(const char *interface); +const char *netspeed_tx(const char *interface); + +/* num_files */ +const char *num_files(const char *path); + +/* ram */ +const char *ram_free(const char *unused); +const char *ram_perc(const char *unused); +const char *ram_total(const char *unused); +const char *ram_used(const char *unused); + +/* run_command */ +const char *run_command(const char *cmd); + +/* swap */ +const char *swap_free(const char *unused); +const char *swap_perc(const char *unused); +const char *swap_total(const char *unused); +const char *swap_used(const char *unused); + +/* temperature */ +const char *temp(const char *); + +/* uptime */ +const char *uptime(const char *unused); + +/* user */ +const char *gid(const char *unused); +const char *uid(const char *unused); +const char *username(const char *unused); + +/* volume */ +const char *vol_perc(const char *card); + +/* wifi */ +const char *wifi_essid(const char *interface); +const char *wifi_perc(const char *interface); diff --git a/.suckless/st/slstatus/slstatus.o b/.suckless/st/slstatus/slstatus.o new file mode 100644 index 0000000000000000000000000000000000000000..b0ecf9767c5f487e01891d0bc741e167d385090d GIT binary patch literal 6768 zcmds5eQX>@6`%9@bYlhDRA4c)fqVmJ12=O)E=L-E;hIu>?nVY@N3#$BJ+_L1}5 zal7l-#VroDY22$(BoI^r#D}V?C?N4ikWwhknKUI;6e$UURG>;lR1jo7LMTOQ3&(qN zGv{~fTmIocp0qP>e)D^8X5Q@1yt~hL_Z}$oc$g9oyPs{e6v|lYrVYJAs2%M6Y&%=b zZLzleyaEw4J7dO8OSf#ZIUEc#+teqGcCFsj9&Vl)fx^ThJi>+_Oc{y&F*6m{=i0Rc zdMs{?YgSTA0<4YaaTbjyqlZT)4a$(MeGA# z&RUhZ^L+8FuRHuE&vY|DYL>nL{l0xOR_(s9ee1?%um6&-eiW!IoV|+G`gxw&i;4q2 znV-xbKbymtoJ!{y<2K6TU1uPuBoi z1Z)A|=+l8yP=VBnzV78W?c2gRp>G7pu9kC@`0r2SnnhzdJLfN6z$;6EbLfz-?!qek z=ib#5uVFj$x!T-zzOn}KwX68*`>^*V=EI%=UpUKGUu;4{-uM^kdDhRL;frVLxaVw7 z+w1mAFgdFYXu`kX@QjG)CLQ*4xOi6UTypv9SA;!}KKTZYw)RF9_dbapcqWw(!sa#6 zaWW#v@Qy714vir2wf`*~JYxmwcxEsFORPDR<(b|6)+RTWiq$0;;_65^lJL zjxNdWCx|m*&sPQm4X_2X4dJ)>>U=KwO=ah^w+I=JN??z4)ue6P#Z;`&+J5zqc^^;$E*5D)Nu7z_|u3EeBACDfwA%PNc7*w_gV`cV$ll5P{YYB7fA8cF~j55ogBFU8|- z@Krz7Ucy&+KJMZKP>`teyI#S>nLv8lg= z{v8+mIT!o~F8E0o{3kB>D=zp27yOC~e$@qk+Xb(ZX9IK>iQfgk#Rb391#fo2TU_w_ z0N)DVi~6oqy8Qd9y6F3o7AJ2To+~Q#aTHs%tR_3 zOIQ;kJ!hshp0q7CF%h@TDK=rG)5$bR+1c;`(NRd#+2|;663J1O$QP&W&<(#P;734o z>hLJ0_{>h>j{%mp6Qj}O^t7HBXK8(UG-aft%sir}@>(o4vxkjCfCL$)XQXHJI2*Gk zSe4v;%qa0HQ1RVO@H)`ngIpEmIKe6Zn*_%*O!1$UIBweq2#((m3XV(tpYxw-R^y`4{2i*lOHrc>hq4e+xVc-{pe0x!~Od$Gb=Q>6N&O z|9c{+j13bU^LoV}A%18)KSOZ5?-k!Be(*e0_&niLKN-TuTS4)^M{vBG75*~uvy0$Y z3I0KX|CRW`yH@#mi|{pqzw5#m8xB0&m)i;dMhqkfR$Jjw{3r4*e>?b9@p+Zt zm`f_$FW*P(ru?wPae4v5Zzp&?!4D8W_}7W@-$(d#o&$uBs*1y;16TXCMEu-A{AXPF z#|a;ED>a|56F%me3jZ$Q(|P`w;2$DS|qLA%Goo;9HOYay(ycTJJLi$8m3#{BJw(+a>-y!D;-}{;2uvmi$wM zkN)qKc$VPJQpf5X!I2-4{EH48^BojTl=*W^ewqT=LvVF>3n*yLOg9Q>GdfKv65)vfn1irFSbuemLkTGH6VI;tL zB54^x__5k7k`&pVOxVFx8ve*kTXWK-F*P~?S&AW-@;_A?SYyaArc^p)qn3i8%kUOZ zyo>P+>nI?3JL>C>hSd#V>LtWV^{L7%= zIpSRZF=_9dmzuY-KLHB*blRVi_9;b_wbTANz)S7lF-$^0)cCl6muf>Ihx{JC}n?2&i_YwH-*w^{~vyeLxlhU literal 0 HcmV?d00001 diff --git a/.suckless/st/slstatus/util.c b/.suckless/st/slstatus/util.c new file mode 100644 index 0000000..bca9b2e --- /dev/null +++ b/.suckless/st/slstatus/util.c @@ -0,0 +1,141 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include + +#include "util.h" + +char *argv0; + +static void +verr(const char *fmt, va_list ap) +{ + vfprintf(stderr, fmt, ap); + + if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } +} + +void +warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verr(fmt, ap); + va_end(ap); +} + +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + verr(fmt, ap); + va_end(ap); + + exit(1); +} + +static int +evsnprintf(char *str, size_t size, const char *fmt, va_list ap) +{ + int ret; + + ret = vsnprintf(str, size, fmt, ap); + + if (ret < 0) { + warn("vsnprintf:"); + return -1; + } else if ((size_t)ret >= size) { + warn("vsnprintf: Output truncated"); + return -1; + } + + return ret; +} + +int +esnprintf(char *str, size_t size, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = evsnprintf(str, size, fmt, ap); + va_end(ap); + + return ret; +} + +const char * +bprintf(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = evsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + return (ret < 0) ? NULL : buf; +} + +const char * +fmt_human(uintmax_t num, int base) +{ + double scaled; + size_t i, prefixlen; + const char **prefix; + const char *prefix_1000[] = { "", "k", "M", "G", "T", "P", "E", "Z", + "Y" }; + const char *prefix_1024[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", + "Zi", "Yi" }; + + switch (base) { + case 1000: + prefix = prefix_1000; + prefixlen = LEN(prefix_1000); + break; + case 1024: + prefix = prefix_1024; + prefixlen = LEN(prefix_1024); + break; + default: + warn("fmt_human: Invalid base"); + return NULL; + } + + scaled = num; + for (i = 0; i < prefixlen && scaled >= base; i++) + scaled /= base; + + return bprintf("%.1f %s", scaled, prefix[i]); +} + +int +pscanf(const char *path, const char *fmt, ...) +{ + FILE *fp; + va_list ap; + int n; + + if (!(fp = fopen(path, "r"))) { + warn("fopen '%s':", path); + return -1; + } + va_start(ap, fmt); + n = vfscanf(fp, fmt, ap); + va_end(ap); + fclose(fp); + + return (n == EOF) ? -1 : n; +} diff --git a/.suckless/st/slstatus/util.h b/.suckless/st/slstatus/util.h new file mode 100644 index 0000000..cf4b027 --- /dev/null +++ b/.suckless/st/slstatus/util.h @@ -0,0 +1,16 @@ +/* See LICENSE file for copyright and license details. */ +#include + +extern char buf[1024]; + +#define LEN(x) (sizeof(x) / sizeof((x)[0])) + +extern char *argv0; + +void warn(const char *, ...); +void die(const char *, ...); + +int esnprintf(char *str, size_t size, const char *fmt, ...); +const char *bprintf(const char *fmt, ...); +const char *fmt_human(uintmax_t num, int base); +int pscanf(const char *path, const char *fmt, ...); diff --git a/.suckless/st/slstatus/util.o b/.suckless/st/slstatus/util.o new file mode 100644 index 0000000000000000000000000000000000000000..8cc5fdbe3ceffa714902af9a60c463cfa4bf19a1 GIT binary patch literal 5576 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 Sv;48Q+VY=NLs`JN4E`6XY3!T; literal 113712 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 diff --git a/.suckless/st/st.1 b/.suckless/st/st.1 index ef8c956..39120b4 100644 --- a/.suckless/st/st.1 +++ b/.suckless/st/st.1 @@ -1,6 +1,6 @@ .TH ST 1 st\-VERSION .SH NAME -st \- simple terminal (bread's build) +st \- simple terminal .SH SYNOPSIS .B st .RB [ \-aiv ] diff --git a/.suckless/st/st.c b/.suckless/st/st.c index 367a3bb..f0a70ff 100644 --- a/.suckless/st/st.c +++ b/.suckless/st/st.c @@ -19,6 +19,7 @@ #include "st.h" #include "win.h" +#include "graphics.h" #if defined(__linux) #include @@ -35,6 +36,11 @@ #define ESC_ARG_SIZ 16 #define STR_BUF_SIZ ESC_BUF_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ +#define HISTSIZE 2000 + +/* PUA character used as an image placeholder */ +#define IMAGE_PLACEHOLDER_CHAR 0x10EEEE +#define IMAGE_PLACEHOLDER_CHAR_OLD 0xEEEE /* macros */ #define IS_SET(flag) ((term.mode & (flag)) != 0) @@ -42,10 +48,9 @@ #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #define ISDELIM(u) (u && wcschr(worddelimiters, u)) - -#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)] -#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size) -#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)]) +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ + term.scr + HISTSIZE + 1) % HISTSIZE] : \ + term.line[(y) - term.scr]) enum term_mode { MODE_WRAP = 1 << 0, @@ -113,21 +118,17 @@ typedef struct { int alt; } Selection; -/* Screen lines */ -typedef struct { - Line* buffer; /* ring buffer */ - int size; /* size of buffer */ - int cur; /* start of active screen */ - int off; /* scrollback line offset */ - TCursor sc; /* saved cursor */ -} LineBuffer; - /* Internal representation of the screen */ typedef struct { int row; /* nb row */ int col; /* nb col */ - LineBuffer screen[2]; /* screen and alternate screen */ - int linelen; /* allocated line length */ + int pixw; /* width of the text area in pixels */ + int pixh; /* height of the text area in pixels */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + Line hist[HISTSIZE]; /* history buffer */ + int histi; /* history index */ + int scr; /* scroll back */ int *dirty; /* dirtyness of lines */ TCursor c; /* cursor */ int ocx; /* old cursor col */ @@ -198,8 +199,8 @@ static void tnewline(int); static void tputtab(int); static void tputc(Rune); static void treset(void); -static void tscrollup(int, int); -static void tscrolldown(int, int); +static void tscrollup(int, int, int); +static void tscrolldown(int, int, int); static void tsetattr(const int *, int); static void tsetchar(Rune, const Glyph *, int, int); static void tsetdirt(int, int); @@ -216,8 +217,6 @@ static void tdeftran(char); static void tstrsequence(uchar); static void drawregion(int, int, int, int); -static void clearline(Line, Glyph, int, int); -static Line ensureline(Line); static void selnormalize(void); static void selscroll(int, int); @@ -228,7 +227,6 @@ static Rune utf8decodebyte(char, size_t *); static char utf8encodebyte(Rune, size_t); static size_t utf8validate(Rune *, size_t); -static char *base64dec(const char *); static char base64dec_getc(const char **); static ssize_t xwrite(int, const char *, size_t); @@ -247,6 +245,10 @@ static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; +/* Converts a diacritic to a row/column/etc number. The result is 1-base, 0 + * means "couldn't convert". Defined in rowcolumn_diacritics_helpers.c */ +uint16_t diacritic_to_num(uint32_t code); + ssize_t xwrite(int fd, const char *s, size_t len) { @@ -423,12 +425,11 @@ int tlinelen(int y) { int i = term.col; - Line line = TLINE(y); - if (line[i - 1].mode & ATTR_WRAP) + if (TLINE(y)[i - 1].mode & ATTR_WRAP) return i; - while (i > 0 && line[i - 1].u == ' ') + while (i > 0 && TLINE(y)[i - 1].u == ' ') --i; return i; @@ -632,6 +633,12 @@ getsel(void) if (gp->mode & ATTR_WDUMMY) continue; + if (gp->mode & ATTR_IMAGE) { + // TODO: Copy diacritics as well + ptr += utf8encode(IMAGE_PLACEHOLDER_CHAR, ptr); + continue; + } + ptr += utf8encode(gp->u, ptr); } @@ -835,7 +842,11 @@ ttyread(void) { static char buf[BUFSIZ]; static int buflen = 0; - int ret, written; + static int already_processing = 0; + int ret, written = 0; + + if (buflen >= LEN(buf)) + return 0; /* append read bytes to unprocessed bytes */ ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); @@ -847,7 +858,24 @@ ttyread(void) die("couldn't read from shell: %s\n", strerror(errno)); default: buflen += ret; - written = twrite(buf, buflen, 0); + if (already_processing) { + /* Avoid recursive call to twrite() */ + return ret; + } + already_processing = 1; + while (1) { + int buflen_before_processing = buflen; + written += twrite(buf + written, buflen - written, 0); + // If buflen changed during the call to twrite, there is + // new data, and we need to keep processing, otherwise + // we can exit. This will not loop forever because the + // buffer is limited, and we don't clean it in this + // loop, so at some point ttywrite will have to drop + // some data. + if (buflen_before_processing == buflen) + break; + } + already_processing = 0; buflen -= written; /* keep any incomplete UTF-8 byte sequence for the next call */ if (buflen > 0) @@ -860,6 +888,9 @@ void ttywrite(const char *s, size_t n, int may_echo) { const char *next; + Arg arg = (Arg) { .i = term.scr }; + + kscrolldown(&arg); if (may_echo && IS_SET(MODE_ECHO)) twrite(s, n, 1); @@ -890,6 +921,7 @@ ttywriteraw(const char *s, size_t n) fd_set wfd, rfd; ssize_t r; size_t lim = 256; + int retries_left = 100; /* * Remember that we are using a pty, which might be a modem line. @@ -898,6 +930,9 @@ ttywriteraw(const char *s, size_t n) * FIXME: Migrate the world to Plan 9. */ while (n > 0) { + if (retries_left-- <= 0) + goto too_many_retries; + FD_ZERO(&wfd); FD_ZERO(&rfd); FD_SET(cmdfd, &wfd); @@ -939,11 +974,16 @@ ttywriteraw(const char *s, size_t n) write_error: die("write error on tty: %s\n", strerror(errno)); +too_many_retries: + fprintf(stderr, "Could not write %zu bytes to tty\n", n); } void ttyresize(int tw, int th) { + term.pixw = tw; + term.pixh = th; + struct winsize w; w.ws_row = term.row; @@ -965,15 +1005,12 @@ int tattrset(int attr) { int i, j; - int y = TLINEOFFSET(0); for (i = 0; i < term.row-1; i++) { - Line line = TSCREEN.buffer[y]; for (j = 0; j < term.col-1; j++) { - if (line[j].mode & attr) + if (term.line[i][j].mode & attr) return 1; } - y = (y+1) % TSCREEN.size; } return 0; @@ -995,17 +1032,14 @@ void tsetdirtattr(int attr) { int i, j; - int y = TLINEOFFSET(0); for (i = 0; i < term.row-1; i++) { - Line line = TSCREEN.buffer[y]; for (j = 0; j < term.col-1; j++) { - if (line[j].mode & attr) { + if (term.line[i][j].mode & attr) { tsetdirt(i, i); break; } } - y = (y+1) % TSCREEN.size; } } @@ -1018,19 +1052,28 @@ tfulldirt(void) void tcursor(int mode) { + static TCursor c[2]; + int alt = IS_SET(MODE_ALTSCREEN); + if (mode == CURSOR_SAVE) { - TSCREEN.sc = term.c; + c[alt] = term.c; } else if (mode == CURSOR_LOAD) { - term.c = TSCREEN.sc; - tmoveto(term.c.x, term.c.y); + term.c = c[alt]; + tmoveto(c[alt].x, c[alt].y); } } void treset(void) { - int i, j; - Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg}; + uint i; + + term.c = (TCursor){{ + .mode = ATTR_NULL, + .fg = defaultfg, + .bg = defaultbg, + .decor = DECOR_DEFAULT_COLOR + }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; memset(term.tabs, 0, term.col * sizeof(*term.tabs)); for (i = tabspaces; i < term.col; i += tabspaces) @@ -1042,37 +1085,19 @@ treset(void) term.charset = 0; for (i = 0; i < 2; i++) { - term.screen[i].sc = (TCursor){{ - .fg = defaultfg, - .bg = defaultbg - }}; - term.screen[i].cur = 0; - term.screen[i].off = 0; - for (j = 0; j < term.row; ++j) { - if (term.col != term.linelen) - term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph)); - clearline(term.screen[i].buffer[j], g, 0, term.col); - } - for (j = term.row; j < term.screen[i].size; ++j) { - free(term.screen[i].buffer[j]); - term.screen[i].buffer[j] = NULL; - } + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); } - tcursor(CURSOR_LOAD); - term.linelen = term.col; - tfulldirt(); } void tnew(int col, int row) { - int i; - term = (Term){0}; - term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line)); - term.screen[0].size = HISTSIZE; - term.screen[1].buffer = NULL; - for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL; - + term = (Term){.c = {.attr = {.fg = defaultfg, + .bg = defaultbg, + .decor = DECOR_DEFAULT_COLOR}}}; tresize(col, row); treset(); } @@ -1080,108 +1105,104 @@ tnew(int col, int row) void tswapscreen(void) { + Line *tmp = term.line; + + term.line = term.alt; + term.alt = tmp; term.mode ^= MODE_ALTSCREEN; tfulldirt(); } void -kscrollup(const Arg *a) +kscrolldown(const Arg* a) { int n = a->i; - if (IS_SET(MODE_ALTSCREEN)) - return; + if (n < 0) + n = term.row + n; - if (n < 0) n = (-n) * term.row; - if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off; - while (!TLINE(-n)) --n; - TSCREEN.off += n; - selscroll(0, n); - tfulldirt(); + if (n > term.scr) + n = term.scr; + + if (term.scr > 0) { + term.scr -= n; + selscroll(0, -n); + tfulldirt(); + } } void -kscrolldown(const Arg *a) +kscrollup(const Arg* a) { - int n = a->i; - if (IS_SET(MODE_ALTSCREEN)) - return; + if (n < 0) + n = term.row + n; - if (n < 0) n = (-n) * term.row; - if (n > TSCREEN.off) n = TSCREEN.off; - TSCREEN.off -= n; - selscroll(0, -n); - tfulldirt(); + if (term.scr <= HISTSIZE-n) { + term.scr += n; + selscroll(0, n); + tfulldirt(); + } } void -tscrolldown(int orig, int n) +tscrolldown(int orig, int n, int copyhist) { int i; Line temp; LIMIT(n, 0, term.bot-orig+1); - /* Ensure that lines are allocated */ - for (i = -n; i < 0; i++) { - TLINE(i) = ensureline(TLINE(i)); + if (copyhist) { + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[term.bot]; + term.line[term.bot] = temp; } - /* Shift non-scrolling areas in ring buffer */ - for (i = term.bot+1; i < term.row; i++) { - temp = TLINE(i); - TLINE(i) = TLINE(i-n); - TLINE(i-n) = temp; - } - for (i = 0; i < orig; i++) { - temp = TLINE(i); - TLINE(i) = TLINE(i-n); - TLINE(i-n) = temp; + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + + for (i = term.bot; i >= orig+n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; } - /* Scroll buffer */ - TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size; - /* Clear lines that have entered the view */ - tclearregion(0, orig, term.linelen-1, orig+n-1); - /* Redraw portion of the screen that has scrolled */ - tsetdirt(orig+n-1, term.bot); - selscroll(orig, n); + if (term.scr == 0) + selscroll(orig, n); } void -tscrollup(int orig, int n) +tscrollup(int orig, int n, int copyhist) { int i; Line temp; LIMIT(n, 0, term.bot-orig+1); - /* Ensure that lines are allocated */ - for (i = term.row; i < term.row + n; i++) { - TLINE(i) = ensureline(TLINE(i)); + if (copyhist) { + term.histi = (term.histi + 1) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[orig]; + term.line[orig] = temp; } - /* Shift non-scrolling areas in ring buffer */ - for (i = orig-1; i >= 0; i--) { - temp = TLINE(i); - TLINE(i) = TLINE(i+n); - TLINE(i+n) = temp; - } - for (i = term.row-1; i >term.bot; i--) { - temp = TLINE(i); - TLINE(i) = TLINE(i+n); - TLINE(i+n) = temp; + if (term.scr > 0 && term.scr < HISTSIZE) + term.scr = MIN(term.scr + n, HISTSIZE-1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + + for (i = orig; i <= term.bot-n; i++) { + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; } - /* Scroll buffer */ - TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size; - /* Clear lines that have entered the view */ - tclearregion(0, term.bot-n+1, term.linelen-1, term.bot); - /* Redraw portion of the screen that has scrolled */ - tsetdirt(orig, term.bot-n+1); - selscroll(orig, -n); + if (term.scr == 0) + selscroll(orig, -n); } void @@ -1210,7 +1231,7 @@ tnewline(int first_col) int y = term.c.y; if (y == term.bot) { - tscrollup(term.top, 1); + tscrollup(term.top, 1, 1); } else { y++; } @@ -1287,7 +1308,6 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ }; - Line line = TLINE(y); /* * The table is proudly stolen from rxvt. @@ -1296,25 +1316,44 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); - if (line[x].mode & ATTR_WIDE) { + if (term.line[y][x].mode & ATTR_WIDE) { if (x+1 < term.col) { - line[x+1].u = ' '; - line[x+1].mode &= ~ATTR_WDUMMY; + term.line[y][x+1].u = ' '; + term.line[y][x+1].mode &= ~ATTR_WDUMMY; } - } else if (line[x].mode & ATTR_WDUMMY) { - line[x-1].u = ' '; - line[x-1].mode &= ~ATTR_WIDE; + } else if (term.line[y][x].mode & ATTR_WDUMMY) { + term.line[y][x-1].u = ' '; + term.line[y][x-1].mode &= ~ATTR_WIDE; + } + + if (u == ' ' && term.line[y][x].mode & ATTR_IMAGE && + tgetisclassicplaceholder(&term.line[y][x])) { + // This is a workaround: don't overwrite classic placement + // placeholders with space symbols (unlike Unicode placeholders + // which must be overwritten by anything). + term.line[y][x].bg = attr->bg; + term.dirty[y] = 1; + return; } term.dirty[y] = 1; - line[x] = *attr; - line[x].u = u; + term.line[y][x] = *attr; + term.line[y][x].u = u; + + if (isboxdraw(u)) + term.line[y][x].mode |= ATTR_BOXDRAW; + + + if (u == IMAGE_PLACEHOLDER_CHAR || u == IMAGE_PLACEHOLDER_CHAR_OLD) { + term.line[y][x].u = 0; + term.line[y][x].mode |= ATTR_IMAGE; + } } void tclearregion(int x1, int y1, int x2, int y2) { - int x, y, L, S, temp; + int x, y, temp; Glyph *gp; if (x1 > x2) @@ -1322,24 +1361,114 @@ tclearregion(int x1, int y1, int x2, int y2) if (y1 > y2) temp = y1, y1 = y2, y2 = temp; - LIMIT(x1, 0, term.linelen-1); - LIMIT(x2, 0, term.linelen-1); + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); LIMIT(y1, 0, term.row-1); LIMIT(y2, 0, term.row-1); - L = TLINEOFFSET(y1); for (y = y1; y <= y2; y++) { term.dirty[y] = 1; for (x = x1; x <= x2; x++) { - gp = &TSCREEN.buffer[L][x]; + gp = &term.line[y][x]; if (selected(x, y)) selclear(); gp->fg = term.c.attr.fg; gp->bg = term.c.attr.bg; + gp->decor = term.c.attr.decor; gp->mode = 0; gp->u = ' '; } - L = (L + 1) % TSCREEN.size; + } +} + +/// Fills a rectangle area with an image placeholder. The starting point is the +/// cursor. Adds empty lines if needed. The placeholder will be marked as +/// classic. +void +tcreateimgplaceholder(uint32_t image_id, uint32_t placement_id, + int cols, int rows, char do_not_move_cursor) +{ + for (int row = 0; row < rows; ++row) { + int y = term.c.y; + term.dirty[y] = 1; + for (int col = 0; col < cols; ++col) { + int x = term.c.x + col; + if (x >= term.col) + break; + Glyph *gp = &term.line[y][x]; + if (selected(x, y)) + selclear(); + gp->mode = ATTR_IMAGE; + gp->u = 0; + tsetimgrow(gp, row + 1); + tsetimgcol(gp, col + 1); + tsetimgid(gp, image_id); + tsetimgplacementid(gp, placement_id); + tsetimgdiacriticcount(gp, 3); + tsetisclassicplaceholder(gp, 1); + } + // If moving the cursor is not allowed and this is the last line + // of the terminal, we are done. + if (do_not_move_cursor && y == term.row - 1) + break; + // Move the cursor down, maybe creating a new line. The x is + // preserved (we never change term.c.x in the loop above). + if (row != rows - 1) + tnewline(/*first_col=*/0); + } + if (do_not_move_cursor) { + // Return the cursor to the original position. + tmoveto(term.c.x, term.c.y - rows + 1); + } else { + // Move the cursor beyond the last column, as required by the + // protocol. If the cursor goes beyond the screen edge, insert a + // newline to match the behavior of kitty. + if (term.c.x + cols >= term.col) + tnewline(/*first_col=*/1); + else + tmoveto(term.c.x + cols, term.c.y); + } +} + +void gr_for_each_image_cell(int (*callback)(void *data, uint32_t image_id, + uint32_t placement_id, int col, + int row, char is_classic), + void *data) { + for (int row = 0; row < term.row; ++row) { + for (int col = 0; col < term.col; ++col) { + Glyph *gp = &term.line[row][col]; + if (gp->mode & ATTR_IMAGE) { + uint32_t image_id = tgetimgid(gp); + uint32_t placement_id = tgetimgplacementid(gp); + int ret = + callback(data, tgetimgid(gp), + tgetimgplacementid(gp), + tgetimgcol(gp), tgetimgrow(gp), + tgetisclassicplaceholder(gp)); + if (ret == 1) { + term.dirty[row] = 1; + gp->mode = 0; + gp->u = ' '; + } + } + } + } +} + +void gr_schedule_image_redraw_by_id(uint32_t image_id) { + for (int row = 0; row < term.row; ++row) { + if (term.dirty[row]) + continue; + for (int col = 0; col < term.col; ++col) { + Glyph *gp = &term.line[row][col]; + if (gp->mode & ATTR_IMAGE) { + uint32_t cell_image_id = tgetimgid(gp); + if (cell_image_id == image_id) { + term.dirty[row] = 1; + break; + } + } + } } } @@ -1354,7 +1483,7 @@ tdeletechar(int n) dst = term.c.x; src = term.c.x + n; size = term.col - src; - line = TLINE(term.c.y); + line = term.line[term.c.y]; memmove(&line[dst], &line[src], size * sizeof(Glyph)); tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); @@ -1371,7 +1500,7 @@ tinsertblank(int n) dst = term.c.x + n; src = term.c.x; size = term.col - dst; - line = TLINE(term.c.y); + line = term.line[term.c.y]; memmove(&line[dst], &line[src], size * sizeof(Glyph)); tclearregion(src, term.c.y, dst - 1, term.c.y); @@ -1381,14 +1510,14 @@ void tinsertblankline(int n) { if (BETWEEN(term.c.y, term.top, term.bot)) - tscrolldown(term.c.y, n); + tscrolldown(term.c.y, n, 0); } void tdeleteline(int n) { if (BETWEEN(term.c.y, term.top, term.bot)) - tscrollup(term.c.y, n); + tscrollup(term.c.y, n, 0); } int32_t @@ -1461,6 +1590,7 @@ tsetattr(const int *attr, int l) ATTR_STRUCK ); term.c.attr.fg = defaultfg; term.c.attr.bg = defaultbg; + term.c.attr.decor = DECOR_DEFAULT_COLOR; break; case 1: term.c.attr.mode |= ATTR_BOLD; @@ -1473,6 +1603,20 @@ tsetattr(const int *attr, int l) break; case 4: term.c.attr.mode |= ATTR_UNDERLINE; + if (i + 1 < l) { + idx = attr[++i]; + if (BETWEEN(idx, 1, 5)) { + tsetdecorstyle(&term.c.attr, idx); + } else if (idx == 0) { + term.c.attr.mode &= ~ATTR_UNDERLINE; + tsetdecorstyle(&term.c.attr, 0); + } else { + fprintf(stderr, + "erresc: unknown underline " + "style %d\n", + idx); + } + } break; case 5: /* slow blink */ /* FALLTHROUGH */ @@ -1496,6 +1640,7 @@ tsetattr(const int *attr, int l) break; case 24: term.c.attr.mode &= ~ATTR_UNDERLINE; + tsetdecorstyle(&term.c.attr, 0); break; case 25: term.c.attr.mode &= ~ATTR_BLINK; @@ -1523,6 +1668,13 @@ tsetattr(const int *attr, int l) case 49: term.c.attr.bg = defaultbg; break; + case 58: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + tsetdecorcolor(&term.c.attr, idx); + break; + case 59: + tsetdecorcolor(&term.c.attr, DECOR_DEFAULT_COLOR); + break; default: if (BETWEEN(attr[i], 30, 37)) { term.c.attr.fg = attr[i] - 30; @@ -1826,11 +1978,11 @@ csihandle(void) case 'S': /* SU -- Scroll line up */ if (csiescseq.priv) break; DEFAULT(csiescseq.arg[0], 1); - tscrollup(term.top, csiescseq.arg[0]); + tscrollup(term.top, csiescseq.arg[0], 0); break; case 'T': /* SD -- Scroll line down */ DEFAULT(csiescseq.arg[0], 1); - tscrolldown(term.top, csiescseq.arg[0]); + tscrolldown(term.top, csiescseq.arg[0], 0); break; case 'L': /* IL -- Insert blank lines */ DEFAULT(csiescseq.arg[0], 1); @@ -1906,6 +2058,39 @@ csihandle(void) goto unknown; } break; + case '>': + switch (csiescseq.mode[1]) { + case 'q': /* XTVERSION -- Print terminal name and version */ + len = snprintf(buf, sizeof(buf), + "\033P>|st-graphics(%s)\033\\", VERSION); + ttywrite(buf, len, 0); + break; + default: + goto unknown; + } + break; + case 't': /* XTWINOPS -- Window manipulation */ + switch (csiescseq.arg[0]) { + case 14: /* Report text area size in pixels. */ + len = snprintf(buf, sizeof(buf), "\033[4;%i;%it", + term.pixh, term.pixw); + ttywrite(buf, len, 0); + break; + case 16: /* Report character cell size in pixels. */ + len = snprintf(buf, sizeof(buf), "\033[6;%i;%it", + term.pixh / term.row, + term.pixw / term.col); + ttywrite(buf, len, 0); + break; + case 18: /* Report the size of the text area in characters. */ + len = snprintf(buf, sizeof(buf), "\033[8;%i;%it", + term.row, term.col); + ttywrite(buf, len, 0); + break; + default: + goto unknown; + } + break; } } @@ -2055,8 +2240,26 @@ strhandle(void) case 'k': /* old title set compatibility */ xsettitle(strescseq.args[0]); return; - case 'P': /* DCS -- Device Control String */ case '_': /* APC -- Application Program Command */ + if (gr_parse_command(strescseq.buf, strescseq.len)) { + GraphicsCommandResult *res = &graphics_command_result; + if (res->create_placeholder) { + tcreateimgplaceholder( + res->placeholder.image_id, + res->placeholder.placement_id, + res->placeholder.columns, + res->placeholder.rows, + res->placeholder.do_not_move_cursor); + } + if (res->response[0]) + ttywrite(res->response, strlen(res->response), + 0); + if (res->redraw) + tfulldirt(); + return; + } + return; + case 'P': /* DCS -- Device Control String */ case '^': /* PM -- Privacy Message */ return; } @@ -2175,7 +2378,7 @@ tdumpline(int n) char buf[UTF_SIZ]; const Glyph *bp, *end; - bp = &TLINE(n)[0]; + bp = &term.line[n][0]; end = &bp[MIN(tlinelen(n), term.col) - 1]; if (bp != end || bp->u != ' ') { for ( ; bp <= end; ++bp) @@ -2402,7 +2605,7 @@ eschandle(uchar ascii) return 0; case 'D': /* IND -- Linefeed */ if (term.c.y == term.bot) { - tscrollup(term.top, 1); + tscrollup(term.top, 1, 1); } else { tmoveto(term.c.x, term.c.y+1); } @@ -2415,7 +2618,7 @@ eschandle(uchar ascii) break; case 'M': /* RI -- Reverse index */ if (term.c.y == term.top) { - tscrolldown(term.top, 1); + tscrolldown(term.top, 1, 1); } else { tmoveto(term.c.x, term.c.y-1); } @@ -2562,11 +2765,38 @@ check_control_code: if (selected(term.c.x, term.c.y)) selclear(); - gp = &TLINE(term.c.y)[term.c.x]; + if (width == 0) { + // It's probably a combining char. Combining characters are not + // supported, so we just ignore them, unless it denotes the row and + // column of an image character. + if (term.c.y <= 0 && term.c.x <= 0) + return; + else if (term.c.x == 0) + gp = &term.line[term.c.y-1][term.col-1]; + else if (term.c.state & CURSOR_WRAPNEXT) + gp = &term.line[term.c.y][term.c.x]; + else + gp = &term.line[term.c.y][term.c.x-1]; + uint16_t num = diacritic_to_num(u); + if (num && (gp->mode & ATTR_IMAGE)) { + unsigned diaccount = tgetimgdiacriticcount(gp); + if (diaccount == 0) + tsetimgrow(gp, num); + else if (diaccount == 1) + tsetimgcol(gp, num); + else if (diaccount == 2) + tsetimg4thbyteplus1(gp, num); + tsetimgdiacriticcount(gp, diaccount + 1); + } + term.lastc = u; + return; + } + + gp = &term.line[term.c.y][term.c.x]; if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { gp->mode |= ATTR_WRAP; tnewline(1); - gp = &TLINE(term.c.y)[term.c.x]; + gp = &term.line[term.c.y][term.c.x]; } if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) { @@ -2579,7 +2809,7 @@ check_control_code: tnewline(1); else tmoveto(term.col - width, term.c.y); - gp = &TLINE(term.c.y)[term.c.x]; + gp = &term.line[term.c.y][term.c.x]; } tsetchar(u, &term.c.attr, term.c.x, term.c.y); @@ -2610,11 +2840,6 @@ twrite(const char *buf, int buflen, int show_ctrl) Rune u; int n; - if (TSCREEN.off) { - TSCREEN.off = 0; - tfulldirt(); - } - for (n = 0; n < buflen; n += charsize) { if (IS_SET(MODE_UTF8)) { /* process a complete utf8 char */ @@ -2640,86 +2865,65 @@ twrite(const char *buf, int buflen, int show_ctrl) return n; } -void -clearline(Line line, Glyph g, int x, int xend) -{ - int i; - g.mode = 0; - g.u = ' '; - for (i = x; i < xend; ++i) { - line[i] = g; - } -} - -Line -ensureline(Line line) -{ - if (!line) { - line = xmalloc(term.linelen * sizeof(Glyph)); - } - return line; -} - void tresize(int col, int row) { int i, j; int minrow = MIN(row, term.row); int mincol = MIN(col, term.col); - int linelen = MAX(col, term.linelen); int *bp; + TCursor c; - if (col < 1 || row < 1 || row > HISTSIZE) { + if (col < 1 || row < 1) { fprintf(stderr, "tresize: error resizing to %dx%d\n", col, row); return; } - /* Shift buffer to keep the cursor where we expect it */ - if (row <= term.c.y) { - term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size; - } - - /* Resize and clear line buffers as needed */ - if (linelen > term.linelen) { - for (i = 0; i < term.screen[0].size; ++i) { - if (term.screen[0].buffer[i]) { - term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph)); - clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen); - } - } - for (i = 0; i < minrow; ++i) { - term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph)); - clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen); - } - } - /* Allocate all visible lines for regular line buffer */ - for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size) - { - if (!term.screen[0].buffer[j]) { - term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph)); - } - if (i >= term.row) { - clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen); - } + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines + */ + for (i = 0; i <= term.c.y - row; i++) { + free(term.line[i]); + free(term.alt[i]); } - /* Resize alt screen */ - term.screen[1].cur = 0; - term.screen[1].size = row; - for (i = row; i < term.row; ++i) { - free(term.screen[1].buffer[i]); + /* ensure that both src and dst are not NULL */ + if (i > 0) { + memmove(term.line, term.line + i, row * sizeof(Line)); + memmove(term.alt, term.alt + i, row * sizeof(Line)); } - term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line)); - for (i = term.row; i < row; ++i) { - term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph)); - clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen); + for (i += row; i < term.row; i++) { + free(term.line[i]); + free(term.alt[i]); } /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); - /* fix tabstops */ + for (i = 0; i < HISTSIZE; i++) { + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); + for (j = mincol; j < col; j++) { + term.hist[i][j] = term.c.attr; + term.hist[i][j].u = ' '; + } + } + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); + } if (col > term.col) { bp = term.tabs + term.col; @@ -2729,16 +2933,26 @@ tresize(int col, int row) for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) *bp = 1; } - /* update terminal size */ term.col = col; term.row = row; - term.linelen = linelen; /* reset scrolling region */ tsetscroll(0, row-1); /* make use of the LIMIT in tmoveto */ tmoveto(term.c.x, term.c.y); - tfulldirt(); + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for (i = 0; i < 2; i++) { + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CURSOR_LOAD); + } + term.c = c; } void @@ -2750,16 +2964,19 @@ resettitle(void) void drawregion(int x1, int y1, int x2, int y2) { - int y, L; + int y; + + xstartimagedraw(term.dirty, term.row); - L = TLINEOFFSET(y1); for (y = y1; y < y2; y++) { - if (term.dirty[y]) { - term.dirty[y] = 0; - xdrawline(TSCREEN.buffer[L], x1, y, x2); - } - L = (L + 1) % TSCREEN.size; + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(TLINE(y), x1, y, x2); } + + xfinishimagedraw(); } void @@ -2773,17 +2990,15 @@ draw(void) /* adjust cursor position */ LIMIT(term.ocx, 0, term.col-1); LIMIT(term.ocy, 0, term.row-1); - if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY) + if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) term.ocx--; - if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY) + if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) cx--; drawregion(0, 0, term.col, term.row); - if (TSCREEN.off == 0) - xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], - term.ocx, term.ocy, TLINE(term.ocy)[term.ocx], - TLINE(term.ocy), term.col); - + if (term.scr == 0) + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); term.ocx = cx; term.ocy = term.c.y; xfinishdraw(); @@ -2797,3 +3012,9 @@ redraw(void) tfulldirt(); draw(); } + +Glyph +getglyphat(int col, int row) +{ + return term.line[row][col]; +} diff --git a/.suckless/st/st.h b/.suckless/st/st.h index 4649646..1e2ad89 100644 --- a/.suckless/st/st.h +++ b/.suckless/st/st.h @@ -11,16 +11,18 @@ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) #define DEFAULT(a, b) (a) = (a) ? (a) : (b) #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) -#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode & (~ATTR_WRAP)) || \ - (a).fg != (b).fg || \ - (a).bg != (b).bg) +#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ + (a).bg != (b).bg || (a).decor != (b).decor) #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ (t1.tv_nsec-t2.tv_nsec)/1E6) #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) #define IS_TRUECOL(x) (1 << 24 & (x)) -#define HISTSIZE 2000 + +// This decor color indicates that the fg color should be used. Note that it's +// not a 24-bit color because the 25-th bit is not set. +#define DECOR_DEFAULT_COLOR 0x0ffffff enum glyph_attribute { ATTR_NULL = 0, @@ -35,7 +37,9 @@ enum glyph_attribute { ATTR_WRAP = 1 << 8, ATTR_WIDE = 1 << 9, ATTR_WDUMMY = 1 << 10, + ATTR_BOXDRAW = 1 << 11, ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, + ATTR_IMAGE = 1 << 14, }; enum selection_mode { @@ -54,6 +58,14 @@ enum selection_snap { SNAP_LINE = 2 }; +enum underline_style { + UNDERLINE_STRAIGHT = 1, + UNDERLINE_DOUBLE = 2, + UNDERLINE_CURLY = 3, + UNDERLINE_DOTTED = 4, + UNDERLINE_DASHED = 5, +}; + typedef unsigned char uchar; typedef unsigned int uint; typedef unsigned long ulong; @@ -67,6 +79,7 @@ typedef struct { ushort mode; /* attribute flags */ uint32_t fg; /* foreground */ uint32_t bg; /* background */ + uint32_t decor; /* decoration (like underline) */ } Glyph; typedef Glyph *Line; @@ -83,6 +96,8 @@ void die(const char *, ...); void redraw(void); void draw(void); +void kscrolldown(const Arg *); +void kscrollup(const Arg *); void printscreen(const Arg *); void printsel(const Arg *); void sendbreak(const Arg *); @@ -107,12 +122,22 @@ void selextend(int, int, int, int); int selected(int, int); char *getsel(void); +Glyph getglyphat(int, int); + size_t utf8encode(Rune, char *); void *xmalloc(size_t); void *xrealloc(void *, size_t); char *xstrdup(const char *); +int isboxdraw(Rune); +ushort boxdrawindex(const Glyph *); +#ifdef XFT_VERSION +/* only exposed to x.c, otherwise we'll need Xft.h for the types */ +void boxdraw_xinit(Display *, Colormap, XftDraw *, Visual *); +void drawboxes(int, int, int, int, XftColor *, XftColor *, const XftGlyphFontSpec *, int); +#endif + /* config.h globals */ extern char *utmp; extern char *scroll; @@ -126,5 +151,70 @@ extern unsigned int tabspaces; extern unsigned int defaultfg; extern unsigned int defaultbg; extern unsigned int defaultcs; -extern float alpha; -extern float alpha_def; +extern const int boxdraw, boxdraw_bold, boxdraw_braille; + +// Accessors to decoration properties stored in `decor`. +// The 25-th bit is used to indicate if it's a 24-bit color. +static inline uint32_t tgetdecorcolor(Glyph *g) { return g->decor & 0x1ffffff; } +static inline uint32_t tgetdecorstyle(Glyph *g) { return (g->decor >> 25) & 0x7; } +static inline void tsetdecorcolor(Glyph *g, uint32_t color) { + g->decor = (g->decor & ~0x1ffffff) | (color & 0x1ffffff); +} +static inline void tsetdecorstyle(Glyph *g, uint32_t style) { + g->decor = (g->decor & ~(0x7 << 25)) | ((style & 0x7) << 25); +} + + +// Some accessors to image placeholder properties stored in `u`: +// - row (1-base) - 9 bits +// - column (1-base) - 9 bits +// - most significant byte of the image id plus 1 - 9 bits (0 means unspecified, +// don't forget to subtract 1). +// - the original number of diacritics (0, 1, 2, or 3) - 2 bits +// - whether this is a classic (1) or Unicode (0) placeholder - 1 bit +static inline uint32_t tgetimgrow(Glyph *g) { return g->u & 0x1ff; } +static inline uint32_t tgetimgcol(Glyph *g) { return (g->u >> 9) & 0x1ff; } +static inline uint32_t tgetimgid4thbyteplus1(Glyph *g) { return (g->u >> 18) & 0x1ff; } +static inline uint32_t tgetimgdiacriticcount(Glyph *g) { return (g->u >> 27) & 0x3; } +static inline uint32_t tgetisclassicplaceholder(Glyph *g) { return (g->u >> 29) & 0x1; } +static inline void tsetimgrow(Glyph *g, uint32_t row) { + g->u = (g->u & ~0x1ff) | (row & 0x1ff); +} +static inline void tsetimgcol(Glyph *g, uint32_t col) { + g->u = (g->u & ~(0x1ff << 9)) | ((col & 0x1ff) << 9); +} +static inline void tsetimg4thbyteplus1(Glyph *g, uint32_t byteplus1) { + g->u = (g->u & ~(0x1ff << 18)) | ((byteplus1 & 0x1ff) << 18); +} +static inline void tsetimgdiacriticcount(Glyph *g, uint32_t count) { + g->u = (g->u & ~(0x3 << 27)) | ((count & 0x3) << 27); +} +static inline void tsetisclassicplaceholder(Glyph *g, uint32_t isclassic) { + g->u = (g->u & ~(0x1 << 29)) | ((isclassic & 0x1) << 29); +} + +/// Returns the full image id. This is a naive implementation, if the most +/// significant byte is not specified, it's assumed to be 0 instead of inferring +/// it from the cells to the left. +static inline uint32_t tgetimgid(Glyph *g) { + uint32_t msb = tgetimgid4thbyteplus1(g); + if (msb != 0) + --msb; + return (msb << 24) | (g->fg & 0xFFFFFF); +} + +/// Sets the full image id. +static inline void tsetimgid(Glyph *g, uint32_t id) { + g->fg = (id & 0xFFFFFF) | (1 << 24); + tsetimg4thbyteplus1(g, ((id >> 24) & 0xFF) + 1); +} + +static inline uint32_t tgetimgplacementid(Glyph *g) { + if (tgetdecorcolor(g) == DECOR_DEFAULT_COLOR) + return 0; + return g->decor & 0xFFFFFF; +} + +static inline void tsetimgplacementid(Glyph *g, uint32_t id) { + g->decor = (id & 0xFFFFFF) | (1 << 24); +} diff --git a/.suckless/st/st.info b/.suckless/st/st.info index efab2cf..ded76c1 100644 --- a/.suckless/st/st.info +++ b/.suckless/st/st.info @@ -195,6 +195,7 @@ st-mono| simpleterm monocolor, Ms=\E]52;%p1%s;%p2%s\007, Se=\E[2 q, Ss=\E[%p1%d q, + Smulx=\E[4:%p1%dm, st| simpleterm, use=st-mono, @@ -215,6 +216,11 @@ st-256color| simpleterm with 256 colors, initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, +# Underline colors + Su, + Setulc=\E[58:2:%p1%{65536}%/%d:%p1%{256}%/%{255}%&%d:%p1%{255}%&%d%;m, + Setulc1=\E[58:5:%p1%dm, + ol=\E[59m, st-meta| simpleterm with meta key, use=st, diff --git a/.suckless/st/st.o b/.suckless/st/st.o index 6f4b43b52a09233ac3f55c8debc691494db1cd60..470a0d782738cfafe4626f52cdea918c1c255ea5 100644 GIT binary patch literal 89504 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 K)N7!h;{O5~K%gc7 literal 82592 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 #include #include +#include +#include #include #include #include @@ -14,13 +16,12 @@ #include #include #include -#include char *argv0; #include "arg.h" #include "st.h" #include "win.h" -#include "hb.h" +#include "graphics.h" /* types used in config.h */ typedef struct { @@ -61,9 +62,12 @@ static void zoom(const Arg *); static void zoomabs(const Arg *); static void zoomreset(const Arg *); static void ttysend(const Arg *); -static void chgalpha(const Arg *); -void kscrollup(const Arg *); -void kscrolldown(const Arg *); +static void previewimage(const Arg *); +static void showimageinfo(const Arg *); +static void togglegrdebug(const Arg *); +static void dumpgrstate(const Arg *); +static void unloadimages(const Arg *); +static void toggleimages(const Arg *); /* config.h for applying patches and the configuration. */ #include "config.h" @@ -86,6 +90,7 @@ typedef XftGlyphFontSpec GlyphFontSpec; typedef struct { int tw, th; /* tty width and height */ int w, h; /* window width and height */ + int hborderpx, vborderpx; int ch; /* char height */ int cw; /* char width */ int mode; /* window state/mode flags */ @@ -110,7 +115,6 @@ typedef struct { XSetWindowAttributes attrs; int scr; int isfixed; /* is fixed geometry? */ - int depth; /* bit depth */ int l, t; /* left and top offset */ int gm; /* geometry mask */ } XWindow; @@ -147,10 +151,11 @@ typedef struct { } DC; static inline ushort sixd_to_16bit(int); -static void xresetfontsettings(ushort mode, Font **font, int *frcflags); static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); -static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int); +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); static void xdrawglyph(Glyph, int, int); +static void xdrawimages(Glyph, Line, int x1, int y1, int x2); +static void xdrawoneimagecell(Glyph, int x, int y); static void xclear(int, int, int, int); static int xgeommasktogravity(int); static int ximopen(Display *); @@ -227,6 +232,7 @@ static DC dc; static XWindow xw; static XSelection xsel; static TermWindow win; +static unsigned int mouse_col = 0, mouse_row = 0; /* Font Ring Cache */ enum { @@ -250,7 +256,6 @@ static char *usedfont = NULL; static double usedfontsize = 0; static double defaultfontsize = 0; -static char *opt_alpha = NULL; static char *opt_class = NULL; static char **opt_cmd = NULL; static char *opt_embed = NULL; @@ -336,10 +341,72 @@ ttysend(const Arg *arg) ttywrite(arg->s, strlen(arg->s), 1); } +void +previewimage(const Arg *arg) +{ + Glyph g = getglyphat(mouse_col, mouse_row); + if (g.mode & ATTR_IMAGE) { + uint32_t image_id = tgetimgid(&g); + fprintf(stderr, "Clicked on placeholder %u/%u, x=%d, y=%d\n", + image_id, tgetimgplacementid(&g), tgetimgcol(&g), + tgetimgrow(&g)); + gr_preview_image(image_id, arg->s); + } +} + +void +showimageinfo(const Arg *arg) +{ + Glyph g = getglyphat(mouse_col, mouse_row); + if (g.mode & ATTR_IMAGE) { + uint32_t image_id = tgetimgid(&g); + fprintf(stderr, "Clicked on placeholder %u/%u, x=%d, y=%d\n", + image_id, tgetimgplacementid(&g), tgetimgcol(&g), + tgetimgrow(&g)); + char stcommand[256] = {0}; + size_t len = snprintf(stcommand, sizeof(stcommand), "%s -e less", argv0); + if (len > sizeof(stcommand) - 1) { + fprintf(stderr, "Executable name too long: %s\n", + argv0); + return; + } + gr_show_image_info(image_id, tgetimgplacementid(&g), + tgetimgcol(&g), tgetimgrow(&g), + tgetisclassicplaceholder(&g), + tgetimgdiacriticcount(&g), argv0); + } +} + +void +togglegrdebug(const Arg *arg) +{ + graphics_debug_mode = (graphics_debug_mode + 1) % 3; + redraw(); +} + +void +dumpgrstate(const Arg *arg) +{ + gr_dump_state(); +} + +void +unloadimages(const Arg *arg) +{ + gr_unload_images_to_reduce_ram(); +} + +void +toggleimages(const Arg *arg) +{ + graphics_display_images = !graphics_display_images; + redraw(); +} + int evcol(XEvent *e) { - int x = e->xbutton.x - borderpx; + int x = e->xbutton.x - win.hborderpx; LIMIT(x, 0, win.tw - 1); return x / win.cw; } @@ -347,7 +414,7 @@ evcol(XEvent *e) int evrow(XEvent *e) { - int y = e->xbutton.y - borderpx; + int y = e->xbutton.y - win.vborderpx; LIMIT(y, 0, win.th - 1); return y / win.ch; } @@ -460,6 +527,9 @@ mouseaction(XEvent *e, uint release) /* ignore Buttonmask for Button - it's set on release */ uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); + mouse_col = evcol(e); + mouse_row = evrow(e); + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { if (ms->release == release && ms->button == e->xbutton.button && @@ -747,6 +817,9 @@ cresize(int width, int height) col = MAX(1, col); row = MAX(1, row); + win.hborderpx = (win.w - col * win.cw) * anysize_halign / 100; + win.vborderpx = (win.h - row * win.ch) * anysize_valign / 100; + tresize(col, row); xresize(col, row); ttyresize(win.tw, win.th); @@ -760,12 +833,12 @@ xresize(int col, int row) XFreePixmap(xw.dpy, xw.buf); xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, - xw.depth); + DefaultDepth(xw.dpy, xw.scr)); XftDrawChange(xw.draw, xw.buf); xclear(0, 0, win.w, win.h); /* resize to new width */ - xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); } ushort @@ -820,13 +893,6 @@ xloadcols(void) else die("could not allocate color %d\n", i); } - - /* set alpha value of bg color */ - if (opt_alpha) - alpha = strtof(opt_alpha, NULL); - dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); - dc.col[defaultbg].pixel &= 0x00FFFFFF; - dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; loaded = 1; } @@ -884,8 +950,8 @@ xhints(void) sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; sizeh->height = win.h; sizeh->width = win.w; - sizeh->height_inc = win.ch; - sizeh->width_inc = win.cw; + sizeh->height_inc = 1; + sizeh->width_inc = 1; sizeh->base_height = 2 * borderpx; sizeh->base_width = 2 * borderpx; sizeh->min_height = win.ch + 2 * borderpx; @@ -1029,7 +1095,8 @@ xloadfonts(const char *fontstr, double fontsize) FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); usedfontsize = 12; } - defaultfontsize = usedfontsize; + if (defaultfontsize <= 0) + defaultfontsize = usedfontsize; } if (xloadfont(&dc.font, pattern)) @@ -1039,7 +1106,7 @@ xloadfonts(const char *fontstr, double fontsize) FcPatternGetDouble(dc.font.match->pattern, FC_PIXEL_SIZE, 0, &fontval); usedfontsize = fontval; - if (fontsize == 0) + if (defaultfontsize <= 0 && fontsize == 0) defaultfontsize = fontval; } @@ -1077,9 +1144,6 @@ xunloadfont(Font *f) void xunloadfonts(void) { - /* Clear Harfbuzz font cache. */ - hbunloadfonts(); - /* Free the loaded fonts in the font cache. */ while (frclen > 0) XftFontClose(xw.dpy, frc[--frclen].font); @@ -1152,23 +1216,11 @@ xinit(int cols, int rows) Window parent, root; pid_t thispid = getpid(); XColor xmousefg, xmousebg; - XWindowAttributes attr; - XVisualInfo vis; if (!(xw.dpy = XOpenDisplay(NULL))) die("can't open display\n"); xw.scr = XDefaultScreen(xw.dpy); - - if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { - parent = XRootWindow(xw.dpy, xw.scr); - xw.depth = 32; - } else { - XGetWindowAttributes(xw.dpy, parent, &attr); - xw.depth = attr.depth; - } - - XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); - xw.vis = vis.visual; + xw.vis = XDefaultVisual(xw.dpy, xw.scr); /* font */ if (!FcInit()) @@ -1177,16 +1229,13 @@ xinit(int cols, int rows) usedfont = (opt_font == NULL)? font : opt_font; xloadfonts(usedfont, 0); - /* Backup default alpha value */ - alpha_def = alpha; - /* colors */ - xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + xw.cmap = XDefaultColormap(xw.dpy, xw.scr); xloadcols(); /* adjust fixed window geometry */ - win.w = 2 * borderpx + cols * win.cw; - win.h = 2 * borderpx + rows * win.ch; + win.w = 2 * win.hborderpx + 2 * borderpx + cols * win.cw; + win.h = 2 * win.vborderpx + 2 * borderpx + rows * win.ch; if (xw.gm & XNegative) xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; if (xw.gm & YNegative) @@ -1202,8 +1251,10 @@ xinit(int cols, int rows) xw.attrs.colormap = xw.cmap; root = XRootWindow(xw.dpy, xw.scr); + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) + parent = root; xw.win = XCreateWindow(xw.dpy, root, xw.l, xw.t, - win.w, win.h, 0, xw.depth, InputOutput, + win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask | CWColormap, &xw.attrs); if (parent != root) @@ -1211,13 +1262,15 @@ xinit(int cols, int rows) memset(&gcvalues, 0, sizeof(gcvalues)); gcvalues.graphics_exposures = False; - xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); - dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + dc.gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, + &gcvalues); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, + DefaultDepth(xw.dpy, xw.scr)); XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); /* font spec buffer */ - xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); /* Xft rendering context */ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); @@ -1269,28 +1322,17 @@ xinit(int cols, int rows) xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); if (xsel.xtarget == None) xsel.xtarget = XA_STRING; -} -void -xresetfontsettings(ushort mode, Font **font, int *frcflags) -{ - *font = &dc.font; - if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { - *font = &dc.ibfont; - *frcflags = FRC_ITALICBOLD; - } else if (mode & ATTR_ITALIC) { - *font = &dc.ifont; - *frcflags = FRC_ITALIC; - } else if (mode & ATTR_BOLD) { - *font = &dc.bfont; - *frcflags = FRC_BOLD; - } + boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); + + // Initialize the graphics (image display) module. + gr_init(xw.dpy, xw.vis, xw.cmap); } int xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) { - float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; + float winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, xp, yp; ushort mode, prevmode = USHRT_MAX; Font *font = &dc.font; int frcflags = FRC_NORMAL; @@ -1301,175 +1343,189 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x FcPattern *fcpattern, *fontpattern; FcFontSet *fcsets[] = { NULL }; FcCharSet *fccharset; - int i, f, length = 0, start = 0, numspecs = 0; - float cluster_xp = xp, cluster_yp = yp; - HbTransformData shaped = { 0 }; - - /* Initial values. */ - mode = prevmode = glyphs[0].mode & ~ATTR_WRAP; - xresetfontsettings(mode, &font, &frcflags); + int i, f, numspecs = 0; for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { - mode = glyphs[i].mode & ~ATTR_WRAP; + /* Fetch rune and mode for current glyph. */ + rune = glyphs[i].u; + mode = glyphs[i].mode; /* Skip dummy wide-character spacing. */ - if (mode & ATTR_WDUMMY && i < (len - 1)) + if (mode == ATTR_WDUMMY) continue; - if ( - prevmode != mode - || ATTRCMP(glyphs[start], glyphs[i]) - || selected(x + i, y) != selected(x + start, y) - || i == (len - 1) - ) { - /* Handle 1-character wide segments and end of line */ - length = i - start; - if (i == start) { - length = 1; - } else if (i == (len - 1)) { - length = (i - start + 1); + /* Draw spaces for image placeholders (images will be drawn + * separately). */ + if (mode & ATTR_IMAGE) + rune = ' '; + + /* Determine font for glyph if different from previous glyph. */ + if (prevmode != mode) { + prevmode = mode; + font = &dc.font; + frcflags = FRC_NORMAL; + runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else if (mode & ATTR_ITALIC) { + font = &dc.ifont; + frcflags = FRC_ITALIC; + } else if (mode & ATTR_BOLD) { + font = &dc.bfont; + frcflags = FRC_BOLD; } + yp = winy + font->ascent; + } + + if (mode & ATTR_BOXDRAW) { + /* minor shoehorning: boxdraw uses only this ushort */ + glyphidx = boxdrawindex(&glyphs[i]); + } else { + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + } + if (glyphidx) { + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + continue; + } - /* Shape the segment. */ - hbtransform(&shaped, font->match, glyphs, start, length); - runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f); - cluster_xp = xp; cluster_yp = yp; - for (int code_idx = 0; code_idx < shaped.count; code_idx++) { - int idx = shaped.glyphs[code_idx].cluster; - - if (glyphs[start + idx].mode & ATTR_WDUMMY) - continue; - - /* Advance the drawing cursor if we've moved to a new cluster */ - if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) { - xp += runewidth; - cluster_xp = xp; - cluster_yp = yp; - runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f); - } - - if (shaped.glyphs[code_idx].codepoint != 0) { - /* If symbol is found, put it into the specs. */ - specs[numspecs].font = font->match; - specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint; - specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.); - specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.); - cluster_xp += shaped.positions[code_idx].x_advance / 64.; - cluster_yp += shaped.positions[code_idx].y_advance / 64.; - numspecs++; - } else { - /* If it's not found, try to fetch it through the font cache. */ - rune = glyphs[start + idx].u; - for (f = 0; f < frclen; f++) { - glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); - /* Everything correct. */ - if (glyphidx && frc[f].flags == frcflags) - break; - /* We got a default font for a not found glyph. */ - if (!glyphidx && frc[f].flags == frcflags - && frc[f].unicodep == rune) { - break; - } - } - - /* Nothing was found. Use fontconfig to find matching font. */ - if (f >= frclen) { - if (!font->set) - font->set = FcFontSort(0, font->pattern, - 1, 0, &fcres); - fcsets[0] = font->set; - - /* - * Nothing was found in the cache. Now use - * some dozen of Fontconfig calls to get the - * font for one single character. - * - * Xft and fontconfig are design failures. - */ - fcpattern = FcPatternDuplicate(font->pattern); - fccharset = FcCharSetCreate(); - - FcCharSetAddChar(fccharset, rune); - FcPatternAddCharSet(fcpattern, FC_CHARSET, - fccharset); - FcPatternAddBool(fcpattern, FC_SCALABLE, 1); - - FcConfigSubstitute(0, fcpattern, - FcMatchPattern); - FcDefaultSubstitute(fcpattern); - - fontpattern = FcFontSetMatch(0, fcsets, 1, - fcpattern, &fcres); - - /* Allocate memory for the new cache entry. */ - if (frclen >= frccap) { - frccap += 16; - frc = xrealloc(frc, frccap * sizeof(Fontcache)); - } - - frc[frclen].font = XftFontOpenPattern(xw.dpy, - fontpattern); - if (!frc[frclen].font) - die("XftFontOpenPattern failed seeking fallback font: %s\n", - strerror(errno)); - frc[frclen].flags = frcflags; - frc[frclen].unicodep = rune; - - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); - - f = frclen; - frclen++; - - FcPatternDestroy(fcpattern); - FcCharSetDestroy(fccharset); - } - - specs[numspecs].font = frc[f].font; - specs[numspecs].glyph = glyphidx; - specs[numspecs].x = (short)xp; - specs[numspecs].y = (short)yp; - numspecs++; - } + /* Fallback on font cache, search the font cache for match. */ + for (f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == rune) { + break; } + } - /* Cleanup and get ready for next segment. */ - hbcleanup(&shaped); - start = i; + /* Nothing was found. Use fontconfig to find matching font. */ + if (f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, + 1, 0, &fcres); + fcsets[0] = font->set; - /* Determine font for glyph if different from previous glyph. */ - if (prevmode != mode) { - prevmode = mode; - xresetfontsettings(mode, &font, &frcflags); - yp = winy + font->ascent; + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, + fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + + FcConfigSubstitute(0, fcpattern, + FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, 1, + fcpattern, &fcres); + + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); } + + frc[frclen].font = XftFontOpenPattern(xw.dpy, + fontpattern); + if (!frc[frclen].font) + die("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = rune; + + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); + + f = frclen; + frclen++; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); } + + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; } return numspecs; } -void -chgalpha(const Arg *arg) -{ - if (arg->f == -1.0f && alpha >= 0.1f) - alpha -= 0.1f; - else if (arg->f == 1.0f && alpha < 1.0f) - alpha += 0.1f; - else if (arg->f == 0.0f) - alpha = alpha_def; - else - return; +/* Draws a horizontal dashed line of length `w` starting at `(x, y)`. `wavelen` + * is the length of the dash plus the length of the gap. `fraction` is the + * fraction of the dash length compared to `wavelen`. */ +static void +xdrawunderdashed(Draw draw, Color *color, int x, int y, int w, + int wavelen, float fraction, int thick) +{ + int dashw = MAX(1, fraction * wavelen); + for (int i = x - x % wavelen; i < x + w; i += wavelen) { + int startx = MAX(i, x); + int endx = MIN(i + dashw, x + w); + if (startx < endx) + XftDrawRect(xw.draw, color, startx, y, endx - startx, + thick); + } +} + +/* Draws an undercurl. `h` is the total height, including line thickness. */ +static void +xdrawundercurl(Draw draw, Color *color, int x, int y, int w, int h, int thick) +{ + XGCValues gcvals = {.foreground = color->pixel, + .line_width = thick, + .line_style = LineSolid, + .cap_style = CapRound}; + GC gc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw), + GCForeground | GCLineWidth | GCLineStyle | GCCapStyle, + &gcvals); + + XRectangle clip = {.x = x, .y = y, .width = w, .height = h}; + XSetClipRectangles(xw.dpy, gc, 0, 0, &clip, 1, Unsorted); + + int yoffset = thick / 2; + int segh = MAX(1, h - thick); + /* Make sure every segment is at a 45 degree angle, otherwise it doesn't + * look good without antialiasing. */ + int segw = segh; + int wavelen = MAX(1, segw * 2); + + for (int i = x - (x % wavelen); i < x + w; i += wavelen) { + XPoint points[3] = {{.x = i, .y = y + yoffset}, + {.x = i + segw, .y = y + yoffset + segh}, + {.x = i + wavelen, .y = y + yoffset}}; + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), gc, points, 3, + CoordModeOrigin); + } - dc.col[defaultbg].color.alpha = (unsigned short)(0xFFFF * alpha); - /* Required to remove artifacting from borderpx */ - cresize(0, 0); - redraw(); + XFreeGC(xw.dpy, gc); } void -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int charlen) +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) { - int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, + int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); + int winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, width = charlen * win.cw; Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; XRenderColor colfg, colbg; @@ -1559,17 +1615,17 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i /* Intelligent cleaning up of the borders. */ if (x == 0) { - xclear(0, (y == 0)? 0 : winy, borderpx, + xclear(0, (y == 0)? 0 : winy, win.hborderpx, winy + win.ch + - ((winy + win.ch >= borderpx + win.th)? win.h : 0)); + ((winy + win.ch >= win.vborderpx + win.th)? win.h : 0)); } - if (winx + width >= borderpx + win.tw) { + if (winx + width >= win.hborderpx + win.tw) { xclear(winx + width, (y == 0)? 0 : winy, win.w, - ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); + ((winy + win.ch >= win.vborderpx + win.th)? win.h : (winy + win.ch))); } if (y == 0) - xclear(winx, 0, winx + width, borderpx); - if (winy + win.ch >= borderpx + win.th) + xclear(winx, 0, winx + width, win.vborderpx); + if (winy + win.ch >= win.vborderpx + win.th) xclear(winx, winy + win.ch, winx + width, win.h); /* Clean up the region we want to draw to. */ @@ -1582,18 +1638,72 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i r.width = width; XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); - /* Render the glyphs. */ - XftDrawGlyphFontSpec(xw.draw, fg, specs, len); - - /* Render underline and strikethrough. */ + /* Decoration color. */ + Color decor; + uint32_t decorcolor = tgetdecorcolor(&base); + if (decorcolor == DECOR_DEFAULT_COLOR) { + decor = *fg; + } else if (IS_TRUECOL(decorcolor)) { + colfg.alpha = 0xffff; + colfg.red = TRUERED(decorcolor); + colfg.green = TRUEGREEN(decorcolor); + colfg.blue = TRUEBLUE(decorcolor); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &decor); + } else { + decor = dc.col[decorcolor]; + } + decor.color.alpha = 0xffff; + decor.pixel |= 0xff << 24; + + /* Float thickness, used as a base to compute other values. */ + float fthick = dc.font.height / 18.0; + /* Integer thickness in pixels. Must not be 0. */ + int thick = MAX(1, roundf(fthick)); + /* The default gap between the baseline and a single underline. */ + int gap = roundf(fthick * 2); + /* The total thickness of a double underline. */ + int doubleh = thick * 2 + ceilf(fthick * 0.5); + /* The total thickness of an undercurl. */ + int curlh = thick * 2 + roundf(fthick * 0.75); + + /* Render the underline before the glyphs. */ if (base.mode & ATTR_UNDERLINE) { - XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, - width, 1); + uint32_t style = tgetdecorstyle(&base); + int liney = winy + dc.font.ascent + gap; + /* Adjust liney to guarantee that a single underline fits. */ + liney -= MAX(0, liney + thick - (winy + win.ch)); + if (style == UNDERLINE_DOUBLE) { + liney -= MAX(0, liney + doubleh - (winy + win.ch)); + XftDrawRect(xw.draw, &decor, winx, liney, width, thick); + XftDrawRect(xw.draw, &decor, winx, + liney + doubleh - thick, width, thick); + } else if (style == UNDERLINE_DOTTED) { + xdrawunderdashed(xw.draw, &decor, winx, liney, width, + thick * 2, 0.5, thick); + } else if (style == UNDERLINE_DASHED) { + int wavelen = MAX(2, win.cw * 0.9); + xdrawunderdashed(xw.draw, &decor, winx, liney, width, + wavelen, 0.65, thick); + } else if (style == UNDERLINE_CURLY) { + liney -= MAX(0, liney + curlh - (winy + win.ch)); + xdrawundercurl(xw.draw, &decor, winx, liney, width, + curlh, thick); + } else { + XftDrawRect(xw.draw, &decor, winx, liney, width, thick); + } + } + + if (base.mode & ATTR_BOXDRAW) { + drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); + } else { + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); } + /* Render strikethrough. Alway use the fg color. */ if (base.mode & ATTR_STRUCK) { - XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3, - width, 1); + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, + width, thick); } /* Reset clip to none. */ @@ -1604,32 +1714,38 @@ void xdrawglyph(Glyph g, int x, int y) { int numspecs; - XftGlyphFontSpec *specs = xw.specbuf; - - numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); - xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? 2 : 1); + XftGlyphFontSpec spec; + + numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); + xdrawglyphfontspecs(&spec, g, numspecs, x, y); + if (g.mode & ATTR_IMAGE) { + gr_start_drawing(xw.buf, win.cw, win.ch); + xdrawoneimagecell(g, x, y); + gr_finish_drawing(xw.buf); + } } void -xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) { Color drawcol; /* remove the old cursor */ if (selected(ox, oy)) og.mode ^= ATTR_REVERSE; - - /* Redraw the line where cursor was previously. - * It will restore the ligatures broken by the cursor. */ - xdrawline(line, 0, oy, len); + xdrawglyph(og, ox, oy); if (IS_SET(MODE_HIDE)) return; + // If it's an image, just draw a ballot box for simplicity. + if (g.mode & ATTR_IMAGE) + g.u = 0x2610; + /* * Select the right color for the right mode. */ - g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW; if (IS_SET(MODE_REVERSE)) { g.mode |= ATTR_REVERSE; @@ -1666,39 +1782,167 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int le case 3: /* Blinking Underline */ case 4: /* Steady Underline */ XftDrawRect(xw.draw, &drawcol, - borderpx + cx * win.cw, - borderpx + (cy + 1) * win.ch - \ + win.hborderpx + cx * win.cw, + win.vborderpx + (cy + 1) * win.ch - \ cursorthickness, win.cw, cursorthickness); break; case 5: /* Blinking bar */ case 6: /* Steady bar */ XftDrawRect(xw.draw, &drawcol, - borderpx + cx * win.cw, - borderpx + cy * win.ch, + win.hborderpx + cx * win.cw, + win.vborderpx + cy * win.ch, cursorthickness, win.ch); break; } } else { XftDrawRect(xw.draw, &drawcol, - borderpx + cx * win.cw, - borderpx + cy * win.ch, + win.hborderpx + cx * win.cw, + win.vborderpx + cy * win.ch, win.cw - 1, 1); XftDrawRect(xw.draw, &drawcol, - borderpx + cx * win.cw, - borderpx + cy * win.ch, + win.hborderpx + cx * win.cw, + win.vborderpx + cy * win.ch, 1, win.ch - 1); XftDrawRect(xw.draw, &drawcol, - borderpx + (cx + 1) * win.cw - 1, - borderpx + cy * win.ch, + win.hborderpx + (cx + 1) * win.cw - 1, + win.vborderpx + cy * win.ch, 1, win.ch - 1); XftDrawRect(xw.draw, &drawcol, - borderpx + cx * win.cw, - borderpx + (cy + 1) * win.ch - 1, + win.hborderpx + cx * win.cw, + win.vborderpx + (cy + 1) * win.ch - 1, win.cw, 1); } } +/* Draw (or queue for drawing) image cells between columns x1 and x2 assuming + * that they have the same attributes (and thus the same lower 24 bits of the + * image ID and the same placement ID). */ +void +xdrawimages(Glyph base, Line line, int x1, int y1, int x2) { + int y_pix = win.vborderpx + y1 * win.ch; + uint32_t image_id_24bits = base.fg & 0xFFFFFF; + uint32_t placement_id = tgetimgplacementid(&base); + // Columns and rows are 1-based, 0 means unspecified. + int last_col = 0; + int last_row = 0; + int last_start_col = 0; + int last_start_x = x1; + // The most significant byte is also 1-base, subtract 1 before use. + uint32_t last_id_4thbyteplus1 = 0; + // We may need to inherit row/column/4th byte from the previous cell. + Glyph *prev = &line[x1 - 1]; + if (x1 > 0 && (prev->mode & ATTR_IMAGE) && + (prev->fg & 0xFFFFFF) == image_id_24bits && + prev->decor == base.decor) { + last_row = tgetimgrow(prev); + last_col = tgetimgcol(prev); + last_id_4thbyteplus1 = tgetimgid4thbyteplus1(prev); + last_start_col = last_col + 1; + } + for (int x = x1; x < x2; ++x) { + Glyph *g = &line[x]; + uint32_t cur_row = tgetimgrow(g); + uint32_t cur_col = tgetimgcol(g); + uint32_t cur_id_4thbyteplus1 = tgetimgid4thbyteplus1(g); + uint32_t num_diacritics = tgetimgdiacriticcount(g); + // If the row is not specified, assume it's the same as the row + // of the previous cell. Note that `cur_row` may contain a + // value imputed earlier, which will be preserved if `last_row` + // is zero (i.e. we don't know the row of the previous cell). + if (last_row && (num_diacritics == 0 || !cur_row)) + cur_row = last_row; + // If the column is not specified and the row is the same as the + // row of the previous cell, then assume that the column is the + // next one. + if (last_col && (num_diacritics <= 1 || !cur_col) && + cur_row == last_row) + cur_col = last_col + 1; + // If the additional id byte is not specified and the + // coordinates are consecutive, assume the byte is also the + // same. + if (last_id_4thbyteplus1 && + (num_diacritics <= 2 || !cur_id_4thbyteplus1) && + cur_row == last_row && cur_col == last_col + 1) + cur_id_4thbyteplus1 = last_id_4thbyteplus1; + // If we couldn't infer row and column, start from the top left + // corner. + if (cur_row == 0) + cur_row = 1; + if (cur_col == 0) + cur_col = 1; + // If this cell breaks a contiguous stripe of image cells, draw + // that line and start a new one. + if (cur_col != last_col + 1 || cur_row != last_row || + cur_id_4thbyteplus1 != last_id_4thbyteplus1) { + uint32_t image_id = image_id_24bits; + if (last_id_4thbyteplus1) + image_id |= (last_id_4thbyteplus1 - 1) << 24; + if (last_row != 0) { + int x_pix = + win.hborderpx + last_start_x * win.cw; + gr_append_imagerect( + xw.buf, image_id, placement_id, + last_start_col - 1, last_col, + last_row - 1, last_row, last_start_x, + y1, x_pix, y_pix, win.cw, win.ch, + base.mode & ATTR_REVERSE); + } + last_start_col = cur_col; + last_start_x = x; + } + last_row = cur_row; + last_col = cur_col; + last_id_4thbyteplus1 = cur_id_4thbyteplus1; + // Populate the missing glyph data to enable inheritance between + // runs and support the naive implementation of tgetimgid. + if (!tgetimgrow(g)) + tsetimgrow(g, cur_row); + // We cannot save this information if there are > 511 cols. + if (!tgetimgcol(g) && (cur_col & ~0x1ff) == 0) + tsetimgcol(g, cur_col); + if (!tgetimgid4thbyteplus1(g)) + tsetimg4thbyteplus1(g, cur_id_4thbyteplus1); + } + uint32_t image_id = image_id_24bits; + if (last_id_4thbyteplus1) + image_id |= (last_id_4thbyteplus1 - 1) << 24; + // Draw the last contiguous stripe. + if (last_row != 0) { + int x_pix = win.hborderpx + last_start_x * win.cw; + gr_append_imagerect(xw.buf, image_id, placement_id, + last_start_col - 1, last_col, last_row - 1, + last_row, last_start_x, y1, x_pix, y_pix, + win.cw, win.ch, base.mode & ATTR_REVERSE); + } +} + +/* Draw just one image cell without inheriting attributes from the left. */ +void xdrawoneimagecell(Glyph g, int x, int y) { + if (!(g.mode & ATTR_IMAGE)) + return; + int x_pix = win.hborderpx + x * win.cw; + int y_pix = win.vborderpx + y * win.ch; + uint32_t row = tgetimgrow(&g) - 1; + uint32_t col = tgetimgcol(&g) - 1; + uint32_t placement_id = tgetimgplacementid(&g); + uint32_t image_id = tgetimgid(&g); + gr_append_imagerect(xw.buf, image_id, placement_id, col, col + 1, row, + row + 1, x, y, x_pix, y_pix, win.cw, win.ch, + g.mode & ATTR_REVERSE); +} + +/* Prepare for image drawing. */ +void xstartimagedraw(int *dirty, int rows) { + gr_start_drawing(xw.buf, win.cw, win.ch); + gr_mark_dirty_animations(dirty, rows); +} + +/* Draw all queued image cells. */ +void xfinishimagedraw() { + gr_finish_drawing(xw.buf); +} + void xsetenv(void) { @@ -1745,6 +1989,8 @@ xsettitle(char *p) int xstartdraw(void) { + if (IS_SET(MODE_VISIBLE)) + XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0); return IS_SET(MODE_VISIBLE); } @@ -1755,16 +2001,20 @@ xdrawline(Line line, int x1, int y1, int x2) Glyph base, new; XftGlyphFontSpec *specs = xw.specbuf; + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); i = ox = 0; - for (x = x1; x < x2; x++) { + for (x = x1; x < x2 && i < numspecs; x++) { new = line[x]; if (new.mode == ATTR_WDUMMY) continue; if (selected(x, y1)) new.mode ^= ATTR_REVERSE; - if ((i > 0) && ATTRCMP(base, new)) { - numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1); - xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x - ox); + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + if (base.mode & ATTR_IMAGE) + xdrawimages(base, line, ox, y1, x); + specs += i; + numspecs -= i; i = 0; } if (i == 0) { @@ -1773,10 +2023,10 @@ xdrawline(Line line, int x1, int y1, int x2) } i++; } - if (i > 0) { - numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1); - xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); - } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); + if (i > 0 && base.mode & ATTR_IMAGE) + xdrawimages(base, line, ox, y1, x); } void @@ -2001,6 +2251,7 @@ cmessage(XEvent *e) } } else if (e->xclient.data.l[0] == xw.wmdeletewin) { ttyhangup(); + gr_deinit(); exit(0); } } @@ -2051,6 +2302,13 @@ run(void) if (XPending(xw.dpy)) timeout = 0; /* existing events might not set xfd */ + /* Decrease the timeout if there are active animations. */ + if (graphics_next_redraw_delay != INT_MAX && + IS_SET(MODE_VISIBLE)) + timeout = timeout < 0 ? graphics_next_redraw_delay + : MIN(timeout, + graphics_next_redraw_delay); + seltv.tv_sec = timeout / 1E3; seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); tv = timeout >= 0 ? &seltv : NULL; @@ -2117,118 +2375,6 @@ run(void) } } - -#define XRESOURCE_LOAD_META(NAME) \ - if(!XrmGetResource(xrdb, "st." NAME, "st." NAME, &type, &ret)) \ - XrmGetResource(xrdb, "*." NAME, "*." NAME, &type, &ret); \ - if (ret.addr != NULL && !strncmp("String", type, 64)) - -#define XRESOURCE_LOAD_STRING(NAME, DST) \ - XRESOURCE_LOAD_META(NAME) \ - DST = ret.addr; - -#define XRESOURCE_LOAD_CHAR(NAME, DST) \ - XRESOURCE_LOAD_META(NAME) \ - DST = ret.addr[0]; - -#define XRESOURCE_LOAD_INTEGER(NAME, DST) \ - XRESOURCE_LOAD_META(NAME) \ - DST = strtoul(ret.addr, NULL, 10); - -#define XRESOURCE_LOAD_FLOAT(NAME, DST) \ - XRESOURCE_LOAD_META(NAME) \ - DST = strtof(ret.addr, NULL); - -void -xrdb_load(void) -{ - /* XXX */ - char *xrm; - char *type; - XrmDatabase xrdb; - XrmValue ret; - Display *dpy; - - if(!(dpy = XOpenDisplay(NULL))) - die("Can't open display\n"); - - XrmInitialize(); - xrm = XResourceManagerString(dpy); - - if (xrm != NULL) { - xrdb = XrmGetStringDatabase(xrm); - - /* handling colors here without macros to do via loop. */ - int i = 0; - char loadValue[12] = ""; - for (i = 0; i < 256; i++) - { - sprintf(loadValue, "%s%d", "st.color", i); - - if(!XrmGetResource(xrdb, loadValue, loadValue, &type, &ret)) - { - sprintf(loadValue, "%s%d", "*.color", i); - if (!XrmGetResource(xrdb, loadValue, loadValue, &type, &ret)) - /* reset if not found (unless in range for defaults). */ - if (i > 15) - colorname[i] = NULL; - } - - if (ret.addr != NULL && !strncmp("String", type, 64)) - colorname[i] = ret.addr; - } - - XRESOURCE_LOAD_STRING("foreground", colorname[defaultfg]); - XRESOURCE_LOAD_STRING("background", colorname[defaultbg]); - XRESOURCE_LOAD_STRING("cursorColor", colorname[defaultcs]) - else { - // this looks confusing because we are chaining off of the if - // in the macro. probably we should be wrapping everything blocks - // so this isn't possible... - defaultcs = defaultfg; - } - XRESOURCE_LOAD_STRING("reverse-cursor", colorname[defaultrcs]) - else { - // see above. - defaultrcs = defaultbg; - } - - XRESOURCE_LOAD_STRING("font", font); - XRESOURCE_LOAD_STRING("termname", termname); - - XRESOURCE_LOAD_INTEGER("blinktimeout", blinktimeout); - XRESOURCE_LOAD_INTEGER("bellvolume", bellvolume); - XRESOURCE_LOAD_INTEGER("borderpx", borderpx); - XRESOURCE_LOAD_INTEGER("cursorshape", cursorshape); - - XRESOURCE_LOAD_FLOAT("cwscale", cwscale); - XRESOURCE_LOAD_FLOAT("chscale", chscale); - } - XFlush(dpy); -} - -void -reload(int sig) -{ - xrdb_load(); - - /* colors, fonts */ - xloadcols(); - xunloadfonts(); - xloadfonts(font, 0); - - /* pretend the window just got resized */ - cresize(win.w, win.h); - - redraw(); - - /* triggers re-render if we're visible. */ - ttywrite("\033[O", 3, 1); - - signal(SIGUSR1, reload); -} - - void usage(void) { @@ -2253,9 +2399,6 @@ main(int argc, char *argv[]) case 'a': allowaltscreen = 0; break; - case 'A': - opt_alpha = EARGF(usage()); - break; case 'c': opt_class = EARGF(usage()); break; @@ -2305,8 +2448,6 @@ run: setlocale(LC_CTYPE, ""); XSetLocaleModifiers(""); - xrdb_load(); - signal(SIGUSR1, reload); cols = MAX(cols, 1); rows = MAX(rows, 1); tnew(cols, rows); diff --git a/.suckless/st/x.o b/.suckless/st/x.o index a0817a6fe0fef82a62eda467502e0de33211cfbb..48c16f4c44f87c0c3c24e8e01355bccbd4ab115d 100644 GIT binary patch literal 87680 zcmeFa3wTw<)jzyX5+q>E4vHET%dsA8P{05I5=G4+C$OUdA{R*o4dLVjA|Z*%2?Vd; zNtE4q)V9=WYv0=1zOQYq+SYo%B;4@|UeQ`byd?$|&;nlb{npH^lgT98^!%{h^I__8QzglOPgj!e43&y@3wCb3UyU&RW-JNjCb&hXG_wL{mB(v)@?31wn z9`-5NABTM^_UYJXV1ESmP%?PvmEbAQIqusXpM2ZhEj6UG)b7Mt{A3bX9KXvC{;tf* z%5nC{W`ZnxWWMFZHu{V6-i@A-1)K_F{3R1>F6PO#dX+uFla1aLyKW z-IyxM6ufk!P@`+VDwI^vJ>bMe@kHsRqC%V4-b}~cU*ztna9UGFWl?5sD3Ch7ZEH$^ zicz^W6hNKQ?uhPgr{!RDqSDN1?OXQNd4bdl=ZxV(klk`Nl!sQC8{;$fo%1QAt;}%3 z-A-I6?6jsG?)ux>?hk?6H}At=cdHZL;Z(htchkGMPWzXqwjUljdV>?*;8eB!>5qRr z-D&UeyF1&z7&&@dJT2e1-Q6*Io2Z4#s%=?Wm22ClOlfJe3wKt!!vjwEH3*WaKJvSV z|Bpq@3nV$=t-1zZwtqQt^p5!OeBX=i;n6$#*I-^C#c|*9wp8a%G=a6BQbkl&bq^g{ zxpr%6YNb0hQ0ZP3NTEMTT~DfN+M9}e_6ObVqrVKgJCrXR6gKonM=3XJ-;?6_UUT=m z8yxpr-CxKnHuf-ez22*xqw=GH)S&xv(0!rMea_C>R%w6Q_F2$Az5O%$hwVY(ZT6WW zvz3EB3)y4#h1}h@2M>J{a^D^OQbBx7N?zAhCsQSB&B-3Q1_iT1zOKBktKSXgz3ILY zv}e9t7*F|LFs~zI&)yu$+i>;nP-}6ZBsh9U$o;};g&MlrT^oAoxy*q{vV@mP=W^V6 zfzt6WhGfZP8x6G{McJyt!edM56h3j&f5V0C`(6n7cA|JMerBJ(^D}$cyg+8qzHdj! z_p}0E#J(Fu{Dxf>$QG8cPZwqX%r4ykG!z)&xH$pmHYc942EXg6vG|eGaLVa1`orE$ zt>|F{AQCn&kkk1fX!!>>`|Or{w2D5gPAm9Tj`^v`E)>X-8CD+JY^B=G6cL3(*blX# zNX=t$-g;GFIC(l%K5Uug_@1Uc3|ZuN+@bC?2+CL#$o$M6v$4{hmWDk_fTMI|uqcp? z3_y)-^})Eq-3}*~0(q@z0SAKpPTm)Gb7L|}M2P!>{ZLg(<=Stol;~^0r~D`lDVE=u z9BR$0%-d`?&qG;JfbB4#s0L&$Oh;@D6;DjMp^2eI8EW7zA zNThPZcr&u7T1%YPYqN#Li`>m>zl8|7A+#`etAL6ru!=ZX?26WGJu3C>jZx0U?k7*t zUZs=VPCObYDUOE%#l>!?NZP|w9yEHX=eo=hBFYj2=@tWcy_m!FMCq?Zaz&(W;=eOpVnY6S;pHgvhTHc}wsf|w- zx}PBDu44=Hu1RaWU!4sGR2Xmch4MsR_D%VyQtAI&MLDJaZ<`rDNckz1idv6z+>DUB znB1>IG=!Hc-4bdD^Qa+|P(!E?4FS|dL+Cv4b$2(+4_C?-l_PreKGicltMln^y1Qd- z8EP;S-sgz23Lg{3aU`X)5ZVf$RT|BKaI-`1EC&h5z|uihMRKpx)~vLAXNupWo4cLeUhF4THY;7;r-gNL?Q zN%qfZFc@p|$tddXM2LJgSu|+3IFR9X+V_c*1e9jqx5d711BE+`5D)FRdyw7SirNnz z+>#PKikn+Y2O=;ap9bv#)cSbfHsnq5=g?N9@60kY|l#H+N4y26@0t)s}@dT1jU5S%ZDQtm&T z_IJ^8P6lM5P_f;~l&3!iEeEd}S{Xa+vzwd2s5)W0c{do92GY@mOZs*clPVH!1w4rW)89eTUi4jsm6M)XK&jnpE$ z@NEG3v@dLVuW>2{g=y|i=`hkdGA8A+S ze^5>cqETYrf~dJghBGvYP(1BCWYJBd#xEcbL#JT-$0r11?++IB^Gfnqq!r?Po0CP? zfWh0;c*>b-^lUpDMsE(rzJYUHfqZdpMeG|ylg08V*=~LqM^5|!QAQd(4j1*~ zb|RY$-Krv#LFZ}|m1+=moqPMCZ!hYO2K+PKFW}~Kr08w{WdwQ=zbFjiYX%Ct#%?J= z!Y4^-U8>V6Lueu@+bzq%=d9gCaaRk>9B(>*_LU6i6nr|@;e^~|7Gm2j8PCplcO`-x zLJr(zQJ~m~-x0WlvL3KSb}oiU3T5&9RLxBg%{VZ%EComBEc9m5Gl{D>J~gScol=VU zCPJO>rOuPVB%)O1&JJ?hd=aM9iHuX~VTRrz;)fzl@DUnh zS0-kE2T%%JOOeg*-^t-8oI|y!$&8S&t&lC+TaR%%g7MH;%e0P*$mF1(zaMHml7TBmqpD3`k;T#cpxO-1v2M!SfcVR~wa{+OP7>RM#jY7hsQG;eb1S^sUl~RPystmam zfr|-5JDe9-g!AgarBu$#tl%T$BQz9TCWnGjHs%79fp+02B2b4oZEhP2IrX*ixn_N0>#Gf&5XuBoyN z2@8rc1u42>mU2Mn3i&<`=6!nYx?&2FPAk;G@Db`bq>j$mx$aA};$j#Cg~C;Zw|Wu( zn~0*~eQi#7n^U#@qWSaZJMEo*x-ioY%cS|*dl~^mWSAmWK!orbb%oMT7bvyiE&O|Y zTao)-(B0>Lp(bI`K)&OC*zwm-5puVnCjAH}p}=B$SV{m{)*;GaK4 z`s?I#=s4(hhSV@RB!@*ocSCUPo^GlOw=LM-F(kNlH=Py6OS|1y*M2}l1kf=?Sc^GA zx9pqj(5vkoM~@;tcZ(dikKWS$K_+r`Xi#Ad*%Eulf1$K{NpN29!Ugl^2Imyw(sRgt zU9TS$Enh$H{Ff6^Rf3NpU>qmY^&8NK|&FyDPTakAp1g zXvcLv4$I|Tn9=y>)?q?5Vg4PO`P)A_TGk3moK_fjeB_vUuU+*U429D$Tw8*0(>WV| z+-ExP6R9^5C)&pFPP*JFO&^S3*iE*`3eHu-%HV}?i$#H);9UE@t#UA2f?6vsS#V(} zUexXGL_Ul43ZV+>{5>S1w!iKUw!b$dG-P+sEwnnf^~%P5EB1l^BI%kJl~5Mnl>_vg z*C&@-QI3{>$nX`~)$(zWtYn4k!uNymd`!Q0+s)SjLE5y~CqC(J!-s64a<^=^TZW)Y z3*C2c`Q3fJ(A^zuEwuhX9@=>1C<{(CWEW{IQ0v=dt1)fuH$=6sr>pl8w!R+R2(Ru= zJ`5|QweLB4$PSSPv-6_Zmf@j13>@s{?NDl_`>$Z@5$;B~7-YDc+dm$%_I+`P(E-=o zmiA9hihYpWvOSv7a?oy`10`DiVK<+TeIc$r1ll`B#CH2)`+O*>y?#R|bw@NK?{zw) z@)kC~Xcu;f>W+?$eVb(8+Sc-7zP)}+e(FBE`DF;nd);n%7JK5(>##%6x$|+6iI)w7 zd7GkT%1<&ow}O`%(m3T)XW|^bkQVFkweQXxg5e37^#q`Bf?REsNg1zH%(#`*av6?E zYG(`wM6AJpfT}6)fZehL$GCzb=wZ-hE76)y*X`8P@h}*-DR8z%L@Uc)-_vD72T7Bz zjq3#NhDUIj{f~})-%h9fqf_F;=ZxMVFM9L{led)IZ~D7s;BIXBI64l~nl;b*95>l< z-{_a??S!<2muw{N_rC4gK}u@3YGk2`J@jNl(V5y$d|nY`F0Qgv!fojISQs?eXi6lP#%<_yCmm=o6uuLg=E0~Y{2a>w47C5SA)b%5!Zal7ri76 zg(M-P>#uy&x9%mwkNZ{icve+ig73E>eV4i&@EFi5$SKFYWB;;El-Vxa(2OATj3CNV zc0P%desvh?WyjOK2-H1`YM}e%*}Ym%*7!C@-JHg~wl6S-q?^Y$Xl%AgyatyU?{)t!ox z;j)rkCu3{=7{Q;PC3hj38EnnZryUPm(%ga(+Hoe|8#u<8OJg#c>PLqzRL5=UrB|_hHy%`gpmn1L4pNEU)UE77t zdt9yOE@iJ7BJLo3-K+X@$IEch+htV+!UnnN0cD8$;Swvnt zigM}hehPyE+01G6Qa|?Z94XiZ4{qk89eyW!QTUa9gmhi)#G8CutfOe8JNnBfhazuj z545-Uq^J3!L&OmCAD}s5%qB6qj^e6H`zOir$_B2+WQjz~pXSir8Ov_oNJ$c8CrqO= z`#v-rtezR<_%<^iEk~fLBOlBXo;XJQEh(X^T_9R;NHT-lS?qq)PdQ-KZ0gZhAW{Tl zQv)OH=HKFwtefg!K$dMc(~WweixbA|A8KH+fz#fV?D)Rw{1!ABn2#Wh_2Jo2L>Uj# z_d)94ter^^C`>XJV|bCgAD@d|nQJxL7~f_&c^jfwG4-V{nzRsO(YHHt(0O%zE@mpY z&hb?*FGiQsan>$OuQn5BDIIMHwHB1L9vS=Mh-gLYmGj)g@m1ZiZ-?49EQF85zQApZ zD{jL z)bdPYDrR!A!)c9sb&Xk95bTzZkw7Z_h#!Zs_GH{H&d0?5SMgeC;jW(kFv!WDwjPYg>V9aulOVLhFdvW`9N+@L^)SQ7w~b`6oP~>osYlpu0b~_8p<;=Gdnxv2NeWC;BU* z`$bT+EW4R5M0CZ+3JP;d|Al#y zyu9;Fa4R2e9hr#a?iSgo;aXs_7H*WQ1F__(w-=UTQ69!PBf-OKR4`SWkuBDw-~eum zYmQFq0*4kyl{m3$^Q}fKLqm{9I)`L?wF?K!###$~^G_%v1ZTT>oyf8Q z7ksbGr0HXZ?54=8gvvA>O~M&fPS2tRyM^XrsGMaBu_}=}Au(2KoeMpsyvtDikXXV& z#}A>8V34DNVrxM@6|0ndp|c70^DIK^+-%XFFGAC9yjW#Ni#SNTbCHtQ>^Q9#;3{>G z1|?Ycsv=%$|M{YGKp-0gqSf{1-eIN3Uqwjc&M5%aKAltNoA4rGFs#*4LyVc+ygcem^p{870)@kzR} zugBP~hlLsY;&=933rU6lx&I2aE-eYJeUHL*$o)We`;WXN28S0AAS`w_04$0K(j*Nx zZT|^Vzuhq&ATBx4;kPd$Vs6D%Z}%gl5v6PSxbYY&i@F*hlXh)&S{L#H84SUjQT zYRhD#L&E%-ZI%^1;^2%DU(}X!wPLCpGD=52?hvR5PP(_&&Wjg9t)Z)IxBX%0-~CeC zDMCmuQ3VinvaBj-dI*}vzVS7tVAMZM>L+@K_b|=q`ZU4z8pL(&CbgvJs6o28OQcp% zNM8L4i>Nsy@$_JKOD|~-F0b&-OY?#it<|QL+H3b_!{%CN)f}KPF9zN2hcc~{s{*yUO)Ov#SN5%QU}YJ??ZS3L!-crae}t3brP5v3RU zip;Qn^_*^SuDXb-w!QPD_O6qISo;I#>=xIRg0Xj!(Ubc^?tZKZr1kY5%7lo*POJ2> zL?_%h9!ns|4MkfP%juQddQ^Vb<3gD1=CX?1Z#DnLNcB6MH+n?~i%~;#uk^d!c4MKt zjc()!C-?Bs&^G1O>SmkEQ+NF`K~d{ugau(@bZ$NO1jTauEb7=)S;WW@1A*RsHVV@_ z4pDu*w0lVZacH6lX%sPQpO^D*#G)wX@p{g7%U4v%mhs%U@1$TszPn-V9$M0X(Ol{%YQY!?rS+)cKM`tFRIdH1`|DL(3h`vd z2t25RVMQDCD8jIr!hjfADu=-0g+!aRTlRtrPSm8$jMrS9X1Sa?EP$Kd9+#)qBOOK?F7 z*S8l?U(Lj-vmZp4G4)!z*-ui)?)H;y zX=~I%Y#6+AFbINcT_|wM7I2>we3G2Flb$ycMxeQ&)4B@LQR`4!uawKYF}ls_8B$?Z zh;XRIOzyr7NZ#9mEsk)7Ow_{>t_Y$s4>b>qmwoQ;!s?GN+>P=wRY=x#Joq`#!!7@i z(~ntiV6-}Whx-Z^2s{%S{lfC@3G(riku6;pI_-x?wRevkeOO+%1j~ld==SB?!P1U4 znP8zOWF!mj)yv@J?y~O_ZCB#-Fq1OCdsXvcQL{X)O zQJa+kLNl+wghof7paNdUrG;R82G#*#g9;N2xr+;Dmuue1BBI4az89U>znCfnDqqxDVAZX^9~#%uH(}E>4$N* zVQK(LMg%JDtc|<`6cK+6#)+6Sa{S+RKrMZfY>V1)E-Ey^>QJa8{x=x3EW_^pD__!Ao zTBii;8{So?Fisonk^|#hvJY0u33R@~u|h165=~lb@hEJG*2xM5@toJGP^xi=(xNL} zl(lOg&fzHNJ%kix4eEF95QR2YIMJOG)mq_q8=Y{o;3{ejZMHCnjs6AH?j2o!#J$2^ z*$&0UH(Un#C2~UO9bq-%W;#8+f{I0)qp35FvQSE)g>VIO7}BhB6$&f4VOzg58Y{Dm zu3ZQKF$+Y^O?rk25GnT`SbHQZ0TS7e_tXBy5FC0?Jm|y=>KW6i!8u z6aFEJpw}o?5{>9Q6D=GUVAPYCN6Ofkw_s`%b6l*%Ts0NF)Jt@yiaL>vvdf?;s!+3~ z?V2;O+CzoT+q9LucqB*9irKH-sRjXb(|k0EjbEMH;g<(>9mk7s6iiAD}|KPt25E{E(&&fBC6OSTpT>-afC z{75t3-MPw9tDdDTg{^(N@X$m9uAX5{oEVVy*CEC>V&Wro)axg2WBoRMs>E)-0TzD@ zKg8fsawq&oMdYrt)KZlcG`*+t(H^8(WW9Gi?7mg(9@0WhY@^RP`(%>SFrMxe&_jN5 z^}Hw}LIo8Zq2iUI)|@x#$=oM+^x3sRJ|0eOoX2qGPw4G_|Uuk(b{0_c|R{2eM?Oz5dO#X^*(+a1bNXgAroP}iC$OGwVbWqdP^q! z%xPVdK|4}PFiKH4ZR*N*d-AZGe-71qh*%b3d~06Tv~(^kJG^^wP9v)u;P7 z1~~NmW9pjou(XsL0qQuFJS$b+x}bXtV(hJpMgv1s((fHN&O~(t1pp+#El#(H=D<^ zUrh*uv9o%=f@B%i5>RF0LDqi-F_C=^3&aD(a?VwZ<7_94XFEo$K_8LiK0O-GVdso2 zboYhmhV=^_Pd zK#!ioSWmjAizImL=l_*!^JoQ~iflXn=xNg3g)g)Y$AGGDJfRTR!_~EmgBSu76&vfL z?h|1yw)KcgdBG{R#RpJGifsWXj>P>;)<9jEaJPzKQ6j`{6b%?xt|oIPv2FQ$1?Rh$ zwunfaNVWT^u5D6h_rQ=1hh9Bp3-0maQO1g%oW%ey);)ODk+KXzv5jgd z5tZC>n%%xrR3sLm$yXDt-AlJWg1D0Fex~&T&4I&c3%Efph8Z}t`a61x`fI60YjUEb z%Tt4~kA1lRohoh>zX%>RfM1h4)^4HJWmUr_6-l<5Kr9~g;Sb~ zeY?ANqt7|fw+2g9hB>W;q*ZjZP!WPzYqvun@nz^C@$@IcTMHg6?e03hV-VgtBFc!z z&aN5G>F8vzqb~TV*^u2l6B(=Ji?|vgr&CZGLq&PnP+qyWo=2f#nTj_eX@>4 zcJhjwoL&h4?J`XlDQ8d6wD~B?R=XgpU=bY2Pzg>Fy-| zoMg7n$sGN(?3d#6hJGt=)?*}(6jI6d-AEx5CtoL0cGOw=EBt0wm)7FEgC-#F{ZnCtNs4fjo-Ditq+(TzB8cc!q}IA! zxa`C%G{5_uTOxMzqsXjRLgRUa&fCGzw;d$> zS;_1C49mPbj{duHdEE0r!@=MW4d-gKitgxmZUF4&@rJMQgovm~8RIaw7G}V63!T3w zX`3EU(t7OOc^(M=hBgsO-^}U%jRZNAVDA4SK{6%y{{JGui+J?9WB=dqN0!-)EPX8c z8%ofYNT58b&l32t#CAK_I#0zB!e8fd+AGuY^_DcQr(21rTov(b5FX*_dRK9G;l``z z)P#zidxRVf7=$vbl@aklmtdkMmmg7i#HA43)jC^fkSCwH?58ovDpy`S?|YEeB2$B_#7>d9q z?@sm_5cHEQ{1sjPkv_u8isT%*`boolva06nKn=$>Wl$)Yg`wV)x1oOYs_~vg(en@d zZo$6!;1bud^w@u9TOK1<6mByD=12L^`^$()&4O@0B^C3jRMO z%e6!51&e)+;GH+_;|uNNLb~CJ`IjH&e?%;|0&-;H8AS5A7qU_{fseV$DNrye!|3@Nt37K=ACu+ z)N{@~FF!b~AXGSghEsI@1;sOG%`Ulc&fIzPFIrIg!;2R#y5!Qb#pO%F6_LuN%c?G0 zUcI8Gw(j!!hG^r;RZXj}xbmv2ud&3-;*ifTZl~=J=UKQoXIX7WV;wK*OYD7_KuQRP zos;pS<&~-Ol+gdHwlkwE>dtJ4j;&fzzBJM>wp47Y1Sl32(JCk|Dw#GrI45Lv|BCWxc-c^ERitX^GQ}Z}`>=$| zYkm;**VaX9{6f&EhM`tnRa2z8q3R0cKz}MC4bl4A)dl6%)l165%dLjG+GufYxI9`_ zTVsW*t0Fbg1ywZ_wX3YEn!3j5+~{iPRy3<%j#W@yRkx(JyuQMZv?_(avb?G~QZdvz zY2k$I>}$ksJng2AsyJKx9~H*g%u!+bXUz-FnNc`zE|4PpW?Q2g{9_~j>PSO_RoD~> zH%7@4{+jX?5nLgz^;g%{EIrFlg*daZq5jMzRW)Z`(Xh;Fh*U?yQI)l%hie zZV`XDwz{_7KMK0l)kh)~RncHHT3@xKF&b&G8lseS_PD%p6YyYyX0?i{hPvwV)kCcw zfmJnC$P3jHuC1x8S~_m1wY0u`bzZi$q?+tkR$4f7T4BgqFtaRFSX?--uxvrmtkCQQ zR@tn=d1XLn1!oo}PKpX<&+3ho6ost$^QPyP&7C)=Xx0pCRCS{zC4c~{o{vA{&z|65 zM?W&|>?Rxpv8P`>j;CQyzbKB8Xg2*;;Gc`pOiUiIu0+u58~-C zhscv9>ts5fAdhpZaa=6Ug@l}CbW990@tb(I04I>3i84V69fCglGW?d~H`6)_e^=mF zgI_Ivb@*M5Uju%P_^rZkHIhLU6bieEa+f1_ljLr)+)a_YT)CTol2Zb?%aOZDayMD- zrpR5c+)W_6B7xlH$lWBln=E%za~yev14nYk2$=#Wm$@$u&iBr_Td5SAfapftlJjpddg%im&AzN}`FMTsxafxU%0R<$P*h_C5ueeZa z0#|<(7yIl)#WhiJi54&+M{(sSSviU;hqx*u%kawUK~{aFVj==TBvLccS{*?=Ts08^ zp)oSSqUc`}EuUzGSC@lG!D3Q2@k@5aZJjzPJSkjR$?SsLTD7by8nI3diw!JdL%GhI zJRxi~Hb63kJO9G5@ws7n`f3zwJ5aejj;VS^xF%&Lu6Rj&3& zR>G?UANt|&a!5mB^i|==vfAp32zt84Ge3q&Y%=9Lm&pEx9{=Za$n4jhIp6Ooc|H~si%k!eM{!I6>j~CsP zZ|Y~tH|d=I*NV|Ae=oY3p3m8CoIj^G>3q&~Q$I7kN$2!`RE#DacfiH=rvd2k|3f4?zFR0QA2OK>ynS^alo@|9t@Zg9FeX8i4-r0Q5%&pwCfs_80a~ z-ZP!<5B0Y(eMo;qU`#jL53@5}+-77@+cTYBuhrkibon4oUk;|5{?GB>ldcRhZvgu+ z-OD~q_p%Suz3jtuFZ(dv%RWr^vJca}?BhvS_PKBX`!L3#B7lENPLSFq(pXMLD%+Sg2P(#`ayer9@;&gr`qqbZ-$d(qAGe9i_o z)0=cYXZfanW_pv(=~J1JGdA{2_p09#m7e7#k}KSkt`aca)Q{u8Ctamqpz`suzZaeL zWjdd;{%jU6I`cEVkN!%~d=LFNAEvWB#(B?lFZmY@AfM@8@|o@>|4B9kD`f1M?j@h; zX8o`jrsp$Y?3wQ6e@yoq>G<@i0rY3Om;Oxm(x2)1oT0I2x|jY;_Z#W>v~2+WneL@O z)4lX(dOl}p?3wPRKhyn2IzDY5K!2ut>Cbd8{h6N685(=0d+EPn0R5Qmr61G1^kcf0 zeoXh$Z{q;^G2Kf)rhDnfbT9pw?xo+R0rX?Kmwrt5(vRt0`Z3*0zs&>a$8<0KnC_(? z)4lX#x|e=i2GEb`UivZJOFyQ2>Bn?0{k9IEAJe_`W4f1qO!v}{>0bJ68$ds%d+En? zFa4PAr61G1^xHmweoXh$kLh0eG2Kf)rhDo4^Z@!jGXVYB0qD;SK!1J!`U?ZlcML$^ zIRO2|0q8FcK!14v`YQv_Umbw{+5q%j1JGX|fd0k+^nVOMe{%r(KL?<{H30qX0qDC2 zpj!iMJn!alq{+^7?gzc-+z)xtxgRj;JnmlZQ4VhRtbEaQ3 z0R4v^bk>jOG0et#F`efjUi7d>`B}ef2cXk;5PENG&ZBcqU)Gz5vo%lYbn%=^A0C+= zPbYeAB%iP4>r7m3vcq@LiI07N(sMeNMdw@wFS?$d)A642|1P>IpVRT4((Cy%&U-%} zSi;_9^9w2~Ps2X{JS4qVk|*Rp_bhFDiB4O-_-pNZO764xT+r7vj|&RC?K+9K)hqfB zwNqihwq9xmai$pcT(Em)LXzzk8_9@vSCl3ex;2tUh9`75Q&A*I{upW)6z3;PMVgUc~kPVbbmZ$T6)%vgM#VV*B=o~ z&y5WZrROKTo1P0Gn4Sd&Fe1UU^i(SAJ|$;4?L+AqNtNlTV{phXq`i#r8R;1}B?Z$n zW67a(f6|6t+xAb8}A^wf>dlbl>-&;ap6PFZ@YC7yRt_-|=Xsh12*PYoU^Xw`~# zp-PjKFKG_W*D0DP=RoBvNjiUMe+6`ow2C3?WhLu0a_E6%-RVnC9n`NhXpQKBUqChN z1GMP$yXDPDza=?&#Gv%sV7WV!%W$^ZLw54@N#H63R~TGZXvaxyIGF}Q(gIMBY0oD;`qo+U{~(!OU!?ObDos7rGjxt6QFHDZnw}wa zotExQPcOMTJ)hdzIQdjx`m*Wib<@+Ef~cYu>5CV! zV3L=nI)EA_?@}di8P2m5?Z?Uv-x+___>yk(sNv)^A6#b|9Ai3MXF6PGI$UQOT&D^e zSt3rzZxvdW=&OYdE*F~QK#%*C9!p6Ny-rJ#D}Cvu!Suxy=_Skb+LbapRJ!j`*@VnR z@Di8}zZ81H65Aftwi2-q<#<{PeiuXY>Cik3O_NGhF)kHqi@u^*$)VRcTC>tK>V(!w z3n?S=rD{cAPxStMDLX0B$CZRl?pA3jR#ROsR&_%@vq{lJOxJDVPb$(aL4LkZ@lI57 zYE&Car7s;}n?ZjdA8wvnf=mJ8n@d*&l7~Hc(1xjuJDP_ zjdl(`iXV3<-aDu*!%pAP=4PbtOBOM5he~rgl2{i@eF8Y_Q21Sh_sTcv7o07{fDAP- znoE4>zgH?hBia~6o2_U`g_1~ia1_n7!{UB$Y70#YKQd9T@RK-=XLP?x`bxyrgbT^> z)&nZ-e9{A(AHQu1s*9N|b?k?*BJ< zURFF6BxeD2Na&XQGt#qWq-S4&ys7~oRy;rYciPCEzNA~z+k8tAt&={)!Ezj+bI9#G z#0(x;33?^yMc7}4J@U%JZpC^1XVfS+ll{Nqy@Ys65Q{EIPbL4mN8uL` z4jC9)(a=()*`{dYh*lxT3>i4wt?=Ur$40*Txx!8+0CAeyY%>ni(g?7urDU7_;`5C3 z`;(HdNKSt^sW80_5po-TJ20r&h5hcNRYZ*?c~GOECjA$PGmupB!m%Dkz{KevK@?eg z{vkW2So^#CV@S4$+ILIl!JR=$h9q;N?h$jP1guPiXIp9Rlw#efC{&lU(Y%KEnUC?~ z6)yT095YU~CjN!k)5h~6qLX~T%9`gPX8SYSkvq}02#>4tRJEru5aB;l=R9|s zrto_e{XweAN-DG_?B?zD0We zQSmKTv=!J>zPr^q#YoyxU|`}uR>{9i!s2hTc$~pqBMt3O`qkzr>sj z*kAD@IX5ak_DFii3E}GuyiMV^D4gqk6V54LejrfH(SYqz_(dYfT9)WZbzDwa^?g82`)GP51IY}~>D*lrdUaoLQ z;aLiQ@cR-VKc;Pp!vCQ7#ry+!5V%ik*Ncf6+l7k%OA*v8OUyZdFB1GIqW+1G?N+7m zIs&mVzFNr%og#5D55eJDg&$CQig^WanzvJZp~YpC%ieY3K!!?u!I%yxwPxpE8Z zmkQ5R4f-xg7V($luTkfm?>&nDF~vVu(H~TJx^jFm_6F@S;3OwY@v%Q|QaH^YXk+;q zBF-oGiL1{G{$w%!A%2$gl9E$GAU2lsrouU{F-|06h=GjtHg`vpGOK=Cmlf$LgJmahbEN&zjI=!31Hk}dIfkizl8pI+Ng zfnz+c{DX{Any$?XwkbmUyzj2V1J! zNu-*paPFU&Fiqj7FrfC1!V3(1mcWN4{Fce{6n_H)YJai9uQu?-3NJJ8N+mzjz^fJR zH}HBT=Q;y#5_qckIJHi?M)Chd2XQRsl<<>Z8~BeD|2+mSF6^RJQU9rLw<`XJbP&gP zDEx5)|ERrrSn{(!>&W#Er0+?;r{2|U#jZ-Ld)Y*F}bqg>A_IfD)S zMTLv&0Gga#N=~Y1mpz2H6u)iAc~9Xd8u&*7AC|ap!;)~h1lzF0xP|e93eRUi?Y~y? z`Mw8}lSIHACg1bLAolWl@?fjb&?jB-bNu8a!xio@p!O#!e6fN11wOcsf1W1rREzp^ zeH)|r8x4IXD4gD_t#7$XPN^a1T*dz*gFmG38w~t>h5yXJOBDVG1HVY&G>+3Zc`bdg z^{jy}QT#6&_%emRVc<0i?=tYH!Veqx6$+>Em%go0_;Ch)y~4k5;5RD#Gz0$$a5Dx| zK0o)sf9-+)$pe4V1K;U^zX5z0dLxdX+@I|B;D65pKj?uE!91|P@}3BM7&==XA0xTg z0v`O?ivJeH$NXn`@XzqT=Xu~uJ@8c?_&N{#4iEg#9(bDv{;~)Dt_M!<;qK4QgK_=8 zKRnX|Kh*;tg#9^{B#feTo3$w5Bwqze31u!r3ZeC2Y!bKe!mC)kO%&>2fot--{pb7=Yj9{ zz*7(y`>U5s4}6pdp6h|^$i2d=u>4CrRf!~VjPW{QD>-7EMUjRQESGF!u*Sq+7 zU^3(oUT5G(DqOv+-r{-Wu^#z;-vd8g$+_uV83*U9c$lN`cNKn@!lxaX5y;8efk3ne36ho$WVg+De!p1X2qjq;F_ zqxeIPWV}T2pX0%QzT*Fl;=e%gFH-nkg{#ZxRz%?^7D-0FuUx0_Y=xh!`kMY2#df*p$;1_$~%RKNaJn(fM_<9fgE)V=~9{7_U_;wHc1rPk69{Bqn z_+AhEa}WGm4?Hcs|N71Jz)$hO&+x#f0H=1h*l2h26uwO1scIipc%8xj6NN_&{9c7O z8Td|xUuEF?6u!p5hvOE=Fsx6wU*Q~QvIU-O-DdEgrTDiQ`1uNd*1#7ie20OT37n%* z&;6hpfgfpQsrz|spQ{y~ZQ#uc&o%IylzdLl`=2SC+cx7?)3~rz)3^ff*##m!b^ zCEk6Hzx8;De^V8{(IDRVZ#Av5R#r7sEvc%mimtXAYgUxk;qBA-w1Bmuwy_~n9u}Vf z5XWUCKpv5h6%EU3>!Wx*e}mP8Hy6t{QCm&bwdED!E&EnexH?i^Z&ie?rV6|fxUmK= zB(5lLScaEgH?1gN9$8wwx^5Z0L%*Re5^k_6>%m_`yD)UXP9`(BgeZYYviz=s6=|xg zZHQRW+NDdYBMOrzOY18lOB$Dwj?u5n+gp`%IkXEObnLVt4U&v*gmiNo>YuM{E@j2Fz~C8J0) zUT{tjJQD;Do|%D|2_lyXBG(B*&IG|fL8P4^QcV=piGn&&@J|$SCJH$dg`9~Z$B82C zM3Hu)NSh4tJdf8i_K@Rs)^KFr8D|KaAO%y@OJl=*;YkWgx>95 zK;QES(w9eOQ6uYxh?lopWo7Wt@ba?ovgKv;=68JSL^jx^#2rFlnuG6#FuG)^wWL-%k%0fQ$mj*T46i2P-Ow0zjov09bor(`pe^ zAw_&fWNzb<1~@#vMgk}#_3VXBM`VJ8nIb^0fY>SE=_nlJy{MW ziqzNF)Ry5`e(?pB1f&-AAT31YL+~I&mDeD^HlRTwoL5>D*I_RJy*c@m%qya&dOiM( z0#VzBuu@_xMu>oz>F^Tbk{QpSEW6J;xEE8s`)zXdZL5y8nl6UU^URXQiK zv;do1qJ

c&q9Y2sVGL_dp>wzmCmoJ|4xIzQY%CsSSniTTy&edX-m-gMe3tfsD5;B=p~jS zK%g2cBJ`CVbnW<43OBA<8j&9%(!SYK8baWRMwE!v){4lA@UnW6Bh4i$Qhc@|!O~D8 zI`v8_VogOMeL;c6CwMBEWqwTq>ZoA4=wrzf_!!|xe9!$WC_N%I(XdwPk1+jp0aHefbJuZgl6=zTjkN{-QS* zt+AvEqcucq=>dEu+o!A*GB>Ay6>887=@S)==%NuqW|2QjD^IVghA+qyq^XIlvg*i? zXtt##@)H+5&9MgQNJX?=xB|YvB(o{6u%Kg9d?pKqk5*LGi*tmI>PCDA3Zu@dx^QhB z1}>md&G3*^1S!#Bh*peJiRjk^ldAzM5{M(=tfChPH`X^G4oW34;82|hhO6kw5Q;?H zQQ@U*MS1;lM3;K{1W64jWCbwFaMZ&|dR5WaKwRvh)^J?iR;f6Bw6oFUv zZgV3dIwa^MNl5`|!A!v=6%#taC+k)R5$5GsS&YJzI+Bi5(`(UqML4&bFm|Ocf62yQ zAU}R0%ve{8P>5D6hnAGB2$OW6EY!3F-@C!g0W%FU>#FkVXafd?2x;X@sIvhlbuUz4 zWLhi^XVz9!RaQmn(eB9=P!Dxg6_$uVA^FKI3+-F<)^nvSKE0@_p|PA|UJ*qIMn&BU zU!7B1%O5O5V2z@SWu8!^F1n1JS%wpe=XK@vhy#2sqT>wpy&O1JRdscu@8hmXHV2kO zt*t=%3gv{pjL(EDp&8{P+>1#t#DbZq%xEoO^ra?Z;!~XK+Y91tl7@DBitJ;vqXcfx=)K*s`)T$$V-L0ml^(7p*4D%z^ z{t`WCq$!L^0zyc2q`tgHgb?_8PvRBjO&~_gtINcEh-C_hnGbLk{~#H=OWXgrItk_E1)8UJj_Vy@ZFq-gsGR*W3&i=RpY_3 zCG?RSbs~LymFQS%>l&<8we=Nq;2s>z_YRbjDduu7TgL&hriYPIW%pz#mZOml}ATTIa*pZ#ZAR zu6?z^&v>&3ezSo;rp{m1a9rZB@EKgOja2z_zPBm9u?D_g;W-*kYaXXkLTrE0aI*hi zfm+ss3McH3_;J2Z82lZIf1827r*K|ZMRNY6$$8!2=X~GS@UX_eSHnrx2z9<+?Fl0v zVLh>^T5N2eF^X@nfwP=6g_E2=YjRFE_}`Oatur-#-9D2&_<4Ohktq+>r`X_UefW6| zQ=dyb_?IZ0`0vG!?NDX#bH4aWz1WEVK8^o+ji1^I%eh6vD>VEUnw-CA_!}BO!lk0cTLV!8cv5dD!Z*!IPD(P_~p7lSzcQ6%lbT{ z$%$zAM6BzgjqStoa}{pNuQd2M4p$pE$J+)2=Xkr?z{z&B^?Zd``ola`--iu;ZYQ*! zj5gMv@i_`7{Ye+L!>zRagIkb_S2!6~zOvB4He2Rus9-k{cXBzn93J)82v%=RH zIQv_R!bvtg!^ZmDYVh9x8f}|2{zq}n{LgAQ`5NPIYdG~qjCU%W)T`8RTDL?S>GK4B z%>RwXzf{8qsSEDJ|D=W=ui;N=_(%j93pXq1$%}W0-J><~(A=*gKE%>oN9D#Kww6Pq{m(~^0M*LgxWB%hc ze4B=!qRCmN;b&<4dOy2Z!}b2?DNPRL&2jG;1HT6}+IAT@$LF^Uoa6Z*tk0m0%lix+ zV9PY{pDVo9z`0*uY2e&1Uv1#*C)X*Q%B1(pKQ;K1)%f7|2G0F*y9fTRfpdSo-@sYU zAr062@uDxw^0Uux8k2<;P)teo`K(?a9W2! z8_Q?@%M6_LtTXUCMQY1hY2d8qwFb_5-e};zQ*vm125qbl=lcf(zf*8wtZv|(FRk04 z&6M*G1LyJHZiQ1l>f=dTZ$X_3GHCw=sGUE#rB<-vc0#;@yt zyM|L9obTTZoW~oFY4{4QT-!B#iH3J-c#VdCrQv$M$EyXtWQSUfe}uw`SC1Q4X#BdI zYc>AoG#{E2$k_@CGKKl0%JRO5d^<6l1qpr`R20!cbl*YdUY1rT1 z_2BQ&_;+gj@}*LJ^%C)uCg!F8m)Z>J>g#B60a0BOY!bk&WeNHoQjz8-RocZrGaMttp3MV_i ztm%2b2mjwS{#P{qr!<`Ma5LY(sr810zltB*ZG*ze4zJ<&mAVf9w1G4K z3m$Ub()f32^1t%H`9%t*KEqE!Vr*P5W0aqtWZ)bh{0b*I^sGPYd6vP?a)JiVa;9r? z-q7UC((r$1_%cJzGNtFu2F~)=YjWPyj_|#y?Bruha00H2$|WyjJ7CT*Fs+ z@L#X-(^Q%Dze(fYt>JfR{8VPP!`&MHI~xC+8m`MZA*8?wAIMs`8 zpG!1;-9A@q{CkilQCx9?;$ZZvgdpFaksNoAVIqz%ur5gWA4X^azU!~z6X#6dj9MY9^ zqU~k_=lS5>2G0DC8h8m65ZfjLe^|oS4g+WTZx}e)l(u&?T=%QJ3Mbul`+Tk8AA-jA zKVl?F>xs{gD?LxtaNU1KDcqDlNyByd=V@{%4d0KLtKqu-muhl!{cAK_m*1qxA^Cj& zBChf4?e`H4?*KpB;TcWNDh+={cWL~(e}1O%cWV5D{5Ziz<|RGZPlow%f{k&S zQ_(ikFLr(KNgA&E`8f(F*?PW38h;aLT;91FzFNaC)^NRCOB7D>_4aa^#;?m+so}ak z*JyHdd;VDCr#z~aAO1$eb$$L@;bwUs(D;eQc5X9pwsV&U{-uWN`lp-%q1Z^auKzI# zC$)9{lRfyyc<@j5;Lq1^iUC}Y>oqx~6ZdzwYy2N+{C64r)jklhJ+9$b;GE^RYjV0Y z{5g$(kA}ac;a6(-yP6!mzxYVwC;hMT;SaViH2f-@vp%Wc7i3}oy&4`=IN4v<-_dZA z&*R3~8h*7VXQ75)qv1<5oaC_GsufPy#~Ocw!5>xSifQ;Bjeos{U#sD_E1cx(cKfS_ z>;CYeCWq3nKkU``Khfk5JryU|lJFz`96G^vtcHJzbGE-Cwn6aIq;X3~h6;8bS z@MAeu8b8tadRM)H{|+?Tes17YX4>vDaPCj)7%HLWIyjbCXHSouf z2W^je;Lj;M1w04vV?AHf@Xs{-9S#3n!_!CM1RLqO20xZ_tcFt_Y@ZVqP8iLVng53x z{~is$OvBe|c&#SqT@C+(#!q?dR`z+o!0%9an}KuvZZ&Y$|5=5T?0;$b?lSl}-**k1 z^ZnGoIp2c@&iQ_&a5LXQr$Gs9>?bTI&A?gCFolzxgZQ!iPuK868a~O8^8{$Lm1+Ek zan5og9{ks6{JQ@?XYjNBFB>@P|GFmU3oYNVKOn879j?Qk^_ie>DzC1mqw&Wy{+S;9 zi!^?c#d?Mfob_C$$8vqorb0u4eqWe$P@k+2%_P zFVS$l-Y?hW2zw$8ZPyw&m+N)|_X}>z`n7?xo`*HO1-z{1;L~M7Q_tfxT=$3XYxr(W z&IAqD%k>L|lYOq&_0uw)ZvuuW-)g$~Z%i6aL2h=P8`> z{95C`MB~@}yh6iuJ*zZ1x}Mh>{ClMu)=xG5-CDj6X}GS>4nxj+O3v#VeuE}ww}$W0 z@Q*b4-)Q(j5B|YpWJXk9>ooo}g_8~Re(g97*Zm}0lS65ETzZzq|E(rJ-{5~7G}5{@z$Vm@__T@I6qY3q)U>f z=SYoTZ%3zTIHh4ZXJ~Tn0nYVxw!zPO78*F~S#01e|3Xc^uIE~Vf4wZGb%TN5rtljz zIrOap*7JxnB?H-~6+gz0((pYRe!RlT26}tEHX8sol5; z{JNayHU1P$|F1pxlgC3aHqs}KAL}zz!}W3vSGZZOlQdk959exfC=J^`WZ-Q7B2A8N z{|7XF-TsehxUOfLCP%mb(;7ZVv(JYbuIoQy0+hx^_96Yb-F@G{x!pA>oOqAW6>INuQ( zuFF5wz`0y!8aV4SOW`Dg-f7M9OEp}#e~Tf9oXT}r!%vtb#0cNnt>L3JT<4#ma5LX!8vjsD&zm${x7)7_ zIUFZ{tMSu2-`M_t((sL%o{wla`6lyk*YKk>{7nr{*YN!c=W_i{n#KCc!0%SLZ?Zfv z`BM#?+uaEU&T{+)PBx-#p@CDMMB6n6PO+7?8x&6UOYcx;JKSRMvp%;P_#HxuW&O&) zS)aQM{7%LHM+476-n9M2z`1@OR5EC4Vv!2%&IG3x%z`5Rkq{$zy$^VlF|NR>Ou^Rsq z8h)IH?=j>oQswP5@Q}jyY50$Uu{{rIIQhg>#s9U!2|HfPw=7pOko}wRV>wM4eu;+v zQp10u;SYM?TQpqn2Yq=+jE(fk)bvT$@U5V+K8qC2e!}%quJNCs$!XB|>Dvn|r&+@( zZ|?6N)#Pl`^4+BI|5U@D*ZBXU;s4ZdeSGqzhM%PAGxRJZ#%7kwRygUXmur*<|LGdP z?r)PcT=%zIG&xb^!S?y5hU@j!sp0E2{-e*v2{zJ`>V)OYS2&eRms76cKLd^VuhQ^a zHGG|hQyvfFN83+4@H-Vwy!34|mjAfMujl)WhBqM%^S`3W`JN`{O^u(+O!~Z|@zXQZ zEax+W|98ZU?JEQ4_;adiuQ(w*e;}p(zARndehaUWKga1^?ZvEWA zdHwl+8F+{?!uEiH2T+%^J!atR6~4j1+0I)PPUSjP^M@A=e$Mxw2F`kR8hC-y^J@*i z1bJ}2$>)-`GM@Y#d$#9L4ZlspkI`__HDAd;Md5^z4OmW=!Owc;Y5aOT3>o}f-gySj zavDA4Ty60Cp%-nxGH{mvpn$- zQsJimtT6aF-|IAfy&Y}S@GPw!kIL^|uE*u6b&iJr9T?a9bcK^Xx||Y?f3zllp@vf$ z*7Ir){FfT8%lV_i&3qr$_;o#ZXt(gT3tWT?fvp&ByaMtI46mIHsuffmqA2x8-^GO3| zJ)bjh*5{uF&id?8xT(((1=3Flr#!h`WGXxf7}JRJdj`(>k1}wc7oV=le@T;{qv2=x zdh*3P6vf8*vYcX#{}oNnQGhRwDsL9%`Xbx}-EJ2{%=M03^! zO`;;Fb=4&Vb8_Fg&ojRLWcshvIWyh$>$l$f)~)JWw?AN9{=UtzE<0AQh94fsL!JBM zI~LAc}{lwb)UpfL%(ehXZ}aTnSZm(@B8g( z=f2;bckcV`CCSh0-41bH_um%h>yh`w*T%zFIA~(L({o(<#PxyVd|nI@XFm)VXFrU9 z9~jbF4xT5+OFZkpM7$CGpMNpdWqJI(W$UEqte^Y4U7Y*-y7(CE?;q6B?jCXW!-wMB z-+gf1-ve5z>*PG@5OE&gq0arh=s4%r2J0U!&U#LQTde)fZ}$J^T)dx$Omc49bfF(E z6X!TQTYM?v=ZW+B@^$flM*Q{S%i%XUzdO{!`tO2Ujc0^d`UV$okG+9BkJj)f#aAQ# z7ven6e2mG~{Taac|k=XteA zoZq*+PP}LZgylwYzVCe(-0Ep`{lCV=+uVW2M7-_2d> z+}dc5VTEhqW=*c$6%s!K@vFq~HIKr2=RXXwtaFpg|9O}HN#|d1{$DPS4JFIb?#ng& zx8lnX|7Hz;8*ckM!L?hd5&w}m>-n?G)9mu}KQ9(w`6oIr!g(I@Jg=+a$GbdVba}=~ zJkOu;;>>@B%QMO4X_ELA;W1k#y7(3sKTYDN#PGr_=c|J8xc`%L>x-p`Uo6i0uM=ne z%i)$|va9E2i64pja~FTCi@#gqIo{qQ&UyHH@wKSGB+hYolQ`%3Tg2BP&$Hr3V|@6P zIQ!={arVz1agINGYj__VIG%^hKS-SU>%{rJ@zFIraqh2!XF9iI$?Nq*@wGUPQ^YsI zFB0DjpDE7oA$N-NJee=PA9)suvwxO~vwyw`w|zP{i%>tinQtY?*TyM8STKepT_zC3c_ zA(wxutLJg&zTKx?9@__=5ATXMBme#yc|LPN4B&BTMEqE|<@W7PmH6?9pH(A%nZ%Dl z{B1SjAC`DNU$)eUf6m4GdC`l`SBEzFeB9yk`1j#Ia`FG-;y-q7eQ_r0KX_`q!+zs_ z4H4&jX{2+Xf2{L=b?u%Ew>Ev9XS?`&T>OL@@mITe%f<6QbMddaJWC~>g%CUHLR9vA00@T@rN{3YD_#*aV0mUxao`)c?B zX?IyYuP>OtFWmCK7=Cyj9xCyCy*X5zc|Il1{!hf&Z>Nc~-_8=}etiLMb^7ngOqTfZ zI9^ko`|JDV&Mgb#BzZu+e_}ACpka+Izx5T->E5zCEDsk@Ded4U=LAceE zy7BoD=QgL{@%>N9(};e1N}ThMZQ^|1y(G>$e3d;F z5EhPa^h4m5|00)vn8fqE8X?X+BVC@0U7p0n-y7Osf1d8#+Gf9fb;f2WII zf9Gpm{=Yc)`TJaCRmIODeEkF9Jm2h?*>a@wABA<+|7qtw z|FJdlpD6J&kpEP1?pLEY&*ur^JT5J8tL2iAn{`fe@ejEAuW;_`Z*_Tm|6C>U?Wq4I zan|{Lan^aK%fH{%vs&W$y7)&f{!+JJ4@*4T-7L;_pKy6Db9tVx5&xo#|B{P;MdCjQ z`(VpX7w^AUR;l3wFOJ*U`SxJ=;qe;c+#i==a5H~gPLg<=OWAUoILGs|#BafIY!>JE zcAhxdLu{v{I6zzN~ z;%B&af9!mw^RQ|6V#m>b{x`06f7WSt>GmCQTl&SD9)Az(1I0hadQrST@(dNX?-bfH zEZ+3wpN-cKM~lA+uM>X@K2rQ$_$cxB;A6x;geT&Eg7bA7`-iV{8YKPz%y%2b4}v#| z_lGx&9|~^~9}J%&-hlU2F+J(&&!Z5}*M+Qq5#leCJjWn@hWImxZxz20uan!wpM-ac zk3;^s;zQBTnfMuq?-D-?zF52!d6tS#MEo-GyAi)!{7m=_;uj*%E#lMRx%ijhE5v8Q zSBmdMyQ{@J5Pz@u6!=>45%411o^E#RXPgqDF5VRA?~IGU=L>z&*yxGGPlogSWc+YE zu~Lbrw~GH{cmy6t=HdCl{-l2o+m|HI9=y@;y7<>lh)@x47#(4+_?sgm^s@%T!gjw7 zuX8>mJiZ<4_F1qkc4PkhLAF*X*k|sx&|ic%h`$1F68|0CK2z{{cERnnmG|9nn=^Rd ziuG3U_YvPI{wH`Q{xN*9c%Qzpy=CG9;5UdL4$sAh!&i!rfYiTHSXGKD3LH$CxZ!ZY!6;JG-z|5*}mL3~+!3cMoD|6i^s{WcBp ziTGvkRD1?J6K{p*;_dK~cqhCpJ{MjQ&*1#L*B-X#Q5QVHE&W=obHC}!;LJx~4$mde z4OlOU--h+F_z$pN5$E5RD&l(_Z1*0-CE^djQ}G|eGx7g`=i-mTOX82g%i>SNE8@?= ziwDLNi}i1ZC*tgvRQ$gYpNYQ)&&7AaOX7ckm&JF(E8;vJMT}3Z{{zG);(Ot#_&#_h z{ug*I{txKKl6YTuS$qJzBAz0D5#tu?AB6ZsoX^u#{BXo);(R{l;zuIBB+hZEEY81Y zRuTUU@)QrQK7Wp9{o;IIF%>_B^^1QFo{N7TUJ`GCm(|hF6>*NM#eUWGPeq)m&Nag zSHvHJ^Y0Mx{O5Hi8Bl#(c-=|GA4Z-`d^0>3|2e!Q{w%yK{sO!r{xZCHNOhgB!V_^` z2UGEkQ{Che)58q+`OFZX?CGqh%j%9Jq4=duF9~K8y z*Ux!NBF=eBD$aRJCeC?FF3x#NNqi6Xw=B;2SVf%kvEpIX^?!&wi8$v)sW|6FnfQM8 zzc}BxDv2MA`9WEH5WFIO1iVTcuD*icv<{Jct!jqIRDNI`~P%!azyoh zodZwBC&4rEsqkEUI=m!46J8eYfLFxl!HY*$*SQFuh+hj&#jl5F;y1%{@gKlT;;Z0g z@ip*@_fXUgIn2P)!xo)(8y*U#rsBF^(X z73X=FiSsMme6AD!t_6f;l=IeZiN6x(Hq@Pp*Q*WA?G(uQx*OYCTB99^OT>SD zYJ^n$=kQGYapcLJ&qD1+CFfT zi|4$W?|*3iRN|jQ{<^TOEtVT4cdw7{{{4yLWn-a5{1mL~=Z06nGl`#z_2uHwJDbCN)O5z-Uwuy6mdtJQ$r(%B%#D1{dHuy;K_3&2leV>kb zR){wr6Mc*LremYOCjLIWi2h?e{5)=qI6uEzDbCLUH;O-ULTs0xn=wD1kNt4np!0LX zRGgo0X5v2`6Z3Bn=jW%}#QAya2jU!;2I757*2D39q&Ua7#o`<%*NXG=-yP!oym%De zZ)Cf-<9g@+-n~7>>);HD=XJA7{7vM!SNx*WW52yD&d+&wix0zfVJO~LWSzWTB;vfj zOc9@n>ruOS245v?Qa^p)bxEz$WtB;(h>w@dtP_#5KGC&xT{#S?fD z@B1R8-WsBdrUYAe*wUbwJO->nPgM~Sz# z?YR1J;jwOxs|^UXK-(xhSU-3EoVK~@y`fJvzJ7eq>YRmLRV$&9IrHbu>$oP=c+LE- zj`~R#Odq?jt8LEJSpUj3i|Vrl^RtcxT}#k#N9UDaUeGqLBX-!|`MWM`ZS!f`o@!QuB_w0m!l#5;Ae zZo{nQ)a_SJi-8UDKwadX-=~FpR+pVue&O~ndkpuBvToDQ-u8dgXv-Sry=dR|-!4JA z|28aGBn$BF+y#y=}klin#rgvSVzYdD^hObW@>oR@}Y`<5*lP z6zX^fw>IzVZU2Yae~u9r&QopwEfw3x%h2$Eulq{Cur%RyR9ySp_PfFZy|v#m zF1EkdCdXm1_HAg<_HPZ=Tl;_fW_Tbhtx{NBw|?^L#&EZ{_M7Y*2CMU zuzezNWo&=6xV2>qZ-3$LcRzq%yRp4DyU*?DzYTY6>LRCkp@QwTF4oQM-w$^zr*2>O zofwhhy2E(g{@w6Eug4!>+}eWYubqE3wdeLcF1@vFZIy1f-(kEtF5X)FL*Ylax960c oqTL&8`Dyn};fMIVBd+~z|JmE|y=`Bxe_R=s{oTJm`KA5;SC4=WKL7v# literal 86736 zcmeFa3wTw<)jzyX5+Fd#2^K9X>QRCX3K$?jBB(j!gzRX*2&k!`7;=F~NMdpVp`t>Q zSaxI7x3#UdzSUOSs%_P_-fJ}>+`OY!#XH^;g9>O7QOWrk% z7KJu$4S4FH!<_Fp(EVxSSW-UFq0}3DdZNkeKK$&Ko}R1&!5i9w543fJF9@F>J}N^o{m)oigcS{m}=mxg+>09EbQg#0iLx zM4X5?8F32Y!H8jD_{*2VCp_b}9!rS|T5sDyX}?=uJ~4QF6G2Y!c*kw*2o+?#7d<^| z`KbCmkr{Of^*bXiGa}xn5wE8+r>Cc?!T?;`3QLM zx?T9|ZE;JsxfL&DUHe|9+rEEH`@ZAHZE#CAxD{=Gyz8!$-S*Cq_k8N7C_dljukQ(QfY>^i1Z{L6X zxNWT?vjZ=9`^IhS-+~3H39k2!zo)vMKhV>&?lY>0@`|2e!^+ofNlYyFW~P>VYf=;F zPh9t7rkc>%_Kn*e_MRBGKkRKYZDFtSVJJG*G_&@16WqWn-fnM$>m88og<3^NuTuA` zq93o@c_R#Pkz0!EZSx9JN9B53B8X9mExHn2?}pU%P@43D+x}@N-}}P#I^6c|P(;_h zCXRT!!@+yDw$2ly>1KX0i_O>)~mNO0pP0C&FZpj?B^+c9dn?e76r zT{7bBXxtG`c^%idCN*ius$u1g`vSqH0Bkjt5o~&ch_z4j(CW7DK)q~pvo-~rz6N3N z!Q8|zw-HMk4u|Q(lW~}&569y$nZy&RK-mKqsQ=tLf=#!m+;U8A>A?qcOm35LXmZQI zp~`KV+qx*VkaA1U_qO}HuN*}`E-u)#0OY8%kPGEtBZ+d!Qexeg>`V`|5ripkpfN5ewu{qfK z2?<+F3*Lo1A}!RYz5TBDsq9$PDl_%Sc$(vA7OJ^3$|GJhbtXA8Gpy#Ei1%@%Z>MZQ zS938~qT9VCIpMR!VDk@Q0gl!Nn>XX=F>-}ea$A~Im&8DuURC^dtcM9siWqU$~Q6_u3Kx-3!j zBJL8+pFrHTjVe<0E~OH9sVhQ=>R76e1zzmB6$o{Y^5;9rCe=U8vFae^`2V_xbj_!% zRDGfnl{cwEJ8#<$!}dytQ;{yvr3m5#Aqw~s)ZVFj~h{5*JZf6N0gcEY~c>|hQs%$udzxBX4c zUPtpM(TQ%0v-atL>&3g?YyE1y9b;Q~Q3q+if1rEYd@NMlmQ|s?t?=@6t=n>W8ZBxD zsk4P0^Lv*Vy3oySSDxC~X_lPsjcQ2L-OX)Dmz1o2)$?W@=z4E=uEin_3%JbARr;W& zvq~NO{SG+1a|w=-rQ32m=>oxQw)qjN3qD`i5nHsTsrA+ueO5Z7CbhZVmnP*V)m`1@ zPl8RO!J(IU-lt|TQluaEv+D8Q#n8~Jl<#eFu@Gt6r>&7CbiJIAl68{|BSeDELoXu> zypNP2l=z*26pn&{1+8PD=5RW@I>)D(%(saG$@c8^tOPrUJ*m72a zcXm?wmYh)-zUC#Bd-IPDZ1v(}Y+kz?d_0mm(rt|ddbFKi?)GvLRQ=NEHA*b?-Xr$L zjcI!L>2N)@w$T?K-7oNIK_B?<U^;ggnzQPLB}4fV-1`e$J{54L`WPP8Q_MOCft z&5s<7JoR!|uWePTuFX*VzaVaUP!W%m#K-hYd@U4J7Umi#O@LvFLKxvVfv8i|leffthlh^8hY zZ?m|6%Pj6YM_^n)>uEk8v^3HnBQ?4Ne{_*S>=7-Pw*B*9ikee{X@CJszSA*wm3!eWPv^w-fkr~}oo;(R^NnAe617yy#Itq=ab|0CgDmXjMXL@F?AK5tm{bD zxG_Xsr9-uPH7Rjh>3R;8NNWxIg01Uu5}?bZ$gaY<;L`UP)lgEs0@-1YhZ>4qVl zO3}TMj}C5uTH&^S4f`+7z|})|zumd7an!1SU_DkIocLAn$CrR2*nA=W#>}2Wto`|6 z67}%@1n2q*F5|ANXGqLegFUtE#P#f8Pb>plGcYWxwSr!pjQiZ%e$$t$M|x(fWZW*C zr8K{;rkLQ|*U-Ao+2+0Ey==Oww>@}|@|5lm!FyCc_tf8h=G%z&mmbgbQneo~Olhv> zlViF}q#BT(fu7zC=ahWzwtqNSY8wJ`)uzg>8(br8gkb+E(`+LQ*Jfm0Ff;lc+7@Y9 zHPhSDxsf~z)egO@(7U@7R+}9Kt%H7zR;`YETXdiNM2RH*FbuJ^9ql+gpLE_riX^jOzSC8vV_ zpsB>HLUc;+HPa`Qn-#U`u{nBY$!)xRpA$WrCdM=|F|H%a8#kse?6?#qz(`P7pc`0$ z=_~r|kwomWaIe$8l6j~$1)3DB9_qixR-e+J)oKmXRS);+qvbd(q+Uxacf882$0FT}-<_I@7 zN7N5S^%O4bQ2wCBcjq6z;0uxqAR`N!#j<> zSR1sjI)XCB`o&C6^`nsSsE$3s3vq!m0(FTM=^C_+?(N)y4x-dk{)I#Kn5&^aX|s{m z%7I$mC|xo&7-yn*eayr#(D_LxE>1U*bB1@(#!sXrr?6#s<5z>Dr7f2)@bS;VM zEcm0jsM*G^1_hg{sEYMitJX56>3q5FBieipt1-z`Olv`)p>f}k;B_~nLUUUe1!jf; z^)&8F2nXjp)pa${EX5RT8WwAWUv5rWi`|>1HzCFTRZ8z>RnwDz%zlCDaE~JqcB>=U z!*_eUuU~vG()?6?Vq;sNabHsXPHCiP%!44<{0UNNJ0A|=u(3V<%gs)<Q+>sD&+>sa_+Sd4CN;t495@>Jk2{zG&Rd}s#l{*^SR-o3XRTf5C&k96Za|32) z{;@RZ^$seSRA@XY*$Z{$A6><04>Wuwn4N_`BMtO2F-K&n-Vtp67|pBu746|LcK+7A zqwL(&_*p_@PhjOg`YWRMRaj4%O=Hj`x>kXLDocC)J8*VNI!b0GQOKCwQ|fA!etrx4EG8_S799 z>>k$ms5dQv+$oi*SZdRjN%b!x|BC>n48LfF&?ciGmVQ-7~ zDcbsCbm(wv-kI1j3kUPJwC@{?`7cTNE#LcR#QQ4$#rMOlvmxIL`DQI2h0Ry@MO=Z> zZW0F4bl8pi7iw=xU2mwij78P>RDh#N`Vab3(5eL8=egJ~^*-jAm#!bPY2L-31*xvv z8cn?jW8o!k>kZ0#dXnBs#G_NogK1Cg30}N) zj~g7{u9lK6u2A*_zrPg{3}1`dajcpvX>3!4qJauy-u8mvtW8}JOGIHw-p*XHP%ZtKme@YZG!ty|4n5v``fEoo9(`7M!6 z4(9ggUm)$-)_oVQ8T({AtZ`;4#-H1e6E-nNJN4MgiD1hXHHE9^NNN!srNKFBF^lTX zP}NPAbycHtX>E8oI(4*_~+!UngGHy2+2=`w!y*Lg~1}YR$n8+y&JT9 zFGgGhhoXzUSX=zj* zyg;pn(KPn29JWp+p&OLu<}8kSjnh`Z(V03+9GQJ-aPB2TcPYGOjTrf0_oTKy*N&ntsQn~{TM|+m_Z=TiYTP%ve%2h^8A>f@s1IL=wv;uUIP}f;#Uc45ik>%hOG^#CA%qAaGg2s)m*rX;;j&g^i83&tb zI(OHNrCLLqz_qR_?juwk{j8t5j#uis#xMm9pq^jyQa>c!SGcVWfz8fgv>`ff|6{|D zw{j^hwBOe&#XxkZ+7o;oGPfj(9kJj&Q5fC+X}sQQ#Kv)&O2qm}E;?xl{k4OV#KhF4 zDKV9}y>pNo*u;7dK#!`9#?3Ufk{qx878YLW#1@p~2Cu6CgSDP~<&XofrqfWlf@W5X z6{CYqzct++gDu)=u!(LtDqGx=MaqmtWJY(q8~D2G0A%{YjV4mwKf$5XH4H7+M|2`Y z{kyd*4ur}jw0!kzbB&j0I`u(pNTy*vh~ftJ{y;PiD_0DV85kA1ztH!|m)T3iBHW(F zMd9&eYD*rrbx}SXq|*VqW**j*G=a5T;L;>k=r&%J?bKsi9=!_bTq>5INiJ@nw&Y~G zS=*u)xMs!E^b^Vm{UX@3US(OA=C)iuie{A*y~0OB>>a-1s9B;_7m0>X`9&0kerxZ2gG97H?_ZIYrG?>j?^92ScpvH!_qjLJ z)%4i}D33h`0Jnw1|DYBTZhRB2e!DXdVA$J8ha+)_)n1pnCHSGLgyv7`vC+bhqjcz` z-CNw2g_*jNXg}kv&W&%veu3vn(+Cmm?3+Jz*6RpcX4n%Eih*;=qN4lbYDDEZJI@=bobM z?p?Q;h0iHQN3&oM;wdnFtNPddCRNR zPO;bcc4s9H(N1ySdao)~*KY2xeHIuEeO4FCXaf%(BR<{jvV+Zo;hB67wYhwn-~h1z(VHxf z==A!A6Vi7-5j=h^5~Hg0^J2%jS<4gacZXZ*3tMJ%9)qHxO~325UOuX4eoFVU+?ExX zBEIdZpJoP{_QrP-!rrsqhIQ{c*gn7%k~o&0QNw_p)N4%WXiSbYZm1V5R-c8D0o#DDN3W%d0tf1@?=8>npRK+8C?|MJTlY5c0A2a@#vj3^ zb7`cQaZ3HCEteV7i3cT-}pX#$RQohC$23O<14 z+RUqhP2VLVs)^uZZAoj*tb!^~Gl0@sA4xr7GqjHhKTgfM>w|vU(Yl>9chF-|qL)s{s7B1PLfF3;+5pTBW%5t|frQS*?0xj%=};+SHZRU&xAB_X`GEg{%M z95_P#(75w=G2&C!q2?EY*Zvfne8GEmM*`2%f*yCh@EH+JO~=zs8zTX_G*Y+1%ERk+ z^c=u*9+A}9G)9CdXJ}_9>gNKmxrtewR(XwW3Fr|?$d5KT6Dd|RN4~cO%mt~X-kWG% z3-HL*vWQojS{e4f>ADboo$Rb1|98?ii!o(m*9CC+-_aENqG3YGy?t~~0SY6QgN%F#T@PUig4=#-0P6Y) zBHY;92ICi`W&mE3nvG-hSRBJq%3Udqr(x;q!Az`tlj!j|dJ0NiUIuWjN!Oq)`53Vi zTMqXMabI(f8-IhUEOO(X;K{RaYtvl42gh#>K8~$QxED2E5*+~!4_sG3R0Px!t@E2DmEYJ zq7v79!3%e(GLOX82wG86V@Dvrar4L-H>ci+`Up0L(ahYIYf{(a?5x(2lUiyM;pfD_ zF7qG+JT%G;>)7x4y(&$MjF_~j1eauYGk*vy-6YKxsZK9@}*+S$eMzXF* zs=rG@L2na@!WjAk=s=lpN2Xb5Uw1CrjOtKsV?!F+P^&su4JM4SL3eexWwn0tB;VVp z+O$hs#aP$VLsq=9#YIxzhbr`ynuU=Qu(8*B`OG_dZtH!iw~`Q6t|{=|QK@^?)P;t2 zi7~x(IGI{9ff&VOP%e5Ime^v^;^jh3Rg8QPrZE$x-9>2+l102_7^EAL@UV>KdN@)M zkr%PQtZ#b7N`RK-D)*qnp%b?2PDo>HV4qvE0A|rMqxx|=x8-oxOF>5HA`4Q}%e}%R zb(D>x@#;2sN6L3jJu5PJGS8nOO^pTFqP3t-f4X&{*Mcd<+?83cBbbsjs-7uel zp0zA)6g;+=%YDB_)zonz|s^^e8k6y#3+egF{F>Z|}y0Xj1R6chxqcn-})p!T|rF z8sM=JSRToWrp~UP7QtF1F~4LJ`;oeB`IMpr@6|8cdk$cueq_sq30Xyn^+_!kCT0~S z1)HwIqh$ICK_8qGisj+%dYD!owebr1;fB~$CJa|di z`=o0&?kABa=iQMI&AB5GUGNV*W9qgG_rYRo>Wie7VtB&jXH`R26lU!ORiGaG_q_z& z|4`RSeW1J4^0yE43!XyV7QGUPEu!ja^hm;!kF!?0n4>sM5?6v2^=qFvRI8{ES z%NE^szkbeKI}xNWr{-@n5P$7~Zz-;E&4Y_(OjM8fZg;af>ag$dYV1NqtyTIo`^JtG zzV@dy<{7xGj@qsKFmkZzM<|G{4D)Q48L`Z6{b}Z#2?=O5k7r}oRF^|{weE+!w+g&3 z$rlF4Y~;>3md@)Y(FRv;#fj66~9ot5maqs=166q=t6f^=K^yQAEa7u=yK`k?|8 zELM{p>Pf40JJ$lFOYlBRLb|JqTZLp38e@_igLDg{-Hsb^DC@wCNxdaIMqmCDi=x7u zLfa(GHAr~yE*qqegeXJRxo8wjS35KZYf|69ew4CA5v#qIUTff0HyF_l){mmlTBGwf zxHw_YvZvKJYnqjH7fNQJE24Fz6g&`GqT6Zv zheNo&;~yl0TUL%5_pH9(-dd1+Kr`*#5TYci^k_^-qB3yf|4SvAH+tL#V@4$IfXPd_ zK+bh7Sn6v~N4@oK8h7y4F{m`D(vN>J5$TbnaM{sm*e_PaxHO=XVb|2$l8tCU+iCU? za0}+(*$CJ5>G@eZgUxr*iVY9n*F22wrSF&G@x{;GKlVq}pJB0wb-XS#u0Wsm*ZU}s zv8A_1(i|+!!a!sSV~QdkDLBL+Q}VUj{?TC7_Lg1&~ig6O?O>CQYvc5RF&EF7XW=aB|Y`r zH*MGCZ?FbaBPgCx+yB}jA9LzD94lN!tsdlP?rAuizOJTwVL@xQexNEpYhUdY^f4@H zRdr#TBXj8b!QyQA`Z#fR*!qB)k<}A#Au)xaBTQ=JoG=HzZ4$Yyg*qoU>$SQg`#xx0 zPuu??x<&GCXZBWlG)5D=5Pd~`j{@rt{XHB1-ehvY=3fFf{}M(i$psL*YYg>sP>gI8gA&p6 z(fto6X5hlY<=`ewcd}BH(cQQGf8KWO%*GvozTdKSY11bY8$?N2&jwrn8=b)Q_JzGK zyytl0#f4eATk@9v4owFJ$3*I@_)0AT(x!HOgo9k~19(3%JZ@9$Vhpry;6Q{{&t5xj zMvyntJD1^(vezDba6#&knB7AK_)_m1qkW@?5 zbB~nDYg2k#3h>QVR4b;j@OcqjgN~-$Mo~uDoef)%32%5d$;aCD5pw+wIAxD?{hpXp;QIa2^;)upx>p({q%DG>+j@OJKiBID zd!3X;{ATU+t_3i-&o;igzlF`7eD9fvx22%vQMDUqj|0>W^Szhz@jX!W9npyQtm=5! z-J%r&>QY}h6?o>VUX2iUtPM6NVo61p)U9!Mgrb$<#%DtjJ-(~Qo~W&2h&b7dDB{wn zr4Tnr(dN}4f$y8>6-@Wie_Qi(k%7L;s3}bL5j10&9d05w;J!_i#w6^^>HTh8YcE01 zGhKAw;H#G6$&BE2-vQRRFYZcw30{@c$TtOewOx>Q0NcjfTSvyz_?QxYSZ&aXd97m6(+XO~lJE7c6tu@z)yPdvWidT}*^Vpr zv2nql(YOC`*-SmTFifp!JMC)0SJ*=$q4kc+V&JFB| z;7LrqC15903VL$!o>$3Qb5Mh-Mq49;d+^kuN$G8{()!ms9+gw|uC29_Sref>Xs8}V zuD;Z{)Oe&&7cRCQBvZ4){r$N45BtkyxTAPs^1S1~7bATos3>k z1l? z&^Jic-BVhuVGQ^fPb2bvPvh6YD;MDkt%$eYwj^w)Y`XI>#RlAUU(+4XXgOUO&&1ZG zw_kIL%EZN(dM3sqrn1ywT%&ur_@d}1(R*n4zztkwxhgNR{UvAXjo;qgs*L0fd~NF< zHk9~YgJCWr21KSvEYEkS5mwV+wc6L`E%F?bu z+<@8s}s#L=^Wr4=%u@g9~u-q3r9v_utPu*p!EvB9{N? z{_-E<>Q|;5Do<)M$NjG{{RQSbL>g3UBgV|pkO_XG}AA6+-ny{$udx~qL4 zg^!`|2t7Tj_gc)O8G1*jYu7-jKR;CJuLep@uPgA)o1VlxWYGpe!8i5rfl?fd+XN6)$Oe|UbNJ1z)+@UV6(0sjj37btEKEgPy3lM z^YPxNd}+lKdqF@E9>2g7DE5ran{1eS)qqL%^d5;7z1tEWTe&Px3^(o!;8{|d;e$;> z(ICxNF4ksF2sVEOj^G1@fuj$(Z(f2R(D(SH>+2eiGyQa}wS-9j9ixtJx`OBxMR~^coSKN1yJG=g* zy2Kh(0`|~Q30I&JdZMxt@?a79uTHQ~oI|fXZ(@(0}0aG2;PpX-eO4qun!rr>=+JLR$ z&R7159h`@7rlIap%-&dSkh~?2B>`-*SXI8J>v@mr-C!9DY zb?iysOB;9cDdQ(hJT-mN3jqKg+_QoN+3w5)vTvWiQWSFWh4uDPtXE?U2GRm18ZT)yUtE1fjt zaSc9nPe8qX5H-(nd3pJniDU!%J~ar8EN9Y(uDJjI7;*~o3um1Zo)>ZIDvPV4PI+}z zG^7qgrPXD1p{nX=Xhm_fWZ5uhRawQ-Wky3EAEZ)JT=l(ZsJf=CDx`?U)(vxNDjLcv z>neUw=BPiVWp&Zo>eV^Lm6c11OO`uzHPz9A>XPDUMRk=^Qdv<}6}_OMsVW^#J^l`5KarL!PBFE4k&e4OUvbBsv@df#FBj;S;ES{UYU@_3S;W@dnll+`>=JrJj^CQlA z3ub37p1)vT{@gsLrnaoCv?3agMr$jU)JMzeoSfdOh*?w@ov2K9#@3B3bxt-X^O2@% zsRLnob#2+w+Uoi$5{eUusIFonaYB^!wRP3CIphoC6lW0DmaQy=5vh%8IJx(TWvi)%B#^=aj@{m6a>2E9+G{aYCZ%+S0Pxng&Y6 zhk&(3b<2utC@XnJ!c|o7N??u%Towb(QC$?S45d%Znm8Ftrm>avj;==34qDe)_%mt7 zWET;A(8!KCrvV3H#Pq4f@hrsjiQ*WE&Y{mr9Ood$XW`Ttlc|YLr9Zj&XwIp#adL~r)984zKF+Abae+El3>nMlm=xyVGi8PXClk>WouH5oAwTC*e3s)g$2kmt zSKw2HPc=R@_*{lh9X|E=tioqClED-d3eglDW$0+Cj;85ox{fk+G#MqQ1Ukyl(NrBx z)6sMtW$I`$`4tItl%b=kI+~`V={m~P(PZ)=66h#HM^kk)O-Ivplu6Nr&M}8MS2@Sv zKRmCzGfhS3kYi9n9Zl8IG#yRRQ3j%O&eduHQ5ZST5EE;)GE9ycMk|A~j&cS$VfYlrOueF%&h5?ik&f~ z)3dTBPjbd&OwF8{3Hg#q(OEQ-% znGC)1;w7b%%bhWqQ;Mf%A${qT%xPIma9&oDKE1>lQ?{fOir^_N%`AidR24AZO_)4& znjVyoS$O8O5?X`Q*A*`+0yRT+JkOv^2D|ta?RRw01R} zRfTBOq@(IkIp)EOhJ|8b;e-XDXhjqt#L?cvRiRaCuB|AAVBrLaOR86_D6T3EEiA5G zS{Ir)apFZWp8uEanNS(RkY5&BSQm}1UK}$A<80CVvS|LC^NTC%%j!-G<;130OxK~J zxskpL3{18~IkjcQ(X#v;sa&G4kXmG*)K$^4hA4C?oL99BF2G6xPUrQ5USVL35=w6v zTiKu#b8Blc{nwRMm16!3%~xv>Xw0pSR+O&}VdATbD!roe=xk(g4wglQ#ZgRZSRJXA z3euJ>r?ll}HKeLmuU}wC?*hVFaeQxV-S@yujtSKkuL{+mX=H|~Dnk>>N|sfJCRCs- zSb|L`sf$+EED24?%t%iM--_z0>bjcZlCsmV(yJ)0tSGKKJz86j)B0%jvWlvxIwKGK zCmhNHKf?`w3i|p%AFICx_m!IjEXQ7fNntpw^asgJ`d=75Th8|Q$sLoCue?Nx#9n%;WwwE0q8@P+x5eW|LtQR%WpPv`fjrf z26p`9Y!A!%oH^|Dww%v7y`TNRGDNJ;{2a5~Fa585(lei*ob6}1pMU)1HotA3&2P&& z{Vhh(FMmI|ou1FxZ=64;x8;1!a@#&Ty)EbTcNs-n&U+w#96;XsKa{h5e;y$H-2=$) z89;vT0P?>KAph$C^1lrrzi$Bf{R7A!7(o8u0P=?hkUu)*hnC0qgqn3 z_|MNjEI%lJ!%^bne*-&ya<+%%wtwyPw%kr{+h?b@<($69DBAp--cN3)=W}+go!*x7 zIrH20+39ULr%z->&e)1s?pMEsCOz}Tk{jGtZW6HEwvWeuU%5$tfyu|u|9*0|m*srU z_Oo03xXGr zp3Q(2v)r%!u{>m@$&vHNeS)R=qS~1K0?B6heeJuC0kL7;$vE0u-miyV) zF@Sw6_p^`Xe)h54&pwv>*|%{3`&jO0AItshW4WJwEcdf-(*X9d+|NFi``O2GKl@nj zXW!-l>|?o~eJuC0kL7;$vE0wTEd$ucazFc6?q?s%{p@48pM6^gu#e?__Oaa0K9>90 z$8taWo*2MBmiyVqazFc6?q?s%{p@>k0Q;UAK>qXq@@EE+KRbZ@xdG(c29Q5Lfc%94 zqds@*M-no&SXY zc;G)C_>Twv?IrHl;pvXe3J(JE@ zK0%7q@!H2J;`(d$2b&h20% zWE28FnCAo$4N1Hn$|=Cq4+9|{P6`Nw(KB@v(sZP-h&h&6k-;tJZ>=9Q6?t-JIwJL6wgXdxh`&2@~CU$XC;SP6J{l+T{9@0oPPD-aB^njkVtZN zTt{*yfN*je6rhL%vyv03th)@4YTuFMl(_Qb#PPrqje}IX&Pz_YHZGhzsxdy29EzJa zEO}H|5rl{5B`0<~OFWrIKb82vvp6}?8HK|_ga4LdO1)%Qa$|Bab&Q_|& zYvRMnp{o;6U5$ex$%XMHfnmvMfKUax=EB3N-Tld=Rj=uW;2N_1YD(LffJ%=)Gj5nl zgDg?o;o(%?9VX2vDmMhZ<&6&pRK23S5RuQ4@ck$XC)1w`lT+qGLU^jdUBao&pp;!{6Dh?sn;eY{eYgJl0SeDg_$Hn`H{MY3H+c8-z7H|NTI6Q8@XM(&WNrvX$uZAY#(}oZ^AnE({<$8cdo}9FtEkHa0H9 z=?zBqU#5*5vahcV#NFuA72=Zu$?F5NlCKZUPQDSJTk*LA=l2FG;8ERHc`{J*y=tqM zDN7=-W3-tF-lcj$xng4i@_bSZ3Kt@m*~p~?tZ_vq%ZpVOY78wkJih{sGdDSoXLWFKdz7cp`mq z5Mc`a0Z)}_`yqp4a#J|EYa;cn1>(br{(R(vq#^i&@E!UCAH@29KRDIfOoOX_tJ(*~vR0atuA#B) zYMPT82gffDsKG2Y4LOxa+hEc*P#KU`j%9htx5mdG9hZDZd~Wi+@w1a30%co#Uh-DN z+v1~;#$69#9)xkLNcRT;oX)1xyKssIOd}^MT#bTy-tIK{ormlwgz)*sV7C#V+I}QC zodyIwekq+0`4-_(`eSqo<0|^-LjQ-Z7I}Ul^#FNCZwpBG$w)gw{(G=((-{^lzmTgpm5{ zIJABxeb%Eor|?~T0?6S8b=G@L62dv@t8o;^tbdZh7b2#>>*7f`rhM^71zv|{;EZ@^ zte|j$jvTrmAiUL_o2KpL8vJJl=e0AnDbl~&;A;JBj^laqGv^dtfKLG4XfyWgAkc(( z#|#(@AU+-SP5eJL`pb=M31TW&n>nZPl0u%*rx)f>xKu~#Z@j~$=>?bKkoXtV7=e(g zBWDF-!W#{)*4w}yK}At*(J%2(;PI;g$D}{jGzql^2X+-cWM|0m?87;QAL2v$D~-Nd zmjnAbK2-15Af{l%&aZJy`Z=ckt93W9-x>T|gBRhP!e8(q{W}e=*4V)QVepp?UWaoE zPvS#%+U@OmgWpI>2<*X^a7_I9rs1l!IIuSjzSQ7q9S-axe5hO;E-?pRA|{@PsDmJ= zwKw88)F0WimOuo~H>mJ<=Xwpt{PSI-PxU~7^}h$4`0GY!T&>e_m}c;n!!>@gj+|2s ze!sz8gP&&bY8spn)H)k+zQMmW`f9BX{9J?o(der=9C#6MDpw|sDX^Wz24AM(7{1)_ zoMZ-KPFH92*IW8)jQ+(&f4-4lZScPsT&>R`YcaSQaB!~H=7@h`_)|@KZihD;JY?a& zG5B~3ztiwvXa)?mhKJ6djs7J@pZ##Z!CecdpV1({Wm`DCwJ8wWwQ`&g&MDA4EUCRU z>C;#{c?~$Vhh4{NL8+0`E3F7$U>dTzHh}CCgMSdxifSE?!+i!n@dS;lH9QXE(a(t| zVT{JrnjD8i4StKkxt|?w@F}TUpZ#~V!515x$GtHIf70MQ4~#eXw+7GCbj~z`N5*PE zt*3E#y201p2+RloK2OC>@pe+cd4G*2*|sr1TRG>Zc_J zH|nu^sWf@h0YqIPxeya_H(tt3pE_W=|w+e|0O4C0qb99^wTZ< zn-m@&n;#R5{C1;1$_!@fUh zI%3%Qz~HCE5Pk5m!R;JAGx%gn{|kkWQ2U5dwBP72ksuwbdL0t0cNPy;c#4z6fQj{< zPD-quvG{PqGnWAq>vm%%BOyCh>5n*w=S0JEnb4)fG=(R|#yb{IH2UipFfqNN%fb5U zS_?nT;5<&TINR{^ek0?vjsEW$FmZvw@3ZiE27k=LixfV>30e5X22Zo_Qp2;^!Y?)Y z+<#eIqi{1RGl+Pl;dxbHbhyUgZ&~;c4gR%-uQxcYQ)IZ-;Pmb_8E#N`qO;b*e`)YL zEc{l(Gs41uZ}1~5{7(uW5nESq-hVZCHUlQs`+FlC*TU87>(EY)viLWed{40OCl#LP zY_srfM*n0Bf7$3Ks{YrT`5OvPIjEn$YjCrSiY4eY{HrYfPYr&Rg@0l2>n(i0!RbZ! zeS#XOhB)_F_+W#p*W`%=dT(=xlV|B4ZuA#g_$Y&KvGC&zPU{XCQVqV>!cR8%s}`Pa z@UJa=n!#zEBEw9B53}%D1|MtTuEDb{e6GPqS@;5jPqFX|4StS=Ut;i!EWF&{)fQf9 z@U<3RYw+tVT<;GLaei;%R~r3I7T#oVJzw>D?#BlI($c@);3HN0>ebil-yzO83%}Xu zueW%9Yw%xL_#Fm+z{2k~_=^^PpTT!p_#+0_^INZ-Z3d_JIm)ov;Cepkl{{_mvn~A> z46f&gUj6L`FSPXEGC2K)r3~*I{0s~K7`VL_pmd-4;9vUS^uuWV@f_)ckMqGN10R7^ zEw7_^g_P-|Khp=F>w_2j;I+Uy3WY(x)F=?avQ?^ug)=K!5tr z`QR`6;P3k2pZefm_~3!T{o8Z64?e~RpX7tneTn|c8}`8$_~1)@@HIa8dLR5(KKMO8 z_y!;R2_Jl$5B`P^-sOXTAc*F;v=YwD3gID90ME z^ub5_;3xaw(|mBc|J7fLI z?Sl`)MoWMGJjMq<$p=sO!Dj+L96MX^&|&AdfG?-4IlhtAN&g+ zJRbLN`t#4>K6uCnpX`HY```sW_(C6ixevb52Vdud|Hucw*$4jo2j^s2LH)v8hF&;FZl3(>VtplgC}ESlI)o~qc8s`;QiI(7^6ST+{i*RP+_9M zM;d&Yxn9gP_?k1co_f{+hY{e^ziKobYpHXM{!z2Ez%%j-f%j)msnP$f(JwRlD}3~S zVDyLNXvMRQ{xt@lXYh#z|Czzp8r<9lb8a>G&kb&}cYY7NKRfR>`rA2Qte+@6%TucQ+)7Iz^R>#F%DxtAMc|-!RW6y`s%q0ly@?4s`vG(^u70~ z!#+HDKKQvlc$p7=sSkdc55C$5U+;te#0S6C2mg}~e!ma?xDWmmaPsF`%bzbPJR#Pv zc>I4~;lmyGM4bT3HWfZMc%g;wH+YeSC*VN>nqN3Q$HNWIZHsZIVPc6>RlfpneqZk3 zMYm4bO1x>;iM<58Y-KH8Gv82w9}`gTq<0!tIV&sbDwb4KRzz1j^;Ij1YaI3Rd%QZe zzOJmeM7^YbMcuOM+Gt6Aw9aY3Yhm^4dYy*K>f%!UBHxCR%Ch2Gr?kXrD8=hpmsYN> zSw?R+ud695sl$uq^;^Wt8fvQR%7`LbU%Rxds${j(P+vtX>P_h4xXvlB1!WaQ4&M1n zZ=BWnDb3=#l8TDOHF$}8RK2KPS)%fTX=RE{y?LMCX^x*xD6X%JDh+xqKi-6^J`OH>S7rx0K9mC zK8dvdYRhWiIH$T{;v}bf^~7`sZ>o=0SJgSS^;L?rWJRfpE2>pQum5%Qt9WajJ}-PP zSy5I;FG;2s|Kk@5@VZ^R0l&J&90IH@y9_S}rXMRPsjt(yQ&lZl0cHHoz>-?LthX56 z2Z)#FqKNdSUwyO$@6xZUbC#Q^4ArutczKysi>Qhcv<|ASveJo@mA%E4HOq?OEWGVs zotLdxQl<-}>WRFiUc&5{8mL8U5U;A2ub@Z6taBz7GgHASVGqEby!sqlJi=uAb@lR8l({w6j&s4=cO{JZt-KP=7GfnYKQ#{iY z&veBwUF$2(>56T-(#Na7QO4QXe5c&me$ueMfrG{b!}BRihf70%AU_J zraos?Ravc5ly?w>UZ968yk&c3x>H(F=HQ*x>K7ft^t%~z>E{^wAnN7Y&f>)wBTAMp zE?KsGF}=~68+Q3p(vD#4n}?s}V01|gg;Dg&93(8RT3U%;t1#+$G*r&UP&pq%C4)xW zsV$??RM~&Q99NA*RJ_>nY;ppKD1A;Pjjgjw%=@;@JHY`c^0Fuc%a-&GxpjD~Wy%doT^5^toG{f_jAef^u~6lXm&*4n+6`loDEPEM8g`rQa&io+&N`X=OcrrzVQm zzpG!BiPn`xODk&CIVq7t(RIrjFm)7Z_v&B45x?kCgsLu!mYD*rc2<eqh!;FqRSbdovKwa%9;O8vCl?{bfs{wZ&C+_~Dxs4%ToLH6_(G znEl}=asw~$)L^B?Mm6A|m#98~1`w?-C!f_+pqSLqBl?$s98{cIam~j{3&V@jgj?rT zSMyID6)7{%uc)gpuFS70uSS{jX%(SX6pOK3;0~ek75!3IJWKP?T~OxPy+7kajkz*5 z&Z?mbW2kYr>Dy|&RKMDzEXATL$DFB=LS+eS%Sz}MeprNTE|`N>6|Dx06&x+~^x_6( zsbRC`udc!{juGbI#yqdArnt8E$DSJCIF*6&w>nZwO!9sO{X7HxdWBO&O^OCd8YC7} z(^0Ht>$xBwx_Y{Tg?;42WLO}&V2;wloQZa<<`uP8S4&QHq*r4+cVVU0P>zLN-7-A~ z<)CGRG5k3VSR2z%Sm_}xNB<~=Dnm^*rYQWDkzR39?klJ#(1GD@pv9k4w0y}diW_Q6 zmtbI_!J>8r)js_~1D5CTd|h>YZAsaj;wmh8Yq_h{u7F?lNu(Gnf3=+D-mQe3kXl^H ziW=SU>MK!MDjUq+tLs)SuB%vDRa}Yd99$q#Q(jeE8LiVTwHTL5_)Vs=D2;dIemJf` z9nPsPtthX+FFQF=8XuujR<)AG1-1AsQVl21{5T2Vs#2`DiZO?Om5~^9Rcx}- z$LMCu0aRC3qC1)+{1h0bWL#X(FIUuQ6IZEUEvc@lb5`LOol24I3e4BAW@WUZRCO`) zlOg!|E@E@)qbPrzx$dNxu1)VX=X~8rcsxGW8=UWpv;NHn|DK5%XP!)fOP*|jQ@*#F zbAGOwdH!VZi%rZpKi9_hzZvJ}*RHVi8E^8zue0#Kne*obPCmh}{;9yvZ&ALB@uBCN zDg2j-*`B90?EKu|RMyAwVIF>NiS@S{eSV&d@r?%mtKex9JnBd8anAajFFjvPf$$Q1 zINzrQPBwjm4~6Fie>=`ue}|=?WX?abaOT-*aN^z|c=-7wwv)>hBmjZ*rGJhzxUCTTd9x6kE@o&3@M-9H!!r2ec2>vaCUq4T){YHIlwE2F_`_yR>D4EQ++rl~DJr>US zera&xpf=9_Ie`EKik`rS@l1nrzWiPDGc26@X`aBJ6g-OrPKTAoKg9;8=qaJEpF`8- zr9Q;=JZ|yu__j&lR74&pUl%wX@;JsXpWt%+(bUJ`gP?@K_$q@hvT&~V5({VlUuxm( z|7wGiEzylUah^R~cceNDh~CNf_xZ-#|)d1qKS zmp9wsc6swGeYR(gz-7JD^Ev(5^IHpNd+xMww&!kx+xEO<>GM3b%fh++>=n3dZwCZU zdEBb3#`h}B{SB_iI}CoRz$MRgffLWoMnBiW*`Bj4{8ppCz`{A-atpuR=>O2dnSZ^& zrJdIbT$cC0Eu4Aow{RW@9x*s^$Z_CVOP_gmTR8LV6+F*?i^q+jiMoL7f7VwoV}OYA zeOBmC^wFPUaMFKH=-+MWbAP$t!nu7uYT?{I+Xeqt!N1+o=l=4Rg>!%3CGe*OPaK|S zpg?|*{rwz)Q`y-5^DUh1zsSPbetJHH0&z(DR|tLCemsFwnmZ^l!tX6SgA7KvTj0dA zUZIZDA@DL_T<>oQocOuk-!nL2vb}w3>2to0xv@)T$b1t8UM}*b=Q=3Z?QN>T?R?J^ z_!CIS_PYY#Ch$cTf0CI`ODvr01;3H30_96;I9b$J%h9Vc|EYp2mjW>ZvYpCA;WddJnV-g zgOi=oetNEh0_$_W$67eAkIxl6+mVjTTP$$;jt%220;eA=XZ&t~zb^0x4bJxLH0|Uu z3+Hlm2%a|t&x-=5?l4}BMi^^=ka)&5zEnDLPYC;hhtK0)X&6Zljg zeR__90+pBgDf1Ty{XTvc`jj{OpVxm~!H>lKWeQ}!^bdaf zR|VTYGlc#-(1?{+;1vR==L9Ga&%5}reznk-evS%#SuZyTeVOm?1b(UDzf17QeD4#u z9QWQ6_iYe_$7dYATC#~j87r5*fvkXqU@8iRMm?!WL1-?k| zP@0>KpKAmz+yCW)NAkA_T=M@+@DTq$4gZ}2m-gQ$cqIQuflK~p1rPC;8vgf$z8rso zDNsTn-H-5L|D0fON?s%IQ-uCzfro{@>|gmpzf4+W;aJ`lLH=VQSm z{kc!*OMf0c0;C8;OE~*;oWN^Wf{=J6(Eeq#-Kd^8f z$GR>2R>QN$!bh5Q+E*6-jnO|~aI*7beAv!G=BwJ=9=!IN$2v;DIzob4~M@X>~Unc)9K@K^ij*IW3l zNJHUr3+H-YEBHT!9@oqDmOk5gqlI(19<^}S*#Dk|v;R8{PX7N)Ae@EcI5IBu(obSzo=Usu{ zCiKbHMaG`L3cOzEw+o&<0^cw6>jWMci4z3&5BCf8(prPBM9g-M6#AwH?#wnT2yZ+-u=nUtb%XY@~OE zvEPQ62lxqpQQ#*ET(*bP49 ze1Xe)Un_Vh&Fz}jx!S_HU$rUpTpwFJi%h<| zEj(iIeFDE+@bn0r$~)8OCz$)agi)C|-=zj8KVK&BwF19L;ExNQZw3Cezz+y~^f5R= z;C{sZJi*{(PY*t9&m^J0M&OqSoSyw){k4JzUz>^HKNk8|3jG^|zPwJkP2d5+(P4Kf%k&W|`(3j=v_R-%Z^!E!pzZUq{0zZE=<)Zz$1Toulo4}=i?h*J^LjMhc z)4W463LgnvwujvYr?SQiKkO6w(huJXeY)qvcBUMwvQgKRg9Lt@!Ks~)J!`d^vqa#s zT%N#5m-&AtaI%Nz*;{<@KO3BM2Mc?)2z_bKy8@T~`BLD-l?pBj3CHP}+y8ijpJL&E z(`rtJ4?f%A#63jVSt#&CfnOnb)*%i1r%B+%^^M8b6Flugf2+`cN8qmt{D%U6OYpoa z@KML(1cCj~23skNvG6qpKh?r{9Wu?r+5Q;@r!vvIy|}!2mOke@&%!z1i!Ge{$=DFO$nP;uw87Az%N#Mf;e!InUzw##5!$SX6VC;u~`sjZm^yNBjW=Ioq zeX;#H7S8s&1}8s|F6Vo*&~Jo{@!uGn^rf8-8l3WN!iV)AHMp(+l+fqAk<59)!r9JO z3{JYz&+iCat{-+Aoa`yVhwa%XaN=VAZw*dZJK^Se!ln~&fl?+V-*gA)Yujch+h z8$8a)`1(3k=%)yKPPX)U{XEqNpKamHUnuzL9fe%p^DTXzca~Z>=X<%p*P{%~vsUoS z{`*s*?+N`Iee~l}p@cwwJ6za#qQJi+@GOI~KY4z?(!%-rsL8@v{}l`8@$Eee=l=DX zg>!%Z!r%$8pY|ZxKSRe-sdRrYLCkoHz$p#$9AR+6+J*jDOP}pL)xz1%X%^1>GX%f1 zv)Cyk6b6eZ|T2}GEg{R;k5TkA$XFGsUEMuhy4>0_>ToXLEz-0f8s+S zV&QLS*f~$&lD}BsEzsqB>jf_P*H}20YrTcDJ%2DbanLj5%>NgG%kkkYi--H;XBN)= zI>l@V5dV>a|73%cKNpK~6$*WN2RqyIQ=v~>+}|G*_{D;!Tky#K9x)AuIOKS~K;TmU zuLkG-%lW=2^ywY&Z0A=3mwuZv4jCda54X?L1Rf9h^-9ZevIS1I^1fWNh4X#YaVP79 z%+L3!&$Mv9&wHMQ^ZnBrgHxXL&Q>m0qYr+!;3-2I)<5o)UVE+)c%cv8+` z*v{vK{)s~W9ZR3<>oW^yo-YK?C&Heuee?&NN?z9Mu>*+N&Ln}$eYPVFPVI;KI_rmo z{$_!nDey6(T;tNAgh2kMy%XliH8}AX9AW;dXeb4_+;JCJLT)56)#y9H0W;Ca;2 z|H9bww1qSOix$rO+Xeq5!T-Lc&;7bf=uZ~<`z-we>ML-**?bze2U2TRDlx@+ld#nsKD)q%X@~wNjF3ACx7xd^Noc!8UFaGDx282af88!TKJ6yA8Fxj20z}yxjmd@ z;cpoI@fObh!7G6HW{4k%o29CSo)QQ|0fpCJootE_X&Rb?LTf;?Up{b&#e~D{LcuU8G`3k zp-=Pa0`q*s4uMnI*l!om{4)jri0Md-KuWSo-4)|E&W5 zx#0hUz$O2meE9!n>1P=J#|8ci!M|DHlK&}#6DIxhf~8M&M&T_B=W=~u;oP2gS~%Cs zZi7?4XMmgi{H@Tx5i-UTGAZ}o^*{3rHMnj6cP#ygu^%ru?8C!$o@U|9f2QD<_xo}! zeO}ki75Zn2a$RWYbH2qE&iUd6b1G1|vIS4AkNzs59~SyoTKfOgv`&lAm-k0*^1<&H zJU4-Z>*Y~_ldWujyWnXT`r9r2M~yvkS^9v+nO_S1AqFRIdA)p`(3km+5&E-)AEpWY zUkUyhLSORd34O&6E(&ujoYy}WS~&Y*vEa`U_Wa04{~DqH8S-O4|6Jg5Kj%)1hxZLO zSU9gAw)x<1`QYCR!B!lz1{XVVcoMGXkQ3eVtPuDT= z$op;`XY`N1F7P?R5APV<&i8=8p9Ce>`;asH=O1Bk;yFj~j}|!D%KdJN!0D1^nWlAS z_~5ezE_p5zxXgE%!EOJn6#BAUR|s6%(;{%0?{6*qZ^r(6Ec_vZKVjiK?>{GSx&QO3 zz$xE98J@isUTNCH*9Irur|{wW8l0^YQoYFQ#KQzG{njXORbIpYiiNX1Zwg%6zf<7E z!{rTxae~0@jqM+7aNGV7mOk@*SKu<=kidzH?VM%doNu1N?R@80`ph$5;4t8Eq(U$7JIU1_;d^BdYNf(%5$i&$F=m?o&^@p^ZrsFe3jtehCH~u z*I4>I@7!$R+zx+d;ru(Xn=PEn`;3Kid0#L%mA6Qg_ZxxB`)I=>P(&d83x)m>0$(Wb zlLSs}jmtaR;8ef=jSu5<1paG*pD*xR1in!4%j>vup?|B;zf9;~B<%47PHma(xk>O` z3?9CZ{G`yAJnsrz@;JFjj6mi64L;02$lwXU7K?mSgucx8M1lWS@JtpwGT%a>FY`T5 z=*xT;3w=4hl?z z&px3q?fF*dOM8aQ1}Os7qg;0-3tZY068P_shU-1u;8gF@p0LpWz0g0`M}LXX|AWx4 z_0hjV=*#+T6u2zcbpoe3obA8GhyP)rFa7zL(3k$)D)i&Q&({&p30(T~9f98|?Aa~& zr9ZzC`m+7>2wdhnJP)J@RKIuO!{t53;C6jw34K}KGlae@Z@$o{a`Aoe^924!k#Cvc zk>zR>`ZC}3LSN?lQ=$Ja!GDXuY0l^J-XnNqzRw7KnePiiU*`Lv(EqdG-z)Sb|40`~ z2=+L6l)>$Ba)Qvm8|m2o(}lj|UnumY{g()RX@6Ac-y`^&gudketZAX*z%LPYejs>g99WAFg}nm5ALq z>mOzO;V+>CICtveAEkocB2>yuRk^BnZm7kqSkDCbXfcB+ZgsvBi!p$Rh$RD;DSgLpuVZyj&25 zYFmL35JYqxP&z|Li-44&*kwS=fQaXJ&-a_2`!)H?ow;}SeD-%9yLs@gYvCzbeqayr@Dlj%)OMH{F1=K@gxm=i;HFdxi~2Q$-p-R@xKcE z&cJOO@bOUPesQ*ci#U((?*k9ZcSY7G+Yk2mi%-`3lP%KsL;q>Qdip<*9wYu9(l(ab z#Q1$t4#!uz{f(|cyu z51%3Nx53$e8PDU|Ch>P8p63VSe+F-t_@BeEn}+t=!|+7nH^JF|S^gvN`4azYIQtRf ze*^E3_-EjY#7~BIi$4eN5q|-mioXc2iT@S8O#Ef|3h~$AtHj@cUnaf_J}CY+{2KAy z@J#%D_*(G~;p@aV;lSP^J{kvhgE+6pw~LQO{7=MJ;JR{;_&kiKx%e9RgW^Y{{EgzT zBYv~^F^GRu{8;$o;wNDLJS9F2@xK#ahWJ7}fsJ`=u8d^UW$_#F5v;!mR9 z9pa}Wey8{e@KU@99>Hzcv~%*I$sVeHR-w}NxME<6$Mf~Vq3;F0JB(qzmywkS@g!M>^W4vHeHEWAX9uM0_GV6>o-T;#1(c zcnmMZr^8F}q4Rg&#`g1f;9_x}r-^tg%1_04K4#)=Y`-|ir9ylb+b`Y@k487Pe=a;0 zKLehKp94?D7r`^}3*fnUKfF*!zb(Z%u0~Cb?O%>^V)0e*MEpv4D!vAuiC+)T#czQZ z;32QFR?hUFNrv>FR3`MFPS*6FS$6cFNHX-FQquIFVX&u?dSC+ z7U%UP5kC<7GZjCW`(K>*hq*ZK4-0YLAC}_0Ka4)!*nZx(#NxbfNyK^Il8W=bB@^d; zOD;YR+gph9eykMd{a7@%vHeG*9R9ux&uiWnB@)m375**^<0o_fOZ=zdx%gDJUwj6< z6h8?bMUCx086JzD0#C$GgQw#2;hFdXcrJcEyb$k&m*O>ebUr^8F}v*6LejqUG*$Kp%iiTH)^RD3x+6JH6>#jk`H;@84U@f+dM zA&u={50Ay~fG6Vj!c*~w;FgA#uxJQH64 zUnjm8ULD_fT>9Y6;@^hH;yhk6#94lu_)3)19(aduLj5@j+%CmeBYuA1Hbgb=?K5?V z&xhkSs{X!ckL^56G3G|SfhU144Ll9JFYqjItMYz~oB0S{h@Xr2Qv52^5n*mKEB}Lu zJ}?&lEj$^)Q*l&R$pUALt;++y5XYV75_7w;{oH@e`*rH7zZpD--y@quZR!e zbxwr$9a%m<&uSKb$U2dm+QdJA_lWo5K6p_4Zun;L$rw+zigTQKN1WqTgyX>WS8)Ht z_kHPK$8~*$#PjpDb>jRyZWLbMu$)(`_1#dZit}^5Zt+nQeEgvJdidkw{Csn}I6n^^ zgY%f};kYzWoa1e)I6rUY`|vD>pU*xj@gFq%_D8smG5(UN-kZhO!rR2}gs%`Ej{#+; z_)VYpbBCzDP;E?BKIzgn zB(;fW_T@M?H7)jYd%0!xpt}xzKW#GnBR*{Cd0gTbdEMoTDi<)H;^tPS=FgG;2w71@ zKF*GJ3)U{{f8oUZypD4nhF>NtHe3w1{p%+A3+_gYkGB1`sM~L+t3`gsJ;Les(|qa& z7;m`Fm+JGFo#GzD<)3ku&#_WAjPjtr#wUN?c#P1g}VZ+OwFGo|1w<1 z7_a%?cPobLKRagmmX5=~Y{$`VJ(-`!b-22%u4cS&nLoi&?tj#{$0P2c`R$z4bI(y_ nRc_CfN`%j;eCmJq-vMsLaQQ3t&wkv*?>pBM?#U}tmCAns&OnvQ diff --git a/.zshrc b/.zshrc index 4434518..38931e7 100644 --- a/.zshrc +++ b/.zshrc @@ -11,7 +11,7 @@ zmodload zsh/complist compinit comp_options+=(globdots) #ohmyzsh -ZSH_THEME="gentoo" && export ZSH=$HOME/.oh-my-zsh && source $ZSH/oh-my-zsh.sh +ZSH_THEME="gentoo" && export ZSH=$HOME/.config/.oh-my-zsh && source $ZSH/oh-my-zsh.sh #source source "$HOME/.config/shell/aliases" && source "$HOME/.config/shell/exports" && source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh && fpath=(/usr/share/zsh/site-functions $fpath) && source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh #farver @@ -19,6 +19,9 @@ autoload -U colors && colors && setopt prompt_subst ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=5" ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=12" #prompt -[ "$(id -u)" = 0 ] && PS1ICON="#" || PS1ICON='%' && PROMPT='%{$(echo -e "\e[38;2;254;128;25m")%}[%{$(echo -e "\e[38;2;235;219;178m")%}%n%{$(echo -e "\e[38;2;200;200;200m")%}@%{$(echo -e "\e[38;2;131;165;152m")%}coast %{$(echo -e "\e[38;2;235;219;178m")%}%~%{$(echo -e "\e[38;2;254;128;25m")%}]%$PS1ICON%{$(echo -e "\e[0m")%} ' +[ "$(id -u)" = 0 ] && PS1ICON="#" || PS1ICON='$' + +PROMPT='%{$(echo -e "\e[38;2;114;47;55m")%}[%{$(echo -e "\e[38;2;197;198;200m")%}%n%{$(echo -e "\e[38;2;114;47;55m")%}@core %{$(echo -e "\e[38;2;197;198;200m")%}%~%{$(echo -e "\e[38;2;114;47;55m")%}]%$PS1ICON$%{$(echo -e "\e[0m")%} ' #coast's zshrc :3 +fpath=(~/.zsh/completions $fpath)