commit fe5a87e14c6ed8ea2d1c8609b4ea0e98ea1f70c4 Author: speediegq Date: Sun Aug 21 11:53:18 2022 +0200 Add speedwm, we'll see if I use this repository instead. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bf1331e --- /dev/null +++ b/LICENSE @@ -0,0 +1,38 @@ +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2009 Jukka Salmi +© 2006-2007 Sander van Dijk +© 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 +© 2021-2022 speedie.gq + +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/Makefile b/Makefile new file mode 100644 index 0000000..9b76236 --- /dev/null +++ b/Makefile @@ -0,0 +1,180 @@ +# speespeedwm // minimal X window manager designed for productivity and aesthetics. +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c speedwm.c util.c +OBJ = ${SRC:.c=.o} + +all: options speedwm + +options: + @echo speedwm build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + ${CC} -c ${CFLAGS} $< + +${OBJ}: config.mk + +speedwm: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} ; rm -f drw.o speedwm.o util.o + +clean: + rm -f speedwm ${OBJ} speedwm-spde-${VERSION}.tar.gz && echo "Cleaned!" + +dist: clean + mkdir -p speedwm-spde-${VERSION} + cp -R *.mk *.c *.h *.png docs patches status LICENSE Makefile scripts speedwm-spde-${VERSION} + tar -cf speedwm-spde-${VERSION}.tar speedwm-spde-${VERSION} + gzip speedwm-spde-${VERSION}.tar + rm -rf speedwm-spde-${VERSION} speedwm + +install: all + touch drw.o speedwm.o util.o + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f speedwm ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/speedwm + cp -f docs/bindlist ${DESTDIR}${PREFIX}/share/speedwm-bindlist + cp -f docs/deplist ${DESTDIR}${PREFIX}/share/speedwm-deplist + cp -f docs/about ${DESTDIR}${PREFIX}/share/speedwm-about + cp -f docs/about2 ${DESTDIR}${PREFIX}/share/speedwm-about-2 + cp -f docs/about3 ${DESTDIR}${PREFIX}/share/speedwm-about-3 + cp -f docs/about4 ${DESTDIR}${PREFIX}/share/speedwm-about-4 + cp -f docs/about5 ${DESTDIR}${PREFIX}/share/speedwm-about-5 + cp -f docs/example.Xresources ${DESTDIR}${PREFIX}/share/speedwm-xresources + cp -f docs/example.fsignal ${DESTDIR}${PREFIX}/share/speedwm-fsignal + cp -f scripts/speedwm-help ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-help + cp -f scripts/speedwm-shutdown ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-shutdown + cp -f scripts/speedwm-winnav ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-winnav + cp -f scripts/speedwm-utils ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-utils + cp -f scripts/speedwm-swal ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-swal + cp -f scripts/speedwm-screenshotutil ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-screenshotutil + cp -f scripts/speedwm-virtualkeyboard ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-virtualkeyboard + cp -f scripts/speedwm-pdfopen ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-pdfopen + cp -f scripts/speedwm-audioctrl ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-audioctrl + cp -f scripts/speedwm-netctrl ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-netctrl + cp -f scripts/speedwm-btctrl ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-btctrl + cp -f scripts/speedwm_run ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm_run + cp -f scripts/speedwm-core ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-core + cp -f scripts/speedwm-dfmpeg ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/speedwm-dfmpeg + cp -f status ${DESTDIR}${PREFIX}/bin ; chmod +x ${DESTDIR}${PREFIX}/bin/status + chmod +x ./scripts/speedwm-compatcheck + chmod +x ./scripts/speedwm-dm + mkdir -p /usr/share + echo "${DESTDIR}${PREFIX}/bin/" > /usr/share/speedwm-bindir + ./scripts/speedwm-compatcheck + ./scripts/speedwm-dm + [ -f speedwm.png ] && cp -f speedwm.png ${DESTDIR}${PREFIX}/share/pixmaps/speedwm.png || : + rm -f drw.o speedwm.o util.o speedwm ; echo "Installed speedwm to ${DESTDIR}${PREFIX}/bin" + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/speedwm\ + ${DESTDIR}${PREFIX}/bin/speedwm-utils \ + ${DESTDIR}${PREFIX}/bin/speedwm-audioctrl \ + ${DESTDIR}${PREFIX}/bin/speedwm-winnav \ + ${DESTDIR}${PREFIX}/bin/speedwm-shutdown \ + ${DESTDIR}${PREFIX}/bin/speedwm-swal \ + ${DESTDIR}${PREFIX}/bin/speedwm-help \ + ${DESTDIR}${PREFIX}/bin/speedwm-netctrl \ + ${DESTDIR}${PREFIX}/bin/speedwm-btctrl \ + ${DESTDIR}${PREFIX}/bin/speedwm-screenshotutil \ + ${DESTDIR}${PREFIX}/bin/speedwm-virtualkeyboard \ + ${DESTDIR}${PREFIX}/bin/speedwm-pdfopen \ + ${DESTDIR}${PREFIX}/bin/speedwm-core \ + ${DESTDIR}${PREFIX}/bin/speedwm-dfmpeg \ + ${DESTDIR}${PREFIX}/bin/status \ + ${DESTDIR}${PREFIX}/share/speedwm-bindlist \ + ${DESTDIR}${PREFIX}/share/speedwm-deplist \ + ${DESTDIR}${PREFIX}/share/speedwm-about \ + ${DESTDIR}${PREFIX}/share/speedwm-about-2 \ + ${DESTDIR}${PREFIX}/share/speedwm-about-3 \ + ${DESTDIR}${PREFIX}/share/speedwm-about-4 \ + ${DESTDIR}${PREFIX}/share/speedwm-about-5 \ + ${DESTDIR}${PREFIX}/share/speedwm-xresources \ + ${DESTDIR}${PREFIX}/share/speedwm-fsignal \ + +docs: + chmod +x scripts/speedwm-help + ./scripts/speedwm-help -a -o + +help: + @echo dynamic window manager makefile help + @echo install: Installs speedwm. You may need to run this as root. + @echo uninstall: Uninstalls speedwm. You may need to run this as root. + @echo libxftfix: This option compiles and installs libXft-bgra which is necessary to prevent speedwm from crashing. + @echo gentoo-libxftfix: This option installs libXft-bgra by patching it for Gentoo only. + @echo arch-libxftfix: This option installs libXft-bgra using the AUR on Arch Linux only. + @echo help: Displays this help sheet. + @echo docs: View documentation for speedwm + @echo html: Write HTML document based on documentation. + @echo markdown: Write Markdown document based on documentation. + @echo readme: Write output of speedwm-help -a to readme. + @echo upload: git commit and git push this build. + @echo page: Creates the https://speedie.gq/speedwm page in HTML form. + @echo page_install: Copy the page to ${HTMLDIR}. + @echo page_push: git commit and git push the page in ${HTMLDIR}. + @echo make page page_install page_push to do all of the above page changes instantly. + @echo make release to run make markdown, make upload, make page, make page_install, make page_push instantly. + +release: + make markdown + make upload + make page + make page_install + make page_push + rm -f speedwm.html + rm -f readme.html + rm -f readme + @echo "Complete!" + +upload: + chmod +x scripts/speedwm-mkpage + ./scripts/speedwm-mkpage -ulspeedwm + @echo "Uploaded everything." + +libxftfix: + chmod +x scripts/libxftfix + ./scripts/libxftfix -source + +page: + chmod +x scripts/speedwm-mkpage + chmod +x scripts/speedwm-help + ./scripts/speedwm-mkpage -mkpage + +page_install: + cp speedwm.html ${HTMLDIR} + @echo "Copied speedwm.html to ${HTMLDIR}." + +page_push: + echo ${HTMLDIR} > /tmp/speedwm-htmldir + chmod +x scripts/speedwm-mkpage + chmod +x scripts/speedwm-help + ./scripts/speedwm-mkpage -page-push + +html: + chmod +x scripts/speedwm-mkpage + chmod +x scripts/speedwm-help + ./scripts/speedwm-mkpage -mkhtml + +markdown: + chmod +x scripts/speedwm-mkpage + chmod +x scripts/speedwm-help + ./scripts/speedwm-mkpage -mkmd + +readme: + chmod +x scripts/speedwm-mkpage + chmod +x scripts/speedwm-help + ./scripts/speedwm-mkpage -mk + +gentoo-libxftfix: + chmod +x scripts/libxftfix + ./scripts/libxftfix -gentoo + +arch-libxftfix: + chmod +x scripts/libxftfix + ./scripts/libxftfix -arch + +.PHONY: all options clean dist install uninstall help libxftfix gentoo-libxftfix arch-libxftfix docs page page_install html markdown readme page_push upload release diff --git a/README.md b/README.md new file mode 100644 index 0000000..75ab773 --- /dev/null +++ b/README.md @@ -0,0 +1,439 @@ + https://speedie.gq/speedwm + + https://speedie.gq/donate + +## What is speedwm? +speedwm is a window manager forked from suckless.org's dwm or dynamic window manager. It manages the user's open windows and tiles them according to a set layout (dynamic). +Unlike dwm, speedwm tries to be minimal just dwm but also has functionality and aesthetics as a goal. It is also much more minimal than other window managers like i3-gaps while offering many more features. + +Tiling window managers (unlike floating window managers that you may be used to) tile windows based on a set layout making them easy to get productive on. +They also encourage the user to use their keyboard instead of the mouse so that the user doesn't have to move their hands much but there are mouse keybinds and more can be added. + +## Installation +In order to install this build of speedwm, all dependencies must be installed. +You can see (Dependencies) for a list of all dependencies required to use this fork. + + - git clone https://codeberg.org/speedie/speedwm + - cd speedwm + - make clean install + - If any warnings show up, fix it by installing the missing dependency. + - If a .xinitrc is used, add 'speedwm_run' to the end. + - NOTE: Your .xinitrc should ONLY contain 'speedwm_run'. speedwm will automatically start everything else. If not, edit autostart.h and 'make clean install'. If a .xinitrc is not used then you don't need to worry. A .desktop file is automatically created when you run 'make clean install'. + +## Layouts +speedwm comes with the following layouts: + + - Tile + - Monocle + - Grid + - Deck + - Centered Master + - Centered Floating Master + - Spiral + - Dwindle + - Three Column + - Bottom Stack + - Horizontal Bottom Stack + - Horizonal Grid + - Tatami + - Tilewide + - Stairs + - Tiling (5:4) + - Column + - Dynamic Grid + +They can be switched between using a little menu (See Keybinds for more information) or by right clicking the Layout indicator. +The more commonly used layouts can be switched between using a quick keybind. + +## Keybinds +Below is a list of all speedwm keybinds. + + ### Applications + + Keybinds for regular applications + + - Super+Shift+Enter | Opens a terminal + - Super+Shift+Colon | Opens a dmenu prompt + - Super+Shift+s | Opens 'maim' to take a screenshot and copies it to the clipboard using 'xclip' + - Super+Shift+f | Opens the defined file manager + - Super+Shift+w | Opens the defined web browser + - Super+Shift+o | Opens the bundled dfmpeg dmenu script to record your screen. + - Super+Shift+e | Opens the dboard dmenu script in dmenu which can copy text to your clipboard + - Super+Shift+t | Opens the defined editor in your terminal + - Super+Shift+p | Kills the defined web browser + - Super+Shift+m | Kills the defined music player + - Super+Shift+a | Opens the defined mixer in your terminal + - Super+Shift+m | Opens the defined music player + - Super+Shift+x | Opens the defined system process viewer in your terminal + - Super+Control+Shift+m | Opens the defined email client + - Super+Control+u | Opens the defined RSS reader + + ### Navigation + + These keybinds are for navigating speedwm + + - Super+t | Reorganize tags and move clients + - Super+f | Full-screen the selected window + - Super+b | Show/hide the speedwm bar + - Super+s | Show/hide the systray (If trayer is installed) + - Super+j/k | Move focus between visible windows + - Super+Alt+j/k | Increase/decrease gaps between windows in tiling layout by 1 + - Super+Alt+u/d | Increase/decrease gaps between windows in tiling layout by 5 + - Super+Shift+j/k | Move focus between hidden windows + - Super+a/d | Increase/decrease size of each window + - Super+o | Hide a window + - Super+Control+o | Show a window + - Super+w | Hide all windows except focused + - Super+Control+w | Show all windows except focused + - Super+a/d | Move to the next/previous tag + - Super+Minus | Show the scratchpad + - Super+Equal | Remove the scratchpad + - Super+Enter | Switch order of windows + - Super+Shift+q | Close the current window + - Super+Space | Set layout + - Super+t | Disable inactive fade + - Super+Shift+Equal | Toggle scratchpads + - Super+Shift+Minus | Hide the scratchpad + - Super+Shift+Space | Unfloat floating windows + - Super+Shift+Arrow | Resizes a window in floating mode + - Super+1 | Move to tag 1 + - Super+2 | Move to tag 2 + - Super+3 | Move to tag 3 + - Super+4 | Move to tag 4 + - Super+5 | Move to tag 5 + - Super+6 | Move to tag 6 + - Super+7 | Move to tag 7 + - Super+8 | Move to tag 8 + - Super+9 | Move to tag 9 + - Super+Shift+1 | Preview tag 1 + - Super+Shift+2 | Preview tag 2 + - Super+Shift+3 | Preview tag 3 + - Super+Shift+4 | Preview tag 4 + - Super+Shift+5 | Preview tag 5 + - Super+Shift+6 | Preview tag 6 + - Super+Shift+7 | Preview tag 7 + - Super+Shift+8 | Preview tag 8 + - Super+Shift+9 | Preview tag 9 + - Super+Shift+h/j/k/l | Rotates a stack. + - Super+Shift+Escape | Ask the user if they want to shutdown or reboot or nothing + - Super+Shift+i | Open a dmenu prompt and open the file the user picks in Zathura + - Super+Shift+p | Open a dmenu prompt and open the file the user picks in Vim + - Super+Control+e | Switch to layout 3 (Grid) + - Super+Control+r | Switch to layout 1 (Monocle) + - Super+Control+t | Switch to layout 0 (Master & stack) + - Super+Control+y | Switch to layout 4 (Deck) + - Super+Control+0 | Set all windows to use the same tag + - Super+Control+Arrow | Moves a window to any corner of your screen (Arrow key) + - Super+Control+Tab | Open a dmenu prompt asking the user what layout to switch to + - Super+Control+h | Open a list of all keybinds in your terminal using less + - Super+Control+Shift+a/d | Move between available layouts + - Super+Alt+s | Make the current selected window sticky + - Super+Control+Shift+Esc | Open speedwm-utils (Main menu) + - Super+Control+Shift+Arrow | Resize the window to the screen size. + - Super+Control+Shift+s | Set a wallpaper + - Super+Control+Shift+n | Connect to wifi (Requires iwd) + - Super+Control+Shift+b | Connect to a bluetooth device (Requires bluez and bluez-utils) + - Super+Control+Shift+e | Open up a list of dotfiles in dmenu that you can edit. + - Alt+Tab | Switch windows quickly and easily + - Alt+Control+j/k | Change window size vertically (cfact) + + ### Chained keybinds + - Super+c & w | Curl wttr.in and open in less + - Super+c & m | Ask the user for a topic and curl cheat.sh + - Super+g & t | Toggle gaps + - Super+g & 0 | Reset gaps + - Super+g & i | Increase inner gaps by 1 + - Super+Shift+g & i | Decrease inner gaps by 1 + - Super+g & o | Increase outer gaps by 1 + - Super+Shift+g & o | Decrease outer gaps by 1 + - Super+r+v | Open the defined music visualizer + + ### Extras + + These will only work if your keyboard has special multimedia buttons. + + - Mute button | Mutes your audio + - Up Volume button | Increases your volume + - Down Volume button | Decreases your volume + - Stop button | Stops your defined music player + - Browser button | Opens your defined web browser + - Power button | Ask if you wanna shut down, restart or lock your computer. + - Email button | Open your defined email client + - System button | Open your defined status viewer in a terminal + + -- Mouse -- + + These binds can be activated using your mouse + + - Tag (Left click) | Switch to tag + - Layout indicator (Left click) | Switch to the next layout + - Layout indicator (Middle click) | Switch to the next layout + - Layout indicator (Right click) | Open a dmenu list of all layouts + - Window title (Left click) | Hide/Show the window + - Window title (Right click) | Open speedwm-utils + - Focused window (Super+Alt+Left click) | Move the focused window around + - Focused window (Super+Alt+Middle click) | Make the focused window floating + - Focused window title (Middle click) | Rotate stack + - Dragging (Super+Right click) | Increase/decrease size of each window + + There are also keybinds for statuscmd, but you must implement it into your own status bar. + See mouse.h for more information. + +## Dependencies + These are absolutely necessary, speedwm will NOT compile without them + - libxft-bgra (Can be installed through 'make -libxftfix') + - NOTE: libXft will do but will cause speedwm and as such all your applications to crash if a colored emoji is displayed in the status bar. + - libXinerama + - Can be disabled through editing config.mk if you're not interested in multiple monitors. + - imlib2 + + ## Features + These are dependencies if you wanna use certain features + NOTE: Do not add any of these to .xinitrc or similar. They are going to be autostarted by speedwm. + If you want to use an alternative, change it in options.h. + - dmenu + - NOTE: dmenu is required for most scripts included with this build of speedwm. My build is required for Pywal support. + - NOTE 2: The build must have the 'grid' patch. If yours does not have this, you can patch it in or get my build here: https://codeberg.org/speedie/dmenu + - picom + - xclip (Required for clipboard support by a few scripts, will start automatically) + - xwallpaper (Required to set wallpapers automatically) + - xmodmap (Install if you want Escape instead of Caps Lock and Right Super+hjkl for arrow keys) + - xrdb (Install if you want .Xresources support) + - pywal (Install if you want pywal support. Requires swal aka the default way to set wallpapers) + - wmctrl (Needed for proper window management) + - xsetroot (Needed for most scripts including Pywal support) + - slock (Required for screen locking) + - maim (Required for built in 'speedwm-screenshotutil' script) + + ## Software + This build of speedwm comes with binds for software. + These must be installed by default but you can change what software is required by editing 'options.h' and running 'make clean install'. + You can also remove keybinds by editing 'keybinds.h' and running 'make clean install'. + - st (Terminal) + - firefox (Web browser) + - htop (Status monitor) + - newsboat (RSS reader) + - zathura (PDF reader) + - alsa-utils (Required for audio controls) + - mocp (Default music player) + - vim (Text editor) + - neomutt (Email client) + - maim (Screenshot tool, automatically copies to clipboard using xclip) + - vifm (File manager) + - slock (Lock screen) + And everything under 'Features'. + +## Important +If you're used to dwm, speedwm might be a little unfamiliar to you at first. This is because speedwm doesn't use config.h (or config.def.h). +Instead, config.h is split into different parts to make it easier to edit. Instead of editing config.h you'll want to edit: + + - autostart.h for starting stuff right before speedwm (For example xclip, pywal, etc.) + - options.h for changing colors and applications to use with keybinds. + - fsignal.h for adding fake signals + - colors.h for changing alpha options, most users won't need to edit it. + - xresources.h for adding .Xresources options + - rules.h for adding rules + - keybinds.h for adding/removing keybinds. + +After you've edited one of the files, you need to run 'make clean install' to reinstall speedwm. +Remember that you can change colors through your .Xresources file (see .Xresources and Pywal) meaning you do not need to recompile speedwm. + +Another important detail you must keep in mind is that this build comes with a status bar simply named 'status'. +It can be found in the speedwm source code directory. It is just a shell script which adds stuff to your status bar. It will automatically be started when speedwm starts. + +You can edit the status bar simply by editing 'status' and running 'make clean install'. +You can also configure it by editing '~/.config/speedwm-de/status/config'. +Please note that most status bars including the built in 'status' depends on xsetroot which must be installed. speedwm-compatcheck is going to tell you about this when compiling. + +If you want to change status bar, edit options.h and set 'static char status' to your status bar binary (must be in $PATH). +Alternatively, you can also set speedwm.status: in .Xresources (See .Xresources and Pywal) + +## .Xresources and Pywal +This fork of speedwm has .Xresources support thanks to the .Xresources patch. +It also has pywal support (tool which grabs colors based on your wallpaper). + +Colors reload automagically because of a reloadxresources function this build has combined with fsignal and a wallpaper script I wrote. +Therefore, if you want colors to reload instantly, you're unfortunately forced to use the bundled wallpaper script. + +If you want to use another script, you can open up speedwm-utils and select 'Reload .Xresources' to reload .Xresources. +Alternatively, you can write a script yourself (18 reloads the colors) + +Below is a list of all .Xresources values you can define. The .Xresources file should be placed in ~ or ~/.config by the user. +If it is not or you want it somewhere else, you can edit 'autostart.h' and 'make clean install'. + +Note that the 'xrdb' dependency is required for both pywal and .Xresources support and 'xsetroot' is required for automatic reloading of colors, the built in status bar, and more so you should install this. + + - speedwm.nmaster: 1 + - speedwm.font: fontawesome:size=8 + - speedwm.font2: NotoSans-Regular:size=8:antialiasing=true + - speedwm.font3: Noto Emoji:size=8 + - speedwm.col_background: #222222 + - speedwm.col_backgroundmid: #222222 + - speedwm.col_textnorm: #bbbbbb + - speedwm.col_textsel: #eeeeee + - speedwm.col_windowbordersel: #eeeeee + - speedwm.col_windowbordernorm: #000000 + - speedwm.col_tag1: #333333 + - speedwm.col_tag1_text: #eeeeee + - speedwm.col_tag2: #333333 + - speedwm.col_tag2_text: #eeeeee + - speedwm.col_tag3: #333333 + - speedwm.col_tag3_text: #eeeeee + - speedwm.col_tag4: #333333 + - speedwm.col_tag4_text: #eeeeee + - speedwm.col_tag5: #333333 + - speedwm.col_tag5_text: #eeeeee + - speedwm.col_tag6: #333333 + - speedwm.col_tag6_text: #eeeeee + - speedwm.col_tag7: #333333 + - speedwm.col_tag7_text: #eeeeee + - speedwm.col_tag8: #333333 + - speedwm.col_tag8_text: #eeeeee + - speedwm.col_tag9: #333333 + - speedwm.col_tag9_text: #eeeeee + - speedwm.col_layouttext: #000000 + - speedwm.col_layoutbgnorm: #222222 + - speedwm.col_layoutbgsel: #bbbbbb + - speedwm.col_status0: #131210 + - speedwm.col_status1: #bf616a + - speedwm.col_status2: #A16F9D + - speedwm.col_status3: #68ABAA + - speedwm.col_status4: #A89F93 + - speedwm.col_status5: #D3A99B + - speedwm.col_status6: #AFC9AC + - speedwm.col_status7: #eae1cb + - speedwm.col_status8: #a39d8e + - speedwm.col_status9: #6D5E8E + - speedwm.col_status10: #A16F9D + - speedwm.col_status11: #D3A99B + - speedwm.col_status12: #AFC9AC + - speedwm.col_status13: #eae1cb + - speedwm.col_status14: #6D5E8E + - speedwm.col_status15: #ffffff + - speedwm.borderpx: 1 + - speedwm.snap: 20 + - speedwm.showbar: 1 + - speedwm.resizehints: 0 + - speedwm.mousemfact: 1 + - speedwm.mfact: 0.50 + - speedwm.startontag: 1 + - speedwm.enablegaps: 1 + - speedwm.smartgaps: 1 + - speedwm.smartgapsize: 0 + - speedwm.gappih: 10 + - speedwm.gappiv: 10 + - speedwm.gappoh: 10 + - speedwm.gappov: 10 + - speedwm.attachdirection: 3 + - speedwm.shell: /bin/sh + - speedwm.sizeicon: 10 + - speedwm.spacingicon: 5 + - speedwm.status: status + - speedwm.defaultname: + - speedwm.refreshrules: 1 + - speedwm.decorhints: 1 + - speedwm.barpaddingv: 0 + - speedwm.barpaddingh: 0 + - speedwm.barheight: 5 + - speedwm.altbar: 0 + - speedwm.altbarclass: Polybar + - speedwm.alttrayname: tray + - speedwm.altbarcmd: polybar & + - speedwm.centerfloating: 1 + - speedwm.savefloat: 1 + - speedwm.warpcursor: 1 + - speedwm.pertag: 1 + - speedwm.i3nmaster: 0 + - speedwm.scalepreview: 4 + - speedwm.tagpreview: 1 + - speedwm.mousepreview: 1 + - speedwm.monocleclientcount: 0 + - speedwm.monoclecount: 0 + - speedwm.monocleformat: [%d/%d] + - speedwm.statusallmons: 1 + - speedwm.hidelayout: 0 + - speedwm.hidetitle: 0 + - speedwm.hidestatus: 0 + - speedwm.hidetags: 0 + - speedwm.hidesticky: 0 + - speedwm.hidefloating: 0 + - speedwm.leftlayout: 1 + - speedwm.fadeinactive: 1 + - speedwm.defaultlayout: 1 + - speedwm.wmclass: 1 + - speedwm.clicktofocus: 0 + - speedwm.stairpx: 20 + - speedwm.stairdirection: 1 + - speedwm.stairsamesize: 1 + - speedwm.deckcount: 0 + - speedwm.deckformat: D %d + - speedwm.roundedcorners: 0 + - speedwm.cornerradius: 3 + - speedwm.swallowclients: 1 + - speedwm.swallowfloating: 1 + - speedwm.movefullscreenmon: 0 + - speedwm.lockfullscreen: 1 + - speedwm.underline: 0 + - speedwm.underlineall: 0 + - speedwm.underlinepad: 5 + - speedwm.underlinestroke: 2 + - speedwm.underlinevoffset: 0 + - speedwm.forcevsplit: 1 + - speedwm.floatscratchpad: 0 + +## Fsignal +Thanks to the 'fsignal' patch available on suckless.org's website, we can easily write shell scripts to interact with dwm and therefore speedwm. +This is exactly what I did and speedwm-utils, speedwm-swal, speedwm-shutdown and more take advantage of it. + +In order to use 'fsignal', your system must have 'xsetroot' installed. +Then simply use the dwm-utils script. Syntax is dwm-utils -exec + +Below is a list of all signums and what they do. + + - 1 | Switch to the Tiling layout + - 2 | Switch to the Floating layout + - 3 | Switch to the Monocle layout + - 4 | Switch to the Grid layout + - 5 | Switch to the Deck layout + - 6 | Switch to the Centered Master layout + - 7 | Switch to the Centered Floating Master layout + - 8 | Switch to the Fibonacci Spiral layout + - 9 | Switch to the Fibonacci Dwindle layout + - 10 | Switch to the Three Column layout + - 11 | Switch to the Bottom Stack Vertical layout + - 12 | Switch to the Bottom Stack Horizontal layout + - 13 | Switch to the Horizontal Grid layout + - 14 | Switch to the Tatami layout + - 15 | To be added + - 16 | Cycle layout (Previous) + - 17 | Cycle layout (Next) + - 18 | Reload colors from .Xresources + - 19 | Set mfact (-0.05) + - 20 | Set mfact (+0.05) + - 21 | Toggle Scratchpad + - 22 | Toggle Sticky + - 23 | Toggle Bar + - 24 | Toggle Fullscreen + - 25 | Restart speedwm keeping all your applications open. + - 26 | Unused at the moment. + - 27 | Switch to the Stairs layout + - 28 | Reset layout and mfact + - 29 | Reorganize tags + - 30 | Restart speedwm + - 31 | Shutdown speedwm + - 32 | To be added + - 33 | To be added + - 34 | To be added + - 35 | Switch to the Tiling (5:4) layout + - 36 | Switch to the Column layout + - 37 | Switch to the Dynamic Grid layout + +## Switching run launcher +Some users may prefer to use a different run launcher than dmenu. +Previously all scripts bundled would only run dmenu from $PATH but you can now switch run launcher very easily. + - Edit options.h and change RUN to your run launcher + - Add export RUNLAUNCHER= to your .rc + +Run launchers must support dmenu arguments because otherwise scripts are going to be incompatible. +It must also support the additional '-g' argument that the dmenu grid patch provides unless you modify the scripts bundled. +Keep in mind that if you use a different run launcher, it may not support Pywal/.Xresources. diff --git a/autostart.h b/autostart.h new file mode 100644 index 0000000..5ee6a49 --- /dev/null +++ b/autostart.h @@ -0,0 +1,44 @@ +/* Anything in here will automatically start before speedwm + * I use it to start my status bar and some other stuff, though. + * + * If you wish to run more commands, add a line below. + * + * Syntax: "shell, "-c", "", NULL," + * + * "shell" is automatically defined as your shell. + * + * If you need help, run speedwm-keybinds. + * Once you're done with your edits, run 'make clean install'. + *************************************************************/ + +#define STATUSBAR status +#define ICONSIZE sizeicon +#define ICONSPACING spacingicon +static const char *const autostart[] = { + + /* Bind Right Super+hjkl to arrow keys */ + shell, "-c", "xmodmap -e 'keycode 134 = Mode_switch'", NULL, + shell, "-c", "xmodmap -e 'keycode 43 = h H Left H'", NULL, + shell, "-c", "xmodmap -e 'keycode 44 = j J Down J'", NULL, + shell, "-c", "xmodmap -e 'keycode 45 = k K Up K", NULL, + shell, "-c", "xmodmap -e 'keycode 46 = l L Right L", NULL, + + /* Caps Lock = Escape */ + shell, "-c", "xmodmap -e 'clear Lock'", NULL, + shell, "-c", "xmodmap -e 'keycode 66 = Escape NoSymbol Escape'", NULL, + + /* Run the defined clipboard manager */ + shell, "-c", CLIPBOARD "&", NULL, + + /* Run the defined compositor */ + shell, "-c", COMPOSITOR "&", NULL, + + /* Run the defined notification daemon */ + shell, "-c", NOTIFICATION "&", NULL, + + /* Run the status bar defined */ + shell, "-c", STATUSBAR, NULL, + + NULL +}; + diff --git a/colors.h b/colors.h new file mode 100644 index 0000000..72eb3f3 --- /dev/null +++ b/colors.h @@ -0,0 +1,73 @@ +/* Color/Alpha settings + * You probably don't need to change these unless you want special opacity settings. + * + * Once you're done with your edits, run 'make clean install'. */ + +/* Misc color options */ +static char *colors[][3] = { + [SchemeBar] = { col_textnorm, col_background, col_windowbordernorm }, + [SchemeTags] = { col_textnorm, col_background, col_windowbordernorm }, + [SchemeNormBorder] = { col_textnorm, col_background, col_windowbordernorm }, + [SchemeSelBorder] = { col_textsel, col_windowbordernorm, col_windowbordersel }, + [SchemeStatus] = { col_textnorm, col_textsel, col_textsel }, + [SchemeNormTitle] = { col_textnorm, col_background, col_background }, + [SchemeSelTitle] = { col_textsel, col_backgroundmid, col_textsel }, + [SchemeHid] = { col_backgroundmid, col_background, col_backgroundmid }, + [SchemeLayout] = { col_layouttext, col_layoutbgsel, col_layoutbgnorm }, +/* text background window border +*/ +}; + +/* Colors for the status bar (.Xresources) */ +static char *colstatus[] = { + col_status0, + col_status1, + col_status2, + col_status3, + col_status4, + col_status5, + col_status6, + col_status7, + col_status8, + col_status9, + col_status10, + col_status11, + col_status12, + col_status13, + col_status14, + col_status15, +}; + +/* Colors to use for opacity +*/ + +static const unsigned int alphas[][3] = { + /* fg bg border */ + [SchemeBar] = { OPAQUE, baropacity, baropacity }, + [SchemeNormTitle] = { OPAQUE, normtitleopacity, normtitleopacity }, + [SchemeSelTitle] = { OPAQUE, seltitleopacity, seltitleopacity }, + [SchemeLayout] = { OPAQUE, layoutopacity, layoutopacity }, + [SchemeStatus] = { OPAQUE, statusopacity, statusopacity }, + [SchemeHid] = { hiddenopacity, hiddenopacity, hiddenopacity }, + [SchemeTags] = { tagselopacity, tagselopacity, tagnormopacity }, +}; + +/* Colors to use for tags */ +static char *tagsel[][2] = { + { col_tag1_text, col_tag1 }, + { col_tag2_text, col_tag2 }, + { col_tag3_text, col_tag3 }, + { col_tag4_text, col_tag4 }, + { col_tag5_text, col_tag5 }, + { col_tag6_text, col_tag6 }, + { col_tag7_text, col_tag7 }, + { col_tag8_text, col_tag8 }, + { col_tag9_text, col_tag9 }, + /* Text Background */ +}; + +/* Alpha for tags */ +static const unsigned int tagalpha[] = { + tagselopacity, + tagnormopacity, +}; diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..591f167 --- /dev/null +++ b/config.mk @@ -0,0 +1,55 @@ +# speedwm compilation options +# +# Depending on your configuration, you may need to uncomment some lines here. + +# OpenBSD support +# If you use OpenBSD, uncomment this line. (remove the # at the start of the line below) +#FREETYPEINC = ${X11INC}/freetype2 + +# Solaris support +# If you use Solaris, uncomment this line. (remove the # at the start of the line below) +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# Xinerama, comment these lines if you don't want multimonitor support +# If you don't know what this means, don't change anything and install: +# Arch: libxinerama +# Gentoo: libXinerama +# Debian: libXinerama-devel +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# Compiling with -Ofast. If you're having issues (such as dwm crashing, change it to -O2. (That's not a zero) +# Using -O2 is not recommended due to the usage of Imlib2. +# If these features are used, they will be slow with the use of -O2. +CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Ofast ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# Compiler and linker. +# You can swap this out if you know what you're doing. +CC = cc + +# Paths +# These should be fine for most users but if you use a distribution of GNU/Linux like NixOS or GNU Guix, consider changing this to fit your use case. +# HTMLDIR is only useful if you are using 'make release'. +PREFIX = /usr +HTMLDIR = "/home/anon/Projects/spdgmr.github.io/" + +################################################################################### + +# speedwm version +VERSION = 0.1 + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lXrender -lImlib2 -lX11-xcb -lxcb -lxcb-res -lXext + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} diff --git a/docs/about b/docs/about new file mode 100644 index 0000000..dc706d7 --- /dev/null +++ b/docs/about @@ -0,0 +1,47 @@ +-- speedwm -- + https://speedie.gq/speedwm + + https://speedie.gq/donate + +-- What is speedwm? -- +speedwm is a window manager forked from suckless.org's dwm or dynamic window manager. It manages the user's open windows and tiles them according to a set layout (dynamic). +Unlike dwm, speedwm tries to be minimal just dwm but also has functionality and aesthetics as a goal. It is also much more minimal than other window managers like i3-gaps while offering many more features. + +Tiling window managers (unlike floating window managers that you may be used to) tile windows based on a set layout making them easy to get productive on. +They also encourage the user to use their keyboard instead of the mouse so that the user doesn't have to move their hands much but there are mouse keybinds and more can be added. + +-- Installation -- +In order to install this build of speedwm, all dependencies must be installed. +You can see (Dependencies) for a list of all dependencies required to use this fork. + + - git clone https://codeberg.org/speedie/speedwm + - cd speedwm + - make clean install + - If any warnings show up, fix it by installing the missing dependency. + - If a .xinitrc is used, add 'speedwm_run' to the end. + - NOTE: Your .xinitrc should ONLY contain 'speedwm_run'. speedwm will automatically start everything else. If not, edit autostart.h and 'make clean install'. If a .xinitrc is not used then you don't need to worry. A .desktop file is automatically created when you run 'make clean install'. + +-- Layouts -- +speedwm comes with the following layouts: + + - Tile + - Monocle + - Grid + - Deck + - Centered Master + - Centered Floating Master + - Spiral + - Dwindle + - Three Column + - Bottom Stack + - Horizontal Bottom Stack + - Horizonal Grid + - Tatami + - Tilewide + - Stairs + - Tiling (5:4) + - Column + - Dynamic Grid + +They can be switched between using a little menu (See Keybinds for more information) or by right clicking the Layout indicator. +The more commonly used layouts can be switched between using a quick keybind. diff --git a/docs/about2 b/docs/about2 new file mode 100644 index 0000000..907e1e8 --- /dev/null +++ b/docs/about2 @@ -0,0 +1,4 @@ + +-- Keybinds -- +Below is a list of all speedwm keybinds. + diff --git a/docs/about3 b/docs/about3 new file mode 100644 index 0000000..6f1fa11 --- /dev/null +++ b/docs/about3 @@ -0,0 +1,40 @@ +-- Important -- +If you're used to dwm, speedwm might be a little unfamiliar to you at first. This is because speedwm doesn't use config.h (or config.def.h). +Instead, config.h is split into different parts to make it easier to edit. Instead of editing config.h you'll want to edit: + + - autostart.h for starting stuff right before speedwm (For example xclip, pywal, etc.) + - options.h for changing colors and applications to use with keybinds. + - fsignal.h for adding fake signals + - colors.h for changing alpha options, most users won't need to edit it. + - xresources.h for adding .Xresources options + - rules.h for adding rules + - keybinds.h for adding/removing keybinds. + +After you've edited one of the files, you need to run 'make clean install' to reinstall speedwm. +Remember that you can change colors through your .Xresources file (see .Xresources and Pywal) meaning you do not need to recompile speedwm. + +Another important detail you must keep in mind is that this build comes with a status bar simply named 'status'. +It can be found in the speedwm source code directory. It is just a shell script which adds stuff to your status bar. It will automatically be started when speedwm starts. + +You can edit the status bar simply by editing 'status' and running 'make clean install'. +You can also configure it by editing '~/.config/speedwm-de/status/config'. +Please note that most status bars including the built in 'status' depends on xsetroot which must be installed. speedwm-compatcheck is going to tell you about this when compiling. + +If you want to change status bar, edit options.h and set 'static char status' to your status bar binary (must be in $PATH). +Alternatively, you can also set "speedwm.status: " in .Xresources (See .Xresources and Pywal) + +-- .Xresources and Pywal -- +This fork of speedwm has .Xresources support thanks to the .Xresources patch. +It also has pywal support (tool which grabs colors based on your wallpaper). + +Colors reload automagically because of a reloadxresources function this build has combined with fsignal and a wallpaper script I wrote. +Therefore, if you want colors to reload instantly, you're unfortunately forced to use the bundled wallpaper script. + +If you want to use another script, you can open up speedwm-utils and select 'Reload .Xresources' to reload .Xresources. +Alternatively, you can write a script yourself (xsetroot -name "fsignal:18" reloads the colors) + +Below is a list of all .Xresources values you can define. The .Xresources file should be placed in ~ or ~/.config by the user. +If it is not or you want it somewhere else, you can edit 'autostart.h' and 'make clean install'. + +Note that the 'xrdb' dependency is required for both pywal and .Xresources support and 'xsetroot' is required for automatic reloading of colors, the built in status bar, and more so you should install this. + diff --git a/docs/about4 b/docs/about4 new file mode 100644 index 0000000..dafc247 --- /dev/null +++ b/docs/about4 @@ -0,0 +1,10 @@ + +-- Fsignal -- +Thanks to the 'fsignal' patch available on suckless.org's website, we can easily write shell scripts to interact with dwm and therefore speedwm. +This is exactly what I did and speedwm-utils, speedwm-swal, speedwm-shutdown and more take advantage of it. + +In order to use 'fsignal', your system must have 'xsetroot' installed. +Then simply use the dwm-utils script. Syntax is dwm-utils -exec + +Below is a list of all signums and what they do. + diff --git a/docs/about5 b/docs/about5 new file mode 100644 index 0000000..45aa5dd --- /dev/null +++ b/docs/about5 @@ -0,0 +1,9 @@ +-- Switching run launcher -- +Some users may prefer to use a different run launcher than dmenu. +Previously all scripts bundled would only run dmenu from $PATH but you can now switch run launcher very easily. + - Edit options.h and change RUN to your run launcher + - Add "export RUNLAUNCHER=" to your .rc + +Run launchers must support dmenu arguments because otherwise scripts are going to be incompatible. +It must also support the additional '-g' argument that the dmenu grid patch provides unless you modify the scripts bundled. +Keep in mind that if you use a different run launcher, it may not support Pywal/.Xresources. diff --git a/docs/bindlist b/docs/bindlist new file mode 100644 index 0000000..e3bcdbe --- /dev/null +++ b/docs/bindlist @@ -0,0 +1,131 @@ + -- Applications -- + + Keybinds for regular applications + + - Super+Shift+Enter | Opens a terminal + - Super+Shift+Colon | Opens a dmenu prompt + - Super+Shift+s | Opens 'maim' to take a screenshot and copies it to the clipboard using 'xclip' + - Super+Shift+f | Opens the defined file manager + - Super+Shift+w | Opens the defined web browser + - Super+Shift+o | Opens the bundled dfmpeg dmenu script to record your screen. + - Super+Shift+e | Opens the dboard dmenu script in dmenu which can copy text to your clipboard + - Super+Shift+t | Opens the defined editor in your terminal + - Super+Shift+p | Kills the defined web browser + - Super+Shift+m | Kills the defined music player + - Super+Shift+a | Opens the defined mixer in your terminal + - Super+Shift+m | Opens the defined music player + - Super+Shift+x | Opens the defined system process viewer in your terminal + - Super+Control+Shift+m | Opens the defined email client + - Super+Control+u | Opens the defined RSS reader + + -- Navigation -- + + These keybinds are for navigating speedwm + + - Super+t | Reorganize tags and move clients + - Super+f | Full-screen the selected window + - Super+b | Show/hide the speedwm bar + - Super+s | Show/hide the systray (If trayer is installed) + - Super+j/k | Move focus between visible windows + - Super+Alt+j/k | Increase/decrease gaps between windows in tiling layout by 1 + - Super+Alt+u/d | Increase/decrease gaps between windows in tiling layout by 5 + - Super+Shift+j/k | Move focus between hidden windows + - Super+a/d | Increase/decrease size of each window + - Super+o | Hide a window + - Super+Control+o | Show a window + - Super+w | Hide all windows except focused + - Super+Control+w | Show all windows except focused + - Super+a/d | Move to the next/previous tag + - Super+Minus | Show the scratchpad + - Super+Equal | Remove the scratchpad + - Super+Enter | Switch order of windows + - Super+Shift+q | Close the current window + - Super+Space | Set layout + - Super+t | Disable inactive fade + - Super+Shift+Equal | Toggle scratchpads + - Super+Shift+Minus | Hide the scratchpad + - Super+Shift+Space | Unfloat floating windows + - Super+Shift+Arrow | Resizes a window in floating mode + - Super+1 | Move to tag 1 + - Super+2 | Move to tag 2 + - Super+3 | Move to tag 3 + - Super+4 | Move to tag 4 + - Super+5 | Move to tag 5 + - Super+6 | Move to tag 6 + - Super+7 | Move to tag 7 + - Super+8 | Move to tag 8 + - Super+9 | Move to tag 9 + - Super+Shift+1 | Preview tag 1 + - Super+Shift+2 | Preview tag 2 + - Super+Shift+3 | Preview tag 3 + - Super+Shift+4 | Preview tag 4 + - Super+Shift+5 | Preview tag 5 + - Super+Shift+6 | Preview tag 6 + - Super+Shift+7 | Preview tag 7 + - Super+Shift+8 | Preview tag 8 + - Super+Shift+9 | Preview tag 9 + - Super+Shift+h/j/k/l | Rotates a stack. + - Super+Shift+Escape | Ask the user if they want to shutdown or reboot or nothing + - Super+Shift+i | Open a dmenu prompt and open the file the user picks in Zathura + - Super+Shift+p | Open a dmenu prompt and open the file the user picks in Vim + - Super+Control+e | Switch to layout 3 (Grid) + - Super+Control+r | Switch to layout 1 (Monocle) + - Super+Control+t | Switch to layout 0 (Master & stack) + - Super+Control+y | Switch to layout 4 (Deck) + - Super+Control+0 | Set all windows to use the same tag + - Super+Control+Arrow | Moves a window to any corner of your screen (Arrow key) + - Super+Control+Tab | Open a dmenu prompt asking the user what layout to switch to + - Super+Control+h | Open a list of all keybinds in your terminal using less + - Super+Control+Shift+a/d | Move between available layouts + - Super+Alt+s | Make the current selected window sticky + - Super+Control+Shift+Esc | Open speedwm-utils (Main menu) + - Super+Control+Shift+Arrow | Resize the window to the screen size. + - Super+Control+Shift+s | Set a wallpaper + - Super+Control+Shift+n | Connect to wifi (Requires iwd) + - Super+Control+Shift+b | Connect to a bluetooth device (Requires bluez and bluez-utils) + - Super+Control+Shift+e | Open up a list of dotfiles in dmenu that you can edit. + - Alt+Tab | Switch windows quickly and easily + - Alt+Control+j/k | Change window size vertically (cfact) + + -- Chained keybinds -- + - Super+c & w | Curl wttr.in and open in less + - Super+c & m | Ask the user for a topic and curl cheat.sh + - Super+g & t | Toggle gaps + - Super+g & 0 | Reset gaps + - Super+g & i | Increase inner gaps by 1 + - Super+Shift+g & i | Decrease inner gaps by 1 + - Super+g & o | Increase outer gaps by 1 + - Super+Shift+g & o | Decrease outer gaps by 1 + - Super+r+v | Open the defined music visualizer + + -- Extras -- + + These will only work if your keyboard has special multimedia buttons. + + - Mute button | Mutes your audio + - Up Volume button | Increases your volume + - Down Volume button | Decreases your volume + - Stop button | Stops your defined music player + - Browser button | Opens your defined web browser + - Power button | Ask if you wanna shut down, restart or lock your computer. + - Email button | Open your defined email client + - System button | Open your defined status viewer in a terminal + + -- Mouse -- + + These binds can be activated using your mouse + + - Tag (Left click) | Switch to tag + - Layout indicator (Left click) | Switch to the next layout + - Layout indicator (Middle click) | Switch to the next layout + - Layout indicator (Right click) | Open a dmenu list of all layouts + - Window title (Left click) | Hide/Show the window + - Window title (Right click) | Open speedwm-utils + - Focused window (Super+Alt+Left click) | Move the focused window around + - Focused window (Super+Alt+Middle click) | Make the focused window floating + - Focused window title (Middle click) | Rotate stack + - Dragging (Super+Right click) | Increase/decrease size of each window + + There are also keybinds for statuscmd, but you must implement it into your own status bar. + See mouse.h for more information. + diff --git a/docs/deplist b/docs/deplist new file mode 100644 index 0000000..79c1dee --- /dev/null +++ b/docs/deplist @@ -0,0 +1,44 @@ +-- Dependencies -- + These are absolutely necessary, speedwm will NOT compile without them + - libxft-bgra (Can be installed through 'make -libxftfix') + - NOTE: libXft will do but will cause speedwm and as such all your applications to crash if a colored emoji is displayed in the status bar. + - libXinerama + - Can be disabled through editing config.mk if you're not interested in multiple monitors. + - imlib2 + + -- Features -- + These are dependencies if you wanna use certain features + NOTE: Do not add any of these to .xinitrc or similar. They are going to be autostarted by speedwm. + If you want to use an alternative, change it in options.h. + - dmenu + - NOTE: dmenu is required for most scripts included with this build of speedwm. My build is required for Pywal support. + - NOTE 2: The build must have the 'grid' patch. If yours does not have this, you can patch it in or get my build here: https://codeberg.org/speedie/dmenu + - picom + - xclip (Required for clipboard support by a few scripts, will start automatically) + - xwallpaper (Required to set wallpapers automatically) + - xmodmap (Install if you want Escape instead of Caps Lock and Right Super+hjkl for arrow keys) + - xrdb (Install if you want .Xresources support) + - pywal (Install if you want pywal support. Requires swal aka the default way to set wallpapers) + - wmctrl (Needed for proper window management) + - xsetroot (Needed for most scripts including Pywal support) + - slock (Required for screen locking) + - maim (Required for built in 'speedwm-screenshotutil' script) + + -- Software -- + This build of speedwm comes with binds for software. + These must be installed by default but you can change what software is required by editing 'options.h' and running 'make clean install'. + You can also remove keybinds by editing 'keybinds.h' and running 'make clean install'. + - st (Terminal) + - firefox (Web browser) + - htop (Status monitor) + - newsboat (RSS reader) + - zathura (PDF reader) + - alsa-utils (Required for audio controls) + - mocp (Default music player) + - vim (Text editor) + - neomutt (Email client) + - maim (Screenshot tool, automatically copies to clipboard using xclip) + - vifm (File manager) + - slock (Lock screen) + And everything under 'Features'. + diff --git a/docs/example.Xresources b/docs/example.Xresources new file mode 100644 index 0000000..63ac3cd --- /dev/null +++ b/docs/example.Xresources @@ -0,0 +1,117 @@ + - speedwm.nmaster: 1 + - speedwm.font: fontawesome:size=8 + - speedwm.font2: NotoSans-Regular:size=8:antialiasing=true + - speedwm.font3: Noto Emoji:size=8 + - speedwm.col_background: #222222 + - speedwm.col_backgroundmid: #222222 + - speedwm.col_textnorm: #bbbbbb + - speedwm.col_textsel: #eeeeee + - speedwm.col_windowbordersel: #eeeeee + - speedwm.col_windowbordernorm: #000000 + - speedwm.col_tag1: #333333 + - speedwm.col_tag1_text: #eeeeee + - speedwm.col_tag2: #333333 + - speedwm.col_tag2_text: #eeeeee + - speedwm.col_tag3: #333333 + - speedwm.col_tag3_text: #eeeeee + - speedwm.col_tag4: #333333 + - speedwm.col_tag4_text: #eeeeee + - speedwm.col_tag5: #333333 + - speedwm.col_tag5_text: #eeeeee + - speedwm.col_tag6: #333333 + - speedwm.col_tag6_text: #eeeeee + - speedwm.col_tag7: #333333 + - speedwm.col_tag7_text: #eeeeee + - speedwm.col_tag8: #333333 + - speedwm.col_tag8_text: #eeeeee + - speedwm.col_tag9: #333333 + - speedwm.col_tag9_text: #eeeeee + - speedwm.col_layouttext: #000000 + - speedwm.col_layoutbgnorm: #222222 + - speedwm.col_layoutbgsel: #bbbbbb + - speedwm.col_status0: #131210 + - speedwm.col_status1: #bf616a + - speedwm.col_status2: #A16F9D + - speedwm.col_status3: #68ABAA + - speedwm.col_status4: #A89F93 + - speedwm.col_status5: #D3A99B + - speedwm.col_status6: #AFC9AC + - speedwm.col_status7: #eae1cb + - speedwm.col_status8: #a39d8e + - speedwm.col_status9: #6D5E8E + - speedwm.col_status10: #A16F9D + - speedwm.col_status11: #D3A99B + - speedwm.col_status12: #AFC9AC + - speedwm.col_status13: #eae1cb + - speedwm.col_status14: #6D5E8E + - speedwm.col_status15: #ffffff + - speedwm.borderpx: 1 + - speedwm.snap: 20 + - speedwm.showbar: 1 + - speedwm.resizehints: 0 + - speedwm.mousemfact: 1 + - speedwm.mfact: 0.50 + - speedwm.startontag: 1 + - speedwm.enablegaps: 1 + - speedwm.smartgaps: 1 + - speedwm.smartgapsize: 0 + - speedwm.gappih: 10 + - speedwm.gappiv: 10 + - speedwm.gappoh: 10 + - speedwm.gappov: 10 + - speedwm.attachdirection: 3 + - speedwm.shell: /bin/sh + - speedwm.sizeicon: 10 + - speedwm.spacingicon: 5 + - speedwm.status: status + - speedwm.defaultname: + - speedwm.refreshrules: 1 + - speedwm.decorhints: 1 + - speedwm.barpaddingv: 0 + - speedwm.barpaddingh: 0 + - speedwm.barheight: 5 + - speedwm.altbar: 0 + - speedwm.altbarclass: Polybar + - speedwm.alttrayname: tray + - speedwm.altbarcmd: polybar & + - speedwm.centerfloating: 1 + - speedwm.savefloat: 1 + - speedwm.warpcursor: 1 + - speedwm.pertag: 1 + - speedwm.i3nmaster: 0 + - speedwm.scalepreview: 4 + - speedwm.tagpreview: 1 + - speedwm.mousepreview: 1 + - speedwm.monocleclientcount: 0 + - speedwm.monoclecount: 0 + - speedwm.monocleformat: [%d/%d] + - speedwm.statusallmons: 1 + - speedwm.hidelayout: 0 + - speedwm.hidetitle: 0 + - speedwm.hidestatus: 0 + - speedwm.hidetags: 0 + - speedwm.hidesticky: 0 + - speedwm.hidefloating: 0 + - speedwm.leftlayout: 1 + - speedwm.fadeinactive: 1 + - speedwm.defaultlayout: 1 + - speedwm.wmclass: 1 + - speedwm.clicktofocus: 0 + - speedwm.stairpx: 20 + - speedwm.stairdirection: 1 + - speedwm.stairsamesize: 1 + - speedwm.deckcount: 0 + - speedwm.deckformat: D %d + - speedwm.roundedcorners: 0 + - speedwm.cornerradius: 3 + - speedwm.swallowclients: 1 + - speedwm.swallowfloating: 1 + - speedwm.movefullscreenmon: 0 + - speedwm.lockfullscreen: 1 + - speedwm.underline: 0 + - speedwm.underlineall: 0 + - speedwm.underlinepad: 5 + - speedwm.underlinestroke: 2 + - speedwm.underlinevoffset: 0 + - speedwm.forcevsplit: 1 + - speedwm.floatscratchpad: 0 diff --git a/docs/example.fsignal b/docs/example.fsignal new file mode 100644 index 0000000..ff8693a --- /dev/null +++ b/docs/example.fsignal @@ -0,0 +1,38 @@ + - xsetroot -name "fsignal:1" | Switch to the Tiling layout + - xsetroot -name "fsignal:2" | Switch to the Floating layout + - xsetroot -name "fsignal:3" | Switch to the Monocle layout + - xsetroot -name "fsignal:4" | Switch to the Grid layout + - xsetroot -name "fsignal:5" | Switch to the Deck layout + - xsetroot -name "fsignal:6" | Switch to the Centered Master layout + - xsetroot -name "fsignal:7" | Switch to the Centered Floating Master layout + - xsetroot -name "fsignal:8" | Switch to the Fibonacci Spiral layout + - xsetroot -name "fsignal:9" | Switch to the Fibonacci Dwindle layout + - xsetroot -name "fsignal:10" | Switch to the Three Column layout + - xsetroot -name "fsignal:11" | Switch to the Bottom Stack Vertical layout + - xsetroot -name "fsignal:12" | Switch to the Bottom Stack Horizontal layout + - xsetroot -name "fsignal:13" | Switch to the Horizontal Grid layout + - xsetroot -name "fsignal:14" | Switch to the Tatami layout + - xsetroot -name "fsignal:15" | To be added + - xsetroot -name "fsignal:16" | Cycle layout (Previous) + - xsetroot -name "fsignal:17" | Cycle layout (Next) + - xsetroot -name "fsignal:18" | Reload colors from .Xresources + - xsetroot -name "fsignal:19" | Set mfact (-0.05) + - xsetroot -name "fsignal:20" | Set mfact (+0.05) + - xsetroot -name "fsignal:21" | Toggle Scratchpad + - xsetroot -name "fsignal:22" | Toggle Sticky + - xsetroot -name "fsignal:23" | Toggle Bar + - xsetroot -name "fsignal:24" | Toggle Fullscreen + - xsetroot -name "fsignal:25" | Restart speedwm keeping all your applications open. + - xsetroot -name "fsignal:26" | Unused at the moment. + - xsetroot -name "fsignal:27" | Switch to the Stairs layout + - xsetroot -name "fsignal:28" | Reset layout and mfact + - xsetroot -name "fsignal:29" | Reorganize tags + - xsetroot -name "fsignal:30" | Restart speedwm + - xsetroot -name "fsignal:31" | Shutdown speedwm + - xsetroot -name "fsignal:32" | To be added + - xsetroot -name "fsignal:33" | To be added + - xsetroot -name "fsignal:34" | To be added + - xsetroot -name "fsignal:35" | Switch to the Tiling (5:4) layout + - xsetroot -name "fsignal:36" | Switch to the Column layout + - xsetroot -name "fsignal:37" | Switch to the Dynamic Grid layout + diff --git a/docs/speedwm.html.template.1 b/docs/speedwm.html.template.1 new file mode 100644 index 0000000..c4e3801 --- /dev/null +++ b/docs/speedwm.html.template.1 @@ -0,0 +1,24 @@ + + +
+ + +

image

diff --git a/docs/speedwm.html.template.2 b/docs/speedwm.html.template.2 new file mode 100644 index 0000000..5764673 --- /dev/null +++ b/docs/speedwm.html.template.2 @@ -0,0 +1,9 @@ +

Auto generated.

+

This page was auto generated by the speedwm-help script bundled with speedwm. It acts as the help script and it writes documentation to HTML, Markdown and plain text from documentation in the docs folder and data grabbed from your current system.

+ +
+

Website made by speedie, proudly written in Vim on Gentoo Linux.

+

This website is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.

+
+ + diff --git a/drw.c b/drw.c new file mode 100644 index 0000000..dacf5fa --- /dev/null +++ b/drw.c @@ -0,0 +1,536 @@ +/* See LICENSE file for copyright and license details. */ +#include +#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}; +unsigned int ew; + +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->picture = XRenderCreatePicture(dpy, drw->drawable, XRenderFindVisualFormat(dpy, visual), 0, NULL); + 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->picture) + XRenderFreePicture(drw->dpy, drw->picture); + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); + drw->picture = XRenderCreatePicture(drw->dpy, drw->drawable, XRenderFindVisualFormat(drw->dpy, drw->visual), 0, NULL); +} + +void +drw_free(Drw *drw) +{ + XRenderFreePicture(drw->dpy, drw->picture); + 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; +} + +Picture +drw_picture_create_resized(Drw *drw, char *src, unsigned int srcw, unsigned int srch, unsigned int dstw, unsigned int dsth) { + Pixmap pm; + Picture pic; + GC gc; + + if (srcw <= (dstw << 1u) && srch <= (dsth << 1u)) { + XImage img = { + srcw, srch, 0, ZPixmap, src, + ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32, + 32, 0, 32, + 0, 0, 0 + }; + XInitImage(&img); + + pm = XCreatePixmap(drw->dpy, drw->root, srcw, srch, 32); + gc = XCreateGC(drw->dpy, pm, 0, NULL); + XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, srcw, srch); + XFreeGC(drw->dpy, gc); + + pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL); + XFreePixmap(drw->dpy, pm); + + XRenderSetPictureFilter(drw->dpy, pic, FilterBilinear, NULL, 0); + XTransform xf; + xf.matrix[0][0] = (srcw << 16u) / dstw; xf.matrix[0][1] = 0; xf.matrix[0][2] = 0; + xf.matrix[1][0] = 0; xf.matrix[1][1] = (srch << 16u) / dsth; xf.matrix[1][2] = 0; + xf.matrix[2][0] = 0; xf.matrix[2][1] = 0; xf.matrix[2][2] = 65536; + XRenderSetPictureTransform(drw->dpy, pic, &xf); + } else { + Imlib_Image origin = imlib_create_image_using_data(srcw, srch, (DATA32 *)src); + if (!origin) return None; + imlib_context_set_image(origin); + imlib_image_set_has_alpha(1); + Imlib_Image scaled = imlib_create_cropped_scaled_image(0, 0, srcw, srch, dstw, dsth); + imlib_free_image_and_decache(); + if (!scaled) return None; + imlib_context_set_image(scaled); + imlib_image_set_has_alpha(1); + + XImage img = { + dstw, dsth, 0, ZPixmap, (char *)imlib_image_get_data_for_reading_only(), + ImageByteOrder(drw->dpy), BitmapUnit(drw->dpy), BitmapBitOrder(drw->dpy), 32, + 32, 0, 32, + 0, 0, 0 + }; + XInitImage(&img); + + pm = XCreatePixmap(drw->dpy, drw->root, dstw, dsth, 32); + gc = XCreateGC(drw->dpy, pm, 0, NULL); + XPutImage(drw->dpy, pm, gc, &img, 0, 0, 0, 0, dstw, dsth); + imlib_free_image_and_decache(); + XFreeGC(drw->dpy, gc); + + pic = XRenderCreatePicture(drw->dpy, pm, XRenderFindStandardFormat(drw->dpy, PictStandardARGB32), 0, NULL); + XFreePixmap(drw->dpy, pm); + } + + return pic; +} + +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); +} + +void +drw_polygon(Drw *drw, int x, int y, int ow, int oh, int sw, int sh, const XPoint *points, int npoints, int shape, int filled) /* wrapper function to scale and draw a polygon with X11 */ +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, drw->scheme[ColFg].pixel); + if (!filled) { /* reduces the scaled width and height by 1 when drawing the outline to compensate for X11 drawing the line 1 pixel over */ + sw -= 1; + sh -= 1; + } + XPoint scaledpoints[npoints]; + memcpy(scaledpoints, points, npoints); + for (int v = 0; v < npoints; v++) + scaledpoints[v] = (XPoint){ .x = points[v].x * sw / ow + x, .y = points[v].y * sh / oh + y }; + if (filled) + XFillPolygon(drw->dpy, drw->drawable, drw->gc, scaledpoints, npoints, shape, CoordModeOrigin); /* Change shape to 'Convex' or 'Complex' in speedwm.c if the shape is not 'Nonconvex' */ + else + XDrawLines(drw->dpy, drw->drawable, drw->gc, scaledpoints, npoints, CoordModeOrigin); +} + +int +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; + 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; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); + + 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_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic) +{ + if (!drw) + return; + XRenderComposite(drw->dpy, PictOpOver, pic, None, drw->picture, 0, 0, 0, 0, x, y, w, h); +} + +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); +} + +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/drw.h b/drw.h new file mode 100644 index 0000000..5e6cbc1 --- /dev/null +++ b/drw.h @@ -0,0 +1,65 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Visual *visual; + unsigned int depth; + Colormap cmap; + Drawable drawable; + Picture picture; + 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_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +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); + +/* 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); + +Picture drw_picture_create_resized(Drw *drw, char *src, unsigned int src_w, unsigned int src_h, unsigned int dst_w, unsigned int dst_h); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +void drw_polygon(Drw *drw, int x, int y, int ow, int oh, int sw, int sh, const XPoint *points, int npoints, int shape, int filled); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); +void drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/fsignal.h b/fsignal.h new file mode 100644 index 0000000..195113f --- /dev/null +++ b/fsignal.h @@ -0,0 +1,41 @@ +/* Signal definitions + * Signum must be greater than 0 + * Trigger signals using `speedwm-utils -exec "` + * + * Once you're done with your edits, run 'make clean install'. */ +static Signal signals[] = { + /* signum function argument */ + { 1, setlayout, {.v = &layouts[0]} }, + { 2, setlayout, {.v = &layouts[1]} }, + { 3, setlayout, {.v = &layouts[2]} }, + { 4, setlayout, {.v = &layouts[3]} }, + { 5, setlayout, {.v = &layouts[4]} }, + { 6, setlayout, {.v = &layouts[5]} }, + { 7, setlayout, {.v = &layouts[6]} }, + { 8, setlayout, {.v = &layouts[7]} }, + { 9, setlayout, {.v = &layouts[8]} }, + { 10, setlayout, {.v = &layouts[9]} }, + { 11, setlayout, {.v = &layouts[10]} }, + { 12, setlayout, {.v = &layouts[11]} }, + { 13, setlayout, {.v = &layouts[12]} }, + { 14, setlayout, {.v = &layouts[13]} }, + { 15, setlayout, {.v = &layouts[14]} }, + { 16, cyclelayout, {.i = -1 } }, + { 17, cyclelayout, {.i = +1 } }, + { 18, livereloadxrdb, {0} }, + { 19, setmfact, {.f = -0.05} }, + { 20, setmfact, {.f = +0.05} }, + { 21, togglescratch, {.v = scratchpadcmd } }, + { 22, togglesticky, {0} }, + { 23, togglebar, {0} }, + { 24, togglefullscr, {0} }, + { 25, self_restart, {0} }, + { 27, setlayout, {.v = &layouts[15]} }, + { 28, resetlayout, {0} }, + { 29, reorganizetags, {0} }, + { 30, quit, {1} }, + { 31, quit, {0} }, + { 35, setlayout, {.v = &layouts[16]} }, + { 36, setlayout, {.v = &layouts[17]} }, + { 37, setlayout, {.v = &layouts[18]} }, +}; diff --git a/keybinds.h b/keybinds.h new file mode 100644 index 0000000..0648263 --- /dev/null +++ b/keybinds.h @@ -0,0 +1,170 @@ +/* These are all your keybinds. + * + * Example keybind: + * + * { MODKEY, -1, XK_1, spawn, SHCMD(TERMINAL "echo "Hello world!") }, + * { MODKEY, XK_F1, XK_1, spawn, SHCMD(TERMINAL "echo "Hello world! Pressing two keys in a row is based!") }, + * + * Modifiers + * + * MODKEY is what you defined (in options.h, default is Super) + * SMODKEY is what you defined (in options.h, default is Alt) + * ShiftMask is unless changed going to be your Shift key. + * ControlMask is unless changed going to be your Control key. + * + * It is recommended that you avoid using 'SMODKEY' (Mod1Mask) by itself because it can break software defined shortcuts. + * + * If you need help, see speedwm-help. + * Once you're done with your edits, run 'make clean install'. + */ + +static Key keys[] = { + /* modifier chain key key function argument */ + + /* Application keybinds */ + { MODKEY|ShiftMask, -1, XK_semicolon, spawn, SHCMD(RUN) }, + { MODKEY|ShiftMask, -1, XK_Return, spawn, SHCMD(TERMINAL) }, + { MODKEY|ShiftMask, -1, XK_s, spawn, SHCMD(SCREENSHOT) }, + { MODKEY|ShiftMask, -1, XK_f, spawn, SHCMD(TERMINAL FILEMANAGER) }, + { MODKEY|ShiftMask, -1, XK_w, spawn, SHCMD(BROWSER) }, + { MODKEY|ShiftMask, -1, XK_o, spawn, SHCMD("speedwm-dfmpeg") }, + { MODKEY|ShiftMask, -1, XK_e, spawn, SHCMD("speedwm-virtualkeyboard") }, + { MODKEY|ShiftMask, -1, XK_t, spawn, SHCMD(TERMINAL EDITOR) }, + { MODKEY|ShiftMask, -1, XK_a, spawn, SHCMD(TERMINAL MIXER) }, + { MODKEY|ShiftMask, -1, XK_m, spawn, SHCMD(TERMINAL MUSIC) }, + { SMODKEY, -1, XK_Tab, spawn, SHCMD("speedwm-winnav") }, + { MODKEY|ShiftMask, -1, XK_x, spawn, SHCMD(TERMINAL SYSTEMSTAT) }, + { MODKEY|ShiftMask, -1, XK_i, spawn, SHCMD(OPENPDF) }, + { MODKEY|ShiftMask, -1, XK_Escape, spawn, SHCMD("speedwm-shutdown") }, + { ControlMask|MODKEY, -1, XK_Tab, spawn, SHCMD("speedwm-utils -layout") }, + { ControlMask|MODKEY, -1, XK_h, spawn, SHCMD(TERMINAL "speedwm-help -3") }, + { ControlMask|MODKEY, -1, XK_s, spawn, SHCMD(SCREENSHOT_FULL) }, + { ControlMask|MODKEY, -1, XK_u, spawn, SHCMD(TERMINAL RSS) }, + { ControlMask|MODKEY, -1, XK_m, spawn, SHCMD(KILLMUSIC) }, + { ControlMask|MODKEY, -1, XK_1, spawn, SHCMD(VOL_MUTE) }, + { ControlMask|MODKEY, -1, XK_2, spawn, SHCMD(VOL_DOWN) }, + { ControlMask|MODKEY, -1, XK_3, spawn, SHCMD(VOL_UP) }, + { ControlMask|MODKEY|ShiftMask, -1, XK_k, spawn, SHCMD(VOL_OUTPUT_SPEAKER) }, + { ControlMask|MODKEY|ShiftMask, -1, XK_Escape, spawn, SHCMD("speedwm-utils") }, + { ControlMask|MODKEY|ShiftMask, -1, XK_s, spawn, SHCMD("speedwm-swal") }, + { ControlMask|MODKEY|ShiftMask, -1, XK_n, spawn, SHCMD(NETWORK) }, + { ControlMask|MODKEY|ShiftMask, -1, XK_b, spawn, SHCMD(BLUETOOTH) }, + { MODKEY, -1, XK_s, spawn, SHCMD("speedwm-core -toggle") }, + { ControlMask|MODKEY|ShiftMask, -1, XK_m, spawn, SHCMD(TERMINAL EMAIL) }, + { ControlMask|MODKEY|ShiftMask, -1, XK_e, spawn, SHCMD("speedwm_run -configure") }, + + /* Layout keybinds */ + { ControlMask|MODKEY|ShiftMask, -1, XK_a, cyclelayout, {.i = -1 } }, + { ControlMask|MODKEY|ShiftMask, -1, XK_d, cyclelayout, {.i = +1 } }, + { MODKEY|ControlMask, -1, XK_y, setlayout, {.v = &layouts[4]} }, + { MODKEY|ControlMask, -1, XK_e, setlayout, {.v = &layouts[3]} }, + { MODKEY|ControlMask, -1, XK_r, setlayout, {.v = &layouts[1]} }, + { MODKEY|ControlMask, -1, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, -1, XK_space, setlayout, {0} }, + + /* Sticky keybinds */ + { SMODKEY|MODKEY, -1, XK_s, togglesticky, {0} }, + + /* Scratchpad keybinds */ + { MODKEY, -1, XK_minus, scratchpad_show, {0} }, + { MODKEY|ShiftMask, -1, XK_minus, scratchpad_hide, {0} }, + { MODKEY, -1, XK_equal, scratchpad_remove, {0} }, + { MODKEY|ShiftMask, -1, XK_equal, togglescratch, {.v = scratchpadcmd } }, + + /* speedwm general binds */ + { MODKEY, -1, XK_f, togglefullscr, {0} }, + { MODKEY, -1, XK_b, togglebar, {0} }, + { MODKEY, -1, XK_j, focusstackvis, {.i = +1 } }, + { MODKEY, -1, XK_k, focusstackvis, {.i = -1 } }, + { MODKEY|ShiftMask, -1, XK_j, focusstackhid, {.i = +1 } }, + { MODKEY|ShiftMask, -1, XK_k, focusstackhid, {.i = -1 } }, + { MODKEY, -1, XK_a, setmfact, {.f = -0.05} }, + { MODKEY, -1, XK_d, setmfact, {.f = +0.05} }, + { SMODKEY|ControlMask, -1, XK_k, setcfact, {.f = +0.25} }, + { SMODKEY|ControlMask, -1, XK_j, setcfact, {.f = -0.25} }, + { SMODKEY|ControlMask, -1, XK_0, setcfact, {.f = 0.00} }, + { MODKEY, -1, XK_Return, zoom, {0} }, + { MODKEY|ShiftMask, -1, XK_q, killclient, {0} }, + { MODKEY|ShiftMask, -1, XK_space, togglefloating, {0} }, + { MODKEY|ControlMask, -1, XK_0, view, {.ui = ~0 } }, + { MODKEY, -1, XK_d, focusmon, {.i = -1 } }, + { MODKEY, -1, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, -1, XK_d, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, -1, XK_period, tagmon, {.i = +1 } }, + { MODKEY|ShiftMask, -1, XK_Tab, livereloadxrdb, {0} }, + { MODKEY|ShiftMask, -1, XK_j, inplacerotate, {.i = +1} }, + { MODKEY|ShiftMask, -1, XK_k, inplacerotate, {.i = -1} }, + { MODKEY|ShiftMask, -1, XK_h, inplacerotate, {.i = +2} }, + { MODKEY|ShiftMask, -1, XK_l, inplacerotate, {.i = -2} }, + { MODKEY, -1, XK_n, incnmaster, {.i = -1 } }, + { MODKEY, -1, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, -1, XK_t, toggleopacity, {0} }, + + /* Floating mode keybinds */ + { MODKEY, -1, XK_Down, moveresize, {.v = "0x 25y 0w 0h" } }, + { MODKEY, -1, XK_Up, moveresize, {.v = "0x -25y 0w 0h" } }, + { MODKEY, -1, XK_Right, moveresize, {.v = "25x 0y 0w 0h" } }, + { MODKEY, -1, XK_Left, moveresize, {.v = "-25x 0y 0w 0h" } }, + { MODKEY|ShiftMask, -1, XK_Down, moveresize, {.v = "0x 0y 0w 25h" } }, + { MODKEY|ShiftMask, -1, XK_Up, moveresize, {.v = "0x 0y 0w -25h" } }, + { MODKEY|ShiftMask, -1, XK_Right, moveresize, {.v = "0x 0y 25w 0h" } }, + { MODKEY|ShiftMask, -1, XK_Left, moveresize, {.v = "0x 0y -25w 0h" } }, + { MODKEY|ControlMask, -1, XK_Up, moveresizeedge, {.v = "t"} }, + { MODKEY|ControlMask, -1, XK_Down, moveresizeedge, {.v = "b"} }, + { MODKEY|ControlMask, -1, XK_Left, moveresizeedge, {.v = "l"} }, + { MODKEY|ControlMask, -1, XK_Right, moveresizeedge, {.v = "r"} }, + { MODKEY|ControlMask|ShiftMask, -1, XK_Up, moveresizeedge, {.v = "T"} }, + { MODKEY|ControlMask|ShiftMask, -1, XK_Down, moveresizeedge, {.v = "B"} }, + { MODKEY|ControlMask|ShiftMask, -1, XK_Left, moveresizeedge, {.v = "L"} }, + { MODKEY|ControlMask|ShiftMask, -1, XK_Right, moveresizeedge, {.v = "R"} }, + + /* Tag keybinds */ + TAGKEYS( -1, XK_1, 0) + TAGKEYS( -1, XK_2, 1) + TAGKEYS( -1, XK_3, 2) + TAGKEYS( -1, XK_4, 3) + TAGKEYS( -1, XK_5, 4) + TAGKEYS( -1, XK_6, 5) + TAGKEYS( -1, XK_7, 6) + TAGKEYS( -1, XK_8, 7) + TAGKEYS( -1, XK_9, 8) + { MODKEY, -1, XK_t, reorganizetags, {0} }, + { SMODKEY, -1, XK_a, viewtoleft, {0} }, + { SMODKEY, -1, XK_d, viewtoright, {0} }, + + /* Hide/Show keybinds */ + { MODKEY, -1, XK_o, hidewin, {0} }, + { MODKEY|ControlMask, -1, XK_o, restorewin, {0} }, + { MODKEY, -1, XK_w, hideotherwins, {0} }, + { MODKEY|ControlMask, -1, XK_w, restoreotherwins, {0} }, + + /* Chained keybinds */ + { MODKEY, XK_c, XK_w, spawn, SHCMD(TERMINAL "speedwm-core -curl-weather") }, + { MODKEY, XK_c, XK_m, spawn, SHCMD(TERMINAL "speedwm-core -curl-cheatsheet") }, + { MODKEY, XK_r, XK_v, spawn, SHCMD(TERMINAL VISUALIZER) }, + + /* Gap keybinds */ + { MODKEY|SMODKEY, -1, XK_j, incrgaps, {.i = +1 } }, + { MODKEY|SMODKEY, -1, XK_k, incrgaps, {.i = -1 } }, + { MODKEY|SMODKEY, -1, XK_d, incrgaps, {.i = +5 } }, + { MODKEY|SMODKEY, -1, XK_u, incrgaps, {.i = -5 } }, + + /* Chained gap keybinds */ + { MODKEY, XK_g, XK_t, togglegaps, {0} }, + { MODKEY, XK_g, XK_0, defaultgaps, {0} }, + { MODKEY, XK_g, XK_i, incrigaps, {.i = +1} }, + { MODKEY|ShiftMask, XK_g, XK_i, incrigaps, {.i = -1} }, + { MODKEY, XK_g, XK_o, incrogaps, {.i = +1} }, + { MODKEY|ShiftMask, XK_g, XK_o, incrogaps, {.i = -1} }, + + /* Media buttons */ + { 0, -1, XF86XK_AudioMute, spawn, SHCMD(VOL_MUTE) }, + { 0, -1, XF86XK_AudioRaiseVolume, spawn, SHCMD(VOL_UP) }, + { 0, -1, XF86XK_AudioLowerVolume, spawn, SHCMD(VOL_DOWN) }, + { 0, -1, XF86XK_AudioStop, spawn, SHCMD(KILLMUSIC) }, + { 0, -1, XF86XK_WWW, spawn, SHCMD(BROWSER) }, + { 0, -1, XF86XK_PowerOff, spawn, SHCMD("speedwm-shutdown") }, + { 0, -1, XF86XK_Sleep, spawn, SHCMD(LOCKER) }, + { 0, -1, XF86XK_Mail, spawn, SHCMD(TERMINAL EMAIL) }, + { 0, -1, XF86XK_TaskPane, spawn, SHCMD(TERMINAL SYSTEMSTAT) }, +}; diff --git a/layouts.c b/layouts.c new file mode 100644 index 0000000..6ac8c5c --- /dev/null +++ b/layouts.c @@ -0,0 +1,981 @@ +static void +tile(Monitor *m) +{ + unsigned int i, n; + int oh, ov, ih, iv; + 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; + + 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 - sw; + 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 * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + my += HEIGHT(c) + ih; + } else { + resize(c, sx, sy, sw - (2*c->bw), sh * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + sy += HEIGHT(c) + ih; + } +} + +void +monocle(Monitor *m) +{ + Client *c; + + if (monocleclientcount && !monoclecount) { + unsigned int n = 0; + + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + } + + for (c = m->stack; c && (!ISVISIBLE(c) || c->isfloating); c = c->snext); + if (c && !c->isfloating) { + XMoveWindow(dpy, c->win, m->wx, m->wy); + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); + c = c->snext; + } + for (; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); +} + +void +grid(Monitor *m) +{ + unsigned int i, n; + int cx, cy, cw, ch, cc, cr, chrest, cwrest, cols, rows; + int oh, ov, ih, iv; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + + /* grid dimensions */ + for (rows = 0; rows <= n/2; rows++) + if (rows*rows >= n) + break; + cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows; + + /* window geoms (cell height/width) */ + ch = (m->wh - 2*oh - ih * (rows - 1)) / (rows ? rows : 1); + cw = (m->ww - 2*ov - iv * (cols - 1)) / (cols ? cols : 1); + chrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; + cwrest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + cc = i / rows; + cr = i % rows; + cx = m->wx + ov + cc * (cw + iv) + MIN(cc, cwrest); + cy = m->wy + oh + cr * (ch + ih) + MIN(cr, chrest); + resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False); + } +} + +void +gaplessgrid(Monitor *m) +{ + unsigned int i, n; + int x, y, cols, rows, ch, cw, cn, rn, rrest, crest; // counters + int oh, ov, ih, iv; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + if (n == 0) + return; + + /* grid dimensions */ + for (cols = 0; cols <= n/2; cols++) + if (cols*cols >= n) + break; + if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ + cols = 2; + rows = n/cols; + cn = rn = 0; // reset column no, row no, client count + + ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; + cw = (m->ww - 2*ov - iv * (cols - 1)) / cols; + rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; + crest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols; + x = m->wx + ov; + y = m->wy + oh; + + for (i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) { + if (i/rows + 1 > cols - n%cols) { + rows = n/cols + 1; + ch = (m->wh - 2*oh - ih * (rows - 1)) / rows; + rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows; + } + resize(c, + x, + y + rn*(ch + ih) + MIN(rn, rrest), + cw + (cn < crest ? 1 : 0) - 2*c->bw, + ch + (rn < rrest ? 1 : 0) - 2*c->bw, + 0); + rn++; + if (rn >= rows) { + rn = 0; + x += cw + ih + (cn < crest ? 1 : 0); + cn++; + } + } +} + +void +fibonacci(Monitor *m, int s) +{ + unsigned int i, n; + int nx, ny, nw, nh; + int oh, ov, ih, iv; + int nv, hrest = 0, wrest = 0, r = 1; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + if (n == 0) + return; + + nx = m->wx + ov; + ny = m->wy + oh; + nw = m->ww - 2*ov; + nh = m->wh - 2*oh; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (r) { + if ((i % 2 && (nh - ih) / 2 <= (bh + 2*c->bw)) + || (!(i % 2) && (nw - iv) / 2 <= (bh + 2*c->bw))) { + r = 0; + } + if (r && i < n - 1) { + if (i % 2) { + nv = (nh - ih) / 2; + hrest = nh - 2*nv - ih; + nh = nv; + } else { + nv = (nw - iv) / 2; + wrest = nw - 2*nv - iv; + nw = nv; + } + + if ((i % 4) == 2 && !s) + nx += nw + iv; + else if ((i % 4) == 3 && !s) + ny += nh + ih; + } + + if ((i % 4) == 0) { + if (s) { + ny += nh + ih; + nh += hrest; + } + else { + nh -= hrest; + ny -= nh + ih; + } + } + else if ((i % 4) == 1) { + nx += nw + iv; + nw += wrest; + } + else if ((i % 4) == 2) { + ny += nh + ih; + nh += hrest; + if (i < n - 1) + nw += wrest; + } + else if ((i % 4) == 3) { + if (s) { + nx += nw + iv; + nw -= wrest; + } else { + nw -= wrest; + nx -= nw + iv; + nh += hrest; + } + } + if (i == 0) { + if (n != 1) { + nw = (m->ww - iv - 2*ov) - (m->ww - iv - 2*ov) * (1 - m->mfact); + wrest = 0; + } + ny = m->wy + oh; + } + else if (i == 1) + nw = m->ww - nw - iv - 2*ov; + i++; + } + + resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False); + } +} + +void +dwindle(Monitor *m) +{ + fibonacci(m, 1); +} + +void +spiral(Monitor *m) +{ + fibonacci(m, 0); +} + +void +tcl(Monitor * m) +{ + int x, y, h, w, mw, sw, bdw; + unsigned int i, n; + Client * c; + + for (n = 0, c = nexttiled(m->clients); c; + c = nexttiled(c->next), n++); + + if (n == 0) + return; + + c = nexttiled(m->clients); + + mw = m->mfact * m->ww; + sw = (m->ww - mw) / 2; + bdw = (2 * c->bw); + resize(c, + n < 3 ? m->wx : m->wx + sw, + m->wy, + n == 1 ? m->ww - bdw : mw - bdw, + m->wh - bdw, + False); + + if (--n == 0) + return; + + w = (m->ww - mw) / ((n > 1) + 1); + c = nexttiled(c->next); + + if (n > 1) + { + x = m->wx + ((n > 1) ? mw + sw : mw); + y = m->wy; + h = m->wh / (n / 2); + + if (h < bh) + h = m->wh; + + for (i = 0; c && i < n / 2; c = nexttiled(c->next), i++) + { + resize(c, + x, + y, + w - bdw, + (i + 1 == n / 2) ? m->wy + m->wh - y - bdw : h - bdw, + False); + + if (h != m->wh) + y = c->y + HEIGHT(c); + } + } + + x = (n + 1 / 2) == 1 ? mw : m->wx; + y = m->wy; + h = m->wh / ((n + 1) / 2); + + if (h < bh) + h = m->wh; + + for (i = 0; c; c = nexttiled(c->next), i++) + { + resize(c, + x, + y, + (i + 1 == (n + 1) / 2) ? w - bdw : w - bdw, + (i + 1 == (n + 1) / 2) ? m->wy + m->wh - y - bdw : h - bdw, + False); + + if (h != m->wh) + y = c->y + HEIGHT(c); + } +} + +static void +bstack(Monitor *m) +{ + unsigned int i, n; + int oh, ov, ih, iv; + 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; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + if (n == 0) + return; + + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh; + mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); + sw = m->ww - 2*ov - iv * (n - m->nmaster - 1); + + if (m->nmaster && n > m->nmaster) { + sh = (mh - ih) * (1 - m->mfact); + mh = mh - ih - sh; + sx = mx; + sy = my + mh + ih; + } + + getfacts(m, mw, sw, &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 * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + mx += WIDTH(c) + iv; + } else { + resize(c, sx, sy, sw * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + sx += WIDTH(c) + iv; + } + } +} + +static void +bstackhoriz(Monitor *m) +{ + unsigned int i, n; + int oh, ov, ih, iv; + 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; + + 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; + sh = m->wh - 2*oh - ih * (n - m->nmaster - 1); + mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1); + sw = m->ww - 2*ov; + + if (m->nmaster && n > m->nmaster) { + sh = (mh - ih) * (1 - m->mfact); + mh = mh - ih - sh; + sy = my + mh + ih; + sh = m->wh - mh - 2*oh - ih * (n - m->nmaster); + } + + getfacts(m, mw, 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 * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + mx += WIDTH(c) + iv; + } else { + resize(c, sx, sy, sw - (2*c->bw), sh * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0); + sy += HEIGHT(c) + ih; + } + } +} + +void +horizgrid(Monitor *m) { + Client *c; + unsigned int n, i; + int oh, ov, ih, iv; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + int ntop, nbottom = 1; + float mfacts = 0, sfacts = 0; + int mrest, srest, mtotal = 0, stotal = 0; + + /* Count windows */ + getgaps(m, &oh, &ov, &ih, &iv, &n); + if (n == 0) + return; + + if (n <= 2) + ntop = n; + else { + ntop = n / 2; + nbottom = n - ntop; + } + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh; + sw = mw = m->ww - 2*ov; + + if (n > ntop) { + sh = (mh - ih) / 2; + mh = mh - ih - sh; + sy = my + mh + ih; + mw = m->ww - 2*ov - iv * (ntop - 1); + sw = m->ww - 2*ov - iv * (nbottom - 1); + } + + /* calculate facts */ + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < ntop) + mfacts += c->cfact; + else + sfacts += c->cfact; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < ntop) + mtotal += mh * (c->cfact / mfacts); + else + stotal += sw * (c->cfact / sfacts); + + mrest = mh - mtotal; + srest = sw - stotal; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < ntop) { + resize(c, mx, my, mw * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + mx += WIDTH(c) + iv; + } else { + resize(c, sx, sy, sw * (c->cfact / sfacts) + ((i - ntop) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + sx += WIDTH(c) + iv; + } +} + +void +dynamicgrid(Monitor *m) +{ + unsigned int n; + int ri = 0, ci = 0; /* counters */ + int oh, ov, ih, iv; /* vanitygap settings */ + unsigned int cx, cy, cw, ch; /* client geometry */ + unsigned int uw = 0, uh = 0, uc = 0; /* utilization trackers */ + unsigned int cols, rows = m->nmaster + 1; + Client *c; + + /* count clients */ + getgaps(m, &oh, &ov, &ih, &iv, &n); + + /* nothing to do here */ + if (n == 0) + return; + + /* force 2 clients to always split vertically */ + if (FORCE_VSPLIT && n == 2) + rows = 1; + + /* never allow empty rows */ + if (n < rows) + rows = n; + + /* define first row */ + cols = n / rows; + uc = cols; + cy = m->wy + oh; + ch = (m->wh - 2*oh - ih*(rows - 1)) / rows; + uh = ch; + + for (c = nexttiled(m->clients); c; c = nexttiled(c->next), ci++) { + if (ci == cols) { + uw = 0; + ci = 0; + ri++; + + /* next row */ + cols = (n - uc) / (rows - ri); + uc += cols; + cy = m->wy + oh + uh + ih; + uh += ch + ih; + } + + cx = m->wx + ov + uw; + cw = (m->ww - 2*ov - uw) / (cols - ci); + uw += cw + iv; + + resize(c, cx, cy, cw - (2*c->bw), ch - (2*c->bw), 0); + } +} + +void +tatami(Monitor *m) { + unsigned int i, n, nx, ny, nw, nh, + mats, tc, + tnx, tny, tnw, tnh; + Client *c; + + for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), ++n); + if(n == 0) + return; + + nx = m->wx; + ny = 0; + nw = m->ww; + nh = m->wh; + + c = nexttiled(m->clients); + + if(n != 1) nw = m->ww * m->mfact; + ny = m->wy; + + resize(c, nx, ny, nw - 2 * c->bw, nh - 2 * c->bw, False); + + c = nexttiled(c->next); + + nx += nw; + nw = m->ww - nw; + + if(n>1) + { + + tc = n-1; + mats = tc/5; + + nh/=(mats + (tc % 5 > 0)); + + for(i = 0; c && (i < (tc % 5)); c = nexttiled(c->next)) + { + tnw=nw; + tnx=nx; + tnh=nh; + tny=ny; + switch(tc - (mats*5)) + { + case 1://fill + break; + case 2://up and down + if((i % 5) == 0) //up + tnh/=2; + else if((i % 5) == 1) //down + { + tnh/=2; + tny += nh/2; + } + break; + case 3://bottom, up-left and up-right + if((i % 5) == 0) //up-left + { + tnw = nw/2; + tnh = (2*nh)/3; + } + else if((i % 5) == 1)//up-right + { + tnx += nw/2; + tnw = nw/2; + tnh = (2*nh)/3; + } + else if((i % 5) == 2)//bottom + { + tnh = nh/3; + tny += (2*nh)/3; + } + break; + case 4://bottom, left, right and top + if((i % 5) == 0) //top + { + tnh = (nh)/4; + } + else if((i % 5) == 1)//left + { + tnw = nw/2; + tny += nh/4; + tnh = (nh)/2; + } + else if((i % 5) == 2)//right + { + tnx += nw/2; + tnw = nw/2; + tny += nh/4; + tnh = (nh)/2; + } + else if((i % 5) == 3)//bottom + { + tny += (3*nh)/4; + tnh = (nh)/4; + } + break; + } + ++i; + resize(c, tnx, tny, tnw - 2 * c->bw, tnh - 2 * c->bw, False); + } + + ++mats; + + for(i = 0; c && (mats>0); c = nexttiled(c->next)) { + + if((i%5)==0) + { + --mats; + if(((tc % 5) > 0)||(i>=5)) + ny+=nh; + } + + tnw=nw; + tnx=nx; + tnh=nh; + tny=ny; + + + switch(i % 5) + { + case 0: //top-left-vert + tnw = (nw)/3; + tnh = (nh*2)/3; + break; + case 1: //top-right-hor + tnx += (nw)/3; + tnw = (nw*2)/3; + tnh = (nh)/3; + break; + case 2: //center + tnx += (nw)/3; + tnw = (nw)/3; + tny += (nh)/3; + tnh = (nh)/3; + break; + case 3: //bottom-right-vert + tnx += (nw*2)/3; + tnw = (nw)/3; + tny += (nh)/3; + tnh = (nh*2)/3; + break; + case 4: //(oldest) bottom-left-hor + tnw = (2*nw)/3; + tny += (2*nh)/3; + tnh = (nh)/3; + break; + default: + break; + } + + ++i; + //i%=5; + resize(c, tnx, tny, tnw - 2 * c->bw, tnh - 2 * c->bw, False); + } + } +} + +void +tilewide(Monitor *m) +{ + unsigned int i, n, w, h, mw, mx, ty; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; + for (i = mx = ty = 0, c = nexttiled(m->clients); c; + c = nexttiled(c->next), i++) + if (i < m->nmaster) { + w = (mw - mx) / (MIN(n, m->nmaster) - i); + resize(c, m->wx + mx, m->wy, w - (2*c->bw), (m->wh - ty) - (2*c->bw), 0); + if (mx + WIDTH(c) < m->ww) + mx += WIDTH(c); + } else { + h = (m->wh - ty) / (n - i); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); + } +} + +void +stairs(Monitor *m) +{ + unsigned int i, n, h, mw, my; + unsigned int ox, oy, ow, oh; /* stair offset values */ + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; + + for (i = my = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->wx, m->wy + my, mw - (2 * c->bw), h - (2 * c->bw), 0); + if (my + HEIGHT(c) < m->wh) + my += HEIGHT(c); + } else { + oy = i - m->nmaster; + ox = stairdirection ? n - i - 1 : (stairsamesize ? i - m->nmaster : 0); + ow = stairsamesize ? n - m->nmaster - 1 : n - i - 1; + oh = stairsamesize ? ow : i - m->nmaster; + resize(c, + m->wx + mw + (ox * stairpx), + m->wy + (oy * stairpx), + m->ww - mw - (2 * c->bw) - (ow * stairpx), + m->wh - (2 * c->bw) - (oh * stairpx), + 0); + } + } +} + +void +centeredmaster(Monitor *m) +{ + unsigned int i, n; + int oh, ov, ih, iv; + int mx = 0, my = 0, mh = 0, mw = 0; + int lx = 0, ly = 0, lw = 0, lh = 0; + int rx = 0, ry = 0, rw = 0, rh = 0; + float mfacts = 0, lfacts = 0, rfacts = 0; + int mtotal = 0, ltotal = 0, rtotal = 0; + int mrest = 0, lrest = 0, rrest = 0; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + if (n == 0) + return; + + /* initialize areas */ + mx = m->wx + ov; + my = m->wy + oh; + mh = m->wh - 2*oh - ih * ((!m->nmaster ? n : MIN(n, m->nmaster)) - 1); + mw = m->ww - 2*ov; + lh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - 1); + rh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - ((n - m->nmaster) % 2 ? 0 : 1)); + + if (m->nmaster && n > m->nmaster) { + /* go mfact box in the center if more than nmaster clients */ + if (n - m->nmaster > 1) { + /* ||<-S->|<---M--->|<-S->|| */ + mw = (m->ww - 2*ov - 2*iv) * m->mfact; + lw = (m->ww - mw - 2*ov - 2*iv) / 2; + rw = (m->ww - mw - 2*ov - 2*iv) - lw; + mx += lw + iv; + } else { + /* ||<---M--->|<-S->|| */ + mw = (mw - iv) * m->mfact; + lw = 0; + rw = m->ww - mw - iv - 2*ov; + } + lx = m->wx + ov; + ly = m->wy + oh; + rx = mx + mw + iv; + ry = m->wy + oh; + } + + /* calculate facts */ + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { + if (!m->nmaster || n < m->nmaster) + mfacts += c->cfact; + else if ((n - m->nmaster) % 2) + lfacts += c->cfact; // total factor of left hand stack area + else + rfacts += c->cfact; // total factor of right hand stack area + } + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + if (!m->nmaster || n < m->nmaster) + mtotal += mh * (c->cfact / mfacts); + else if ((n - m->nmaster) % 2) + ltotal += lh * (c->cfact / lfacts); + else + rtotal += rh * (c->cfact / rfacts); + + mrest = mh - mtotal; + lrest = lh - ltotal; + rrest = rh - rtotal; + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { + if (!m->nmaster || i < m->nmaster) { + /* nmaster clients are stacked vertically, in the center of the screen */ + resize(c, mx, my, mw - (2*c->bw), mh * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + my += HEIGHT(c) + ih; + } else { + /* stack clients are stacked vertically */ + if ((i - m->nmaster) % 2 ) { + resize(c, lx, ly, lw - (2*c->bw), lh * (c->cfact / lfacts) + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0); + ly += HEIGHT(c) + ih; + } else { + resize(c, rx, ry, rw - (2*c->bw), rh * (c->cfact / rfacts) + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0); + ry += HEIGHT(c) + ih; + } + } + } +} + +void +centeredfloatingmaster(Monitor *m) +{ + unsigned int i, n; + float mfacts, sfacts; + float mivf = 1.0; // master inner vertical gap factor + int oh, ov, ih, iv, mrest, srest; + int mx = 0, my = 0, mh = 0, mw = 0; + int sx = 0, sy = 0, sh = 0, sw = 0; + Client *c; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + if (n == 0) + return; + + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh; + mw = m->ww - 2*ov - iv*(n - 1); + sw = m->ww - 2*ov - iv*(n - m->nmaster - 1); + + if (m->nmaster && n > m->nmaster) { + mivf = 0.8; + /* go mfact box in the center if more than nmaster clients */ + if (m->ww > m->wh) { + mw = m->ww * m->mfact - iv*mivf*(MIN(n, m->nmaster) - 1); + mh = m->wh * 0.9; + } else { + mw = m->ww * 0.9 - iv*mivf*(MIN(n, m->nmaster) - 1); + mh = m->wh * m->mfact; + } + mx = m->wx + (m->ww - mw) / 2; + my = m->wy + (m->wh - mh - 2*oh) / 2; + + sx = m->wx + ov; + sy = m->wy + oh; + sh = m->wh - 2*oh; + } + + getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest); + + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + /* nmaster clients are stacked horizontally, in the center of the screen */ + resize(c, mx, my, mw * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0); + mx += WIDTH(c) + iv*mivf; + } else { + /* stack clients are stacked horizontally */ + resize(c, sx, sy, sw * (c->cfact / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0); + sx += WIDTH(c) + iv; + } +} + +void +deck(Monitor *m) +{ + unsigned int i, n; + int oh, ov, ih, iv; + 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; + + getgaps(m, &oh, &ov, &ih, &iv, &n); + if (n == 0) + return; + + sx = mx = m->wx + ov; + sy = my = m->wy + oh; + sh = mh = m->wh - 2*oh - ih * (MIN(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 - sw; + sx = mx + mw + iv; + sh = m->wh - 2*oh; + } + + getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest); + + if (n - m->nmaster > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "D %d", n - m->nmaster); + + 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 * (c->cfact / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0); + my += HEIGHT(c) + ih; + } else { + resize(c, sx, sy, sw - (2*c->bw), sh - (2*c->bw), 0); + } +} + +void +tile54(Monitor *m) +{ + unsigned int i, n, h, mw, my, ty, move; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + if (n == 1) { + mw = m->ww * 0.703125; // 16:9 to horizontal 5:4 + //mw = m->ww * 0.45; // 16:9 to vertical 5:4 + move = (m->ww - mw) / 2; + h = m->wh; + c = nexttiled(m->clients); + i = my = ty = 0; + resize(c, m->wx + move, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + return; + } + + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + h = (m->wh - my) / (MIN(n, m->nmaster) - i); + resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); + my += HEIGHT(c); + } else { + h = (m->wh - ty) / (n - i); + resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); + ty += HEIGHT(c); + } +} + +void +col(Monitor *m) +{ + unsigned int i, n, h, w, x, y, mw; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (n == 0) + return; + + if (n > m->nmaster) + mw = m->nmaster ? m->ww * m->mfact : 0; + else + mw = m->ww; + for (i = x = y = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + w = (mw - x) / (MIN(n, m->nmaster) - i); + resize(c, x + m->wx, m->wy, w - (2 * c->bw), m->wh - (2 * c->bw), 0); + x += WIDTH(c); + } else { + h = (m->wh - y) / (n - i); + resize(c, x + m->wx, m->wy + y, m->ww - x - (2 * c->bw), h - (2 * c->bw), 0); + y += HEIGHT(c); + } +} + diff --git a/layouts.h b/layouts.h new file mode 100644 index 0000000..156ef7c --- /dev/null +++ b/layouts.h @@ -0,0 +1,28 @@ +/* These are all available layouts in speedwm. + * Any new layouts should be added in the exact same way as existing layouts. + * Layouts that are added through patches should be in layouts.c + * + * Once you're done with your edits, run 'make clean install'. */ + +static const Layout layouts[] = { + { "(L1)", tile }, + { "(L2)", NULL }, + { "(L3)", monocle }, + { "(L4)", grid }, + { "(L5)", deck }, + { "(L6)", centeredmaster }, + { "(L7)", centeredfloatingmaster }, + { "(L8)", spiral }, + { "(L9)", dwindle }, + { "(L10)", tcl }, + { "(L11)", bstack }, + { "(L12)", bstackhoriz }, + { "(L13)", horizgrid }, + { "(L14)", tatami }, + { "(L15)", tilewide }, + { "(L16)", stairs }, + { "(L17)", tile54 }, + { "(L18)", col }, + { "(L19)", dynamicgrid }, + { NULL, NULL }, +}; diff --git a/mouse.h b/mouse.h new file mode 100644 index 0000000..f055570 --- /dev/null +++ b/mouse.h @@ -0,0 +1,38 @@ +/* Mouse bindings + * + * This header contains mouse binds. + * Change them, or remove them if you prefer. You can also add more if you want. + * Once you're done with your edits, run 'make clean install'. + */ + +/* Actions when the mouse clicks a part of the screen */ +static Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button3, layoutmenu, {0} }, + { ClkLtSymbol, 0, Button1, cyclelayout, {.i = +1 } }, + { ClkLtSymbol, 0, Button2, cyclelayout, {.i = -1 } }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkClientWin, MODKEY, Button1, moveorplace, {.i = 1} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkStatusText, 0, Button1, spawn, {.v = statuscmd } }, + { ClkStatusText, 0, Button2, spawn, {.v = statuscmd } }, + { ClkStatusText, 0, Button3, spawn, {.v = statuscmd } }, + { ClkWinTitle, 0, Button1, togglewin, {0} }, + { ClkWinTitle, 0, Button3, spawn, SHCMD("speedwm-utils") }, + { ClkTagBar, 0, Button1, view, {0} }, +}; + +/* Commands that will run when a part of the status bar is pressed + * Built in status bar does not have support for this yet but you can add it by adding: + * '\x + * before you print anything. Keep in mind MUST be two digits. + * + * Invalid: xsetroot -name "\x01This is a test" + * Valid: xsetroot -name "$(printf '\x01This is a test')" + */ +static const StatusCmd statuscmds[] = { + { "notify-send 'Status item (1) was pressed.'", 1 }, + { "notify-send 'Status item (2) was pressed.'", 2 }, + { "notify-send 'Status item (3) was pressed.'", 3 }, +}; diff --git a/options.h b/options.h new file mode 100644 index 0000000..8a82d69 --- /dev/null +++ b/options.h @@ -0,0 +1,312 @@ +/* speedwm + * + * speedwm is a fork of suckless.org's dwm, a window manager for X. + * Unlike the original 'dwm' window manager, speedwm tries to be minimal, just like dwm but also has functionality and aesthetics as a goal. + * speedwm has a bigger codebase than suckless.org's dwm, however is still much more minimal than other tiling window managers such as i3-gaps while providing more features for the user. + * + * Keep in mind that speedwm is a personal window manager and therefore comes with keybinds for software I use and find appealing. + * If you do not like these keybinds, consider: + * - Copying this source code folder to another folder (such as speedwm-orig) + * - Deleting the lines you don't want in the files + * - Creating a patch using 'diff -up speedwm speedwm-orig > speedwm-settings.diff' + * - Copy the diff elsewhere. When updating speedwm (such as with 'git pull'), you can copy it to the source code folder and run 'patch < speedwm-settings.diff'. + * Otherwise you may maintain your own fork of speedwm. + * + * In addition to this, if speedwm doesn't have a feature you want, there's nothing stopping you from adding it as it still follows the suckless philosophy somewhat. + * Below is a configuration file. Read the instructions below for information about how to configure it. + * + * To use speedwm, you must first install all the dependencies. See 'speedwm-help -a' for a list of dependencies. + * Then, you need to run 'speedwm_run' after X has started. This can usually be done by adding 'speedwm_run' to ~/.xinitrc and running 'startx'. + * If you use a display manger/login manager like sddm, lightdm or something that uses entries, the entry should be created after installation. + * + * The header you are currently reading is for changing what software you want to use. + * + * Because this build of speedwm auto-starts software such as your compositor, you may want to change this by changing '#define COMPOSITOR'. Same goes for other software. + * + * If you aren't aware of this, do not add anything to your .xinitrc if it is listed in autostart.h + * + * - If you were looking for the keybinds, they can be found in keybinds.h + * - If you use another operating system such as OpenBSD, FreeBSD or Solaris (Not GNU/Linux), edit config.mk + * + * This build comes with defines for common software such as your web + * browser or terminal. + * If you wish to use a different browser for example, simply change the values. + * + * You do not need to edit 'keybinds.h' or 'rules.h' because that's where these are used. + * Once you're done with your edits, run 'make clean install'. + * + * Alternatively, you can change some of these in ~/.config/speedwm-de/speedwmrc. + * Software options must be changed in this file though. + ******************************************************/ + +/* Software options + * Note that any software named "speedwm-*" is bundled with this build and does not need to be replaced unless you want to. + */ +#define TERMINAL "st -e " /* Terminal to use */ +#define BROWSER "firefox" /* Web browser to use */ +#define SYSTEMSTAT "htop" /* System stat viewer to use */ +#define RSS "newsboat" /* RSS reader to use */ +#define PDF "zathura" /* PDF reader to use */ +#define MUSIC "cmus" /* Music player to use */ +#define VISUALIZER "~/.local/bin/vis || vis" /* Music visualizer to use */ +#define EMAIL "aerc" /* Email client to use */ +#define EDITOR "nvim" /* Text editor to use */ +#define RUN "dmenu_run -l 0 -p 'Run:' -h 19" /* Run launcher */ +#define SCREENSHOT "speedwm-screenshotutil -s" /* How to take screenshots (Selection) */ +#define SCREENSHOT_FULL "speedwm-screenshotutil -f" /* How to take screenshots (Full screen) */ +#define FILEMANAGER "vifmrun || vifm" /* File manager that will be used */ +#define LOCKER "slock" /* Screen locker that will be used */ +#define OPENPDF "speedwm-pdfopen ~/Documents zathura || ~/Scripts/speedwm-pdfopen ~/Documents zathura" /* Command to run when listing and opening PDFs. */ +#define KILLMUSIC "pkill cmus" /* Command to run when killing the music player */ + +/* Software classes */ +#define TERMINAL_CLASS "St" /* Terminal to use for rules */ +#define BROWSER_CLASS "firefox" /* Web browser to use for rules */ +#define PDF_CLASS "Zathura" /* PDF reader to use for rules */ +#define FILEMANAGER_CLASS "vifmrun" /* File manager that will be used for rules */ + +/* Audio options */ +#define MIXER "speedwm-audioctrl -runmixer" /* Audio mixer to use */ +#define VOL_DOWN "speedwm-audioctrl -lower" /* Command to run when decreasing volume */ +#define VOL_UP "speedwm-audioctrl -raise" /* Command to run when increasing volume */ +#define VOL_MUTE "speedwm-audioctrl -mute" /* Command to run when muting volume */ +#define VOL_OUTPUT_SPEAKER "speedwm-audioctrl -switch" /* Command to run when enabling speakers */ + +/* Modifier keys + * Mod4Mask | Super (Windows/command) key + * Mod1Mask | Alt key + */ +#define MODKEY Mod4Mask +#define SMODKEY Mod1Mask + +/* Managers */ +#define CLIPBOARD "xclip" /* Clipboard to use */ +#define COMPOSITOR "picom --experimental-backends" /* Compositor to use */ +#define NETWORK "speedwm-netctrl" /* Network manager to use */ +#define BLUETOOTH "speedwm-btctrl" /* Bluetooth manager to use */ +#define NOTIFICATION "dunst" /* Notification daemon to use */ + +/* Paths */ +#define SESSION_FILE "/tmp/speedwm-session" /* Session file. This file may be used as a list of windows and their tagnum */ + +/* Misc */ +#define SHCMD(cmd) { .v = (const char*[]){ shell, "-c", cmd, NULL } } +#define TAGKEYS(CHAIN,KEY,TAG) { MODKEY, CHAIN, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, CHAIN, KEY, previewtag, {.ui = TAG} }, +/* Options + * + * If xrdb is installed, you can simply edit ~/.config/speedwm-de/speedwmrc instead of recompiling. + * Once you're done with your edits, run 'make clean install'. + */ + +/* Window alignment options */ +static unsigned int borderpx = 1; /* How big your border is */ +static unsigned int snap = 20; /* Snap pixel */ +static int nmaster = 1; +static int resizehints = 0; +static int decorhints = 1; /* Respect decoration hints */ +static int savefloat = 1; /* Save position of floating windows */ +static int refreshrules = 1; /* Refresh rules when a CLASS or TITLE changes */ +static int i3nmaster = 0; /* Enable i3-gaps like nmaster (1/0) */ +static int mousemfact = 1; /* Enable adjusting mfact using the mouse (1/0) */ +static float mfact = 0.50; + +/* Window gap options */ +static int enablegaps = 1; /* Enable gaps */ +static unsigned int gappih = 10; /* horiz inner gap between windows */ +static unsigned int gappiv = 10; /* vert inner gap between windows */ +static unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ +static unsigned int gappov = 10; /* vert outer gap between windows and screen edge */ +static unsigned int smartgapsize = 0; /* Gaps for smartgaps. If set to zero, gaps will be disabled. */ +static int smartgaps = 0; /* 1 means use different gap size when there is only one window */ + +/* Window aesthetic options */ +static int fadeinactive = 1; /* Fade inactive windows */ +static double activeopacity = 1.0f; /* Window opacity when it's focused (0 <= opacity <= 1) */ +static double inactiveopacity = 0.875f; /* Window opacity when it's inactive (0 <= opacity <= 1) */ +static Bool bUseOpacity = True; /* Starts with opacity on any unfocused windows */ + +/* Rounded corners + * In order to use: + * - borderpx must also be set to 0 or else rounded corners will be disabled. + */ +static int roundedcorners = 0; /* Enable (1) rounded corners or disable (0) rounded corners. */ +static int cornerradius = 3; /* Radius of rounded corners, 10 is the default. */ + +/* Tag preview options */ +static int tagpreview = 1; /* Enable tag previews */ +static int mousepreview = 1; /* Display tag previews if hovering over a tag */ +static int scalepreview = 4; /* Size of tag preview */ + +/* Window spawning options */ +static int spawncd = 1; /* Spawn clients in the working directory of the focused client */ +static int attachdirection = 3; /* 0 default, 1 above, 2 aside, 3 below, 4 bottom, 5 top */ +static int swallowclients = 1; /* Swallow windows or not */ +static int swallowfloating = 1; /* Swallow floating windows by default */ +static int centerfloating = 1; /* Center floating windows by default */ +static int startontag = 1; /* Start on a tag or not? */ +static int floatscratchpad = 0; /* Float the scratchpad window on hide (1/0) */ + +/* Font options */ +static char font[] = { "fontawesome:size=8" }; /* What font should we use? */ +static char font2[] = { "NotoSans-Regular:size=8:antialiasing=true" }; /* Second font */ +static char font3[] = { "Noto Emoji:size=8" }; /* Third font */ +static const char *fonts[] = { font, font2, font3 }; +static char defaultname[] = ""; /* What to print when a status bar is not running */ + +/* Bar options */ +static char status[] = "status"; /* Status bar to use, speedwmblocks for speedwmblocks, slstatus for slstatus, etc. */ + +/* Alternate bar + * + * Some users may prefer to use a different bar than what speedwm provides. + * Polybar users for example might want to use their bspwm bar in speedwm. + * If so, follow these instructions: + * - Use xprop to find the class (WM_CLASS) and add that to altbarclass + * - Set altbarcmd to the bar you want to use. + * - Set altbar to 1 (0 will disable it). + * - Recompile speedwm and restart it using 'speedwm_run -r' + * + * If you didn't do any changes to the source code, just run speedwm_run -r' + * If you use Polybar, the default settings here will work, just set altbar to 1 and set altbarcmd to 'polybar &'. + */ +static char *altbarcmd = ""; +static int altbar = 0; /* 1 means use non-speedwm status bar */ +static char *altbarclass = "Polybar"; /* Alternate bar class name */ +static char *alttrayname = "tray"; /* Polybar tray instance name */ + +/* Misc */ +static char shell[] = "/bin/sh"; /* Shell to use */ +static char *scratchpadcmd[] = {"s", TERMINAL, "-t", "scratchpad", NULL}; +static int movefullscreenmon = 1; /* Move fullscreen windows to another monitor */ +static int lockfullscreen = 1; +static int warpcursor = 1; /* Warp cursor when switching client/monitor */ +static int pertag = 1; /* Use different mfact and layout for each layout */ +static int wmclass = 1; /* Enable (1) workaround for when a class cannot be grabbed */ +static int clicktofocus = 0; /* Click to focus a window, instead of focusing when touched. (1/0) */ + +/* Icon options */ +static int sizeicon = 10; /* size of the icon */ +static int spacingicon = 5; /* spacing between the title and icon */ + +/* Bar options */ +static int barheight = 5; /* Bar height in px, 0 = calculate automatically */ +static int barposition = 1; /* Bar position. Top: 0, Bottom: 1 */ +static int barpaddingv = 0; /* How much padding to have vertically in pixels */ +static int barpaddingh = 0; /* How much padding to have horizontally in pixels */ +static int leftlayout = 1; /* Layout indicator on the left (1) or on the right (0) */ + +/* Tag text options */ +static int underline = 0; /* Underline tags (1) or not (0) */ +static int underlineall = 0; /* 1 to show underline on all tags, 0 for just the active ones */ +static int underlinepad = 5; /* Horizontal padding between the underline and tag */ +static int underlinestroke = 2; /* Height of the underline */ +static int underlinevoffset = 0; /* How far above the bottom of the bar the line should appear */ + +/* Bar item options */ +static int showbar = 1; /* Show the bar or not? */ +static int hidelayout = 0; /* Hide layout indicator (1) or show (0) */ +static int hidetitle = 0; /* Hide title (1) or show (0) */ +static int hidestatus = 0; /* Hide status bar (1) or show (0) */ +static int hidetags = 0; /* Hide status bar (1) or show (0) */ +static int hidefloating = 0; /* Hide floating indicator (1) or show (0) */ +static int hidesticky = 0; /* Hide sticky indicator (1) or show (0) */ + +/* Status options */ +static int statusallmons = 1; /* Draw status bar on all monitors */ + +/* Layout options */ +static unsigned int monocleclientcount = 0; /* Display client count in the Monocle layout */ +static unsigned int monoclecount = 0; /* Display focused client and number of total clients in the Monocle layout */ +static char monocleformat[] = "[%d/%d]"; /* Format of the monocle count. Unsigned integer monoclecount must be set to 1 for this to be used. */ +static unsigned int deckcount = 0; /* Display deck count in the deck layout */ +static char deckformat[] = "D %d"; /* Format of the deck count. deckcount must be set to 1 for this to be used. */ +static unsigned int stairpx = 20; /* depth of the stairs layout */ +static int stairdirection = 1; /* 0: left-aligned, 1: right-aligned */ +static int stairsamesize = 1; /* 1 means shrink all the staired windows to the same size */ +static int defaultlayout = 1; /* Reset layout when there is only one client visible */ +static int forcevsplit = 1; /* Force two clients to always split vertically in nrowgrid layout */ + +/* Bar colors */ +static char col_background[] = "#222222"; /* speedwm dark bg & slstatus bg */ +static char col_backgroundmid[] = "#222222"; /* speedwm middle background */ + +/* General text colors */ +static char col_textnorm[] = "#bbbbbb"; /* application title bar/font for norm */ +static char col_textsel[] = "#eeeeee"; /* speedwm text/font for selected */ + +/* Window border colors */ +static char col_windowbordernorm[] = "#000000"; /* speedwm norm window border */ +static char col_windowbordersel[] = "#eeeeee"; /* speedwm sel window border */ + +/* Tag text/background colors */ +static char col_tag1[] = "#333333"; /* tag 1 background */ +static char col_tag1_text[] = "#eeeeee"; /* tag 1 text (fg) */ +static char col_tag2[] = "#333333"; /* tag 2 background */ +static char col_tag2_text[] = "#eeeeee"; /* tag 2 text (fg) */ +static char col_tag3[] = "#333333"; /* tag 3 background */ +static char col_tag3_text[] = "#eeeeee"; /* tag 3 text (fg) */ +static char col_tag4[] = "#333333"; /* tag 4 background */ +static char col_tag4_text[] = "#eeeeee"; /* tag 4 text (fg) */ +static char col_tag5[] = "#333333"; /* tag 5 background */ +static char col_tag5_text[] = "#eeeeee"; /* tag 5 text (fg) */ +static char col_tag6[] = "#333333"; /* tag 6 background */ +static char col_tag6_text[] = "#eeeeee"; /* tag 6 text (fg) */ +static char col_tag7[] = "#333333"; /* tag 7 background */ +static char col_tag7_text[] = "#eeeeee"; /* tag 7 text (fg) */ +static char col_tag8[] = "#333333"; /* tag 8 background */ +static char col_tag8_text[] = "#eeeeee"; /* tag 8 text (fg) */ +static char col_tag9[] = "#333333"; /* tag 9 background */ +static char col_tag9_text[] = "#eeeeee"; /* tag 9 text (fg) */ + +/* Layout indicator colors */ +static char col_layouttext[] = "#000000"; /* Layout indicator text (fg) */ +static char col_layoutbgnorm[] = "#222222"; /* Layout indicator background (norm) */ +static char col_layoutbgsel[] = "#bbbbbb"; /* Layout indicator background (norm) */ + +/* status2d colors */ +static char col_status0[] = "#131210"; +static char col_status1[] = "#bf616a"; +static char col_status2[] = "#A16F9D"; +static char col_status3[] = "#68ABAA"; +static char col_status4[] = "#A89F93"; +static char col_status5[] = "#D3A99B"; +static char col_status6[] = "#AFC9AC"; +static char col_status7[] = "#eae1cb"; +static char col_status8[] = "#a39d8e"; +static char col_status9[] = "#6D5E8E"; +static char col_status10[] = "#A16F9D"; +static char col_status11[] = "#D3A99B"; +static char col_status12[] = "#AFC9AC"; +static char col_status13[] = "#eae1cb"; +static char col_status14[] = "#6D5E8E"; +static char col_status15[] = "#ffffff"; + +/* Opacity settings + * These options set the opacity of the status bar modules. + * They can be anything from 0 (fully transparent) to 255 (fully opaque). + * + * You can also set OPAQUE and TRANSPARENT which are the same as 0 and 255 respectively. + */ +static const unsigned int baropacity = 160; /* Opacity for the overall bar */ +static const unsigned int layoutopacity = 160; /* Opacity for the layout indicator */ +static const unsigned int tagnormopacity = OPAQUE; /* Opacity for other tags */ +static const unsigned int tagselopacity = OPAQUE; /* Opacity for the selected tag */ +static const unsigned int normtitleopacity = 160; /* Opacity for all other windows in the speedwm bar */ +static const unsigned int seltitleopacity = 160; /* Opacity for the focused window in the speedwm bar */ +static const unsigned int hiddenopacity = 0; /* Opacity for hidden/minimized windows */ +static const unsigned int statusopacity = 180; /* Opacity for speedwm status bar */ + +/* Tag text options */ +static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +/* NOTE: alttags will not be used if taglabels are used. */ +static char *alttags[] = { "[1]", "[2]", "[3]", "[4]", "[5]", "[6]", "[7]", "[8]", "[9]" }; + +/* Icon misc */ +static XPoint stickyicon[] = { {0,0}, {4,0}, {4,8}, {2,6}, {0,8}, {0,0} }; /* represents the icon as an array of vertices */ +static XPoint stickyiconbb = {4,8}; /* defines the bottom right corner of the polygon's bounding box (speeds up scaling) */ + +/* You have reached the end of the configuration file. + * For keybinds, see 'keybinds.h' + * Once you're done editing, run 'make clean install'. + */ diff --git a/rules.h b/rules.h new file mode 100644 index 0000000..1c7ef12 --- /dev/null +++ b/rules.h @@ -0,0 +1,38 @@ +/* Rules + * Any applications defined here must follow the rules specified. + * The user can get the class by running a program like xprop (must be installed by the user) and then selecting the software. + * + * - tags mask: Disable tags where the CLASS, INSTANCE or TITLE will spawn. (X << Y) + * - isfloating: Choose whether a CLASS, INSTANCE or TITLE will spawn as floating. (1/0) + * - ispermanent: Whether or not you can kill a client + * - isterminal: Whether or not the CLASS, INSTANCE or TITLE is a terminal or not (1/0) + * - noswallow: Whether or not the CLASS, INSTANCE or TITLE gets swallowed by a terminal or not (1/0) + * - ignoretransient: Whether or not to ignore transient windows for CLASS, INSTANCE or TITLE (1/0) + * + * For the rest, leave as they currently are and copy them for new rules. + * Once you're done with your edits, run 'make clean install'. + * + ***************************************************************/ +static const Rule rules[] = { + /* class instance title tags mask isfloating ispermanent isterminal noswallow monitor unmanaged ignoretransient scratch key */ + { TERMINAL_CLASS, NULL, NULL, 0, 0, 0, 1, 0, -1, 0, 0, 0 }, + { TERMINAL_CLASS, NULL, "sxiv", 0, 0, 0, 0, 0, -1, 0, 0, 0 }, + { TERMINAL_CLASS, NULL, FILEMANAGER_CLASS, 0, 0, 0, 1, 0, -1, 0, 0, 0 }, + { PDF_CLASS, NULL, NULL, 0, 0, 0, 0, 0, -1, 0, 0, 0 }, + { TERMINAL_CLASS, NULL, EDITOR, 0, 0, 0, 1, 1, -1, 0, 0, 0 }, + { TERMINAL_CLASS, NULL, MUSIC, 0, 0, 0, 0, 1, -1, 0, 0, 0 }, + { TERMINAL_CLASS, NULL, broken, 0, 0, 0, 0, 1, -1, 0, 0, 0 }, + { TERMINAL_CLASS, NULL, MIXER, 0, 1, 0, 0, 1, -1, 0, 0, 0 }, + { BROWSER_CLASS, NULL, NULL, 0, 0, 0, 0, 0, -1, 0, 0, 0 }, + { "mpv", NULL, NULL, 0, 0, 0, 0, 0, -1, 0, 0, 0 }, + { "tabbed", NULL, NULL, 0, 0, 0, 0, 1, -1, 0, 0, 0 }, + { "trayer", NULL, NULL, 0, 1, 1, 0, 1, -1, 1, 0, 0 }, + { NULL, NULL, "CustomizeMii 3.11 by Leathl", 0, 1, 0, 0, 1, -1, 0, 0, 0 }, + { NULL, NULL, "Picture-in-Picture", 0, 1, 0, 0, 1, -1, 0, 0, 0 }, + { NULL, NULL, "About GNU IceCat", 0, 1, 0, 0, 1, -1, 0, 0, 0 }, + { NULL, NULL, "About LibreWolf", 0, 1, 0, 0, 1, -1, 0, 0, 0 }, + { NULL, NULL, "About Mozilla Firefox", 0, 1, 0, 0, 1, -1, 0, 0, 0 }, + { NULL, NULL, "Save Image", 0, 1, 0, 0, 1, -1, 0, 0, 0 }, + { NULL, NULL, "scratchpad", 0, 0, 0, -1, 's' }, +}; + diff --git a/scripts/libxftfix b/scripts/libxftfix new file mode 100755 index 0000000..0dedeed --- /dev/null +++ b/scripts/libxftfix @@ -0,0 +1,34 @@ +#!/bin/sh +# libxft_bgra install script. + +echo "WARNING: This script has not been properly tested and may not work as expected or at all. Expect to install libXft-bgra manually if necessary." + +libxft_arch() { + ls /usr/bin/git + git clone https://aur.archlinux.org/libxft-bgra && echo "Cloned libXft-bgra AUR repository." + cd libxft-bgra && echo "Changed directory to libxft_bgra" + makepkg -si && echo "Installed libXft-bgra" + cd .. + rm -r libxft-bgra && echo "Cleaned." + echo "Done installing libXft-bgra." +} + +libxft_gentoo() { + mkdir -p /etc/portage/patches/x11-libs/libXft && echo "Created directory." + curl -o /etc/portage/patches/x11-libs/libXft/bgra.diff https://raw.githubusercontent.com/speediegq/spDE-resources/main/1.diff && echo "Downloaded patch." + emerge x11-libs/libXft && echo "Installed libXft-bgra." +} + +libxft_source() { + git clone https://github.com/uditkarode/libxft-bgra && cd libxft-bgra && echo "Cloned and changed directory." + sh autogen.sh --sysconfdir=/etc --prefix=/usr --mandir=/usr/share/man && echo "Ran autogen script" + make install && echo "Compiled libXft-bgra." + cd .. && rm -r libxft-bgra && echo "Cleaned." + echo "Done installing libXft-bgra." +} + +case "$1" in +"-gentoo") libxft_gentoo ;; +"-arch") libxft_arch ;; +"-source") libxft_source ;; +esac diff --git a/scripts/speedwm-audioctrl b/scripts/speedwm-audioctrl new file mode 100755 index 0000000..c55868e --- /dev/null +++ b/scripts/speedwm-audioctrl @@ -0,0 +1,142 @@ +#!/bin/sh +# speedwm-audioctrl +# This simple shell script handles audio controls for speedwm. +# Run speedwm-audioctrl -help for more information! +# License: GPLv3. + +BINDIR=$(cat /usr/share/speedwm-bindir) + +MUTE() { + # Mute for pulseaudio/pipewire + if [ "$AUDIO" = "pulse" ]; then + if [ -e "${BINDIR}pulsemixer" ]; then + pulsemixer --toggle-mute + if [ -e "${BINDIR}notify-send" ]; then + if [ "$remute" = "" ]; then + notify-send " Toggled mute" + fi + fi + else + amixer set Master toggle + if [ -e "${BINDIR}notify-send" ]; then + if [ "$remute" = "" ]; then + notify-send " Toggled mute" + fi + fi + fi + fi +} + +RAISE() { + if [ "$AUDIO" = "pulse" ]; then + if [ -e "${BINDIR}pulsemixer" ]; then + if [ "$(pulsemixer --get-volume | awk '{ print $1 }')" = "100" ]; then + a=$a + else + pulsemixer --change-volume +7 + test ${BINDIR}notify-send && notify-send " $(pulsemixer --get-volume | awk '{ print $1 }')%" + fi + fi + else + if [ "$(amixer -c 0 get Master | tail -n 1 | sed -r "s/.*\[(.*)%\].*/\1/")" = "100" ]; then + a=$a + else + amixer -c 0 set Master 7%+ + ls ${BINDIR}notify-send && notify-send " $(amixer -c 0 get Master | tail -n 1 | sed -r "s/.*\[(.*)%\].*/\1/")%" + fi + fi +} + +LOWER() { + if [ "$AUDIO" = "pulse" ]; then + if [ -e "${BINDIR}pulsemixer" ]; then + if [ "$(pulsemixer --get-volume | awk '{ print $1 }')" = "0" ]; then + a=$a + else + pulsemixer --change-volume -7 + test ${BINDIR}notify-send && notify-send " $(pulsemixer --get-volume | awk '{ print $1 }')%" + fi + + fi + else + if [ "$(amixer -c 0 get Master | tail -n 1 | sed -r "s/.*\[(.*)%\].*/\1/")" = "0" ]; then + a=$a + else + amixer -c 0 set Master 7%- + test ${BINDIR}notify-send && notify-send " $(amixer -c 0 get Master | tail -n 1 | sed -r "s/.*\[(.*)%\].*/\1/")%" + fi + fi +} + +GETVOL() { + if [ "$AUDIO" = "pulse" ]; then + if [ -e "${BINDIR}pulsemixer" ]; then + echo "$(pulsemixer --get-volume | awk '{ print $1 }')%" + fi + else + echo "$(amixer -c 0 get Master | tail -n 1 | sed -r "s/.*\[(.*)%\].*/\1/")%" + fi +} + +GETMUTE() { + if [ "$AUDIO" = "pulse" ]; then + if [ -e "${BINDIR}pulsemixer" ]; then + pulsemixer --get-mute | grep 1 > /dev/null && echo "Muted" + pulsemixer --get-mute | grep 0 > /dev/null && echo "Not muted" + fi + fi +} + +SWITCH() { + ls $HOME/.local/share/audioctrl-status || echo "0" > $HOME/.local/share/audioctrl-status + if [ "$(cat $HOME/.local/share/audioctrl-status)" = "0" ]; then + amixer -c 0 sset 'Auto-Mute Mode' Enabled ; echo "1" > $HOME/.local/share/audioctrl-status + if [ -e "${BINDIR}notify-send" ]; then + notify-send " Switched to headphones." + fi + else + amixer -c 0 sset 'Auto-Mute Mode' Disabled ; echo "0" > $HOME/.local/share/audioctrl-status + if [ -e "${BINDIR}notify-send" ]; then + notify-send " Switched to speakers." + fi + fi +} + +RUNMIXER() { + if [ "$AUDIO" = "pulse" ]; then + if [ -e "${BINDIR}pulsemixer" ]; then + pulsemixer + elif [ -e "${BINDIR}alsamixer" ]; then + alsamixer + fi + fi +} + +AUDIO=$2 +case "$2" in +"") AUDIO=alsa +if [ -e "${BINDIR}pulsemixer" ]; then + AUDIO=pulse +fi ;; +esac + +# Update status +UPDATESTATUS() { + pkill status && status & +} + +case "$1" in +"-mute") MUTE && GETMUTE > /tmp/speedwm-audioctrl-mutestatus ;; +"-remute") remute=true ; MUTE && GETMUTE > /tmp/speedwm-audioctrl-mutestatus ; MUTE && GETMUTE > /tmp/speedwm-audioctrl-mutestatus ; remute="" ; exit 0 ;; +"-raise") RAISE ;; +"-lower") LOWER ;; +"-switch") SWITCH && exit 0 ;; +"-getvol") GETVOL && exit 0 ;; +"-getmute") GETMUTE > /tmp/speedwm-audioctrl-mutestatus && exit 0 ;; +"-getbackend") printf "$AUDIO\n" && exit 0 ;; +"-runmixer") RUNMIXER && exit 0 ;; +"-help") printf "speedwm-audioctrl\n-mute | Toggle mute\n-raise | Raise the volume by 7\n-lower | Lower the volume by 7\n-switch | Toggle output\n-getvol | Get current volume in percentage\n-getmute | Get mute status\n-getbackend | Get audio backend (ALSA, PulseAudio, etc.)\n-runmixer | Run the audio mixer detected on the system\n-remute | Mute and unmute.\n-help | Display this help screen\nNo arguments | Display this help screen\n"; exit 0 ;; +"") $0 -help && exit 0 ;; +esac + +pgrep -x status && UPDATESTATUS diff --git a/scripts/speedwm-btctrl b/scripts/speedwm-btctrl new file mode 100755 index 0000000..08544c2 --- /dev/null +++ b/scripts/speedwm-btctrl @@ -0,0 +1,115 @@ +#!/bin/sh +# speedwm-btctrl +# Basic dmenu/other run launcher Bluetooth manager written for speedwm. +# Licensed under GNU GPLv3. + +# Check stuff +CHECK() { + case "$RUNLAUNCHER" in + "") RUNLAUNCHER=dmenu ;; + esac + + BINDIR=$(cat /usr/share/speedwm-bindir) # Set binary directory to the contents of this file for NixOS support +} + +CHECK + +# Help +HELP() { + printf "\n1. Turn on your device\n2. Make sure the bluetooth service is running. If it is not, start it.\n3. Press the pair button on your device\n4. Select your device in the list of devices. If it does not show up, select 'Refresh'.\n5. Select 'Pair' and then optionally 'Trust' to save it in the list of devices.\n6. And finally, select 'Connect' to connect the device." | $RUNLAUNCHER -l 12 -g 1 -p 'How to use' + $0 && exit 0 +} + +# Enable bluetooth and scan for devices +ENABLE_BT() { + bluetoothctl power on > /dev/null && echo "Power: On" + bluetoothctl scan on & # Start scanning for devices + USEROPT_1="$(printf "$(bluetoothctl devices | cut -d\ -f3-)\n------\nRefresh\nHelp\nExit" | $RUNLAUNCHER -l 12 -g 1 -p "Select a device")" + + # Check what to do + case "$USEROPT_1" in + "") $0 && exit 0 ;; + "Refresh") ENABLE_BT ;; + "Exit") exit 0 ;; + "Help") HELP && $0 && exit 0 ;; + esac + + SELDEVICE_MAC="$(bluetoothctl devices | grep "$USEROPT_1$" | awk '{ print $2 }')" + echo "$SELDEVICE_MAC" + + # Check if a MAC was grabbed + case "$SELDEVICE_MAC" in + "") echo "Could not get MAC" && exit 1 ;; + esac +} + +ENABLE_BT + +# List options for the device +LIST_OPTIONS() { + USEROPT_2="$(echo "-- Options --\nConnect\nDisconnect\n-- Toggle --\nPair\nRemove\nTrust\nUntrust\n--\nExit\nHelp" | $RUNLAUNCHER -g 1 -l 20 -p "What do you want to do with this device?" | awk '{ print $1 }')" +} + +LIST_OPTIONS + +# Trust device +TRUST() { + bluetoothctl trust $SELDEVICE_MAC +} + +# Pair device +PAIR() { + bluetoothctl pair $SELDEVICE_MAC +} + +# Remove device +REMOVE() { + bluetoothctl remove $SELDEVICE_MAC +} + +UNTRUST() { + bluetoothctl untrust $SELDEVICE_MAC +} + +# Connect +CONNECT() { + bluetoothctl connect $SELDEVICE_MAC +} + +# Disconnect +DISCONNECT() { + bluetoothctl disconnect $SELDEVICE_MAC +} + +# Notification when connecting +NOTIFY_CONNECT() { + notify-send " Connected to $USEROPT_1" +} + +# Notification when disconnecting +NOTIFY_DISCONNECT() { + notify-send " Disconnected from $USEROPT_1" +} + +NOTIFY_REMOVE() { + notify-send " Removed $USEROPT_1" +} + +# Perform actions as per user choice +PERFORM() { + case "$USEROPT_2" in + "Trust") TRUST && LIST_OPTIONS ;; + "Pair") PAIR && LIST_OPTIONS ;; + "Connect") CONNECT && NOTIFY_CONNECT && LIST_OPTIONS ;; + "Disconnect") DISCONNECT && NOTIFY_DISCONNECT && LIST_OPTIONS ;; + "Untrust") UNTRUST && LIST_OPTIONS ;; + "Remove") REMOVE && NOTIFY_REMOVE && LIST_OPTIONS ;; + "Exit") exit 0 ;; + "Help") HELP ;; + "") $0 && exit 0 ;; + esac +} + +PERFORM + +$0 && exit 0 diff --git a/scripts/speedwm-compatcheck b/scripts/speedwm-compatcheck new file mode 100755 index 0000000..46c2aba --- /dev/null +++ b/scripts/speedwm-compatcheck @@ -0,0 +1,68 @@ +#!/bin/sh +# speedwm-compatcheck +# compatibility checker used by 'make' when compiling speedwm. + +BINDIR=$(cat /usr/share/speedwm-bindir) + +# Check if xrdb exists +if [ -e "${BINDIR}xrdb" ]; then + xrdb_exists=true +else + printf "\nWARNING: xrdb was not found. .Xresources and Pywal support will not work." +fi + +# Check if wmctrl exists +if [ -e "${BINDIR}wmctrl" ]; then + wmctrl_exists=true +else + printf "\nWARNING: wmctrl was not found. Window management using 'Alt+Tab' will not work." +fi + +# Check if xsetroot exists +if [ -e "${BINDIR}xsetroot" ]; then + xsetroot_exists=true +else + printf "\nerror: ${BINDIR}xsetroot was not found\n" && exit 1 +fi + +# Check if xwallpaper exists +if [ -e "${BINDIR}xwallpaper" ]; then + xwallpaper_exists=true +else + printf "\nWARNING: xwallpaper was not found. This means setting your wallpaper and Pywal support won't work." +fi + +# Check if xmodmap exists +if [ -e "${BINDIR}xmodmap" ]; then + xmodmap_exists=true +else + printf "\nWARNING: xmodmap was not found. This means the Alt+Tab script will not be able to remap Alt+hjkl temporarily." +fi + +# Check if trayer exists +if [ -e "${BINDIR}trayer" ]; then + trayer_exists=true +elif [ -e "${BINDIR}trayer-srg" ]; then + trayer_exists=true + trayer_srg=true +fi + +# Check if srg or not. +if [ "$trayer_exists" = "false" ]; then + printf "\nWARNING: trayer was not found. This means the systray will not be available.\n" +elif [ "$trayer_srg" = "true" ]; then + printf "\nWARNING: Before trayer-srg can be used instead of trayer, you must edit ~/.config/speedwm-de/systray/config and set USE_SRG to true.\n" +fi + +# Check if dmenu exists +dmenu -v > /dev/null || printf "\nWARNING: dmenu was not found. This means running applications will potentially be very inconvenient. It also means most bundled scripts will NOT work unless modified to work with rofi. See help for more information" + +# Check if slock exists +slock -v > /dev/null || printf "\nWARNING: slock was not found. This means the shutdown menu can't lock your screen." + +result="$(echo "$xrdb_exists$wmctrl_exists$xsetroot_exists$xwallpaper_exists$xmodmap_exists$trayer_exists")" + +case "$result" in +"truetruetruetruetruetrue") printf "\nAll dependencies were found.\n\n" ;; +"") printf "\nSome dependencies could not be found." ;; +esac diff --git a/scripts/speedwm-core b/scripts/speedwm-core new file mode 100755 index 0000000..fd59a0f --- /dev/null +++ b/scripts/speedwm-core @@ -0,0 +1,183 @@ +#!/bin/sh +# speedwm-core +# This script handles the systray and other things necessary for keybinds. + +# Set binary directory if not set already. +if [ -e "/usr/share/dwm-bindir" ]; then + BINDIR=$(cat /usr/share/dwm-bindir) # Set binary directory to the contents of this variable. +else + BINDIR="/usr/bin/" +fi + +TINT="#222222" # Default tint +EDGE="top" # Part of the screen where your systray will be placed +GET_EDGE_AUTO="true" # Get edge automatically (true/false) +SYSTRAY_COL=1 # When using pywal, set color to background (1) or backgroundmid (0) +USE_SRG=false # Use $SYSTRAY-srg (available in Gentoo repos and AUR as $SYSTRAY-srg-git) instead of $SYSTRAY adding more features (true/false) +USE_DEFAULT_TINT=false # Use default $TINT or use $SYSTRAY_COL +ALPHA=0 # Opacity of the systray (0 is opaque, 255 is fully transparent) +HIDE_BAR_WHEN_SYSTRAY_SHOW=false # Hide the dwm bar when the systray is running. +ALPHA_BARHIDDEN=255 # Opacity of the systray when the dwm bar is hidden (0 is opaque, 255 is fully transparent) +TRANSPARENT=true # Transparent or not (true/false) +EXPORTDIR=$HOME/.config/dwm-de/systray # Config directory +HEIGHT=18 # Height of the systray +WIDTH=30 # Width of the systray +mkdir -p $EXPORTDIR + +# Load config if available and override settings +if [ -e "$EXPORTDIR/config" ]; then + . $EXPORTDIR/config +else + printf "TINT=$TINT # Tint when pywal is not used." > $EXPORTDIR/config + printf "\nEDGE=$EDGE # Part of the screen where your systray will be placed" >> $EXPORTDIR/config + printf "\nGET_EDGE_AUTO=$GET_EDGE_AUTO # Get edge automatically (true/false)" >> $EXPORTDIR/config + printf "\nSYSTRAY_COL=$SYSTRAY_COL # When using pywal, set color to background (1) or backgroundmid (0)" >> $EXPORTDIR/config + printf "\nALPHA=$ALPHA # Opacity of the systray (0 is opaque, 255 is fully transparent)" >> $EXPORTDIR/config + printf "\nALPHA_BARHIDDEN=$ALPHA_BARHIDDEN # Opacity of the systray when the dwm bar is hidden (0 is opaque, 255 is fully transparent)" >> $EXPORTDIR/config + printf "\nTRANSPARENT=true # Transparent or not (true/false)" >> $EXPORTDIR/config + printf "\nHIDE_BAR_WHEN_SYSTRAY_SHOW=$HIDE_BAR_WHEN_SYSTRAY_SHOW # Hide the dwm bar when the systray is running." >> $EXPORTDIR/config + printf "\nUSE_SRG=$USE_SRG # Use $SYSTRAY-srg (available in Gentoo repos and AUR as $SYSTRAY-srg-git) instead of $SYSTRAY adding more features (true/false)" >> $EXPORTDIR/config + printf "\nUSE_DEFAULT_TINT=$USE_DEFAULT_TINT # Use default $TINT or use $SYSTRAY_COL" >> $EXPORTDIR/config + printf "\nHEIGHT=$HEIGHT # Height of the systray" >> $EXPORTDIR/config + printf "\nWIDTH=$WIDTH # Width of the systray\n" >> $EXPORTDIR/config +fi + +TINT="$(echo $TINT | sed "s|#||g")" + +OPT=$1 + +# Pywal tint +PYWAL_TINT() { + # Pywal tint + if [ "$USE_DEFAULT_TINT" = "false" ]; then + if [ -e "$HOME/.cache/wal/colors" ]; then + if [ "$SYSTRAY_COL" = "1" ]; then + TINT=$(sed -n 1,1p $HOME/.cache/wal/colors | sed "s|#||g") + elif [ "$SYSTRAY_COL" = "0" ]; then + TINT=$(sed -n 13,13p $HOME/.cache/wal/colors | sed "s|#||g") + fi + fi + fi +} + +# Set edge +SETEDGE() { + if [ "$GET_EDGE_AUTO" = "true" ]; then + if [ -e "/usr/share/dwm-topbar" ]; then + EDGE="top" + else + EDGE="bottom" + fi + fi +} + +# Set alpha +SETALPHA() { + if [ "$HIDE_BAR_WHEN_SYSTRAY_SHOW" = "true" ]; then + ALPHA=$ALPHA_BARHIDDEN + else + ALPHA=$ALPHA + fi +} + +PYWAL_TINT +SETEDGE +SETALPHA + +# Set systray +if [ "$USE_SRG" = "true" ]; then + SYSTRAY=trayer-srg +else + SYSTRAY=trayer +fi + +USE() { + # Toggle + TOGGLE() { + if [ -e "/tmp/systray-started" ]; then + OPT="-stop" + else + OPT="-start" + fi + + } + + # Update dwm + UPDATE() { + + FAIL_SOURCEDOESNOTEXIST() { + echo "The dwm source code directory could not be located. This means an automatic update cannot be performed. Clone a new build (instructions on https://speedie.gq/dwm) and update manually." + exit 1 + } + + FAIL_NOT_INSTALLED_USING_GIT() { + echo "Your build of dwm was likely not installed through Git (maybe installed using a tarball?) so therefore the update cannot continue. Clone a new build (instructions on https://speedie.gq/dwm) and update manually." + exit 1 + } + + FAIL_NO_AUTH() { + echo "Failed to authenticate." + exit 1 + } + + SOURCEDIR=$(cat /usr/share/dwm-sourcedir) + test $SOURCEDIR || FAIL_SOURCEDOESNOTEXIST + + # Check if Git is available + if [ -e "${BINDIR}git" ]; then + echo "Git found!" + else + echo "Git was not found, therefore the dwm update cannot continue." ; exit 1 + fi + + cd $SOURCEDIR + git stash + git pull || FAIL_NOT_INSTALLED_USING_GIT + clear + + echo "dwm requires root permissions briefly to run the following command:\nmake clean install\nEnter your password to allow this.\n" + su -c "make clean install" || FAIL_NO_AUTH + echo 'dwm has been updated!' + + exit 0 + } + + case "$OPT" in + "-toggle") TOGGLE ;; + "-update-dwm") UPDATE ;; + esac + + case "$OPT" in + "-start") $SYSTRAY --edge $EDGE --align right --SetDockType true --SetPartialStrut true --expand true --width $WIDTH --transparent $TRANSPARENT --alpha $ALPHA --tint 0x${TINT} --height $HEIGHT & + touch /tmp/systray-started ; pgrep -x status && pkill status ; status & + if [ "$HIDE_BAR_WHEN_SYSTRAY_SHOW" = "true" ]; then + xsetroot -name "fsignal:23" + fi ;; + "-stop") rm -f /tmp/systray-started ; pkill $SYSTRAY ; pgrep -x status && pkill status ; status & + if [ "$HIDE_BAR_WHEN_SYSTRAY_SHOW" = "true" ]; then + xsetroot -name "fsignal:23" + fi ;; + "") echo "dwm-core\n-start | Start the systray and restart status\n-stop | Stop the systray and restart status\n-toggle | Toggle systray and restart status\n-update-dwm | Update dwm using git\n-curl-weather | Curl wttr.in in a readable format\n-curl-cheatsheet | Ask the user what cheatsheet they want and then curl it." ;; + esac +} + +case "$OPT" in +"-curl-weather") clear ; curl -s wttr.in | head -n 38 | tail -n 37 && sleep 60 ;; +"-curl-cheatsheet") clear ; echo -n "What cheatsheet do you want to view?\nExample: vim\n > " ; read CHEATSHEET ; curl -s cheat.sh/$(echo $CHEATSHEET | tr '[:upper:]' '[:lower:]') > /tmp/cheatsheet +grep "Unknown topic." /tmp/cheatsheet && $0 -curl-cheatsheet && exit 0 +less /tmp/cheatsheet ;; +esac + +# Start systray +if [ -e "${BINDIR}$SYSTRAY" ]; then + if [ -e "/usr/share/dwm-nopadding" ]; then + if [ -e "$HOME/.config/dwm-de/dwmrc" ]; then + grep "vertpad" $HOME/.config/dwm-de/dwmrc > /tmp/out + grep "0" /tmp/out > /dev/null && USE + else + USE + fi + fi +else + echo "Trayer not installed, exiting." && exit 1 +fi diff --git a/scripts/speedwm-dfmpeg b/scripts/speedwm-dfmpeg new file mode 100755 index 0000000..1c70881 --- /dev/null +++ b/scripts/speedwm-dfmpeg @@ -0,0 +1,177 @@ +#!/bin/sh +# speedwm-dfmpeg +# dmenu/other run launcher GUI for recording your screen with ffmpeg +# +# Licensed under MIT, written by speedie +# Modified version for speedwm (available https://speedie.gq/speedwm) +# https://speedie.gq/dmenu-scripts + +# speedwm specfic dotfile directory, probably don't change +DOTDIR=$HOME/.config/speedwm-de/dfmpeg +NOMKCONFIG=false # Do not create a config file if this is set to true + +# Set runlauncher +case "$RUNLAUNCHER" in +"") RUNLAUNCHER=dmenu +esac + +# Write default configuration +defaultConfig() { + RESOLUTION="1920x1080" # The resolution to record in + AUDIO_DEVICE="alsa" # How to capture audio (alsa/pulseaudio) + FRAME_RATE="60" # Frame rate to capture in. + RECORD_DEVICE="x11grab" # Probably do not change. + OUTPUT_PATH="$HOME/Recordings" # Path where videos will be saved. + OUTPUT_FORMAT="mp4" # What format to use + OUTPUT_FILENAME="dfmpeg-output" # File name of the output. Probably don't need to change. + ENCODE=true # Encode or not (true/false) + ENCODE_CODEC=libx264 # Codec to encode in + ENCODE_PRESET=ultrafast # ffmpeg preset. Available options: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow + WH=":0.0" # Width and height, no need to change, defaults should work. + TERM="$TERMINAL" # Terminal to use when editing the configuration file. + EDITOR="vim" # Editor to edit the config file with + MEDIA_PLAYER="mpv" # Media player to play videos in +} + +defaultConfig + +USECONFIG() { + echo "Using $DOTDIR/config for configuration!" && . $DOTDIR/config +} + +MKCONFIG() { + if [ -e "$DOTDIR/config" ]; then + USECONFIG + else + if [ "$NOMKCONFIG" = "false" ]; then + mkdir -p $DOTDIR + printf "RESOLUTION=$RESOLUTION # The resolution to capture in" > $DOTDIR/config + printf "\nAUDIO_DEVICE=$AUDIO_DEVICE # How to capture audio (alsa/pulseaudio)" >> $DOTDIR/config + printf "\nFRAME_RATE=$FRAME_RATE # Frame rate to capture in." >> $DOTDIR/config + printf "\nOUTPUT_PATH=$OUTPUT_PATH # Directory where captures will be saved." >> $DOTDIR/config + printf "\nOUTPUT_FORMAT=$OUTPUT_FORMAT # What format to use" >> $DOTDIR/config + printf "\n#OUTPUT_FILENAME='filename'" >> $DOTDIR/config + printf "\n\nRECORD_DEVICE=$RECORD_DEVICE" >> $DOTDIR/config + printf "\nWH=$WH" >> $DOTDIR/config + printf "\nTERM=$TERMINAL # Terminal to use when editing the config file" >> $DOTDIR/config + printf "\nEDITOR=$EDITOR # Editor to edit the config file" >> $DOTDIR/config + printf "\nMEDIA_PLAYER=$MEDIA_PLAYER # Media player to play videos in" >> $DOTDIR/config + printf "\nENCODE=$ENCODE # Encode or not on 'Stop' (true/false)" >> $DOTDIR/config + printf "\nENCODE_CODEC=$ENCODE_CODEC # Codec to encode in (libx264 is default)" >> $DOTDIR/config + printf "\nENCODE_PRESET=$ENCODE_PRESET # ffmpeg preset. Available options: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow" >> $DOTDIR/config + fi + fi +} + +MKCONFIG + +OUTPUT_FILENAME="$OUTPUT_PATH/$OUTPUT_FILENAME.$OUTPUT_FORMAT" + +# Create the output path if it does not exist +if [ -e "$OUTPUT_PATH" ]; then + a=$a +else + mkdir -p $OUTPUT_PATH +fi + +# Check if we should encode the video or not. +if [ "$ENCODE" = "false" ]; then + startrec="ffmpeg -f $RECORD_DEVICE -s $RESOLUTION -i $WH -f $AUDIO_DEVICE -r $FRAME_RATE -i default $OUTPUT_FILENAME" + startrec_no_audio="ffmpeg -y -f $RECORD_DEVICE -s $RESOLUTION -r $FRAME_RATE -i $WH $OUTPUT_FILENAME" +else + startrec="ffmpeg -y -f x11grab -framerate $FRAME_RATE -s $RESOLUTION -i $WH -f $AUDIO_DEVICE -i default -r 30 -c:v $ENCODE_CODEC -crf 0 -preset ultrafast -c:a aac $OUTPUT_FILENAME" + startrec_no_audio="ffmpeg -y -f x11grab -framerate $FRAME_RATE -s $RESOLUTION -i $WH -r 30 -c:v $ENCODE_CODEC -crf 0 -preset ultrafast -c:a aac $OUTPUT_FILENAME" +fi + +# Call this function to encode a video manually. +# /tmp/rec.$OUTPUT_FORMAT must be the file. Consider copying it there and then copying it back. +encode() { + if [ "$ENCODE" = "true" ]; then + cp $(cat /tmp/outputfilename) /tmp/rec.$OUTPUT_FORMAT + ffmpeg -i /tmp/rec.$OUTPUT_FORMAT -c:v $ENCODE_CODEC -preset $ENCODE_PRESET -threads 0 -b:v -pass 1 -f $OUTPUT_FORMAT -qp 0 + cp /tmp/rec.$OUTPUT_FORMAT $(cat /tmp/outputfilename) + rm -f "/tmp/rec.$OUTPUT_FORMAT" + fi +} + +# Hardcoded version number +dfmpeg_ver="2022-08-06-r1" +about_screen="dfmpeg $dfmpeg_ver." + +# About screen text +about_screen_2="Licensed under MIT, written by speedie.gq and jornmann." +about_screen_3="Modified for https://speedie.gq/speedwm" +about_screen_4="https://speedie.gq/dmenu-scripts" + +# Conversion function +convert() { + # Convert mp4 to mp3 + mp42mp3() { + COPT=$(printf 'Enter a path\n------\nExit' | $RUNLAUNCHER -l 3 -p 'Convert which file?') + case "$COPT" in + "Enter a path") $0 && exit 0 ;; + "------") $0 && exit 0 ;; + "Exit") exit 0 ;; + "") $0 && exit 0 ;; + esac + + echo $COPT | grep ".mp4" && ffmpeg -i $COPT -vn $(echo $COPT | sed "s/.mp4/.mp3/g") + } + + case "$(printf '.mp4 to .mp3\n------\n..\nExit' | $RUNLAUNCHER -l 40 -p 'Convertion options')" in + ".mp4 to .mp3") mp42mp3 ;; + "Exit") exit 0 ;; + "------") $0 && exit 0 ;; + "..") $0 && exit 0 ;; + esac +} + +encodeopts() { + + case "$(printf 'Encode last recording\nEncode video\n------\n..\nExit' | $RUNLAUNCHER -l 40 -p 'Encoding options')" in + "Encode last recording") encode ; exit 0 ;; + "Encode video") echo "$(echo "" | $RUNLAUNCHER -l 1 -g 1 -p "What file do you want to encode?")" > /tmp/outputfilename && encode ; exit 0 ;; + "------") $0 && exit 0 ;; + "Exit") exit 0 ;; + "..") $0 && exit 0 ;; + "") $0 && exit 0 ;; + esac +} + +# More options +MORE_OPTIONS() { + case "$(printf 'Encode\nConvert\nConfigure\n------\n..\nExit' | $RUNLAUNCHER -l 40 -p 'More options')" in + "Encode last recording") encode ; exit 0 ;; + "Encode video") echo "$(echo "" | $RUNLAUNCHER -l 1 -g 1 -p "What file do you want to encode?")" > /tmp/outputfilename && encode ; exit 0 ;; + "Encode") encodeopts ; exit 0 ;; + "Configure") $TERM $EDITOR $DOTDIR/config ; exit 0 ;; + "Convert") convert ; exit 0 ;; + "") $0 && exit 0 ;; + "..") $0 && exit 0 ;; + "------") $0 && exit 0 ;; + esac +} + +case "$(printf 'Start\nStop\nStart without audio\nPlay last\n------\nMore options\nAbout\nExit' | $RUNLAUNCHER -l 40 -p 'Record your screen:')" in + "Exit") STATUS=idle && exit 0 ;; + "Start") echo "$OUTPUT_FILENAME" > /tmp/outputfilename && STATUS=recording && touch /tmp/dfmpeg-recording + pgrep -x status && pkill status && status & + $startrec && exit 0 ;; + "Stop") pkill ffmpeg ; rm /tmp/dfmpeg-recording ; STATUS=idle + pgrep -x status && pkill status && status & ;; + "Start without audio") echo "$OUTPUT_FILENAME" > /tmp/outputfilename && STATUS=recording && touch /tmp/dfmpeg-recording + pgrep -x status && pkill status && status & + $startrec_no_audio && exit 0 ;; + "Play last") DFMPEG_STATUS=idle && $MEDIA_PLAYER $(cat /tmp/outputfilename) && exit 0 ;; + "More options") MORE_OPTIONS && exit 0 ;; + "") $0 && exit 0 ;; + "About") + echo $about_screen > /tmp/dfmpeg_about + echo $about_screen_2 >> /tmp/dfmpeg_about + echo $about_screen_3 >> /tmp/dfmpeg_about + echo $about_screen_4 >> /tmp/dfmpeg_about + cat /tmp/dfmpeg_about | $RUNLAUNCHER -l 40 -g 1 + ;; +esac + +exit 0 # This fixes a small bug. diff --git a/scripts/speedwm-dm b/scripts/speedwm-dm new file mode 100755 index 0000000..f644e11 --- /dev/null +++ b/scripts/speedwm-dm @@ -0,0 +1,27 @@ +#!/bin/sh +# speedwm-dm +# Create a .desktop file for people who use display managers. + +# Delete the topbar file +rm -f /usr/share/speedwm-topbar + +# Delete the padding file +rm -f /usr/share/speedwm-nopadding + +# Make the directory for the .desktop file if it does not exist +mkdir -p /usr/share/xsessions + +# Write the .desktop entry +printf "[Desktop Entry]\nEncoding=UTF-8\nName=speedwm\nComment=Dynamic window manager\nExec=/usr/bin/speedwm_run\nIcon=speedwm\nType=XSession" > /usr/share/xsessions/speedwm.desktop + +# If the bar is on the top, touch /usr/share/speedwm-topbar +TOPBAR=$(grep "barposition" options.h | grep "1" | awk '{ print $5 }') +PADDING=$(grep "barpaddingv" options.h | grep "0" | awk '{ print $5 }') +echo $TOPBAR | grep "1" && touch /usr/share/speedwm-topbar +echo $PADDING | grep "0" && touch /usr/share/speedwm-nopadding +echo $(pwd) > /usr/share/speedwm-sourcedir + +# Copy .Xresources file +cp docs/example.Xresources /usr/share/example.Xresources + +exit 0 diff --git a/scripts/speedwm-help b/scripts/speedwm-help new file mode 100755 index 0000000..7a820d8 --- /dev/null +++ b/scripts/speedwm-help @@ -0,0 +1,83 @@ +#!/bin/bash +# speedwm-help +# Documentation generator/list for speedwm. +# Licensed under the GNU GPLv3 free software license. + +# No color if NOCOLOR is exported as true. +if [ "$NOCOLOR" != "true" ]; then + COL2="$(printf '\033[0;35m')" # Color 2 + COL1="$(printf '\033[0m')" # Color 1 +fi + +ARG1=$1 +ARG2=$2 +ARGINF=$@ + +if [ "$ARG2" = "" ]; then + VIEWER=less +else + VIEWER="$ARG2" + echo "Using $ARG2 as output." | sed "s|-o|stdout|g" +fi + +case "$VIEWER" in +"-o") VIEWER="cat" ;; +esac + +echo "${0}. Run '$0 -h' to see a list of arguments or to jump to a specific section.\n$(cat /usr/share/speedwm-about /usr/share/speedwm-about-2 /usr/share/speedwm-bindlist /usr/share/speedwm-deplist /usr/share/speedwm-about-3 /usr/share/speedwm-xresources /usr/share/speedwm-about-4 /usr/share/speedwm-fsignal /usr/share/speedwm-about-5 | sed 's|xsetroot -name "fsignal:||g' | sed 's|"||g')" > /tmp/speedwm-doc + +case "$ARG1" in +"-w") tail -n $(expr $(cat /tmp/speedwm-doc | wc -l) "-" 1) /tmp/speedwm-doc > ../readme ;; +"-wmd") tail -n $(expr $(cat /tmp/speedwm-doc | wc -l) "-" 1) /tmp/speedwm-doc | \ + sed "s|-- speedwm --|# speedwm|g" | \ + sed "s|-- What is speedwm? --|## What is speedwm?|g" | \ + sed "s|-- Installation --|## Installation|g" | \ + sed "s|-- Layouts --|## Layouts|g" | \ + sed "s|-- Patches --|## Patches|g" | \ + sed "s|-- Keybinds --|## Keybinds|g" | \ + sed "s|-- Applications --|### Applications|g" | \ + sed "s|-- Navigation --|### Navigation|g" | \ + sed "s|-- Chained keybinds --|### Chained keybinds|g" | \ + sed "s|-- Extras --|### Extras|g" | \ + sed "s|-- Dependencies --|## Dependencies|g" | \ + sed "s|-- Features --|## Features|g" | \ + sed "s|-- Software --|## Software|g" | \ + sed "s|-- Important --|## Important|g" | \ + sed "s|-- .Xresources and Pywal --|## .Xresources and Pywal|g" | \ + sed "s|-- Fsignal --|## Fsignal|g" | \ + sed "s|-- Switching run launcher --|## Switching run launcher|g" > ../README.md ;; +"-whtml") tail -n $(expr $(cat /tmp/speedwm-doc | wc -l) "-" 1) /tmp/speedwm-doc | \ + sed "s|-- speedwm --|# speedwm|g" | \ + sed "s|-- What is speedwm? --|## What is speedwm?|g" | \ + sed "s|-- Installation --|## Installation|g" | \ + sed "s|-- Layouts --|## Layouts|g" | \ + sed "s|-- Patches --|## Patches|g" | \ + sed "s|-- Keybinds --|## Keybinds|g" | \ + sed "s|-- Applications --|### Applications|g" | \ + sed "s|-- Navigation --|### Navigation|g" | \ + sed "s|-- Chained keybinds --|### Chained keybinds|g" | \ + sed "s|-- Mouse --|### Mouse|g" | \ + sed "s|-- Extras --|### Extras|g" | \ + sed "s|-- Dependencies --|## Dependencies|g" | \ + sed "s|-- Features --|## Features|g" | \ + sed "s|-- Software --|## Software|g" | \ + sed "s|-- Important --|## Important|g" | \ + sed "s|-- .Xresources and Pywal --|## .Xresources and Pywal|g" | \ + sed "s|-- Fsignal --|## Fsignal|g" | \ + sed "s|-- Switching run launcher --|## Switching run launcher|g" > ../README.md + markdown ../README.md > ../readme.html || printf "\nmarkdown-to-html not found, install using:\nnpm install markdown-to-html -g\n" && exit 1 ;; +"-whtml-wtemplate") test ../readme.html && cat ../docs/speedwm.html.template.1 ../readme.html ../docs/speedwm.html.template.2 >> ../speedwm.html && exit 0 + printf "\nYou need to $0 -whtml first." ; exit 1 ;; +"") $0 -h && exit 0 ;; +"-h") printf "speedwm help\n\nNo arguments to view this list of arguments.\n-h | View this list of arguments.\n-a | View everything.\n-1 | What is speedwm?\n-2 | List of all keybinds\n-3 | List of all dependencies\n-4 | .Xresources/Pywal information.\n-5 | Fsignal information\n-6 | Switching run launcher\n-7 | Installation\n-8 | Layouts\n-9| Important information.\n-w | Write documentation to ../readme\n-wmd | Write documentation (Markdown)\n-whtml | Write documentation (HTML) using markdown-to-html.\n-whtml-wtemplate | Combine template 1, output of -whtml and template 2 (from docs/) to create a full HTML document.\n\nIf second argument is -o, the requested output will be sent to stdout.\n-o can be replaced with a text editor or reader of your choice (such as less, vim, emacs, etc.)\n"; exit 0 ;; +"-1") sed "s|--|${COL2}--${COL1}|g" /usr/share/speedwm-about | head -n 10 > /tmp/stdout ; $VIEWER /tmp/stdout ;; +"-2") cat /usr/share/speedwm-about-2 /usr/share/speedwm-bindlist | sed "s|--|${COL2}--${COL1}|g" > /tmp/stdout ; $VIEWER /tmp/stdout ;; +"-3") sed "s|--|${COL2}--${COL1}|g" /usr/share/speedwm-deplist > /tmp/stdout ; $VIEWER /tmp/stdout ;; +"-4") cat /usr/share/speedwm-about-3 /usr/share/speedwm-xresources | tail -n 59 | sed "s|--|${COL2}--${COL1}|g" > /tmp/stdout ; $VIEWER /tmp/stdout ;; +"-5") cat /usr/share/speedwm-about-4 /usr/share/speedwm-fsignal | sed "s|--|${COL2}--${COL1}|g" | sed 's|xsetroot -name "fsignal:||g' | sed 's|"||g' > /tmp/stdout ; $VIEWER /tmp/stdout ;; +"-6") sed "s|--|${COL2}--${COL1}|g" /usr/share/speedwm-about-5 > /tmp/stdout ; $VIEWER /tmp/stdout ;; +"-7") sed "s|--|${COL2}--${COL1}|g" /usr/share/speedwm-about | head -n 22 | tail -n 11 > /tmp/stdout ; $VIEWER /tmp/stdout ;; +"-8") sed "s|--|${COL2}--${COL1}|g" /usr/share/speedwm-about | tail -n 22 | head -n 19 > /tmp/stdout ; $VIEWER /tmp/stdout ;; +"-9") sed "s|--|${COL2}--${COL1}|g" /usr/share/speedwm-about-3 | head -n 24 > /tmp/stdout ; $VIEWER /tmp/stdout ;; +"-a") sed "s|--|${COL2}--${COL1}|g" /tmp/speedwm-doc > /tmp/stdout ; $VIEWER /tmp/stdout ;; +esac diff --git a/scripts/speedwm-mkpage b/scripts/speedwm-mkpage new file mode 100755 index 0000000..bc6a5fe --- /dev/null +++ b/scripts/speedwm-mkpage @@ -0,0 +1,53 @@ +#!/bin/sh +# speedwm-mkpage + +cd scripts + +MKPAGE() { + ./speedwm-help -whtml + ./speedwm-help -whtml-wtemplate + cd .. + rm -f readme.html + echo "Created speedwm.html" +} + +PUSHPAGE() { + HTMLDIR=$(cat /tmp/speedwm-htmldir) + cd $HTMLDIR || exit 1 + git commit -a -m "speedwm-mkpage: Uploaded new page." + git push && exit 0 + echo "Could not git push." + exit 1 +} + +MKHTML() { + ./speedwm-help -whtml + cd .. + echo "Created readme.html" +} + +MKMD() { + ./speedwm-help -wmd + cd .. + echo "Created README.md" +} + +MK() { + ./speedwm-help -w + cd .. + echo "Created readme" +} + +# Perform actions based on user input +case "$1" in +"") echo "speedwm-mkpage\nNo arguments | Show the help screen\n-h | Show the help screen\n-mkpage | Create full page with templates from docs/\n-mkhtml | Create HTML document based on speedwm documentation\n-mkmd | Create markdown file (usually for use with GitHub repositories)\n-mk | Copy the output of speedwm-help -a to 'readme'.\n-ulspeedwm | Auto-push speedwm changes\n" ; exit 0 ;; +"-help") $0 && exit 0 ;; +"-h") $0 && exit 0 ;; +"-mkpage") MKPAGE && exit 0 ;; +"-mkhtml") MKHTML && exit 0 ;; +"-mkmd") MKMD && exit 0 ;; +"-mk") MK && exit 0 ;; +"-page-push") PUSHPAGE && exit 0 ;; +"-ulspeedwm") cd .. && git add * && git commit -a -m "speedwm-mkpage: Auto-pushed speedwm changes!" && git push && exit 0 ;; +esac + diff --git a/scripts/speedwm-netctrl b/scripts/speedwm-netctrl new file mode 100755 index 0000000..7406e0e --- /dev/null +++ b/scripts/speedwm-netctrl @@ -0,0 +1,70 @@ +#!/bin/sh +# speedwm-netctrl +# dmenu/other run launcher GUI for iwd +# Licensed under the GNU GPLv3 free software license. + +case "$RUNLAUNCHER" in +"") RUNLAUNCHER=dmenu ;; +esac + +BINDIR=$(cat /usr/share/speedwm-bindir) + +test ${BINDIR}iwctl || exit 0 + +# Get the device used to connect. +getdevice() { + seldevice=$1 + if [ -e "$HOME/.local/share/seldevice" ]; then + seldevice=$(cat $HOME/.local/share/seldevice) + else + case "$1" in + "") devices="$(printf "\n$(ip link)" | awk '{ print $2 }' | awk 'NR%2==0' | sed "s/://g")" + seldevice=$(echo $devices | sed 's/ /\n/g' | $RUNLAUNCHER -p "What device do you want to connect with?" -l 10 -g 1) && echo $seldevice > $HOME/.local/share/seldevice ;; + esac + fi + + case "$seldevice" in + "") exit 0 ;; + esac +} + +connectwifi() { + iwctl station $seldevice scan + network="$(printf "$(iwctl station $seldevice get-networks | sed -n 6,20p | sed "s|>||g" | awk '{ print $1 }')\n------\nExit" | sed "s| |No networks found|g" | $RUNLAUNCHER -l 10 -g 1 -p "Select a network to connect to" | sed "s|------||g" | sed "s|No networks found| |g")" + + case "$network" in + "") $0 && exit 0 ;; + "Exit") exit 0 ;; + esac + + iwctl station $seldevice get-networks | grep "$network" > /dev/null || exit 1 + + connect_pass() { + PASSPHRASE=$(printf "\n" | $RUNLAUNCHER -p "This network is protected. Enter the passphrase." -l 1 -g 1) + case "$PASSPHRASE" in + "") exit 0 ;; + esac + + printf "$network\n" >> $HOME/.local/share/networks + + iwctl station $seldevice connect $network -P $PASSPHRASE || connect_pass + if [ -e "${BINDIR}notify-send" ]; then + notify-send " Connected to $network!" + fi + exit 0 + } + + connect_no_pass() { + iwctl station $seldevice connect $network || exit 0 + if [ -e "${BINDIR}notify-send" ]; then + notify-send " Connected to $network!" + fi + exit 0 + } + + grep $network $HOME/.local/share/networks && connect_no_pass && exit 0 + iwctl station $seldevice get-networks | grep psk && connect_pass && exit 0 +} + +getdevice && connectwifi +pkill status && status & diff --git a/scripts/speedwm-pdfopen b/scripts/speedwm-pdfopen new file mode 100755 index 0000000..676c760 --- /dev/null +++ b/scripts/speedwm-pdfopen @@ -0,0 +1,17 @@ +#!/bin/sh +# speedwm-pdfopen +# list of pdfs for the user to open in dmenu. + +cd $1 + +case "$RUNLAUNCHER" in +"") RUNLAUNCHER=dmenu ;; +esac + +SELPDF=$(ls -Ap1 *.pdf | $RUNLAUNCHER -g 1 -l 50 -p "What PDF do you want to open?") + +case "$SELPDF" in +"") exit 0 ;; +esac + +$2 "$1/$SELPDF" diff --git a/scripts/speedwm-screenshotutil b/scripts/speedwm-screenshotutil new file mode 100755 index 0000000..64fff49 --- /dev/null +++ b/scripts/speedwm-screenshotutil @@ -0,0 +1,133 @@ +#!/bin/sh +# speedwm-screenshotutil +# Built in screenshot utility for my build of speedwm +# Requires curl, maim and xclip. +# +# curl snippet by nezbednik, thank you! + +rm -f /tmp/screenshot* + +case "$RUNLAUNCHER" in +"") RUNLAUNCHER=dmenu ;; +esac + +if [ -e "/usr/share/speedwm-bindir" ]; then + BINDIR=$(cat /usr/share/speedwm-bindir) +else + BINDIR="/usr/bin/" +fi + +EXPORTDIR=$HOME/.config/speedwm-de/screenshotutil + +mkdir -p $EXPORTDIR + +# Config +SHOWCURSOR="true" # Show cursor or not (true/false) +DEFAULTSCREENSHOTPATH=$HOME/Screenshots # Default screenshot path +FORMAT="+%T_%D" # Screenshot format + +if [ -e "$EXPORTDIR/config" ]; then + . $EXPORTDIR/config +else + printf "SHOWCURSOR=$SHOWCURSOR # Show cursor or not (true/false)" > $EXPORTDIR/config + printf "\nDEFAULTSCREENSHOTPATH=$DEFAULTSCREENSHOTPATH # Where screenshots are saved\n" >> $EXPORTDIR/config + echo "FORMAT='$FORMAT' # Where screenshots are saved. : and / will be replaced with _ and nothing respectively." >> $EXPORTDIR/config +fi + +case "$SHOWCURSOR" in +"true") ARG2="" ;; +"false") ARG2=u ;; +esac + +# Help argument action +HELP() { + printf "speedwm-screenshotutil\n-t | Wait seconds and then take the screenshot." + printf "\n-f | Take a full screen screenshot instead of selecting manually." + printf "\n-s | Take a screenshot, allowing the user to select a section manually." + printf "\n-o | Select a file and allow the user to perform actions with it." + printf "\n-tf | Wait seconds and then take a full screen screenshot." + printf "\n-h | View this help screen" + printf "\nNo arguments will print this screen.\n" +} + +# Sleep argument (-t) +if [ "$1" = "-t" ]; then + sleep $2 && maim -s${ARG2}B > /tmp/screenshot-$DATE || exit 0 +fi + +# Open argument (-o) +if [ "$1" = "-o" ]; then + cat $2 > /tmp/screenshot-$DATE +fi + +# Sleep and full argument (-tf) +if [ "$1" = "-tf" ]; then + sleep $2 && maim -${ARG2}B > /tmp/screenshot-$DATE || exit 0 +elif [ "$1" = "-ft" ]; then + sleep $2 && maim -${ARG2}B > /tmp/screenshot-$DATE || exit 0 +fi + +# Full argument (-f) +if [ "$1" = "" ]; then + HELP ; exit 0 +else + if [ "$1" = "-f" ]; then + if [ -e "/tmp/screenshot-$DATE" ]; then + exists=true + else + maim -${ARG2}B > /tmp/screenshot-$DATE || exit 0 + fi + fi + + if [ "$1" = "-s" ]; then + if [ -e "/tmp/screenshot-$DATE" ]; then + exists=true + else + maim -s${ARG2}B > /tmp/screenshot-$DATE || exit 0 + fi + fi +fi + +# Help argument (-h) +if [ "$1" = "-h" ]; then + HELP ; exit 0 +fi + +# User action +if [ -e "/tmp/screenshot-$DATE" ]; then + U_INPUT="$(printf "Image\nURL\nSave" | $RUNLAUNCHER -l 3 -p "Copy to clipboard as an: ")" +else + exit 0 +fi + +# Send notification for URL +SENDNOTIF_URL() { + if [ -e "${BINDIR}notify-send" ]; then + notify-send "Screenshot copied to clipboard." + fi +} + +# Send notifcation for local image save +SENDNOTIF_SAVE() { + if [ -e "${BINDIR}notify-send" ]; then + notify-send "Screenshot saved to $SAVEDIR." + fi +} + +# Send notification for image copied to clipboard +SENDNOTIF_IMG() { + if [ -e "${BINDIR}notify-send" ]; then + notify-send "Screenshot copied to clipboard." + fi +} + +mkdir -p $DEFAULTSCREENSHOTPATH + +# Perform actions based on user input +case "$U_INPUT" in +"Image") cat /tmp/screenshot-$DATE | xclip -selection clipboard -t image/png && SENDNOTIF_IMG ;; +"URL") printf "\n" | xclip -selection clipboard && curl -s -F source=@"/tmp/screenshot-$DATE" -F "type=file" -F "action=upload" "https://imgbb.com/json" | sed "s/\\\\//g; s/\"/\\n/g" | grep -m 1 -A 2 url | tail -n 1 | xclip -selection clipboard && SENDNOTIF_URL ;; +"Save") SAVEDIR=$(printf "$DEFAULTSCREENSHOTPATH/screenshot-$(date "$FORMAT" | sed "s|:|-|g" | sed "s|/||g").png" | $RUNLAUNCHER -l 1 -g 1 -p "Where do you want to save it? (Including filename)") && cat /tmp/screenshot-$DATE > $SAVEDIR && SENDNOTIF_SAVE ;; +esac + +rm -f /tmp/screenshot* # Remove the screenshots diff --git a/scripts/speedwm-shutdown b/scripts/speedwm-shutdown new file mode 100755 index 0000000..3673fb1 --- /dev/null +++ b/scripts/speedwm-shutdown @@ -0,0 +1,91 @@ +#!/bin/sh +# speedwm-shutdwon +# This simple script uses $RUNLAUNCHER to ask the user what action they want to perform. +# +# NOTE: You must either permit nopass for poweroff and reboot commands for your user or simply enable nopass entirely!! + +case "$RUNLAUNCHER" in +"") RUNLAUNCHER=dmenu ;; +esac + +case "$TERMINAL" in +"") TERMINAL=st ;; +esac + +if [ -e "/usr/share/speedwm-bindir" ]; then + BINDIR=$(cat /usr/share/speedwm-bindir) +else + BINDIR="/usr/bin/" +fi + +EXPORTDIR=$HOME/.config/speedwm-de/powermenu +mkdir -p $EXPORTDIR + +ls ${BINDIR}sudo && PERM=sudo +ls ${BINDIR}doas && PERM=doas +ls ${BINDIR}rootdo && PERM=rootdo + +case "$PERM" in +"") PERM=su ;; +esac + +COL_NORMAL_TEXT_NORMAL="#ffffff" +COL_NORMAL_TEXT_SELECTED="#ffffff" +COL_NORMAL_BACKGROUND_NORMAL="#333333" +COL_NORMAL_BACKGROUND_SELECTED="#222222" +COL_WARNING_TEXT_NORMAL="#ffffff" +COL_WARNING_TEXT_SELECTED="#000000" +COL_WARNING_BACKGROUND_NORMAL="#DC143C" +COL_WARNING_BACKGROUND_SELECTED="#8b0000" +LOCK_TEXT="" + +# Load config if available and override settings +if [ -e "$EXPORTDIR/config" ]; then + . $EXPORTDIR/config +else + printf "COL_NORMAL_TEXT_NORMAL=$COL_NORMAL_TEXT_NORMAL # Text color for not selected options (Regular prompt)" > $EXPORTDIR/config + printf "\nCOL_NORMAL_TEXT_SELECTED=$COL_NORMAL_TEXT_SELECTED # Text color for selected option (Regular prompt)" >> $EXPORTDIR/config + printf "\nCOL_NORMAL_BACKGROUND_NORMAL=$COL_NORMAL_BACKGROUND_NORMAL # Background color for not selected options (Regular prompt)" >> $EXPORTDIR/config + printf "\nCOL_NORMAL_BACKGROUND_SELECTED=$COL_NORMAL_BACKGROUND_SELECTED # Background color for selected option (Regular prompt)" >> $EXPORTDIR/config + printf "\nCOL_WARNING_TEXT_NORMAL=$COL_WARNING_TEXT_NORMAL # Text color for not selected options (Warning prompt)" >> $EXPORTDIR/config + printf "\nCOL_WARNING_TEXT_SELECTED=$COL_WARNING_TEXT_SELECTED # Text color for selected option (Warning prompt)" >> $EXPORTDIR/config + printf "\nCOL_WARNING_BACKGROUND_NORMAL=$COL_WARNING_BACKGROUND_NORMAL # Background color for not selected options (Warning prompt)" >> $EXPORTDIR/config + printf "\nCOL_WARNING_BACKGROUND_SELECTED=$COL_WARNING_BACKGROUND_SELECTED # Background color for selected option (Warning prompt)" >> $EXPORTDIR/config + printf "\nPERM=$PERM # Root access tool (sudo, doas, etc.). Must support nopass for poweroff and reboot commands." >> $EXPORTDIR/config + printf "\nLOCK_TEXT=$LOCK_TEXT # Text to display on the lock screen" >> $EXPORTDIR/config +fi + +case "$LOCK_TEXT" in +"") MESSAGEFLAG="" ;; +esac + +MOREOPTS() { + USER_OPT3=$(printf "Restart speedwm\nEnd task\nspeedwm command\nUpdate speedwm\n.." | $RUNLAUNCHER -l 4 -g 1 -p "What do you want to do?" -nb "$COL_NORMAL_BACKGROUND_NORMAL" -sb "$COL_NORMAL_BACKGROUND_SELECTED" -nf "$COL_NORMAL_TEXT_NORMAL" -sf "$COL_NORMAL_TEXT_SELECTED") + + case "$USER_OPT3" in + "") exit 0 ;; + "Restart speedwm") speedwm_run -r && exit 0 ;; + "End task") ENDTASK=$(printf "\n" | $RUNLAUNCHER -l 1 -p "What task to end?" -nb "$COL_NORMAL_BACKGROUND_NORMAL" -sb "$COL_NORMAL_BACKGROUND_SELECTED" -nf "$COL_NORMAL_TEXT_NORMAL" -sf "$COL_NORMAL_TEXT_SELECTED") && pkill $ENDTASK && exit 0 ;; + "speedwm command") speedwmutils && exit 0 ;; + "Update speedwm") $TERMINAL -e speedwm-core -update-speedwm ; exit 0 ;; + "..") $0 && exit 0 ;; + esac +} + +USER_OPT1=$(printf "Shutdown\nReboot\nExit\nLock\nMore" | $RUNLAUNCHER -l 5 -p "What do you want to do?" -nb "$COL_NORMAL_BACKGROUND_NORMAL" -sb "$COL_NORMAL_BACKGROUND_SELECTED" -nf "$COL_NORMAL_TEXT_NORMAL" -sf "$COL_NORMAL_TEXT_SELECTED") + +case "$USER_OPT1" in +"Shutdown") CMD="$PERM poweroff" ;; +"Reboot") CMD="$PERM reboot" ;; +"Exit") exit 0 ;; +"Lock") CMD="slock $MESSAGEFLAG '$LOCK_TEXT'" ;; +"More") MOREOPTS ;; +"") exit 0 ;; +esac + +USER_OPT2=$(printf "Yes\nNo" | $RUNLAUNCHER -l 2 -p "Are you sure?" -nb "$COL_WARNING_BACKGROUND_NORMAL" -sb "$COL_WARNING_BACKGROUND_SELECTED" -nf "$COL_WARNING_TEXT_NORMAL" -sf "$COL_WARNING_TEXT_SELECTED") +case "$USER_OPT2" in +"Yes") $CMD ;; +"No") exit 0 ;; +"") exit 0 ;; +esac diff --git a/scripts/speedwm-swal b/scripts/speedwm-swal new file mode 100755 index 0000000..72fe26b --- /dev/null +++ b/scripts/speedwm-swal @@ -0,0 +1,347 @@ +#!/bin/sh +# speedwm-swal +# wallpaper utility written for speedwm. +# Licensed under the GNU GPLv3 free software license. + +# Default configuration, should be fine for most users. +SWAL_DEFAULT_CONFIG() +{ + SWAL_WALLPAPERDIR=$HOME/Wallpapers + SWAL_OLDWALLPAPERDIR=$SWAL_WALLPAPERDIR + case "$RUNLAUNCHER" in + "") RUNLAUNCHER=dmenu ;; + esac + SWAL_OPT=xwallpaper + SWAL_BINDIR=$BINDIR + SWAL_EXPORTDIR=$HOME/.config/speedwm-de/swal # Config directory + SWAL_FSIGNAL_RELOAD_XRESOURCES=18 # Fsignal signum when reloading + SWAL_IMAGE_VIEWER="sxiv -to" # Image viewer to use () + SWAL_ASK_WAL=false # Ask whether or not to use Pywal if possible (true/false) + SWAL_RANDOMIZE=false # Select a random wallpaper on login +} + +SWAL_MKCONFIG() { + mkdir -p $SWAL_EXPORTDIR + touch $SWAL_EXPORTDIR/config + SWAL_ASK_WAL_NEW=$SWAL_ASK_WAL + SWAL_WALLPAPERDIR_NEW=$SWAL_WALLPAPERDIR + + SWAL_CHANGE_WALLPAPERDIR() { + SWAL_WALLPAPERDIR_NEW=$(printf "Your wallpaper directory" | $RUNLAUNCHER -g 1 -p "Where are all your wallpapers stored?" -l 1) + } + + SWAL_CHANGE_ASK_WAL() { + wal -v > /dev/null && SWAL_ASK_WAL_NEW=$(printf "Yes\nNo" | $RUNLAUNCHER -g 1 -p "Ask whether or not to use Pywal?" -l 2 | sed "s|No|false|g" | sed "s|Yes|true|g") + } + + SWAL_CHANGE_IMAGE_VIEWER() { + SWAL_IMAGE_VIEWER=$(printf "" | $RUNLAUNCHER -g 1 -p "What image viewer do you want to use for previews?" -l 1) + } + + SWAL_CHANGE_RANDOMIZE() { + SWAL_RANDOMIZE=$(printf "Yes\nNo" | $RUNLAUNCHER -g 1 -p "Pick a random wallpaper on login?" -l 2 | sed "s|No|false|g" | sed "s|Yes|true|g") + } + + USER_OPT_ACTION=$(printf "Wallpaper directory\nPywal\nImage Viewer\nRandomize\n------\nExit" | $RUNLAUNCHER -g 1 -l 10 -p "What setting do you want to change?") + + case "$USER_OPT_ACTION" in + "") $0 && exit 0 ;; + "Wallpaper directory") SWAL_CHANGE_WALLPAPERDIR && CHANGED=true ;; + "Pywal") SWAL_CHANGE_ASK_WAL && CHANGED=true ;; + "Image Viewer") SWAL_CHANGE_IMAGE_VIEWER && CHANGED=true ;; + "Randomize") SWAL_CHANGE_RANDOMIZE && CHANGED=true ;; + "Exit") exit 0 ;; + "------") $0 && exit 0 ;; + esac + + case "$CHANGED" in + "") $0 && exit 0 ;; + esac + + case "$SWAL_ASK_WAL_NEW" in + "true") ASKED=true ;; + "false") ASKED=true ;; + esac + + if [ "$ASKED" = "" ]; then + $0 && exit 0 + fi + + printf "SWAL_WALLPAPERDIR=$SWAL_WALLPAPERDIR_NEW" > $SWAL_EXPORTDIR/config + printf "\nRUNLAUNCHER=$RUNLAUNCHER" >> $SWAL_EXPORTDIR/config + printf "\nSWAL_OPT=$SWAL_OPT" >> $SWAL_EXPORTDIR/config + printf "\nSWAL_BINDIR=$SWAL_BINDIR" >> $SWAL_EXPORTDIR/config + printf "\nSWAL_EXPORTDIR=$SWAL_EXPORTDIR" >> $SWAL_EXPORTDIR/config + printf "\nSWAL_IMAGE_VIEWER='$SWAL_IMAGE_VIEWER'" >> $SWAL_EXPORTDIR/config + printf "\nSWAL_ASK_WAL=$SWAL_ASK_WAL_NEW" >> $SWAL_EXPORTDIR/config + printf "\nSWAL_RANDOMIZE=$SWAL_RANDOMIZE" >> $SWAL_EXPORTDIR/config +} + +BINDIR=$(cat /usr/share/speedwm-bindir) +SWAL_OLDWALLPAPERDIR=$SWAL_WALLPAPERDIR + +# Source things +SWAL_SOURCE() +{ + ls "$SWAL_EXPORTDIR/config" || SWAL_MKCONFIG + . "$SWAL_EXPORTDIR/config" || rm -f "$SWAL_EXPORTDIR/config" +} + +SWAL_CHECK() +{ + if [ "$SWAL_IMAGE_VIEWER" = "" ]; then + ls "$SWAL_BINDIR/feh" && SWAL_IMAGE_VIEWER=feh + ls "$SWAL_BINDIR/sxiv" && SWAL_IMAGE_VIEWER=sxiv + fi + + ls "$SWAL_BINDIR/xwallpaper" || SWAL_DIE +} + +SWAL_DIE() +{ + SWAL_EXIT=1 && exit 1 +} + +SWAL_HELP() { + echo "swal is a minimal xwallpaper frontend for dmenu/rofi." + echo "-w /path/to/wallpaper.png | Set the wallpaper specified." + echo "-h | View this help screen." + echo "-f X | fsignal signum to reload .Xresources (with reloadxresources, fsignal and .Xresources speedwm patches)" +} + +SWAL_USAGE() { + echo "To set a wallpaper, you must create a configuration. To do this, select 'Options' in the menu and set all the options to your liking." + echo "The image viewer must be set to an image viewer that supports printing the filename to stdout. If you use sxiv, set the image viewer to 'sxiv -o'." + echo "In the case of sxiv, This means you can press 'm' when previewing an image to select it and then set it." + echo "Once you've selected your image using the method you prefer, select how you want to set it." + echo "Then your pywal colors will reload, and the wallpaper will be set." + echo "For more information, see speedwm-help -a." +} + +SWAL_SELWALLPAPER() +{ + case "$NEW1" in + "") ls /tmp/swal_cpath || SWAL_SELWALLPAPER="$(printf "$(ls "$SWAL_WALLPAPERDIR")\n------\n..\nPreview\nPrevious\nRandom\nFavorites\nAdd used wallpaper to favorites\nOptions\nHelp\nExit" | $RUNLAUNCHER -g 1 -l 100 -p "What wallpaper would you like to set?")" ;; + "-w") SWAL_SELWALLPAPER="$(basename "$NEW2")" && touch /tmp/swal_cpath ;; + "-h") SWAL_HELP | $RUNLAUNCHER -g 1 -l 90 ;; + "-f") SWAL_FSIGNAL_RELOAD_XRESOURCES="$NEW2" ;; + esac + + case "$NEW3" in + "-w") SWAL_SELWALLPAPER="$(basename "$NEW4")" && touch /tmp/swal_cpath ;; + "-h") SWAL_HELP | $RUNLAUNCHER -g 1 -l 90 ;; + "-f") SWAL_FSIGNAL_RELOAD_XRESOURCES="$NEW4" ;; + esac + + case "$NEW5" in + "-w") SWAL_SELWALLPAPER="$(basename "$NEW6")" && touch /tmp/swal_cpath ;; + "-h") SWAL_HELP | $RUNLAUNCHER -g 1 -l 90 ;; + "-f") SWAL_FSIGNAL_RELOAD_XRESOURCES="$NEW6" ;; + esac +} + +SWAL_SETTYPE() +{ + case "$SWAL_SELWALLPAPER" in + "Options") SWAL_MKCONFIG && $0 $@ && exit 0 ;; + "") $0 && exit 0 ;; + "Favorites") SWAL_SELWALLPAPER="$(printf "$(cat $SWAL_EXPORTDIR/favorites.filename)\n------\n..\nClear\n\nExit" | $RUNLAUNCHER -g 1 -l 100 -p "What wallpaper would you like to set?")" ; FAVORITES=true ;; + "Add used wallpaper to favorites") echo "$(readlink "$SWAL_EXPORTDIR/CurrentWallpaper")" >> $SWAL_EXPORTDIR/favorites ; echo "$(basename "$(readlink "$SWAL_EXPORTDIR/CurrentWallpaper")")" >> $SWAL_EXPORTDIR/favorites.filename && $0 && exit 0 ;; + "Preview") SWAL_PREVIEWIMG="$(basename $($SWAL_IMAGE_VIEWER "$SWAL_WALLPAPERDIR"/*))" ;; + "..") cd "$SWAL_WALLPAPERDIR/.." && SWAL_WALLPAPERDIR="$(pwd)" && SWAL_SELWALLPAPER && SET && exit 0 ;; + "Random") SWAL_FULL="$(find $SWAL_WALLPAPERDIR/* -type f | shuf -n 1)" ; SWAL_WALLPAPERDIR="$(dirname "$SWAL_FULL")" ; SWAL_SELWALLPAPER="$(basename "$SWAL_FULL")" ; SWAL_SELWAL=$SWAL_SELWALLPAPER ;; + "Previous") SWAL_PREVWAL=$(readlink $SWAL_EXPORTDIR/CurrentWallpaper_prev) ; SWAL_WALLPAPERDIR="$(dirname "$SWAL_PREVWAL")" ; SWAL_SELWALLPAPER="$(basename "$SWAL_PREVWAL")" ;; + "Exit") exit 0 ;; + "Help") SWAL_USAGE | $RUNLAUNCHER -g 1 -l 50 -p "How to use" && $0 && exit 0 ;; + "------") $0 && exit 0 ;; + esac + + if [ "$FAVORITES" = "true" ]; then + case "$SWAL_SELWALLPAPER" in + "") $0 && exit 0 ;; + "Clear") rm -f $SWAL_EXPORTDIR/favorites* ; $0 && exit 0 ;; + "Exit") exit 0 ;; + "..") $0 && exit 0 ;; + "------") $0 && exit 0 ;; + esac + SWAL_WALLPAPERDIR="$(dirname "$(cat "$SWAL_EXPORTDIR/favorites" | grep "$SWAL_SELWALLPAPER")")" + fi + + # If it's an actual image, use it as wallpaper + if [ "$SWAL_SELWALLPAPER" = "Preview" ]; then + if [ "$SWAL_PREVIEWIMG" = "" ]; then + $0 && exit 0 + else + SWAL_SELWALLPAPER="$SWAL_PREVIEWIMG" + fi + fi + + # Check if it's a directory + if [ -d "$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER" ]; then + cd "$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER" && SWAL_WALLPAPERDIR="$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER" && SWAL_SELWALLPAPER && SET && exit 0 + fi + + FAIL_NOT_REAL() { + test ${BINDIR}notify-send && notify-send "The selected wallpaper is not valid/does not exist." + $0 ; exit 1 + } + + ls "$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER" || FAIL_NOT_REAL + + SWAL_TYPE=$(printf "Center\nTile\nZoom\nStretch\n------\nExit" | $RUNLAUNCHER -p "How do you want to set the wallpaper?" -g 1 -l 7) + + case "$SWAL_TYPE" in + "Center") SWAL_TYPE=center ;; + "Tile") SWAL_TYPE=tile ;; + "Zoom") SWAL_TYPE=zoom ;; + "Stretch") SWAL_TYPE=stretch ;; + "Exit") exit 0 ;; + "------") $0 && exit 0 ;; + "") $0 && exit 0 ;; + esac + + SWAL_TYPE="--$SWAL_TYPE" + SWAL_WAL_STATUS="0" + + case "$SWAL_ASK_WAL" in + "true") wal -v && SWAL_USE_WAL=$(printf "Yes\nNo" | $RUNLAUNCHER -g 1 -l 90 -p "Do you want to use Pywal?" -l 2) && SWAL_WAL_STATUS="1" ;; + "false") wal -v && SWAL_USE_WAL="Yes" && SWAL_WAL_STATUS="1" ;; + esac + + # Don't perform pywal actions if not installed/used + if [ "$SWAL_WAL_STATUS" = "0" ]; then + SWAL_USE_WAL="No" + fi +} + +SWAL_SETWALLPAPER() +{ + $SWAL_OPT $SWAL_TYPE "$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER" || FAIL_NOT_REAL +} + +SWAL_EXPORT_SCRIPT() +{ + if [ "$SWAL_RANDOMIZE" = "false" ]; then + printf "#!/bin/sh\n$SWAL_OPT $SWAL_TYPE ""'$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER'""" > $SWAL_EXPORTDIR/swal_wm + if [ -e "$SWAL_EXPORTDIR/CurrentWallpaper" ]; then + rm -f $SWAL_EXPORTDIR/CurrentWallpaper_prev ; ln -s "$(readlink $SWAL_EXPORTDIR/CurrentWallpaper)" $SWAL_EXPORTDIR/CurrentWallpaper_prev + fi + + rm -f $SWAL_EXPORTDIR/CurrentWallpaper ; ln -s "$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER" $SWAL_EXPORTDIR/CurrentWallpaper + else + printf "#!/bin/sh" > $SWAL_EXPORTDIR/swal_wm + printf "\nSWAL_WALLPAPERDIR=""'$SWAL_OLDWALLPAPERDIR'""" >> $SWAL_EXPORTDIR/swal_wm + printf "\nSWAL_EXPORTDIR=""'$SWAL_EXPORTDIR'""" >> $SWAL_EXPORTDIR/swal_wm + printf '\nSWAL_SELWALLPAPER="$(find "$SWAL_WALLPAPERDIR"/* -type f | shuf -n 1)"' >> $SWAL_EXPORTDIR/swal_wm + printf "\n$SWAL_OPT $SWAL_TYPE " >> $SWAL_EXPORTDIR/swal_wm + printf '"$SWAL_SELWALLPAPER"' >> $SWAL_EXPORTDIR/swal_wm + printf "\nrm -f $SWAL_EXPORTDIR/CurrentWallpaper_prev" >> $SWAL_EXPORTDIR/swal_wm + printf '\nln -s "$(readlink $SWAL_EXPORTDIR/CurrentWallpaper)" ' >> $SWAL_EXPORTDIR/swal_wm + printf "$SWAL_EXPORTDIR/CurrentWallpaper_prev" >> $SWAL_EXPORTDIR/swal_wm + printf "\nrm -f $SWAL_EXPORTDIR/CurrentWallpaper" >> $SWAL_EXPORTDIR/swal_wm + printf '\nln -s "$SWAL_SELWALLPAPER" ' >> $SWAL_EXPORTDIR/swal_wm + printf "$SWAL_EXPORTDIR/CurrentWallpaper" >> $SWAL_EXPORTDIR/swal_wm + fi + + chmod +x $SWAL_EXPORTDIR/swal_wm +} + +SWAL_EXPORT_SCRIPT_WAL() +{ + if [ "$SWAL_RANDOMIZE" = "false" ]; then + printf "#!/bin/sh\n$SWAL_OPT $SWAL_TYPE ""'$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER'""\nwal -qi ""'$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER'"" || rm -rf $HOME/.cache/wal ; xrdb -remove\nxrdb ~/.cache/wal/colors.Xresources\nxsetroot -name 'fsignal:$SWAL_FSIGNAL_RELOAD_XRESOURCES'" > $SWAL_EXPORTDIR/swal_wm + else + printf "#!/bin/sh" > $SWAL_EXPORTDIR/swal_wm + printf "\nSWAL_WALLPAPERDIR=""'$SWAL_OLDWALLPAPERDIR'""" >> $SWAL_EXPORTDIR/swal_wm + printf "\nSWAL_EXPORTDIR=""'$SWAL_EXPORTDIR'""" >> $SWAL_EXPORTDIR/swal_wm + printf '\nSWAL_SELWALLPAPER="$(find "$SWAL_WALLPAPERDIR"/* -type f | shuf -n 1)"' >> $SWAL_EXPORTDIR/swal_wm + printf "\n$SWAL_OPT $SWAL_TYPE " >> $SWAL_EXPORTDIR/swal_wm + printf '"$SWAL_SELWALLPAPER"' >> $SWAL_EXPORTDIR/swal_wm + printf '\nwal -nqi "$SWAL_SELWALLPAPER" || rm -rf $HOME/.cache/wal ; xrdb -remove\nxrdb $HOME/.cache/wal/colors.Xresources\nxrdb $HOME/.cache/wal/colors.Xresources' >> $SWAL_EXPORTDIR/swal_wm + printf "\nxsetroot -name "fsignal:$SWAL_FSIGNAL_RELOAD_XRESOURCES"" >> $SWAL_EXPORTDIR/swal_wm + printf "\nrm -f $SWAL_EXPORTDIR/CurrentWallpaper_prev" >> $SWAL_EXPORTDIR/swal_wm + printf '\nln -s "$(readlink $SWAL_EXPORTDIR/CurrentWallpaper)" ' >> $SWAL_EXPORTDIR/swal_wm + printf "$SWAL_EXPORTDIR/CurrentWallpaper_prev" >> $SWAL_EXPORTDIR/swal_wm + printf "\nrm -f $SWAL_EXPORTDIR/CurrentWallpaper" >> $SWAL_EXPORTDIR/swal_wm + printf '\nln -s "$SWAL_SELWALLPAPER" ' >> $SWAL_EXPORTDIR/swal_wm + printf "$SWAL_EXPORTDIR/CurrentWallpaper" >> $SWAL_EXPORTDIR/swal_wm + fi + + chmod +x $SWAL_EXPORTDIR/swal_wm + rm -rf $HOME/.cache/wal # Delete previous colors + wal -nqi "'$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER'" && xrdb ~/.cache/wal/colors.Xresources && xsetroot -name "fsignal:$SWAL_FSIGNAL_RELOAD_XRESOURCES" || xrdb -remove # Compatibility with my speedwm build + if [ -e "$SWAL_EXPORTDIR/CurrentWallpaper" ]; then + rm -f $SWAL_EXPORTDIR/CurrentWallpaper_prev ; ln -s "$(readlink $SWAL_EXPORTDIR/CurrentWallpaper)" $SWAL_EXPORTDIR/CurrentWallpaper_prev + fi + + rm -f $SWAL_EXPORTDIR/CurrentWallpaper ; ln -s "$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER" $SWAL_EXPORTDIR/CurrentWallpaper + + if [ -e "$HOME/.config/speedwm-de/speedwmrc" ]; then + . $HOME/.config/speedwm-de/speedwmrc + fi + + wal -nqi "$SWAL_WALLPAPERDIR/$SWAL_SELWALLPAPER" + + # Reload dmenu colors if supported + if [ -e "/usr/bin/${RUNLAUNCHER}_run" ]; then + grep "PYWAL" ${BINDIR}${RUNLAUNCHER}_run && printf "\n${RUNLAUNCHER}_run -r" >> $SWAL_EXPORTDIR/swal_wm + ${RUNLAUNCHER}_run -r # For speedie.gq/dmenu + fi + + speedwm_run -loadconfig # For speedie.gq/speedwm + speedwm_run -rnoload # For speedie.gq/speedwm + pgrep -x status && pkill status ; status & +} + + +SWAL_XRESOURCES() +{ + xrdb -query | grep "swal" | sed "s|: |=|" | sed "s|swal.|swal_|" > /tmp/swal + . /tmp/swal +} + +SWAL_SENDNOTIFICATION() { + if [ "$SWAL_SELWAL" = "" ]; then + test ${BINDIR}notify-send && notify-send " $SWAL_SELWALLPAPER set as wallpaper in $(echo $SWAL_TYPE | sed "s|--||g") mode." + else + test ${BINDIR}notify-send && notify-send " $SWAL_SELWAL set as wallpaper in $(echo $SWAL_TYPE | sed "s|--||g") mode." + fi + + exit 0 +} + +# Arguments +NEW1=$1 +NEW2=$2 +NEW3=$3 +NEW4=$4 +NEW5=$5 +NEW6=$6 + +rm -f /tmp/swal_cpath +SWAL_DEFAULT_CONFIG +SWAL_SOURCE || SWAL_DIE +SWAL_XRESOURCES +SWAL_CHECK || SWAL_DIE +SWAL_SELWALLPAPER || SWAL_DIE + +SET() { + SWAL_SETTYPE || SWAL_DIE + SWAL_SETWALLPAPER || SWAL_DIE + + # Pywal + case "$SWAL_USE_WAL" in + "Yes") SWAL_EXPORT_SCRIPT_WAL || SWAL_DIE ;; + "No") SWAL_EXPORT_SCRIPT || SWAL_DIE ;; + "") exit 0 ;; + esac + + rm -rf $HOME/.local/share/swal_wm + cp $SWAL_EXPORTDIR/swal_wm $HOME/.local/share/swal_wm + chmod +x $HOME/.local/share/swal_wm + SWAL_SENDNOTIFICATION +} + +SET + +$0 && exit 0 diff --git a/scripts/speedwm-utils b/scripts/speedwm-utils new file mode 100755 index 0000000..2b39bd9 --- /dev/null +++ b/scripts/speedwm-utils @@ -0,0 +1,127 @@ +#!/bin/sh +# speedwm-utils +# Because controlling speedwm using shell scripts is based. +# Licensed under GNU GPLv3. + +case "$RUNLAUNCHER" in +"") RUNLAUNCHER=dmenu # Run launcher to use (dmenu, rofi, etc.) ;; +esac + +VARS() { + BINDIR=$(cat /usr/share/speedwm-bindir) + + SIGNAL_1=1 # Layout: Tiling + SIGNAL_2=2 # Layout: Floating + SIGNAL_3=3 # Layout: Monocle + SIGNAL_4=4 # Layout: Grid + SIGNAL_5=5 # Layout: Deck + SIGNAL_6=6 # Layout: Centered Master + SIGNAL_7=7 # Layout: Centered Floating Master + SIGNAL_8=8 # Layout: Fibonacci Spiral + SIGNAL_9=9 # Layout: Fibonacci Dwindle + SIGNAL_10=10 # Layout: Three Column + SIGNAL_11=11 # Layout: Bottom Stack Vertical + SIGNAL_12=12 # Layout: Bottom Stack Horizontal + SIGNAL_13=13 # Layout: Horizontal Grid + SIGNAL_14=14 # Layout: Tatami + SIGNAL_15=15 # Layout: Tilewide + SIGNAL_16=16 # Layout: Previous + SIGNAL_17=17 # Layout: Next + SIGNAL_18=18 # General: Reload resources (xrdb) + SIGNAL_19=19 # General: Set mfact (-0.05) + SIGNAL_20=20 # General: Set mfact (+0.05) + SIGNAL_21=21 # General: Toggle Scratchpad + SIGNAL_22=22 # General: Toggle Sticky + SIGNAL_23=23 # General: Toggle Bar + SIGNAL_24=24 # General: Toggle Fullscreen + SIGNAL_25=25 # General: Restart speedwm + SIGNAL_26=26 # Unused: Unused 26 + SIGNAL_27=27 # Layout: Stairs + SIGNAL_28=28 # General: Reset layout/mfact + SIGNAL_29=29 # General: Reorganize Tags + SIGNAL_30=30 # General: Quit speedwm + SIGNAL_31=31 # General: Restart speedwm + SIGNAL_32=32 # Unused: Unused 32 + SIGNAL_33=33 # Unused: Unused 33 + SIGNAL_34=34 # Unused: Unused 34 + SIGNAL_35=35 # Layout: Tiling 5:4 + SIGNAL_36=36 # Layout: Columns + SIGNAL_37=37 # Layout: Dynamic Grid +} + +VARS + +GENERAL() { +GENERALOPTS=$(printf "mfact +\nmfact -\nToggle Scratchpad\nToggle Sticky\nToggle Bar\nToggle Fullscreen\nToggle rmaster\nReset Layout/mfact\nReorganize Tags" | $RUNLAUNCHER -l 20 -p 'What do you want to do?') +case "$GENERALOPTS" in +"mfact +") xsetroot -name "fsignal:$SIGNAL_20" ;; +"mfact -") xsetroot -name "fsignal:$SIGNAL_19" ;; +"Toggle Scratchpad") xsetroot -name "fsignal:$SIGNAL_21" ;; +"Toggle Sticky") xsetroot -name "fsignal:$SIGNAL_22" ;; +"Toggle Bar") xsetroot -name "fsignal:$SIGNAL_23" ;; +"Toggle Fullscreen") xsetroot -name "fsignal:$SIGNAL_24" ;; +"Toggle rmaster") xsetroot -name "fsignal:$SIGNAL_26" ;; +"Reorganize Tags") xsetroot -name "fsignal:$SIGNAL_29" ;; +"Reset Layout/mfact") xsetroot -name "fsignal:$SIGNAL_28" ;; +esac +} + +OPT() { +MENUOPTS=$(printf "Layout\nReload .Xresources\nSpawn\nGeneral\nExit" | $RUNLAUNCHER -l 20 -p 'What do you want to do?') + +case "$MENUOPTS" in +"Layout") ${0} -layout $2 && exit 0 ;; +"Reload .Xresources") ${0} -reloadxrdb && exit 0 ;; +"Spawn") ${0} -spawn $2 && exit 0 ;; +"General") ${0} -general $2 && exit 0 ;; +"Exit") exit 0 ;; +esac +} + +HELP() { + printf "speedwmutils allows you to control speedwm using your terminal or dmenu/rofi.\n-- Arguments --\n-layout | Let the user select a speedwm layout\n-reloadxrdb | Reload .Xresources\n-spawn | Spawn a command through speedwm\n-exec | Execute an fsignal signum\n-list | List all available signums\n" > /tmp/speedwm-printed-help + pgrep -x $RUNLAUNCHER || cat /tmp/speedwm-printed-help + pgrep -x $RUNLAUNCHER && cat /tmp/speedwm-printed-help | $RUNLAUNCHER -p 'speedwmutils help' -g 1 -l 20 +} + +case "$1" in +"-layout") SEL_LAYOUT=$(printf "L1: Tiling\nL2: Floating\nL3: Monocle\nL4: Grid\nL5: Deck\nL6: Centered Master\nL7: Centered Floating Master\nL8: Fibonacci Spiral\nL9: Fibonacci Dwindle\nL10: Three Column\nL11: Bottom Stack Vertical\nL12: Bottom Stack Horizontal\nL13: Horizontal Grid\nL14: Tatami\nL15: Tilewide\nL16: Stairs\nL17: Tiling (5:4)\nL18: Column\nL19: Dynamic Grid\nPrevious Layout\nNext Layout" | $RUNLAUNCHER -l 21 -p "Pick a layout: ") ;; +"-spawn") printf "Not complete" ;; +"-list") sed 's|xsetroot -name "fsignal:||g' /usr/share/speedwm-fsignal | sed 's|"||g' ;; +"-reloadxrdb") xsetroot -name "fsignal:$SIGNAL_18" ;; +"-help") HELP ;; +"-exec") + if [ "$2" = "" ]; then + exit 0 + else + xsetroot -name "fsignal:$2" + fi ;; +"-h") HELP ;; +"-general") GENERAL ;; +"") OPT ;; +esac + +case "$SEL_LAYOUT" in +"Exit") exit 0 ;; +"L1: Tiling") xsetroot -name "fsignal:$SIGNAL_1" ;; +"L2: Floating") xsetroot -name "fsignal:$SIGNAL_2" ;; +"L3: Monocle") xsetroot -name "fsignal:$SIGNAL_3" ;; +"L4: Grid") xsetroot -name "fsignal:$SIGNAL_4" ;; +"L5: Deck") xsetroot -name "fsignal:$SIGNAL_5" ;; +"L6: Centered Master") xsetroot -name "fsignal:$SIGNAL_6" ;; +"L7: Centered Floating Master") xsetroot -name "fsignal:$SIGNAL_7" ;; +"L8: Fibonacci Spiral") xsetroot -name "fsignal:$SIGNAL_8" ;; +"L9: Fibonacci Dwindle") xsetroot -name "fsignal:$SIGNAL_9" ;; +"L10: Three Column") xsetroot -name "fsignal:$SIGNAL_10" ;; +"L11: Bottom Stack Vertical") xsetroot -name "fsignal:$SIGNAL_11" ;; +"L12: Bottom Stack Horizontal") xsetroot -name "fsignal:$SIGNAL_12" ;; +"L13: Horizontal Grid") xsetroot -name "fsignal:$SIGNAL_13" ;; +"L14: Tatami") xsetroot -name "fsignal:$SIGNAL_14" ;; +"L15: Tilewide") xsetroot -name "fsignal:$SIGNAL_15" ;; +"L16: Stairs") xsetroot -name "fsignal:$SIGNAL_27" ;; +"L17: Tiling (5:4)") xsetroot -name "fsignal:$SIGNAL_35" ;; +"L18: Column") xsetroot -name "fsignal:$SIGNAL_36" ;; +"L19: Dynamic Grid") xsetroot -name "fsignal:$SIGNAL_37" ;; +"Previous Layout") xsetroot -name "fsignal:$SIGNAL_16" ;; +"Next Layout") xsetroot -name "fsignal:$SIGNAL_17" ;; +esac diff --git a/scripts/speedwm-virtualkeyboard b/scripts/speedwm-virtualkeyboard new file mode 100755 index 0000000..f8d68a9 --- /dev/null +++ b/scripts/speedwm-virtualkeyboard @@ -0,0 +1,2181 @@ +#!/bin/sh +# speedwm-virtualkeyboard +# Licensed under GNU GPLv3 +# https://speedie.gq/speedwm + +case "$RUNLAUNCHER" in +"") RUNLAUNCHER=dmenu ;; +esac + +BINDIR=$(cat /usr/share/speedwm-bindir) + +EMOJILIST() +{ +printf "🙂 Slightly smiling face +😀 Smiling face +😃 Smiling face with big eyes +😄 Smiling face with smiling eyes +😁 Beaming face with smiling eyes +😅 Smiling face with tears +😆 Grinning face +🤣 Rolling on the floor laughing +😂 Lauging with tears +🙃 Upside down face +😉 Winking face +😊 Smiling face with smiling eyes +😇 Smiling face with halo +😎 Smiling face with sunglasses +🤓 Nerdy face +🧐 Face with monocle +🥳 Partying face +🥰 Smiling face with hearts +😍 Smiling face with heart eyes +🤩 Star-struck +😘 Face blowing kiss +😗 Kissing face +☺ Smiling face +😚 Kissing face with closed eyes +😙 Kissng face with smiling eyes +🥲 Smiling face with tears +😋 Yummy face +😛 Face with tongue +😜 WInking face with tongue +🤪 Zanny face +😝 Squinting face with tongue +🤑 Money face with money tongue +🤗 Hugs +🤭 Face with hand in mouth +🤫 Shushing face +🤔 Thinkin face +😐 Neutral face +🤐 Zipped mouth +🤨 Face with raised eyebrow +😑 Expressionless face +😶 Face with no mouth +😏 Smirking face +😒 Unamused face +🙄 Face with rolling eyes +😬 Grimacing face +😮 💨 Grimacing face +🤥 Lying face +😪 Sleepy face +😴 Sleeping face +😌 Relieved face +😔 Pensive face +🤤 Drooling face +😷 Face with mask +🤒 Face with thermometer +🤕 Face with bandage +🤢 Nauseous face +🤮 Vomiting face +🤧 Sneezing face +🥵 Hot face +🥶 Cold face +🥴 Woozy face +😵 Face with crossed-out face +🤯 Face with exploding head +😕 Confused face +😟 Worried face +🙁 Slightly frowning face +☹ Frowning face +😮 Face with open mouth +😯 Hushed face +😲 Astonished face +😳 Flushed face +🥺 Begging face +😦 Frowning face with open mouth +😧 Angushed face +😨 Fearful face +😰 Anxious face with sweat +😥 Sad but relieved face +😢 Crying face +😭 Loudly crying face +😱 Screaming face +😖 Confounded face +😣 Persevering face +😞 Disapointed face +😓 Downcast face with sweat +😩 Weary face +😫 Tired face +🥱 Yawning face +😤 Face with steam +😡 Pouting face +😠 Angry face +🤬 Face with symbols on mouth +😈 Smiling face with horns +👿 Angry face with horns +💀 Skull +☠ Skull and cross-bone +💩 Pile of poo +🤡 Clown +👹 Ogre +👺 Goblin +👻 Ghost +👽 Alien +👾 Alien monster +🤖 Robot +😺 Grinnig cat +😸 Grinning cat with smiling eyes +😹 Grinning cat with tears +😻 Smiling cat with heart eyes +😼 Cat with wry smile +😽 Kissing cat +🙀 Weary cat +😿 Crying cat +😾 Pouting cat +🙈 See no evil monkey +🙉 Hear no evil monkey +🙊 Speak no evil monkey +💋 Kiss +💌 Love letter +💘 Heart with arrow +💝 HEart with ribbon +💖 Sparking heart +💗 Growing heart +💓 Beating heart +💞 Revolving heart +💕 Two hearts +💟 Heart decoration +❣ Heart exclamation +💔 Broken heart +❤️ 🔥 Heart on fire +❤️ 🩹 Mending heart +❤ Red heart +🧡 Orange heart +💛 Yellow heart +💚 Green heart +💙 Blue heart +💜 Purple heart +🤎 Brown heart +🖤 Black heart +🤍 White heart +💯 Hundred(correct) +💢 Anger +💥 collision +💫 Dizzy +💦 Sweat droplets +💨 Dashing away +🕳 Hole +💣 Bomb +💬 Message baloon +👁️ 🗨️ Eye in speech bubble +🗨 Left speech bubble +🗯 Anger bubble +💭 Thought baloon +💤 zzz +👋 Waving hand +🤚 Raised back of hand +🖐 Hands with splayed finger +✋ Raised hand +🖖 Vulcan salute +👌 Ok +🤌 Pinched fingers +🤏 Pinched hand +✌ Victory hand +🤞 Crossed fingers +🤟 Love you +🤘 Horn sign +🤙 Call me hand +👈 Index finger pointing left +👉 Index finger pointing right +👆 Index finger pointing up +👇 Index finger pointing down +🖕 Middle finger +☝ Forehand Index finger pointing up +🫵 Index finger pointing at viewer +👍 Thumbs up +👎 Thumbs down +✊ Raised fist +👊 Fist +🤛 Left facing fist +🤜 Right facing fist +👏 Clapping hands +🙌 Raised hands +👐 OPen hands +🤲 Palms together hands +🤝 Handshake +🙏 Praying hands +✍ Writing hands +💅 Nail polish +🤳 Selfie hand +💪 Flexed biceps +🦾 MEchanical arm +🦵 Leg +🦿 Mechanical leg +🦶 Foot +👂 Ear +🦻 Ear with earing aid +👃 Nose +🧠 Brain +👣 Footprint +🫀 MEchanical heart +🫁 Lungs +🦷 Tooth +🦴 Bone +👀 Eyes +👁 Eye +👅 Nose +👄 Mouth +🧑 Person +👶 Baby +🧒 Child +👦 Boy +👧 Girl +👱 Person with blonde hair +👨 Man +🧔 Bearded person +🧔 ♂‍ Bearded man +🧔 ♀‍ Bearded woman +👨 🦰 MAn with red hair +👨 🦱 Man with curly hair +👨 🦳 Man with white hair +👨 🦲 Bald man +👩 Woman +👩 🦰 Woman with red hair +👩 🦱 Woman with curly hair +👩 🦳 Woman with white hair +👩 🦲 Bald woman +👱 ♀‍ Woman with blode hair +👱 ♂‍ Man with blonde hair +🧓 Old person +👴 Old man +👵 Old woman +🙍 Person frowning +🙍 ♂‍ Man frowning +🙍 ♀‍ Woman frowning +🙎 Person pouting +🙎 ♂‍ Man pouting +🙎 ♀‍ Woman pouting +🙅 Person gesturing no +🙅 ♂‍ Man gesturing no +🙅 ♀‍ Woman gesturing no +🙆 Person stretching +🙆 ♂‍ Man stretching +🙆 ♀‍ Woman stretching +💁 Person tipping hand +💁 ♂‍ Man tipping hand +💁 ♀‍ Woman tipping hand +🙋 Person rainsing hand +🙋 ♂‍ Man raising hand +🙋 ♀‍ Woman raisning hand +🧏 Deaf person +🧏 ♂‍ Deaf man +🧏 ♀‍ Deaf woman +🙇 Person bowing +🙇 ♂‍ Man bowing +🙇 ♀‍ Woman bowing +🤦 Person facepalming +🤦 ♂‍ Man facepalming +🤦 ♀‍ Woman facepalming +🤷 Person shrugging +🤷 ♂‍ Man shrugging +🤷 ♀‍ Woman shrugging +🧑 ⚕‍ Health worker +👨 ⚕‍ Man health worker +👩 ⚕‍ Woman health worker +🧑 🎓 Student +👨 🎓 Man student +🧑 🏫 Teacher +👨 🏫 Man teacher +👩 🏫 Woman teacher +🧑 ⚖‍ Judge +👨 ⚖‍ Man judge +👩 ⚖‍ Woman judge +🧑 🌾 Farmer +👨 🌾 Man farmer +👩 🌾 Woman farmer +🧑 🍳 Cook +👨 🍳 Man cook +👩 🍳 Woman cook +🧑 🔧 Mechanic +👨 🔧 Man mechanic +👩 🔧 Woman mechanic +🧑 🏭 Factory worker +👨 🏭 Man factory worker +👩 🏭 Woman factory worker +🧑 💼 Office worker +👨 💼 Man office worker +👩 💼 Woma office worker +🧑 🔬 Scientist +👨 🔬 Man scientist +👩 🔬 Woman scientis +🧑 💻 Technologist +👨 💻 Man texhnologist +👩 💻 Woman technologist +🧑 🎤 Singer +👨 🎤 Man singer +👩 🎤 Woman singer +🧑 🎨 Artist +👨 🎨 Man artist +👩 🎨 Woman artist +🧑 ✈‍ Pilot +👨 ✈‍ Man pilot +👩 ✈‍ Woman pilot +🧑 🚀 Astronaut +👨 🚀 Man astronaut +👩 🚀 Woman astronaut +🧑 🚒 Firefighter +👨 🚒 Man firefighter +👩 🚒 Woman firefighter +👮 Police +👮 ♂‍ Policeman +👮 ♀‍ Policewoman +🕵 Detective +🕵️ ♂‍ Man detective +🕵️ ♀‍ Woman detective +💂 Guard +💂 ♂‍ Man guard +💂 ♀‍ Woman guard +🥷 Ninja +👷 Construction worker +👷 ♂‍ Man construction worker +👷 ♀‍ Woman construction worker +🤴 Prince +👸 Princess +👳 Person wearing turban +👳 ♂‍ Man wearing turban +👳 ♀‍ Woman wearing turban +👲 Person with skullcap +🧕 Woman with headscaff +🤵 Person in tuxedo +🤵 ♂‍ Man in tuxedo +🤵 ♀‍ Woman in tuxedo +👰 Person in veil +👰 ♂‍ Man in veil +👰 ♀‍ Woman in veil +🤰 Pregnant woman +🤱 Breast-feeding +🧑 🍼 Person feeding baby +👩 🍼 WOman feeding baby +👨 🍼 Man feeding baby +👼 Baby angel +🎅 Santa claus +🤶 Mrs Claus +🧑 🎄 Mx Claus +🦸 Superhero +🦸 ♂‍ Man superhero +🦸 ♀‍ Woman superhero +🦹 Supervillain +🦹 ♂‍ Man superhero +🦹 ♀‍ Woman superhero +🧙 Mage +🧙 ♂‍ Man mage +🧙 ♀‍ Woman mage +🧚 Fairy +🧚 ♂‍ Man fairy +🧚 ♀‍ Woman fairy +🧛 Vampire +🧛 ♂‍ Man vampire +🧛 ♀‍ Woman vampire +🧜 Merperson +🧜 ♂‍ Merman +🧜 ♀‍ Mermaid +🧝 Elf +🧝 ♂‍ Man elf +🧝 ♀‍ Woman elf +🧞 Genie +🧞 ♂‍ Man genie +🧞 ♀‍ Woman genie +🧟 Zombie +🧟 ♂‍ Man zombie +🧟 ♀‍ Woman zombie +💆 Person getting massage +💆 ♂‍ Man getting massage +💆 ♀‍ Woman getting massage +💇 Person getting haircut +💇 ♂‍ Man getting haircut +💇 ♀‍ Woman getting haircut +🚶 Person walking +🚶 ♂‍ Man walking +🚶 ♀‍ Woman walking +🧍 Person standing +🧍 ♂‍ Man standing +🧍 ♀‍ Woman standing +🧎 Person kneeling +🧎 ♂‍ Man kneeling +🧎 ♀‍ Woman kneeling +🧑 🦯 PErson with walking stick +👨 🦯 Man with walking stick +👩 🦯 Woman with walking stick +🧑 🦼 Person in motorized wheelchair +👨 🦼 Man in motorized wheelchair +👩 🦼 Womain in motorized wheelchair +🧑 🦽 Person in manual wheelchair +👨 🦽 Man in manual wheelchair +👩 🦽 Womaan in motorized wheelchair +🏃 Person sprinting +🏃 ♂‍ Man sprinting +🏃 ♀‍ Woman sprinting +💃 Woman dancing +🕺 Man dancing +🕴 Person in suit levitating +👯 People with bunny ears +👯 ♂‍ Men with bunny ears +👯 ♀‍ Women in bunny ears +🧖 Person in steaming room +🧖 ♂‍ Man in steaming room +🧖 ♀‍ Woman in steaming room +🧗 Person climbing +🧗 ♂‍ Man climbing +🧗 ♀‍ Woman climbing +🤺 Person fencing +🏇 Horse racing +⛷ Skier +🏂 Snowball +🏌 Person playing golf +🏌️ ♂‍ Man playing golf +🏌️ ♀‍ Woman playing golf +🏄 Person surfing +🏄 ♂‍ Man surfing +🏄 ♀‍ Woman surfing +🚣 Person rowing boat +🚣 ♂‍ Man rowing boat +🚣 ♀‍ Woman rowing boat +🏊 Person swimming +🏊 ♂‍ Man swimming +🏊 ♀‍ Woman swimming +⛹ Person bouncing ball +⛹️ ♂‍ Man bouncing ball +⛹️ ♀‍ Woman bouncing ball +🏋 Person lifting weight +🏋️ ♂‍ Man lifting weight +🏋️ ♀‍ Woman lifting weight +🚴 Person cycling +🚴 ♂‍ Man cycling +🚴 ♀‍ Woman cycling +🚵 Person mountain biking +🚵 ♂‍ Man mountain biking +🚵 ♀‍ Woman mountain biking +🤸 Person catwheeling +🤸 ♂‍ Man catwheeling +🤸 ♀‍ Woman catwheeling +🤼 People wrestling +🤼 ♂‍ Men wrestling +🤼 ♀‍ Women wrestling +🤽 Person playing water polo +🤽 ♂‍ Man playing water polo +🤽 ♀‍ Woman playing water polo +🤾 Person playing handball +🤾 ♂‍ Man playing handball +🤾 ♀‍ Woman playing handball +🤹 Person juggling +🤹 ♂‍ Man juggling +🤹 ♀‍ Woman juggling +🧘 Person lotus position +🧘 ♂‍ Man in lotus position +🧘 ♀‍ Woman in lotus position +🛀 Person bathing +🛌 Person in bed +👪 Family +👨 👩‍👦 Family: man, woman, and boy +👨 👩‍👧 Family: man, woman, and girl +👨 👩‍👧‍👦 Family: man, woman, boy, and girl +👨 👩‍👦‍👦 Family: man, woman, and two boys +👨 👩‍👧‍👧 Family: man, woman, and two girls +👨 👨‍👦 Family: two men and boy +👨 👨‍👧 Family: two men and girl +👨 👨‍👧‍👦 Family: two men, girl, and boy +👨 👨‍👦‍👦 Family: two men and two boys +👨 👨‍👧‍👧 Family: two men and two girls +👩 👩‍👦 Family: two women and boy +👩 👩‍👧 Family: two women and girl +👩 👩‍👧‍👦 Family: two women, girl, and boy +👩 👩‍👦‍👦 Family: two women and two boys +👩 👩‍👧‍👧 Family: two women and two girls +👨 👦 Family: man and boy +👨 👦‍👦 Family: man and two boys +👨 👧 Family: man and girl +👨 👧‍👦 Family: man, girl and boy +👨 👧‍👧 Family: man and two girls +👩 👦 Family: woman and boy +👩 👦‍👦 Family: woman and two boys +👩 👧 Family: woman and girl +👩 👧‍👦 Family: woman, girl, and boy +👩 👧‍👧 Family: woman and two girls +🧑 🤝‍🧑 People holding hands +👭 Women holding hands +👫 Woman and man holding hands +👬 Men holding hands +💏 Kiss +👩 ❤‍💋‍👨 Woman and man kissing +👨 ❤‍💋‍👨 Man and man kissing +👩 ❤‍💋‍👩 Womand and woman kissing +💑 Couple with heart +🗣 Person speaking +👤 Bust in silhouhette +👥 Busts in silhouette +🫂 People hugging +🐵 Monkey face +🐒 Monkey +🦍 Gorilla +🦧 Orangutan +🐶 Dog face +🐕 Dog +🦮 Guide dog +🐕 🦺 Service dog +🐩 Poodle +🐺 Wolf +🦊 Fox +🦝 Racoon +🐱 Cat face +🐈 Cat +🐈 ⬛ Black cat +🦁 Lion +🐯 Tiger face +🐅 Tiger +🐆 Leopard +🐴 Horse face +🐎 Horse +🦄 Unicorn +🦓 Zebra +🦌 Deer +🦬 Bison +🐮 Cow face +🐄 Cow +🐂 Ox +🐃 Water buffalo +🐷 Pig face +🐖 Pig +🐗 Boar +🐽 Pig nose +🐏 Ram +🐑 Ewe +🐐 Goat +🐪 Camel +🐫 Two hump camel +🦙 Ilama +🦒 Giraffe +🐘 Elephant +🦣 Mammoth +🦏 Rhiniceros +🦛 Hippopotamus +🐭 Mouse face +🐁 Mouse +🐀 Rat +🐹 Hamster +🐰 Rabbit face +🐇 Rabbit +🐿 Chipmunk +🦫 Beaver +🦔 Hedgehog +🦇 Bat +🐻 Bear +🐻 ❄️ Polar bear +🐨 Koala +🐼 Panda +🦥 Sloth +🦦 Otter +🦨 Skunk +🦘 Kangaroo +🦡 Badger +🐾 Paw prints +🦃 Turkey +🐔 Chicken +🐓 Rooster +🐣 Hatching +🐤 Baby chick +🐥 Front-facing chick +🐦 Bird +🐧 Penguin +🕊 Dove +🦅 Eagle +🦆 Duck +🦢 Swan +🦉 Owl +🦤 Dodo +🪶 Feather +🦩 Flamingo +🦜 Peacock +🐸 Frog +🐊 Crocodile +🐢 Turtle +🦎 Lizard +🐍 Snake +🐲 Dragon face +🐉 Dragon +🦕 Sauropod +🦖 Tyranosaurus +🐳 Spouting whale +🐋 Whale +🐬 Dolphin +🦭 Seal +🐟 Fish +🐠 Tropical fish +🐡 Blowfish +🦈 Shark +🐙 Octopus +🐚 Spiral shell +🐌 Snail +🦋 Butterfly +🐛 Bug +🐜 Ant +🐝 Honeybee +🪲 Beetle +🐞 Lady Beetle +🦗 Cricket +🪳 Cockroach +🕷 Spider +🕸 Spider web +🦂 Scorpion +🦟 Mosquito +🪰 Fly +🪱 Worm +🦠 Microbe +💐 Bouquet +🌸 Cherry blossom +💮 White flower +🏵 Rosette +🌹 Rose +🥀 Wilted flower +🌺 Hibiscus +🌻 Sunflower +🌼 Blossom +🌷 Tulip +🌱 Seedling +🪴 Potted plant +🌲 Evergreen tree +🌳 Deciduous plant +🌴 Palm tree +🌵 Cactus +🌾 Sheaf of rice +🌿 Herb +☘ Shamrock +🍀 Four leaf clover +🍁 Maple leaf +🍂 Fallen leaf +🍃 Leaf fluttering in wind +🪴 Empty nest +🪴 Nest with eggs +🍇 Grapes +🍈 Melon +🍉 Water melon +🍊 Tangerine +🍋 Lime +🍌 Banana +🍍 Pineapple +🥭 Mango +🍎 Red apple +🍏 Green apple +🍐 Pear +🍑 Peach +🍒 Cherries +🍓 Strawberries +🫐 Blueberries +🥝 Kiwi fruit +🍅 Tomato +🫒 Olive +🥥 Coconut +🥑 Avocado +🍆 Eggplant +🥔 Potato +🥕 Carrot +🌽 Corn +🌶 Pepper +🫑 Bell pepper +🥒 Cucumber +🥬 Leafy green +🥦 Broccoli +🧄 Garlic +🧅 Onion +🍄 Mushroom +🥜 Peanuts +🫑 Beans +🌰 Chestnut +🍞 Bread +🥐 Croissant +🥖 Baguette bread +🫓 Flat bread +🥨 Pretzel +🥯 Bagel +🥞 Pancake +🧇 Waffle +🧀 Cheese wedge +🍖 Meat with bone +🍗 Poultry leg +🥩 Cut of meat +🥓 Bacon +🍔 Hamburger +🍟 French fries +🍕 Pizza +🌭 Hot dog +🥪 Sandwich +🌮 Taco +🌯 Burrito +🫔 Tamale +🥙 Stuffed flatbread +🧆 Falafel +🥚 Egg +🍳 Cooking +🥘 Shallow pan of food +🍲 Pot of food +🫕 Fondue +🥣 Bowl with food +🥗 Green salad +🍿 Popcorn +🧈 Butter +🧂 Salt +🥫 Canned food +🍱 Bento box +🍘 RIce cracker +🍙 Rice ball +🍚 Cooked rice +🍛 Curry rice +🍜 Steaming bowl +🍝 Spaghetti +🍠 Roasted sweet potato +🍢 Oden +🍣 Sushi +🍤 Fried shrimp +🍥 Fish cake with swiri +🥮 Moon cake +🍡 Dango +🥟 Dumpling +🥠 Fortune cookie +🥡 Take out box +🦀 Crab +🦞 Lobster +🦐 Shrimp +🦑 Squid +🦪 Oyster +🍨 Ice cream +🍧 Shaved ice cream +🍦 Soft ice cream +🍩 Doughnut +🍪 Cookie +🎂 Birthday cake +🍰 Short cake +🧁 Cup cake +🥧 Pie +🍫 Chocoloate +🍬 Candy +🍭 Lollipop +🍮 Custard +🍯 Honey pot +🍼 Baby bottle +🥛 Glass of milk +☕ Hot beverage +🫖 Teapot +🍵 Teacup without handle +🍶 Sake +🍾 Bottle with poppin cork +🍷 Wine glass +🍸 Cocktail glass +🍹 Tropical drink +🍺 Beer mug +🍻 Clinking beer mug +🥂 Clinking glasses +🥃 Tumbler glass +🥤 Cup with strawberry +🧋 Bubble tea +🧃 Beverage box +🧉 Mate +🧊 Ice +🥢 Chopsticks +🍽 Fork and knife with plate +🍴 Fork and knife +🥄 Spoon +🔪 Kitchen knife +🧋 Jar +🏺 Amphora +🌍 Globe showing Africa and Europe +🌎 Globe showing Americas +🌏 Globe showing Asia and Australia +🌐 Globe with meridians +🗺 World map +🧭 Compass +⛰ Mountain +🏔 Snowcap mountain +🌋 Volcanic mountain +🗻 Fuji mountain +🏕 Camping +🏖 Beach with umbrella +🏜 Desert +🏝 Desertified island +🏞 National park +🏟 Stadium +🏛 Classical building +🏗 Building construction +🧱 Brick +🪨 Rock +🪵 Wood +🛖 Hut +🏘 Houses +🏚 Derelict house +🏠 House +🏡 House with garden +🏢 Office building +🏣 Japanese office +🏤 Post office +🏥 Hospital +🏦 Bank +🏨 Hotel +🏩 Love hotel +🏪 Convenience store +🏫 School +🏬 Department +🏭 Factory +🏯 Japanese castle +🏰 Castle +💒 Wedding house +🗼 Tokyo tower +🗽 Statue of liberty +⛪ Church +🕌 Mosque +🛕 Hindu temple +🕍 Synagogue +⛩ Shinto shrine +🕋 Kaaba +⛲ Fountain +⛺ Tent +🌁 Foggy +🌃 Night with starrs +🏙 Citscape +🌅 Sunrise +🌄 Sunrise over mountains +🌆 Cityscape at dusk +🌇 Sunset +🌉 Bridge at night +♨ Hot springs +🎠 Carousel horse +🎡 Ferris wheel +🎢 Roller coaster +💈 Barber poll +🎪 Circus tent +🚂 Locomotive +🚃 Railway car +🚄 High speed train +🚅 Bullet train +🚆 Train +🚇 Metro +🚈 Light rail +🚉 Station +🚊 Tram +🚝 Monorail +🚞 Mountain railway +🚋 Tram car +🚌 us +🚍 Oncoming bus +🚎 Trolley bus +🚐 Minibus +🚑 Ambulance +🚒 Fire engine +🚓 Police car +🚔 Oncoming police car +🚕 Taxi +🚖 Oncoming taxi +🚗 Automobile +🚘 Oncoming automobile +🚙 Sport utility vehicle +🛻 Pickup truck +🚚 Delivery truck +🚛 Articulated lorry +🚜 Tractor +🏎 Racing car +🏍 Motorcycle +🛵 Scooter +🦽 Manual wheelchair +🦼 Motorized wheelchair +⌛ Hourglass done +⏳ Hourglass starting +⌚ Watch +⏰ Alarm +⏱ Stopwatch +⏲ Timer clock +🕰 Mantelpiece clock +🕛 Twelve +🕧 Twelve-thirty +🕐 One +🕜 One-thirty +🕑 Two +🕝 Two-thirty +🕒 Three +🕞 Three-thirty +🕓 Four +🕟 Four-thirty +🕔 Five +🕠 Five-thirty +🕕 Six +🕡 Six-thirty +🕖 Seven +🕢 Seven-thirty +🕗 Eight +🕣 Eight-thirty +🕘 Nine +🕤 Nine-thirty +🕙 Ten +🕥 Ten-thirty +🕚 Eleven +🕦 Eleven-thirty +🌑 New moon +🌒 Waxing crescent moon +🌓 First quarter moon +🌔 Waxing gibbous moon +🌕 Full moon +🌖 Waning gibbous moon +🌗 Last quarter moon +🌘 Waning crescent moon +🌙 Crescent moon +🌚 New moon face +🌛 First quarter moon face +🌜 Last quartermoon face +🌡 Thermometer +☀ Sun +🌝 Full moon face +🌞 Sun with face +🪐 Ringed planet +⭐ Star +🌟 Glowing star +🌠 Shooting star +🌌 Milky way +☁ Cloud +⛅ Sun behind cloud +⛈ Cloud with lighting and rain +🌤 Sun behind small cloud +🌥 Sun behind large cloud +🌦 Sun behind rain cloud +🌧 Cloud with rain +🌨 Cloud with snow +🌩 Cloud with lighting +🌪 Tornado +🌫 Fog +🌬 Wind face +🌀 Cyclone +🌈 Rainbow +🌂 Closed umbrella +☂ Umbrella +☔ Umbrella with raindrops +⛱ Umbrella on ground +⚡ High voltage +❄ Snowflake +☃ Snowman +⛄ Snowman without snow +☄ Comet +🔥 Fire +💧 Droplet +🌊 Water wave +🎃 Jack-o-lantern +🎄 Christmas tree +🎆 Fireworks +🎇 Sparkler +🧨 Firecracker +✨ Sparkles +🎈 Baloon +🎉 Party popper +🎊 Confetti ball +🎋 Tanabata tree +🎍 Pine decoration +🎎 Japanese dolls +🎏 Carp streamer +🎑 Moon viewing ceremony +🧧 Red envelope +🎀 Ribbon +🎁 Wrapped gift +🎗 Reminder ribbon +🎟 Admission ticket +🎫 Ticket +🎖 Military medal +🏆 Trophy +🏅 Sports medal +🥇 Gold medal - first position +🥈 Silver medal - second position +🥉 Bronze medal - third position +⚽ Soccer ball +⚾ Baseball +🥎 Softball +🏀 BAsketball +🏐 Volleyball +🏈 American football +🏉 Rugby +🎾 Tennis +🥏 Flying disk +🎳 Bowling +🏏 Cricket +🏑 Field hockey +🏒 Ice hockey +🥍 Lacrose +🏓 Ping pong +🏸 Badminton +🥊 Boxing glove +🥋 Martial arts uniform +🥅 Goal net +⛳ Flag in a hole +⛸ Ice skate +🎣 Fishing poll +🤿 Driving mask +🎽 Running shirt +🎿 Skis +🛷 Sled +🥌 Curling stone +🎯 Bullseye +🪀 Yo-yo +🪁 Kite +🎱 8 ball +🔮 Crystal ball +🪄 Magic wand +🧿 Nazar amulet +🪄 Hamsa +🎮 Video game pad +🕹 Joystick +🎰 Slot machine +🎲 Game die +🧩 Puxxle piece +🧸 Teddy bear +🪅 Pinata +🪆 Mirror +🪆 Nesting doll +♠ Spade suit +♥ Heart suit +♣ Club suit +♟ Chess pawn +🃏 Joker +🀄 Mahjong red dragon +🎴 Flower playing cards +🎭 Performing arts +🖼 Framed picture +🎨 Artist pallete +🧵 Thread +🪡 Sewing needle with thred +🧶 Yarn +🪢 Knot +👓 Glasses +🕶 Sunglasses +🥽 Googles +🥼 Lab coat +🦺 Safety vest +👔 Necktie +👕 T-shirt +👖 Jeans +🧣 Scarf +🧤 Gloves +🧥 Coat +🧦 Socks +👗 Dress +👘 Kimono +🥻 Sari +🩱 One piece suit +🩲 Briefs +🩳 Shorts +👙 Bikini +👚 Woma +👛 Purse +👜 Handbag +👝 Clutch bag +🛍 Shopping bags +🎒 Backpack +🩴 Thong sandals +👞 Ma +👟 Running shoe +🥾 Hiking boot +🥿 Flat shoe +👠 High-heeled shoe +👡 Woma +🩰 Ballet shoes +👢 Woma +👑 Crown +👒 Woma +🎩 Top hat +🎓 Graduation cap +🧢 Billed cap +🪖 Military helmet +⛑ Rescuew worke +📿 PRayer beads +💄 Lipstick +💍 Ring +💎 Gemstone +🔇 Muted speaker +🔈 Low volume speaker +🔉 Mid volume speaker +🔊 High volume speaker +📢 Loudspeaker +📣 Megaphone +📯 Postal horn +🔔 Bell +🔕 Bell with slash +🎼 Musical score +🎵 Musical note +🎶 Musical notes +🎙 Studio microphone +🎚 Level slider +🎛 Control knobs +🎤 Microphone +🎧 Headphone +📻 Radio +🎷 Saxophone +🪗 Accordion +🎸 Guitar +🎹 Musical keyboard +🎺 Trumpet +🎻 Violin +🪕 Banjo +🥁 Drum +🪘 Long drum +📱 Mobile phone +📲 MObile phone with arrow +☎ Telephone +📞 Telephone receiver +📟 Pager +📠 Fax machine +🔋 Full battery +🪫 Low battery +🔌 Electric plug +💻 Laptop +🖥 Desktop computer +🖨 Printer +⌨ Keyboard +🖱 Mouse +🖲 Trackball +💽 Computer disk +💾 Floppy disk +💿 Optical disk +📀 DVD +🧮 Abacus +🎥 Movie camera +🎞 Film frames +📽 Film Projector +🎬 Clapper board +📺 Television +📷 Camera +📸 Camera with flash +📹 Video camera +📼 Video cassete +🔍 Magnifying glass tilted left +🔎 Magnifying glass tilted right +🕯 Candle +💡 Light bulb +🔦 Flashlight +🏮 Red pepper lantern +🪔 Diya lamp +📔 Notebook with decorative cover +📕 Closed notebook +📖 Opened notebook +📗 Green book +📘 Blue book +📙 Orange book +📚 Orange books +📓 Notebook +📒 Ledger +📃 Page with curl +📜 Scroll +📄 Page facing up +📰 Newspaper +🗞 Rolled-up newspaper +📑 Bookmark tabs +🔖 Bookmark +🏷 Label +💰 Money bag +🪙 Coin +💴 Yen banknote +💵 Dollar banknote +💶 Euro banknote +💷 Pound banknote +💸 Money with wings +💳 Credit card +🧾 Receipt +💹 Chart increase woth Yen +✉ Envelope +📧 e-mail +📩 Envelope with arrow +📤 Outbox tray +📥 Inbox tray +📦 Package +📫 Closed mailbox with raised flag +📪 Closed mailbox with lowered flag +📬 Open mailbox with raised flag +📭 Open mailbox with lowered flag +📮 Postbox +🗳 Ballot box with ballot +✏ Pencil +✒ Black nib +🖋 Fountain pen +🖊 Pen +🖌 Paintbrush +🖍 Crayon +📝 Memo +💼 Briefcase +📁 File folder +📂 Open the folder +🗂 Card index dividers +📅 Calender +📆 Tear off calender +📇 Card index +📈 Increasing chart +📉 Decreasing chart +📊 Bar chart +📋 Clipboard +📌 Pushpin +📍 Round pushpin +📎 Paperclip +🖇 Linked paperclips +📏 Straight ruler +📐 Triangular ruler +✂ Scissors +🗃 Card file box +🗄 File cabinet +🗑 Waste basket +🔒 Locked +🔓 Unlocked +🔏 Locked with pen +🔐 Locked with key +🔑 Key +🗝 Old key +🔨 Hammer +🪓 Axe +⛏ Pick +⚒ Hammer and pick +🛠 Hammer and wrench +🗡 Sword +⚔ Crossed swords +🔫 Water gun +🪃 Boomerang +🏹 Bow and arrow +🛡 Shield +🪚 Carpentry saw +🔧 Wrench +🪛 Screwdriver +🔩 Bolt and nut +⚙ Wheel +🗜 Clamp +⚖ Balance scale +🦯 White cane +🔗 Link +⛓ Chains +🪝 Hook +🧰 Toolbox +🧲 Magnet +🪜 Ladder +⚗ Alembic +🧪 Test tube +🧫 Petri dish +🧬 DNA +🔬 Microscope +🔭 Telescope +📡 Satelite antenna +💉 Syringe +🩸 A droplet of blood +💊 Pill +🩹 Adhesive bandage +🩼 Clutch +🩺 Stethoscope +🩻 X-ray +🚪 Door +🛗 Elevator +🪞 Mirror +🪟 Window +🛏 Bed +🛋 Couch and lamp +🪑 Chair +🚽 Toilet +🪠 Plunger +🚿 Shower +🛁 Bathtub +🪤 Mouse trap +🪒 Razor +🧴 Lotion bottle +🧷 Safety pin +🧹 Broom +🧺 Basket +🧻 Roll of paper +🪣 Bucket +🧼 Soap +🫧 Bubbles +🪥 Toothbrush +🧽 Sponge +🧯 Fire extinguisher +🛒 Shopping cart +🚬 Cigarette +⚰ Casket +🪦 Headstone +⚱ Funeral urn +🗿 Mole +🪧 Placard +🪪 ID Card +🏧 ATM Sign +🚮 Litter in bin +🚰 Portable water +♿ Wheelchair symbol +🚹 Me +🚺 Wome +🚻 Restroom symbol +🚼 Baby symbol +🚾 Water closet +🛂 Passport control +🛂 Customs +🛄 Baggage claim +🛅 Left laugage +⚠ Warning +🚸 Children crossing +⛔ No entry +🚫 Prohibited +🚳 No bicycles +🚭 No smoking +🚯 No littering +🚱 Non-portable water +🚷 No pedestrians +📵 No mobile phones +🔞 No one under 18 +☢ Radioactive +☣ Biohazard +⬆ Up Arrow +↗ Up-right arrow +➡ Right arrow +↘ Down-right arrow +⬇ Down arrow +↙ Down-left arrow +⬅ Left arrow +↖ Up-left arrow +↕ Up-down arrow +↔ Left arrow +↩ Right arrow curving left +↪ Left arrow curving right +⤴ Right arrow curving up +⤵ Right arrow curving down +🔃 Clockwise vertical arrow +🔄 Counterclockwise arrows button +🔙 Back arrow +🔚 End arrow +🔛 On arrow +🔜 Soon arrow +🔝 Top arrow +🛐 Place of worship +⚛ Atom symbol +🕉 OM +✡ Star of David +☸ Wheel of Dharma +☯ Yin yang +✝ Latin cross +☦ ORthodox cross +☪ Star and cresent moon +☮ Peace +🕎 Menorah +🔯 Six-pointed star +♈ Aries +♉ Taurus +♊ Gemini +♋ Cancer +♌ Leo +♍ Virgo +♎ Libra +♏ Scorpio +♐ Sagittarius +♑ Capricon +♒ Acquarius +♓ Pisces +⛎ Ophiucus +🔀 Shuffle tracks +🔁 Repeat all +🔂 Repeat one +▶ Play +⏸ Pause +⏩ Fast-forward +⏭ Next track +⏯ Play or pause +◀ Reverse +⏪ Fast-reverse +⏮ Previous track +🔼 Upwards +⏫ Fst-up +🔽 Downwards +⏬ Fast down +⏹ Stop +⏺ Record +⏏ Eject +🎦 Cinema +🔅 Dim +🔆 Bright +📶 Network antenna bars +📳 Vibration mode +📴 Mobile phone off +♀ Female +♂ Male +⚧ Transgender +✖ Times +➕ Plus +➖ Minus +➗ Divide +🟰 Equals +♾ Infinity +‼ Double exclamation +⁉ Exclamation and question mark +❓ Red question mark +❔ White question mark +❗ Red exclamation mark +❕ White exclamation mark +〰 Wavy dash +💱 Currency exchange +💲 Heavy green dollar sign +⚕ Medical symbol +♻ Recycling symbol +⚜ Fleur-de-lis +🔱 Trident +📛 Name badge +🔰 Japanese symbol for beginner +⭕ Hollow red circle +✅ Green box with checkmark +☑ Blue box with checkmark +✔ Checkmark +❌ Crossmark +❎ Green crossmark +➰ Curly loop +➿ Double curly loop +〽 PArt alternation mark +✳ Eight-spoked asterik +✴ Eight-pointed star +❇ Sparkle +© Copyright symbol +® Registered +™ Trademark +*️⃣ * Keyca +0️⃣ 0 Keycap +1️⃣ 1 Keycap +2️⃣ 2 Keycap +3️⃣ 3 Keycap +4️⃣ 4 Keycap +5️⃣ 5 Keycap +6️⃣ 6 Keycap +7️⃣ 7 Keycap +8️⃣ 8 Keycap +9️⃣ 9 Keycap +🔟 10 Keycap +🔠 Input Latin uppercase +🔡 Input Latin lowercase +🔢 Input numbers +🔣 Input symbols +🔤 Input Latin letters +🅰 A blood type +🆎 AB blood type +🅱 B blood type +🅾 O blood type +🆑 CL button +🆒 Cool button +🆓 Free button +ℹ Info button +🆔 ID button +Ⓜ Circled M +🆕 New button +🆖 NG button +🆗 OK button +🅿 P button +🆘 SOS button +🆙 UP! button +🆚 VS Button +🈁 Japanese here button +🈂 Japanese service charge button +🈷 Japanese monthly amount button +🈶 Japanese not free of charge button +🈯 Japanese reserved button +🉐 Japanese bargain button +🈹 Japanese discount button +🈚 Japanese free of charge button +🈲 Japanese prohibited button +🉑 Japanese acceptable button +🈸 Japanese application button +🈴 Japanese passing grade button +🈳 Japanese vacancy button +㊗ Japanese congratulations button +㊙ Japanese secret button +🈺 Japanese open for business button +🈵 Japanese no vacancy button +🔴 Red circle +🟠 Orange circle +🟡 Yellow circle +🟢 Green circle +🔵 Blue circle +🟣 Purple circle +🟤 Brown circle +⚫ Black circle +⚪ White circle +🟥 Red square +🟧 Orange square +🟨 Yellow square +🟩 Green square +🟦 Blue square +🟪 Purple square +🟫 Brown square +⬛ Black square +⬜ White square +🔶 Large orange diamond +🔷 Large blue diamond +🔸 Small orange diamond +🔹 Small blue diamond +🔺 Red triangle pointed up +🔻 Red triangle pointed down +💠 Diamond with a dot +🔘 Radio button +🔳 White square button +🔲 Black square button +🏁 Chequered flag +🚩 Triangular flag +🎌 Crossed flag +🏴 Black flag +🏳 White flag +🏳️‍🌈 Rainbow flag +🏳️‍⚧️ Transgender flag +🏴‍☠️ Pirate flag +🇦🇨 Ascension Island flag +🇦🇩 Andorra flag +🇦🇪 UAE flag +🇦🇫 Afghanistan flag +🇦🇬 Antigua and Berbuda flag +🇦🇮 Anguila flag +🇦🇱 Albania flag +🇦🇲 Armenia flag +🇦🇴 Angola flag +🇦🇶 Antarctica flag +🇦🇷 Argentina flag +🇦🇸 American Samoa flag +🇦🇹 Austria flag +🇦🇺 Australia flag +🇦🇼 Aruba flag +🇦🇽 Åland Islands flag +🇦🇿 Azerbaijan flag +🇧🇦 Bosnia flag +🇧🇧 Barbados flag +🇧🇩 Bangladesh flag +🇧🇪 Belgium flag +🇧🇫 Burkina Faso flag +🇧🇬 Bulgaria flag +🇧🇭 Bahrain flag +🇧🇮 Burundi flag +🇧🇯 Benin Republic flag +🇧🇱 St. Barthélemy +🇧🇲 Bermuda flag +🇧🇳 Brunei flag +🇧🇴 Bolivia flag +🇧🇶 Caribbean Netherlands flag +🇧🇷 Brazil flag +🇧🇸 Bahamas flag +🇧🇹 Bhutan flag +🇧🇻 Bouvet Island flag +🇧🇼 Botswana flag +🇧🇾 Belarus flag +🇧🇿 Belize flag +🇨🇦 Canada flag +🇨🇨 Cocos Islands flag +🇨🇩 Congo DR flag +🇨🇫 CAR (Central African Republic) flag +🇨🇬 Congo Brazzaville flag +🇨🇭 Switzerland flag +🇨🇮 Côte d’Ivoire flag +🇨🇰 Cook Islands flag +🇨🇱 Chile flag +🇨🇲 Cameroun flag +🇨🇳 China flag +🇨🇴 Columbia flag +🇨🇵 Clipperton Island flag +🇨🇷 Costa Rica flag +🇨🇺 Cuba flag +🇨🇻 Cape Verde Island flag +🇨🇼 Curaçao flag +🇨🇽 Christmas Island flag +🇨🇾 Cyprus flag +🇩🇪 Germany flag +🇩🇬 Diego Garcia flag +🇩🇯 Djibouti flag +🇩🇰 Denmark flag +🇩🇲 Dominica flag +🇩🇴 Dominican Republic flag +🇩🇿 Algeria flag +🇪🇦 Ceuta +🇪🇨 Ecuador flag +🇪🇪 Estonia +🇪🇬 Egypt flag +🇪🇭 Western Sahara flag +🇪🇷 Eritrea flag +🇪🇸 Spain flag +🇪🇹 Ethiopia flag +🇪🇺 EU flag +🇫🇮 Finland flag +🇫🇯 Fiji flag +🇫🇰 Falkland Island flag +🇫🇲 Micronesia flag +🇫🇴 Faroe Island flag +🇫🇷 France flag +🇬🇦 Gabon flag +🇬🇧 United Kingdom flag +🇬🇩 Grenada flag +🇬🇪 Georgia flag +🇬🇫 French Guiana flag +🇬🇬 Guernsey flag +🇬🇭 Ghana flag +🇬🇮 Gibraltar flag +🇬🇱 Greenland flag +🇬🇲 Gambia flag +🇬🇳 Guinea flag +🇬🇵 Guadeloupe flag +🇬🇶 Equitorial Guinea flag +🇬🇷 Greece flag +🇬🇸 South Georgia +🇬🇹 Guatemala flag +🇬🇺 Guam flag +🇬🇼 Guinea Bissau flag +🇬🇾 Guyana flag +🇭🇰 Hong Kong flag +🇭🇲 Heard +🇭🇳 Honduras flag +🇭🇷 Croatia flag +🇭🇹 Haiti flag +🇭🇺 Hungary flag +🇮🇨 Canary Islands flag +🇮🇩 Indonesia flag +🇮🇪 Island flag +🇮🇱 Israel flag +🇮🇲 Isle of Man flag +🇮🇳 India flag +🇮🇴 British Indian Ocean Territory flag +🇮🇶 Iraq flag +🇮🇷 Iran flag +🇮🇸 Iceland flag +🇮🇹 Italy flag +🇯🇪 Jersey flag +🇯🇲 Jamaica flag +🇯🇴 Jordan flag +🇯🇵 Japan flag +🇰🇪 Kenya flag +🇰🇬 Kyrgyzstan flag +🇰🇭 Cambodia flag +🇰🇮 Kiribati flag +🇰🇲 Comoros Island flag +🇰🇳 St. Kitts +🇰🇵 North Korea flag +🇰🇷 South Korea flag +🇰🇼 Kuwait flag +🇰🇾 Cayman Island flag +🇰🇿 Kazakhstan flag +🇱🇦 Laos flag +🇱🇧 Lebanon flag +🇱🇨 St. Lucia flag +🇱🇮 Liechtenstein flag +🇱🇰 Sri Lanka flag +🇱🇷 Liberia flag +🇱🇸 Lesotho flag +🇱🇹 LIthuania flag +🇱🇻 Latvia flag +🇱🇾 Libya flag +🇲🇦 Morocco flag +🇲🇨 Monaco flag +🇲🇩 Moldova flag +🇲🇪 Montenegro flag +🇲🇬 Montenegro flag +🇲🇭 Marshall Islands flag +🇲🇰 North Macedonia flag +🇲🇱 Mali flag +🇲🇲 Myanmar flag +🇲🇳 Mongolia flag +🇲🇴 Macao flag +🇲🇵 Northern Mariana Islands flag +🇲🇶 Marthinique flag +🇲🇷 Mauritania flag +🇲🇸 Montserrat flag +🇲🇹 Malta flag +🇲🇺 Mauritius flag +🇲🇻 Maldives flag +🇲🇼 Malawi flag +🇲🇽 Mexico flag +🇲🇾 Malaysia flag +🇲🇿 Mozambique flag +🇳🇦 Namibia flag +🇳🇨 New Caledonia flag +🇳🇪 Niger flag +🇳🇫 Norfolk Island flag +🇳🇬 Nigeria flag +🇳🇮 Nicaragua flag +🇳🇱 Netherlands flag +🇳🇴 Norway flag +🇳🇵 Nepal flag +🇳🇷 Nauru flag +🇳🇺 Niue flag +🇳🇿 New Zealand flag +🇴🇲 Oman flag +🇵🇦 Panama flag +🇵🇪 Peru flag +🇵🇫 French Polynesia flag +🇵🇬 Papua New Guinea flag +🇵🇭 Philipines flag +🇵🇰 Pakistan flag +🇵🇱 Poland flag +🇵🇲 St. Pierre +🇵🇳 Pitcairn Islands flag +🇵🇷 Puerto Rico flag +🇵🇸 Palestinian Territories flag +🇵🇹 Portugal flag +🇵🇼 Palau flag +🇵🇾 Paraguay flag +🇶🇦 Qatar flag +🇷🇪 Réunion flag +🇷🇸 Serbia flag +🇷🇺 Russia flag +🇷🇼 Rwanda flag +🇸🇦 Saudi Arabia flag +🇸🇧 Solomon Islands +🇸🇨 Seychelles flag +🇸🇩 Sudan flag +🇸🇪 Sweeden flag +🇸🇬 Singapore flag +🇸🇭 St. Helena flag +🇸🇮 Slovenia flag +🇸🇯 Svalbard +🇸🇰 Slovakia flag +🇸🇱 Sierra Leone flag +🇸🇲 San Marino flag +🇸🇳 Senegal flag +🇸🇴 Somalia flag +🇸🇷 Suriname flag +🇸🇸 South Sudan flag +🇸🇹 São Tomé +🇸🇻 El Salvador flag +🇸🇽 Saint Maarten flag +🇸🇾 Syria flag +🇸🇿 Eswatini (Swaziland) flag +🇹🇦 Tristan da Cunha flag +🇹🇨 Turks +🇹🇩 Chad flag +🇹🇫 French Southern Territories flag +🇹🇬 Togo flag +🇹🇭 Thailand flag +🇹🇯 Tajikistan flag +🇹🇰 Tokelau flag +🇹🇱 Timor-Leste flag +🇹🇲 Turkmenistan flag +🇹🇳 Tunisia flag +🇹🇴 Tonga flag +🇹🇷 Turkey flag +🇹🇹 Trinidad and Tobago flag +🇹🇻 Tuvalu flag +🇹🇼 Taiwan flag +🇹🇿 Tanzania flag +🇺🇦 Ukraine flag +🇺🇬 Uganda flag +🇺🇲 US Outlying Islands flag +🇺🇳 United Nations (UN) flag +🇺🇸 United State flag +🇺🇾 Uruguay flag +🇺🇿 Uzbekistan flag +🇻🇦 Vatical City flag +🇻🇨 St. Vincent +🇻🇪 Venezuela flag +🇻🇬 British Virgin Islands flag +🇻🇮 U.S. Virgin Islands +🇻🇮 Vietnam flag +🇻🇺 Vanuatu flag +🇼🇫 Wallis +🇼🇸 Samoa flag +🇽🇰 Kosovo flag +🇾🇪 Yemen flag +🇾🇹 Mayotte flag +🇿🇦 South Africa flag +🇿🇲 Zambia flag +🇿🇼 Zimbabwe flag" +} + +SWEDISH_FINNISH_CHARACTERS_LIST() +{ +printf "å +ä +ö" +} + +SPANISH_CHARACTERS_LIST() +{ +printf "á +é +í +ó +ú +¿ +¡ +ü +ñ" +} + +CHINESE_CHARACTERS_LIST() +{ +printf "安 ān +吧 ba +八 bā +爸 bā +百 bǎi +北 běi +不 bù - bú +大 dà - dài +岛 dǎo +的 de - dí - dì +弟 dì +地 dì - de +东 dōng +都 dōu - dū +对 duì +多 duō +儿 ér +二 èr +方 fāng +港 gǎng +哥 gē +个 gè - ge +关 guān +贵 guì +国 guó +过 guò +海 hǎi +好 hǎo - hào +很 hěn +会 huì - kuài +家 jiā +见 jiàn +叫 jiào +姐 jiě +京 jīng +九 jiǔ +可 kě +老 lǎo +李 lǐ +零 líng +六 liù +吗 ma - má - mǎ +妈 mā +么 me - yāo +没 méi - mò +美 měi +妹 mèi +们 men +名 míng +明 míng +哪 nǎ +那 nà - nèi +南 nán +你 nǐ +您 nín +朋 péng +七 qī +起 qǐ +千 qiān +去 qù +人 rén +认 rèn +日 rì +三 sān +上 shàng +谁 shéi - shuí +什 shén - shí +生 shēng +师 shī +十 shí +识 shí +是 shì +四 sì +他 tā +她 tā +台 tái +天 tiān +湾 wān +万 wàn +王 wáng +我 wǒ +五 wǔ +西 xī +息 xí +系 xì - jì +先 xiān +香 xiāng +想 xiǎng +小 xiǎo +谢 xiè +姓 xìng +休 xiū +学 xué +也 yě +一 yī +亿 yì +英 yīng +友 yǒu +月 yuè +再 zài +张 zhāng +这 zhè +中 zhōng - zhòng +字 zì" +} + +BAYBAIN_CHARACTERS_LIST() +{ +printf "ᜀ +ᜁ +ᜂ +ᜃ +ᜄ +ᜅ +ᜆ +ᜇ +ᜈ +ᜉ +ᜊ +ᜋ +ᜌ +ᜍ +ᜎ +ᜏ +ᜐ +ᜑ +ᜃᜒ +ᜄᜒ +ᜅᜒ +ᜆᜒ +ᜇᜒ +ᜈᜒ +ᜉᜒ +ᜊᜒ +ᜋᜒ +ᜌᜒ +ᜍᜒ +ᜎᜒ +ᜏᜒ +ᜐᜒ +ᜑᜒ +ᜃᜓ +ᜄᜓ +ᜅᜓ +ᜆᜓ +ᜇᜓ +ᜈᜓ +ᜉᜓ +ᜊᜓ +ᜋᜓ +ᜌᜓ +ᜍᜓ +ᜎᜓ +ᜏᜓ +ᜐᜓ +ᜑᜓ +ᜃ᜔ +ᜄ᜔ +ᜅ᜔ +ᜆ᜔ +ᜇ᜔ +ᜈ᜔ +ᜉ᜔ +ᜊ᜔ +ᜋ᜔ +ᜌ᜔ +ᜍ᜔ +ᜎ᜔ +ᜏ᜔ +ᜐ᜔ +ᜑ᜔ +᜶ᜒ +᜶ᜓ +᜶᜔ " +} + +ARABIC_CHARACTERS_LIST() +{ +printf "ء hamza +ا alef +آ alef +أ alef with hamza above +إ alef with hamza below +ى alef maksura +ب beh +ة teh marbuta +ت teh +ث theh +ج jeem +ح hah +خ khah +د dal +ذ thal +ر reh +ز zain +س seen +ش sheen +ص sad +ض dad +ط tah +ظ zah +ع ain +غ ghain +ـ tatweel +ف feh +ق qaf +ك kaf +ل lam +م meem +ن noon +ه heh +و waw +ؤ waw with hamza above +ي yeh +ئ yeh with hamza above" +} + +JAPANESE_CHARACTERS_LIST() +{ +printf "あ a +い i +う o +え e +お o +は ha +ひ hi +ふ fu +へ he +ほ ho +か kka +き ki +く ko +け ke +こ ko +ま ma +み mi +む mu +め me +も mo +さ sa +し shi +す su +せ se +そ so +や ya +ゆ yu +よ yo +た ta +ち chi +つ tsu +て te +と to +ら ra +り ri +る ru +れ re +ろ ro +な na +に ni +ぬ nu +ね ne +の no +わ wa +ゐ wi +ん n +ゑ we +を wo" +} + +ROMANIAN_CHARACTERS_LIST() +{ +printf "ă +â +î +ș +ț" +} + +JAPANESECOPY() +{ + JAPANESE_LETTER_CHOICE="$(printf "$(JAPANESE_CHARACTERS_LIST)" | $RUNLAUNCHER -l 5 -p 'Pick a character: ' | awk '{ print $1 }')" + printf "$JAPANESE_LETTER_CHOICE" | xclip -selection clipboard && notify-send "$JAPANESE_LETTER_CHOICE copied to clipboard." +} + +ARABICCOPY() +{ + ARABIC_LETTER_CHOICE="$(printf "$(ARABIC_CHARACTERS_LIST)" | $RUNLAUNCHER -l 5 -p 'Pick a character: ' | awk '{ print $1 }')" + printf "$ARABIC_LETTER_CHOICE" | xclip -selection clipboard && notify-send "$ARABIC_LETTER_CHOICE copied to clipboard." +} + +BAYBAINCOPY() +{ + BAYBAIN_LETTER_CHOICE="$(printf "$(BAYBAIN_CHARACTERS_LIST)" | $RUNLAUNCHER -l 5 -p 'Pick a character: ')" + printf "$BAYBAIN_LETTER_CHOICE" | xclip -selection clipboard && notify-send "$BAYBAIN_LETTER_CHOICE copied to clipboard." +} + +SPANISHCOPY() +{ + SPANISH_LETTER_CHOICE="$(printf "$(SPANISH_CHARACTERS_LIST)" | $RUNLAUNCHER -l 5 -p 'Pick a character: ')" + printf "$SPANISH_LETTER_CHOICE" | xclip -selection clipboard && notify-send "$SPANISH_LETTER_CHOICE copied to clipboard." +} + +CHINESECOPY() +{ + CHINESE_LETTER_CHOICE="$(printf "$(CHINESE_CHARACTERS_LIST)" | $RUNLAUNCHER -l 5 -p 'Pick a character: ')" + printf "$CHINESE_LETTER_CHOICE" | awk '{ print $1 }' | xclip -selection clipboard && notify-send "$(printf "$CHINESE_LETTER_CHOICE" | awk '{ print $1 }') copied to clipboard." +} + +SWEDISHFINNISHCOPY() +{ + SWEDISH_LETTER_CHOICE="$(printf "$(SWEDISH_FINNISH_CHARACTERS_LIST)" | $RUNLAUNCHER -l 5 -p 'Pick a character: ')" + printf "$SWEDISH_LETTER_CHOICE" | xclip -selection clipboard && notify-send "$SWEDISH_LETTER_CHOICE copied to clipboard." +} + +ROMANIANCOPY() +{ + ROMANIAN_LETTER_CHOICE="$(printf "$(ROMANIAN_CHARACTERS_LIST)" | $RUNLAUNCHER -l 5 -p 'Pick a character: ')" + printf "$ROMANIAN_LETTER_CHOICE" | xclip -selection clipboard && notify-send "$ROMANIAN_LETTER_CHOICE copied to clipboard." +} + +COPYPASTALIST() +{ + printf "I'd just like to interject for a moment. What you're referring to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.Many computer users run a modified version of the GNU system every day, without realizing it. Through a peculiar turn of events, the version of GNU which is widely used today is often called Linux, and many of its users are not aware that it is basically the GNU system, developed by the GNU Project. There really is a Linux, and these people are using it, but it is just a part of the system they use. Linux is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. Linux is normally used in combination with the GNU operating system: the whole system is basically GNU with Linux added, or GNU/Linux. All the so-called Linux distributions are really distributions of GNU/Linux. +No, Richard, it's 'Linux', not 'GNU/Linux'. The most important contributions that the FSF made to Linux were the creation of the GPL and the GCC compiler. Those are fine and inspired products. GCC is a monumental achievement and has earned you, RMS, and the Free Software Foundation countless kudos and much appreciation. Following are some reasons for you to mull over, including some already answered in your FAQ. One guy, Linus Torvalds, used GCC to make his operating system (yes, Linux is an OS -- more on this later). He named it 'Linux' with a little help from his friends. Why doesn't he call it GNU/Linux? Because he wrote it, with more help from his friends, not you. You named your stuff, I named my stuff -- including the software I wrote using GCC -- and Linus named his stuff. The proper name is Linux because Linus Torvalds says so. Linus has spoken. Accept his authority. To do otherwise is to become a nag. You don't want to be known as a nag, do you? (An operating system) != (a distribution). Linux is an operating system. By my definition, an operating system is that software which provides and limits access to hardware resources on a computer. That definition applies wherever you see Linux in use. However, Linux is usually distributed with a collection of utilities and applications to make it easily configurable as a desktop system, a server, a development box, or a graphics workstation, or whatever the user needs. In such a configuration, we have a Linux (based) distribution. Therein lies your strongest argument for the unwieldy title 'GNU/Linux' (when said bundled software is largely from the FSF). Go bug the distribution makers on that one. Take your beef to Red Hat, Mandrake, and Slackware. At least there you have an argument. Linux alone is an operating system that can be used in various applications without any GNU software whatsoever. Embedded applications come to mind as an obvious example. Next, even if we limit the GNU/Linux title to the GNU-based Linux distributions, we run into another obvious problem. XFree86 may well be more important to a particular Linux installation than the sum of all the GNU contributions. More properly, shouldn't the distribution be called XFree86/Linux? Or, at a minimum, XFree86/GNU/Linux? Of course, it would be rather arbitrary to draw the line there when many other fine contributions go unlisted. Yes, I know you've heard this one before. Get used to it. You'll keep hearing it until you can cleanly counter it. You seem to like the lines-of-code metric. There are many lines of GNU code in a typical Linux distribution. You seem to suggest that (more LOC) == (more important). However, I submit to you that raw LOC numbers do not directly correlate with importance. I would suggest that clock cycles spent on code is a better metric. For example, if my system spends 90% of its time executing XFree86 code, XFree86 is probably the single most important collection of code on my system. Even if I loaded ten times as many lines of useless bloatware on my system and I never excuted that bloatware, it certainly isn't more important code than XFree86. Obviously, this metric isn't perfect either, but LOC really, really sucks. Please refrain from using it ever again in supporting any argument. Last, I'd like to point out that we Linux and GNU users shouldn't be fighting among ourselves over naming other people's software. But what the heck, I'm in a bad mood now. I think I'm feeling sufficiently obnoxious to make the point that GCC is so very famous and, yes, so very useful only because Linux was developed. In a show of proper respect and gratitude, shouldn't you and everyone refer to GCC as 'the Linux compiler'? Or at least, 'Linux GCC'? Seriously, where would your masterpiece be without Linux? Languishing with the HURD? If there is a moral buried in this rant, maybe it is this: Be grateful for your abilities and your incredible success and your considerable fame. Continue to use that success and fame for good, not evil. Also, be especially grateful for Linux' huge contribution to that success. You, RMS, the Free Software Foundation, and GNU software have reached their current high profiles largely on the back of Linux. You have changed the world. Now, go forth and don't be a nag. Thanks for listening. +Stop using the term 'Open Source'. By supporting 'open source', you're not supporting software that respects the user's freedom. Instead you're supporting "fake" freedom. If that's what you actually support, please use the term 'Free software' or 'Libre software' instead. If you support 'open source' you're supporting companies Microsoft, Google, Apple and many more who don't care about your (free)dom but just the "collaborate" aspect of open source. They like open source because they get YOUR (as in people who write code for the project) work and your code and you get nothing in return. Open source is important but it's just part of freedom (specifically freedom 1 and 3). In addition to this, many people who are unaware of the free software movement/project think these big tech companies are nice people who care about their users when this is far from the truth. Open source does not guarantee the user's freedom. This is why tech companies support 'Open source' but not free software. Free software is evil to them because they want control, something free software doesn't and cannot allow. If you support 'open source' then you don't deserve anything except the evil you're responsible for. + " +} + +COPYPASTACOPY() +{ + COPYPASTASEL="$(printf "$(COPYPASTALIST)" | $RUNLAUNCHER -l 5 -p 'Pick a copypasta: ')" + printf "$COPYPASTASEL" | xclip -selection clipboard && notify-send "$COPYPASTASEL copied to clipboard." +} + +EMOJICOPY() +{ + EMOJISEL="$(printf "$(EMOJILIST)" | $RUNLAUNCHER -l 5 -p 'Pick an emoji: ' | awk '{ print $1 }')" + printf $EMOJISEL | xclip -selection clipboard && notify-send "$EMOJISEL copied to clipboard." +} + +DBOARD_CREDITS() +{ + printf "speedie - Wrote the script & Swedish characters\njornmann - Helped out with some code\nLuke Smith - The idea\nLucss21a - Baybayin characters\nDamaj301Damaj - Arabic characters" +} + +DBOARD_ABOUT() +{ + printf "dboard 0.2\nReleased and licensed to you (the end user) under the GNU GPLv3 free software license.\nYou are using this software at your own risk." +} + +########################################################### + +# Ask the user what they wanna do +ACTION=$(printf "Emoji\nCopypasta\nSwedish/Finnish letters\nSpanish letters\nChinese letters\nJapanese letters\nBaybain letters\nArabic letters\nRomanian letters\nCredits\nAbout\nExit" | $RUNLAUNCHER -p 'What would you like to copy?' -l 20) + +if [ "$ACTION" = "Emoji" ]; then +EMOJICOPY +elif [ "$ACTION" = "Copypasta" ]; then +COPYPASTACOPY +elif [ "$ACTION" = "Swedish/Finnish letters" ]; then +SWEDISHFINNISHCOPY +elif [ "$ACTION" = "Spanish letters" ]; then +SPANISHCOPY +elif [ "$ACTION" = "Chinese letters" ]; then +CHINESECOPY +elif [ "$ACTION" = "Japanese letters" ]; then +JAPANESECOPY +elif [ "$ACTION" = "Baybain letters" ]; then +BAYBAINCOPY +elif [ "$ACTION" = "Arabic letters" ]; then +ARABICCOPY +elif [ "$ACTION" = "Romanian letters" ]; then +ROMANIANCOPY +elif [ "$ACTION" = "Credits" ]; then +CREDIT_SEL=$(printf "$(DBOARD_CREDITS)" | $RUNLAUNCHER -p 'dboard was created with the help of and by: ' -l 20 -g 1) +case "$CREDIT_SEL" in +"speedie - Wrote the script & Swedish characters") $BROWSER 'https://speedie.gq' ;; +"jornmann - Helped out with some code") $BROWSER 'https://donut.gq' ;; +"Luke Smith - The idea") $BROWSER 'https://lukesmith.xyz' ;; +"Lucss21a - Baybayin characters") $BROWSER 'https://alexisgaming95.neocities.org' ;; +"Damaj301Damaj - Arabic characters") $BROWSER 'https://github.com/Damaj301damaj-lol' ;; +"Gabubu - Romanian characters") $BROWSER 'https://github.com/GabubuAvailable' ;; +esac +elif [ "$ACTION" = "Exit" ]; then +exit 0 +elif [ "$ACTION" = "About" ]; then +printf "$(DBOARD_ABOUT)" | $RUNLAUNCHER -p 'About dboard: ' -l 20 -g 1 +fi + +exit 0 diff --git a/scripts/speedwm-winnav b/scripts/speedwm-winnav new file mode 100755 index 0000000..85f0597 --- /dev/null +++ b/scripts/speedwm-winnav @@ -0,0 +1,98 @@ +#!/bin/sh +# speedwm-winnav +# Alt+tab navigation for speedwm +# +# This script is from the suckless.org website. +# I made the following changes to it: +# - POSIX compliant +# - Tab goes down, j/k to navigate +# - Removed class from $windows making it look nicer +# - Configuration file +# - dmenu center patch support +# - Other small improvements. + +RESTORE() { + if [ -e "${BINDIR}xmodmap" ]; then + xmodmap -e "keycode 23 = Tab" + xmodmap -e "keycode 116 = Down" + + # Vim + xmodmap -e "keycode 44 = j" + xmodmap -e "keycode 45 = k" + fi +} + +case "$RUNLAUNCHER" in +"") RUNLAUNCHER=dmenu ;; +esac + +BINDIR=$(cat /usr/share/speedwm-bindir) +DOTDIR=$HOME/.config/speedwm-de/winnav +POSITION="bottom" # top, bottom, center (your $RUNLAUNCHER must support center for this to work) + +mkdir -p $HOME/.config/speedwm-de/winnav + +# Create config if it does not exist. +if [ -e "$HOME/.config/speedwm-de/winnav/config" ]; then + . $HOME/.config/speedwm-de/winnav/config +else + printf "POSITION=$POSITION # top, bottom, center (Your installed $RUNLAUNCHER must support center (-c) for this to work)" > $HOME/.config/speedwm-de/winnav/config +fi + +# Set position based on config +case "$POSITION" in +"bottom") POSARG="-b" ;; +"top") POSARG="" ;; +"center") POSARG="-c" ;; +esac + +# Check if wmctrl is available +if [ -e "${BINDIR}wmctrl" ]; then + echo "wmctrl found" + + case "$(wmctrl -xl | wc -l)" in + "") echo "No clients found." ; exit 1 ;; + "1") echo "One client running, will not open menu." ; exit 0 ;; + esac +else + echo "wmctrl not found, exiting." && exit 1 +fi + +SET() { + if [ -e "${BINDIR}xmodmap" ]; then + xmodmap -e "keycode 23 = Down" + xmodmap -e "keycode 116 = Tab" + + # Vim + xmodmap -e "keycode 44 = Down" + xmodmap -e "keycode 45 = Up" + fi +} + +SET + +windows=$(wmctrl -xl | tr -s '[:blank:]' | cut -d ' ' -f 3-3,5- | sed 's/^[a-zA-Z0-9-]*\.//' | sort | uniq) +echo "Windows are:\n $windows\n============================" +windows_class=$(echo $windows | awk '{ print $1 }') +echo "Class is:\n $windows_class\n============================" + +# Add spaces to align the WM_NAMEs of the windows +max=$(echo "$windows" | awk '{cur=length($1); max=(cur>max?cur:max)} END{print max}') +echo "Max is:\n$max" + +windows=$(echo "$windows" | \ + awk -v max="$max" \ + '{cur=length($1); printf $1; \ + for(i=0; i < max - cur + 1; i++) printf " "; \ + $1 = ""; printf "%s\n", $0}') + + +target=$(printf "\n$windows" | cut -f 2- -d ' ' | sed 's/^ *//g' | $RUNLAUNCHER $POSARG -l 10 -g 1 -p "Which window?" | tr -s '[:blank:]' | sed "s/$class | //g") + +case "$target" in +"") RESTORE && exit 0 ;; +esac + +wmctrl -a "$target" && echo "Switched focus" + +RESTORE diff --git a/scripts/speedwm_run b/scripts/speedwm_run new file mode 100755 index 0000000..569295c --- /dev/null +++ b/scripts/speedwm_run @@ -0,0 +1,158 @@ +#!/bin/sh +# External script which starts speedwm! +# https://speedie.gq/speedwm for instructions and usage! + +BINDIR=$(cat /usr/share/speedwm-bindir) +ARGS=$1 + +# No second argument is supported +if [ "$2" = "" ]; then + ARGS2="" +else + echo "Unknown argument: $2" ; exit 1 +fi + +# Don't make config if asked not to. +if [ -e "$HOME/.local/share/speedwm-do-not-mkconfig" ]; then + DONOTMKCONFIG=true +else + DONOTMKCONFIG=false +fi + +# Load .Xresources if available +XRESOURCES() { + pgrep -x status > /dev/null && pkill status + if [ -e "$HOME/.Xresources" ]; then + if [ -e "${BINDIR}xrdb" ]; then + xrdb $HOME/.Xresources + fi + elif [ -e "$HOME/.config/.Xresources" ]; then + if [ -e "${BINDIR}xrdb" ]; then + xrdb $HOME/.config/.Xresources + fi + fi +} + +# Load speedwm config, create if it does not exist. +LOADCONFIG() { + mkdir -p $HOME/.config/speedwm-de + if [ -e "${BINDIR}xrdb" ]; then + if [ "$DONOTMKCONFIG" = "false" ]; then + if [ -e "$HOME/.config/speedwm-de/speedwmrc" ]; then + xrdb -merge -quiet $HOME/.config/speedwm-de/speedwmrc + else + echo "! dynamic window manager configuration file" > $HOME/.config/speedwm-de/speedwmrc && echo "Wrote comment 1" + echo "! This is your speedwm configuration file. It is configured in .Xresources syntax." >> $HOME/.config/speedwm-de/speedwmrc && echo "Wrote comment 2" + echo "! It is loaded on startup but you can reload it during runtime by running $0 -r.\n" >> $HOME/.config/speedwm-de/speedwmrc && echo "Wrote comment 3" + sed "s| - ||g" /usr/share/example.Xresources >> $HOME/.config/speedwm-de/speedwmrc && echo "Wrote example configuration file" + fi + + fi + fi +} + +LOADSWAL() { + if [ "$DONOTLOADCONFIG" = "true" ]; then + DONOTLOADCONFIG=true + else + $HOME/.config/speedwm-de/swal/swal_wm > /dev/null + echo "Loaded wallpaper and pywal colors" + fi + +} + +# Reset speedwm +RESET() { + if [ "$DONOTRELOAD" = "true" ]; then + DONOTRELOAD=true + else + xsetroot -name "Loading" # To hide the ugly fsignal status + xsetroot -name "fsignal:31" # Send fsignal to restart speedwm + xsetroot -name "Loading" # To hide the ugly fsignal status + fi +} + +# Check for running window managers +CHECKEXISTINGWM() { + if [ "$CARG" = "force" ]; then + echo "WARNING: Bypassing all checks (due to -f argument). Use this with caution!" + else + pgrep -x speedwm > /dev/null && echo "speedwm is already running. Use $0 -r to restart it." + pgrep -x speedwm > /dev/null && exit 1 + fi +} + +# Start speedwm itself +START_DWM() { + speedwm-audioctrl -remute # Fix a weird bug + while true; do + speedwm > /tmp/speedwm-log + done +} + +# Config +CONFIGURE() { + test $HOME/.config/speedwm-de/speedwmrc && DOTFILE1="$HOME/.config/speedwm-de/speedwmrc" + test $HOME/.config/speedwm-de/dfmpeg/config && DOTFILE2="$HOME/.config/speedwm-de/dfmpeg/config" + test $HOME/.config/speedwm-de/powermenu/config && DOTFILE3="$HOME/.config/speedwm-de/powermenu/config" + test $HOME/.config/speedwm-de/screenshotutil/config && DOTFILE4="$HOME/.config/speedwm-de/screenshotutil/config" + test $HOME/.config/speedwm-de/status/config && DOTFILE5="$HOME/.config/speedwm-de/status/config" + test $HOME/.config/speedwm-de/swal/config && DOTFILE6="$HOME/.config/speedwm-de/swal/config" + test $HOME/.config/speedwm-de/systray/config && DOTFILE7="$HOME/.config/speedwm-de/systray/config" + test $HOME/.config/speedwm-de/winnav/config && DOTFILE8="$HOME/.config/speedwm-de/winnav/config" + + case "$TERMINAL" in + "") TERMINAL="st -e" + esac + + case "$EDITOR" in + "") EDITOR='vim' + esac + + case "$RUNLAUNCHER" in + "") RUNLAUNCHER=dmenu + esac + + $TERMINAL $EDITOR "$(printf "$DOTFILE1\n$DOTFILE2\n$DOTFILE3\n$DOTFILE4\n$DOTFILE5\n$DOTFILE6\n$DOTFILE7\n$DOTFILE8" | $RUNLAUNCHER -l 8 -g 1 -p "Edit which configuration?")" +} + +# List of arguments +HELP() { + printf "dynamic window manager\nNo arguments | Start speedwm using default options\n-f | Start speedwm bypassing all checks (such as whether or not a window manager is running)\n-noxrdb | Start speedwm without loading colors.\n-nomkconfig | Start speedwm without creating a config file (or loading it)\n-h | View this list of arguments\n-r | Restart speedwm and reload colors\n-loadconfig | Load and create config if it does not exist\n-unloadconfig | Unload the speedwm configuration\n-deleteconfig | Delete the speedwm configuration\n-rnoload | Restart speedwm without reloading colors\n-configure | Bring up a list of speedwm-de dotfiles in $RUNLAUNCHER and ask the user which one to open in \$EDITOR inside \$TERMINAL" +} + +# Unload speedwm configuration +UNLOADCONFIG() { + if [ -e "${BINDIR}xrdb" ]; then + xrdb -remove speedwm* && echo "Unloaded configuration" + else + echo "Unable to unload configuration" ; exit 1 + fi +} + +# Delete speedwm configuration +DELETECONFIG() { + if [ -e "$HOME/.config/speedwm-de/speedwmrc" ]; then + rm -f $HOME/.config/speedwm-de/speedwmrc && echo "Deleted $HOME/.config/speedwm-de/speedwmrc." + else + echo "Failed to delete configuration file." ; exit 1 + fi +} + +case "$ARGS" in +"") CHECKEXISTINGWM ; XRESOURCES ; LOADSWAL ; LOADCONFIG ; RESET ; START_DWM ; exit 0 ;; +"-configure") CONFIGURE ; exit 0 ;; +"-r") XRESOURCES ; LOADSWAL ; LOADCONFIG ; RESET ; echo "Restarted speedwm." ; exit 0 ;; +"-rnoload") RESET ; echo "Restarted speedwm." ; exit 0 ;; +"-h") HELP ; exit 0 ;; +"-f") CARG="force" ; XRESOURCES ; LOADSWAL ; LOADCONFIG ; RESET ; START_DWM ; exit 0 ;; +"-loadconfig") LOADCONFIG ; exit 0 ;; +"-unloadconfig") UNLOADCONFIG ; RESET ; exit 0 ;; +"-deleteconfig") DELETECONFIG ; RESET ; exit 0 ;; +"-noxrdb") START_DWM ; exit 0 ;; +"-stop") pgrep -x speedwm && pkill speedwm ; echo "speedwm has been stopped." ; exit 0 + echo "speedwm is not running, cannot stop it." ; exit 1 ;; +"-nomkconfig") CHECKEXISTINGWM ; XRESOURCES ; LOADSWAL ; RESET ; START_DWM ; exit 0 ;; +esac + +exit 0 diff --git a/speedwm b/speedwm new file mode 100755 index 0000000..e9183ff Binary files /dev/null and b/speedwm differ diff --git a/speedwm.c b/speedwm.c new file mode 100644 index 0000000..97a6c91 --- /dev/null +++ b/speedwm.c @@ -0,0 +1,5321 @@ +/* See LICENSE file for copyright and license details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include +#include +#include +#include +#ifdef __OpenBSD__ +#include +#include +#endif /* __OpenBSD */ + +#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)->mw) - MAX((x),(m)->mx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->mh) - MAX((y),(m)->my))) +#define INTERSECTC(x,y,w,h,z) (MAX(0, MIN((x)+(w),(z)->x+(z)->w) - MAX((x),(z)->x)) \ + * MAX(0, MIN((y)+(h),(z)->y+(z)->h) - MAX((y),(z)->y))) +#define HIDDEN(C) ((getstate(C->win) == IconicState)) +#define ISVISIBLEONTAG(C, T) ((C->tags & T)) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) +#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 TAGMASK ((1 << LENGTH(tags)) - 1) +#define TAGSLENGTH (LENGTH(tags)) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define RIGHTOF(a,b) (a.y_org > b.y_org) || \ + ((a.y_org == b.y_org) && (a.x_org > b.x_org)) + +#define OPAQUE 0xffU +#define TRANSPARENT 0 + +#define MWM_HINTS_FLAGS_FIELD 0 +#define MWM_HINTS_DECORATIONS_FIELD 2 +#define MWM_HINTS_DECORATIONS (1 << 1) +#define MWM_DECOR_ALL (1 << 0) +#define MWM_DECOR_BORDER (1 << 1) +#define MWM_DECOR_TITLE (1 << 3) + +/* enums */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + +/* color schemes */ +enum { SchemeNormBorder, + SchemeSelBorder, + SchemeTags, + SchemeBar, + SchemeHid, + SchemeLayout, + SchemeNormTitle, + SchemeSelTitle, + SchemeStatus, +}; + +enum { NetSupported, NetWMName, NetWMIcon, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDesktop, + NetWMWindowTypeDialog, NetClientList, NetDesktopNames, NetDesktopViewport, NetNumberOfDesktops, NetCurrentDesktop, NetWMWindowsOpacity, NetClientListStacking, NetClientInfo, NetLast }; /* EWMH atoms */ +enum { WMClass, WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +typedef struct Monitor Monitor; +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + float cfact; + int x, y, w, h; + int sfx, sfy, sfw, sfh; /* stored float geometry, used on mode revert */ + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; + unsigned int tags; + int isfixed, ispermanent, isfloating, isurgent, neverfocus, oldstate, isfullscreen, ignoretransient, issticky, isterminal, noswallow, needresize; + pid_t pid; + char scratchkey; + unsigned int icw, ich; Picture icon; + int issteam; + int beingmoved; + Client *next; + Client *snext; + Client *swallowing; + Monitor *mon; + Window win; +}; + +typedef struct { + unsigned int mod; + KeySym chain; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + unsigned int signum; + void (*func)(const Arg *); + const Arg arg; +} Signal; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +} Layout; + +typedef struct Pertag Pertag; +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int by, bh; /* bar geometry */ + int tx, tw; /* bar tray geometry */ + int btw; + int bt; + 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 previewshow; + int showbar; + int barposition; + int hidsel; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; + Window traywin; + Window tagwin; + Pixmap *tagmap; + const Layout *lt[2]; + Pertag *pertag; +}; + +/* Order of the rules */ +typedef struct { + const char *class; + const char *instance; + const char *title; + unsigned int tags; + int isfloating; + int ispermanent; + int isterminal; + int noswallow; + int monitor; + int unmanaged; + int ignoretransient; + const char scratchkey; +} Rule; + +typedef struct { + const char *cmd; + int id; +} StatusCmd; + +/* Xresources preferences */ +enum resource_type { + STRING = 0, + INTEGER = 1, + FLOAT = 2 +}; + +typedef struct { + char *name; + enum resource_type type; + void *dst; +} ResourcePref; + +/* function declarations */ +static void applyrules(Client *c); +static void centeredmaster(Monitor *m); +static void centeredfloatingmaster(Monitor *m); +static void tilewide(Monitor *m); +static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc); +static void getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr); +static void stairs(Monitor *m); +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +static void spawnscratch(const Arg *arg); +static void inplacerotate(const Arg *arg); +static void togglescratch(const Arg *arg); +static void togglesticky(const Arg *arg); +static void previewtag(const Arg *arg); +static void setcurrentdesktop(void); +static void setdesktopnames(void); +static void setnumdesktops(void); +static void setviewport(void); +static void arrange(Monitor *m); +static void arrangemon(Monitor *m); +static void attach(Client *c); +static void attachabove(Client *c); +static void attachaside(Client *c); +static void attachbelow(Client *c); +static void attachbottom(Client *c); +static void attachtop(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 col(Monitor *); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void livereloadxrdb(const Arg *arg); +static void configurerequest(XEvent *e); +static void copyvalidchars(char *text, char *rawtext); +static void autostart_exec(void); +static void moveresize(const Arg *arg); +static void moveresizeedge(const Arg *arg); +static Monitor *createmon(void); +static void deck(Monitor *m); +static void cyclelayout(const Arg *arg); +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 drawroundedcorners(Client *c); +static int drawstatusbar(Monitor *m, int bh, char* text); +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 focusstackvis(const Arg *arg); +static void focusstackhid(const Arg *arg); +static void focusstack(int inc, int vis); +static Atom getatomprop(Client *c, Atom prop); +static Picture geticonprop(Window w, unsigned int *icw, unsigned int *ich); +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 hide(Client *c); +static void incnmaster(const Arg *arg); +static void keypress(XEvent *e); +static int fake_signal(void); +static void killclient(const Arg *arg); +static void layoutmenu(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void managealtbar(Window win, XWindowAttributes *wa); +static void managetray(Window win, 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 void moveorplace(const Arg *arg); +static Client *nexttagged(Client *c); +static Client *nexttiled(Client *c); +static void placemouse(const Arg *arg); +static void opacity(Client *c, double opacity); +static void pop(Client *); +static void propertynotify(XEvent *e); +static void quit(const Arg *arg); +static Client *recttoclient(int x, int y, int w, int h); +static Monitor *recttomon(int x, int y, int w, int h); +static void resetlayout(const Arg *arg); +static void reorganizetags(const Arg *arg); +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 scantray(void); +static void scan(void); +static void scratchpad_hide (); +static _Bool scratchpad_last_showed_is_killed (void); +static void scratchpad_remove (); +static void scratchpad_show (); +static void scratchpad_show_client (Client * c); +static void scratchpad_show_first (void); +static int sendevent(Client *c, Atom proto); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setclienttagprop(Client *c); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setgaps(int oh, int ov, int ih, int iv); +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); +static void defaultgaps(const Arg *arg); +static void setlayout(const Arg *arg); +static void setcfact(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void show(Client *c); +static void showwin(Client *c); +static void showhide(Client *c); +static void showtagpreview(unsigned int i); +static void sigchld(int unused); +#ifdef XINERAMA +static void sortscreens(XineramaScreenInfo *screens, int n); +#endif +static void sighup(int unused); +static void sigterm(int unused); +static void spawn(const Arg *arg); +static void spawnbar(); +static void unmanagealtbar(Window w); +static void unmanagetray(Window w); +static void getpreview(void); +static void tagmon(const Arg *arg); +static void tile(Monitor *); +static void tile54(Monitor *); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void toggleopacity(const Arg *arg); +static void hidewin(const Arg *arg); +static void restorewin(const Arg *arg); +static void hideotherwins(const Arg *arg); +static void restoreotherwins(const Arg *arg); +static void togglefullscr(const Arg *arg); +static void freeicon(Client *c); +static void togglewin(const Arg *arg); +static void unfocus(Client *c, int setfocus); +static void unmanage(Client *c, int destroyed); +static void unmapnotify(XEvent *e); +static void updatecurrentdesktop(void); +static void updatebarpos(Monitor *m); +static void updatebars(void); +static void updateclientlist(void); +static int updategeom(void); +static void updatemotifhints(Client *c); +static void updatenumlockmask(void); +static void resizemouse(const Arg *arg); +static void updatesizehints(Client *c); +static void updatestatus(void); +static void updaterules(Client *c); +static void updatetitle(Client *c); +static void updatepreview(void); +static void updateicon(Client *c); +static void updatewindowtype(Client *c); +static void updatewmhints(Client *c); +static void view(const Arg *arg); +static void viewtoleft(const Arg *arg); +static void viewtoright(const Arg *arg); +static void warp(const Client *c); +static Client *wintoclient(Window w); +static Monitor *wintomon(Window w); +static int wmclasscontains(Window win, const char *class, const char *name); +static int xerror(Display *dpy, XErrorEvent *ee); +static int xerrordummy(Display *dpy, XErrorEvent *ee); +static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void xinitvisual(); +static void zoom(const Arg *arg); +static void load_xresources(void); +static void resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst); +static pid_t getparentprocess(pid_t p); +static int isdescprocess(pid_t p, pid_t c); +static Client *swallowingclient(Window w); +static Client *termforwin(const Client *c); +static pid_t winpid(Window w); + +/* variables */ +static const char broken[] = "speedwm"; +static const char *layoutmenu_cmd = "speed-utils -layout"; +static char stext[1024]; +static char rawstext[1024]; +static int statusw; +static int statuscmdn; +static char lastbutton[] = "-"; +static int screen; +static int tw, sh; /* X display screen geometry width, height */ +static int bh, blw = 0; /* bar geometry */ +static int unmanaged = 0; /* whether the window manager should manage the new window or not */ +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], motifatom; +static int running = 1; +static int restart = 0; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Clr **tagscheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; +static KeySym keychain = -1; + +#define hiddenWinStackMax 100 +static int hiddenWinStackTop = -1; +static Client *hiddenWinStack[hiddenWinStackMax]; + +/* scratchpad */ +#define SCRATCHPAD_MASK (1u << sizeof tags / sizeof * tags) +static Client * scratchpad_last_showed = NULL; + +static int useargb = 0; +static Visual *visual; +static int depth; +static Colormap cmap; + +static xcb_connection_t *xcon; +#define FORCE_VSPLIT forcevsplit + +/* configuration, allows nested code to access above variables */ +#include /* Enable multimedia button support */ +#include "options.h" /* Include options */ +#include "layouts.c" /* Enable patched layouts */ +#include "autostart.h" /* Add autostart support */ +#include "colors.h" /* Include colors */ +#include "rules.h" /* Include rules */ +#include "layouts.h" /* Include layout list */ +#include "xresources.h" /* Include .Xresources/Pywal support */ +#include "keybinds.h" /* Include keybinds */ + +static const char *statuscmd[] = { shell, "-c", NULL, NULL }; + +#include "mouse.h" /* Include mouse support */ + +char *get_speedwm_path(){ + struct stat s; + int r, length, rate = 42; + char *path = NULL; + + if(lstat("/proc/self/exe", &s) == -1){ + perror("lstat:"); + return NULL; + } + + length = s.st_size + 1 - rate; + + do{ + length+=rate; + + free(path); + path = malloc(sizeof(char) * length); + + if(path == NULL){ + perror("malloc:"); + return NULL; + } + + r = readlink("/proc/self/exe", path, length); + + if(r == -1){ + perror("readlink:"); + return NULL; + } + }while(r >= length); + + path[r] = '\0'; + + return path; +} + +void self_restart(const Arg *arg) { + char *const argv[] = {get_speedwm_path(), NULL}; + + if(argv[0] == NULL){ + return; + } + + execv(argv[0], argv); +} + +#include "fsignal.h" /* Include fsignal support */ + +unsigned int tagw[LENGTH(tags)]; + +struct Pertag { + unsigned int curtag, prevtag; /* current and previous tag */ + int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ + unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ + const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ + int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ +}; + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +/* speedwm 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, "speedwm: execvp %s\n", *p); + perror(" failed"); + _exit(EXIT_FAILURE); + } + /* skip arguments */ + while (*++p); + } +} + +/* 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; + c->scratchkey = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + if (strstr(class, "Steam") || strstr(class, "steam_app_")) + c->issteam = 1; + + 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->isterminal = r->isterminal; + c->noswallow = r->noswallow; + c->isfloating = r->isfloating; + c->ignoretransient = r->ignoretransient; + c->ispermanent = r->ispermanent; + c->tags |= r->tags; + unmanaged = r->unmanaged; + c->scratchkey = r->scratchkey; + 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); + + if(c->tags & TAGMASK) c->tags = c->tags & TAGMASK; + else if(c->mon->tagset[c->mon->seltags]) c->tags = c->mon->tagset[c->mon->seltags]; + else c->tags = 1; + if (c->tags != SCRATCHPAD_MASK) + 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 > tw) + *x = tw - 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) { + /* 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) +{ + memcpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol)); + + if (m->lt[m->sellt]->arrange) { + m->lt[m->sellt]->arrange(m); + } + + if (roundedcorners) { + Client *c; + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + drawroundedcorners(c); + } +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +attachabove(Client *c) +{ + if (c->mon->sel == NULL || c->mon->sel == c->mon->clients || c->mon->sel->isfloating) { + attach(c); + return; + } + + Client *at; + for (at = c->mon->clients; at->next != c->mon->sel; at = at->next); + c->next = at->next; + at->next = c; +} + +void +attachaside(Client *c) { + Client *at = nexttagged(c); + if(!at) { + attach(c); + return; + } + c->next = at->next; + at->next = c; +} + +void +attachbelow(Client *c) +{ + if(c->mon->sel == NULL || c->mon->sel == c || c->mon->sel->isfloating) { + attach(c); + return; + } + c->next = c->mon->sel->next; + c->mon->sel->next = c; +} + +void +attachbottom(Client *c) +{ + Client *below = c->mon->clients; + for (; below && below->next; below = below->next); + c->next = NULL; + if (below) + below->next = c; + else + c->mon->clients = c; +} + +void +attachtop(Client *c) +{ + int n; + Monitor *m = selmon; + Client *below; + + for (n = 1, below = c->mon->clients; + below && below->next && (below->isfloating || !ISVISIBLEONTAG(below, c->tags) || n != m->nmaster); + n = below->isfloating || !ISVISIBLEONTAG(below, c->tags) ? n + 0 : n + 1, below = below->next); + c->next = NULL; + if (below) { + c->next = below->next; + below->next = c; + } + else + c->mon->clients = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +swallow(Client *p, Client *c) +{ + + if (c->noswallow || c->isterminal) + return; + if (c->noswallow && !swallowfloating && c->isfloating) + return; + + if (!swallowclients) + return; + + 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; + updatetitle(p); + XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h); + arrange(p->mon); + configure(p); + updateclientlist(); +} + +void +unswallow(Client *c) +{ + c->win = c->swallowing->win; + + free(c->swallowing); + c->swallowing = NULL; + + /* unfullscreen the client */ + setfullscreen(c, 0); + updatetitle(c); + arrange(c->mon); + XMapWindow(dpy, c->win); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + setclientstate(c, NormalState); + focus(NULL); + arrange(c->mon); +} + +void +buttonpress(XEvent *e) +{ + unsigned int i, x, click, occ; + Arg arg = {0}; + Client *c; + Monitor *m; + XButtonPressedEvent *ev = &e->xbutton; + *lastbutton = '0' + ev->button; + + click = ClkRootWin; + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + +/* For !leftlayout */ +if (!leftlayout) { + if (ev->window == selmon->barwin) { + i = x = occ = 0; + + /* Bitmask of occupied tags */ + unsigned int occ = 0; + for (c = m->clients; c; c = c->next) + occ |= c->tags; + do { + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; + x += TEXTW(occ & 1 << i ? alttags[i] : tags[i]); + } while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + blw) + click = ClkLtSymbol; + else if (ev->x > (selmon->ww - TEXTW(stext) + lrpad) - 2) { + x = selmon->ww - TEXTW(stext) + lrpad - 2; + click = ClkStatusText; + + char *text = rawstext; + int i = -1; + char ch; + statuscmdn = 0; + while (text[++i]) { + if ((unsigned char)text[i] < ' ') { + ch = text[i]; + text[i] = '\0'; + x += TEXTW(text) - lrpad; + text[i] = ch; + text += i+1; + i = -1; + if (x >= ev->x) break; + statuscmdn = ch; + } + } + } else { + x += blw; + c = m->clients; + + if (c) { + do { + if (!ISVISIBLE(c)) + continue; + else + x += (1.0 / (double)m->bt) * m->btw; + } while (ev->x > x && (c = c->next)); + + click = ClkWinTitle; + arg.v = c; + } + } + + } else if ((c = wintoclient(ev->window))) { + focus(c); + + if (clicktofocus) { + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + } + + click = ClkClientWin; + } +} else { + if (ev->window == selmon->barwin) { + i = x = occ = 0; + /* Bitmask of occupied tags */ + for (c = m->clients; c; c = c->next) + occ |= c->tags; + x += blw; + if (ev->x < x) { + click = ClkLtSymbol; + } else { + do { + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; + x += TEXTW(occ & 1 << i ? alttags[i] : tags[i]); + } while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x > selmon->ww - statusw + lrpad - 2) { + x = selmon->ww - TEXTW(stext) + lrpad - 2; + click = ClkStatusText; + char *text = rawstext; + int i = -1; + char ch; + statuscmdn = 0; + while (text[++i]) { + if ((unsigned char)text[i] < ' ') { + ch = text[i]; + text[i] = '\0'; + x += TEXTW(text) - lrpad; + text[i] = ch; + text += i+1; + i = -1; + if (x >= ev->x) break; + //if (ch <= LENGTH(statuscmds)) statuscmdn = ch - 1; + statuscmdn = ch; + } + } + } else { + x += blw; + c = m->clients; + + if (c) { + do { + if (!ISVISIBLE(c)) + continue; + else + x += (1.0 / (double)m->bt) * m->btw; + } while (ev->x > x && (c = c->next)); + + click = ClkWinTitle; + arg.v = c; + } + } + } + } else if ((c = wintoclient(ev->window))) { + focus(c); + if (clicktofocus) { + 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 || click == ClkWinTitle) && 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) + 1; 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; + size_t i; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + for (i = 0; i < LENGTH(tags); i++) { + if (mon->tagmap[i]) + XFreePixmap(dpy, mon->tagmap[i]); + free(mon->tagmap); + if (!altbar) { + XUnmapWindow(dpy, mon->tagwin); + XDestroyWindow(dpy, mon->tagwin); + } + + free(mon); + } +} + +void +clientmessage(XEvent *e) +{ + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + unsigned int i; + + 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]) { + for (i = 0; i < LENGTH(tags) && !((1 << i) & c->tags); i++); + if (i < LENGTH(tags)) { + const Arg a = {.ui = 1 << i}; + selmon = c->mon; + view(&a); + focus(c); + restack(selmon); + } + } +} + +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 = (tw != ev->width || sh != ev->height); + tw = ev->width; + sh = ev->height; + if (updategeom() || dirty) { + drw_resize(drw, tw, 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, m->bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void drawroundedcorners(Client *c) +{ + if (cornerradius <= 0 || !c || c->isfullscreen) { + return; + } + + if (borderpx >= 1) + return; + + if (!roundedcorners) + return; + + Window win; + win = c->win; + if (!win) + return; + + XWindowAttributes win_attr; + if (!XGetWindowAttributes(dpy, win, &win_attr)) + return; + + int dia = 2 * cornerradius; + int w = c->w; + int h = c->h; + if (w < dia || h < dia) + return; + + Pixmap mask; + mask = XCreatePixmap(dpy, win, w, h, 1); + if (!mask) + return; + + XGCValues xgcv; + GC shape_gc; + shape_gc = XCreateGC(dpy, mask, 0, &xgcv); + + if (!shape_gc) { + XFreePixmap(dpy, mask); + free(shape_gc); + return; + } + + XSetForeground(dpy, shape_gc, 0); + XFillRectangle(dpy, mask, shape_gc, 0, 0, w, h); + XSetForeground(dpy, shape_gc, 1); + XFillArc(dpy, mask, shape_gc, 0, 0, dia, dia, 0, 23040); + XFillArc(dpy, mask, shape_gc, w-dia-1, 0, dia, dia, 0, 23040); + XFillArc(dpy, mask, shape_gc, 0, h-dia-1, dia, dia, 0, 23040); + XFillArc(dpy, mask, shape_gc, w-dia-1, h-dia-1, dia, dia, 0, 23040); + XFillRectangle(dpy, mask, shape_gc, cornerradius, 0, w-dia, h); + XFillRectangle(dpy, mask, shape_gc, 0, cornerradius, w, h-dia); + XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, mask, ShapeSet); + XFreePixmap(dpy, mask); + XFreeGC(dpy, shape_gc); +} + +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 (!c->issteam) { + 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 + c->needresize = 1; + } 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); +} + +void +copyvalidchars(char *text, char *rawtext) +{ + int i = -1, j = 0; + + while(rawtext[++i]) { + if ((unsigned char)rawtext[i] >= ' ') { + text[j++] = rawtext[i]; + } + } + text[j] = '\0'; +} + +Monitor * +createmon(void) +{ + Monitor *m; + unsigned int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = startontag ? 1 : 0; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->barposition = barposition; + m->bh = bh; + m->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + m->pertag = ecalloc(1, sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + + for (i = 0; i <= LENGTH(tags); i++) { + m->pertag->nmasters[i] = m->nmaster; + m->pertag->mfacts[i] = m->mfact; + + m->pertag->ltidxs[i][0] = m->lt[0]; + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + + m->pertag->showbars[i] = m->showbar; + } + + m->tagmap = ecalloc(LENGTH(tags), sizeof(Pixmap)); + + return m; +} + +void +cyclelayout(const Arg *arg) { + Layout *l; + for(l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++); + if(arg->i > 0) { + if(l->symbol && (l + 1)->symbol) + setlayout(&((Arg) { .v = (l + 1) })); + else + setlayout(&((Arg) { .v = layouts })); + } else { + if(l != layouts && (l - 1)->symbol) + setlayout(&((Arg) { .v = (l - 1) })); + else + setlayout(&((Arg) { .v = &layouts[LENGTH(layouts) - 2] })); + } +} + +void +destroynotify(XEvent *e) +{ + Client *c; + Monitor *m; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); + else if ((c = swallowingclient(ev->window))) + unmanage(c->swallowing, 1); + + if (altbar && (m = wintomon(ev->window)) && m->barwin == ev->window) + unmanagealtbar(ev->window); + else if (altbar && m->traywin == ev->window) + unmanagetray(ev->window); +} + +void +layoutmenu(const Arg *arg) { + FILE *p; + char c[3]; + int i; + + if (!(p = popen(layoutmenu_cmd, "r"))) + return; + pclose(p); + + i = atoi(c); + setlayout(&((Arg) {.v = &layouts[i]})); +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; +} + +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 +} + +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; +} + +Bool +isHexDigit(char digit) { + return (digit >= '0' && digit <= '9') || (digit >= 'A' && digit <= 'F') || (digit >= 'a' && digit <= 'f'); +} + +Bool +isHexColor(char* hex) { + return isHexDigit(hex[1]) && isHexDigit(hex[2]) && isHexDigit(hex[3]) && + isHexDigit(hex[4]) && isHexDigit(hex[5]) && isHexDigit(hex[6]) && + hex[0] == '#' && hex[7] == '\0'; +} + +void +readAndSetColor(int clrIdx, char* txt) { + char buf[8]; + memcpy(buf, txt, 7); + buf[7] = '\0'; + if(isHexColor(buf)) { + drw_clr_create(drw, &drw->scheme[clrIdx], buf, alphas[SchemeStatus][ColBg]); + } +} + +int +drawstatusbar(Monitor *m, int bh, char* stext) { + int ret, i, j, w, x, len; + short isCode = 0; + char *text; + char *p; + + len = strlen(stext) + 1 ; + if (!(text = (char*) malloc(sizeof(char)*len))) + die("malloc"); + p = text; + + i = -1, j = 0; + while (stext[++i]) + if ((unsigned char)stext[i] >= ' ') + text[j++] = stext[i]; + text[j] = '\0'; + + /* compute width of the status text */ + w = 0; + i = -1; + while (text[++i]) { + if (text[i] == '^') { + if (!isCode) { + isCode = 1; + text[i] = '\0'; + w += TEXTW(text) - lrpad; + text[i] = '^'; + if (text[++i] == 'f') + w += atoi(text + ++i); + } else { + isCode = 0; + text = text + i + 1; + i = -1; + } + } + } + if (!isCode) + w += TEXTW(text) - lrpad; + else + isCode = 0; + text = p; + + w += 2; /* 1px padding on both sides */ + ret = x = m->ww - w - 2 * sp; + + drw_setscheme(drw, scheme[LENGTH(colors)]); + drw->scheme[ColFg] = scheme[SchemeStatus][ColFg]; + drw->scheme[ColBg] = scheme[SchemeStatus][ColBg]; + drw_rect(drw, x, 0, w, bh, 1, 1); + x++; + + /* process status text */ + i = -1; + while (text[++i]) { + if (text[i] == '^' && !isCode) { + isCode = 1; + + text[i] = '\0'; + w = TEXTW(text) - lrpad; + drw_text(drw, x, 0, w, bh, 0, text, 0); + + x += w; + + /* process code */ + while (text[++i] != '^') { + if (text[i] == 'c') { + readAndSetColor(ColFg, (char*)text+i+1); + i += 7; + } else if (text[i] == 'b') { + readAndSetColor(ColBg, (char*)text+i+1); + i += 7; + } else if (text[i] == 'C') { + int c = atoi(text + ++i); + drw_clr_create(drw, &drw->scheme[ColFg], colstatus[c], alphas[SchemeStatus][ColBg]); + } else if (text[i] == 'B') { + int c = atoi(text + ++i); + drw_clr_create(drw, &drw->scheme[ColBg], colstatus[c], alphas[SchemeStatus][ColBg]); + } else if (text[i] == 'd') { + drw->scheme[ColFg] = scheme[SchemeStatus][ColFg]; + drw->scheme[ColBg] = scheme[SchemeStatus][ColBg]; + } else if (text[i] == 'r') { + int rx = atoi(text + ++i); + while (text[++i] != ','); + int ry = atoi(text + ++i); + while (text[++i] != ','); + int rw = atoi(text + ++i); + while (text[++i] != ','); + int rh = atoi(text + ++i); + + drw_rect(drw, rx + x, ry, rw, rh, 1, 0); + } else if (text[i] == 'f') { + x += atoi(text + ++i); + } + } + + text = text + i + 1; + i=-1; + isCode = 0; + } + } + + if (!isCode) { + w = TEXTW(text) - lrpad; + drw_text(drw, x, 0, w, bh, 0, text, 0); + w = 0; + } + + drw_setscheme(drw, scheme[SchemeStatus]); + free(p); + + return ret; +} + +void +drawbar(Monitor *m) +{ + int x, w = 0, tw = 0, scm; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0, n = 0, a = 0, s = 0; + const char *tagtext; + Client *c; + + if (!m->showbar) + return; + + if (altbar) + return; + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon || 1) { /* status is only drawn on selected monitor */ + char *text, *s, ch; + if (!hidestatus) { + tw = statusw = m->ww - drawstatusbar(m, bh, stext); + } + + x = 0; + + for (text = s = stext; *s; s++) { + if ((unsigned char)(*s) < ' ') { + ch = *s; + *s = '\0'; + tw = TEXTW(text) - lrpad; + x += tw; + *s = ch; + text = s + 1; + } + } + + } + + for (c = m->clients; c; c = c->next) { + if (ISVISIBLE(c)) + n++; + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + //} + x = 0; + + /* Draw the layout bar on the left if leftlayout = 1 */ + /* If colorlayoutnorm = 0, draw using SchemeLayout */ + if (leftlayout && !hidelayout) { + w = blw = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeLayout]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + } + + for (i = 0; i < LENGTH(tags); i++) { + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; + tagtext = occ & 1 << i ? alttags[i] : tags[i]; + if (!hidetags) { + w = TEXTW(tagtext); + } + + drw_setscheme(drw, (m->tagset[m->seltags] & 1 << i ? tagscheme[i] : scheme[SchemeTags])); + drw_text(drw, x, 0, w, bh, lrpad / 2, tagtext, urg & 1 << i); + + if (underlineall || m->tagset[m->seltags] & 1 << i) + if (underline) + drw_rect(drw, x + underlinepad, bh - underlinestroke - underlinevoffset, w - (underlinepad * 2), underlinestroke, 1, 0); + + x += w; + } + + if (monoclecount) { + if (m->lt[m->sellt]->arrange == monocle) { + for (c = nexttiled(m->clients), a = 0, s = 0; c; c = nexttiled(c->next), a++) + if (c == m->stack) + s = a + 1; + if (!s && a) + s = 1; + snprintf(m->ltsymbol, sizeof m->ltsymbol, monocleformat, s, a); + } + } + + /* Draw the layout bar on the right if leftlayout is not 0 */ + /* If colorlayoutnorm = 0, draw using SchemeLayout */ + if (!leftlayout && !hidelayout) { + w = blw = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeLayout]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + } + + if ((w = m->ww - tw - x) > bh) { + if (n > 0) { + int remainder = w % n; + int tabw = (1.0 / (double)n) * w + 1; + for (c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c)) + continue; + if (m->sel == c) + scm = SchemeSelTitle; + else if (HIDDEN(c)) + scm = SchemeHid; + else + scm = SchemeNormTitle; + drw_setscheme(drw, scheme[scm]); + + if (remainder >= 0) { + if (remainder == 0) { + tabw--; + } + remainder--; + } + + if (!hidetitle) { + drw_text(drw, x, 0, tabw, bh, lrpad / 2 + (c->icon ? c->icw + ICONSPACING : 0), c->name, 0); + if (c->icon) drw_pic(drw, x + lrpad / 2, (bh - c->ich) / 2, c->icw, c->ich, c->icon); + } + + if (c->issticky && !hidesticky) { + drw_polygon(drw, x + boxs, c->isfloating ? boxs * 2 + boxw : boxs, stickyiconbb.x, stickyiconbb.y, boxw, boxw * stickyiconbb.y / stickyiconbb.x, stickyicon, LENGTH(stickyicon), Nonconvex, c->tags & c->mon->tagset[c->mon->seltags]); + } + if (c->isfloating && !hidefloating) { + drw_rect(drw, x + boxs, boxs, boxw, boxw, c->isfixed, 0); + } + x += tabw; + } + } else { + drw_setscheme(drw, scheme[SchemeBar]); + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } + m->bt = n; + m->btw = w; + 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) +{ + if (!clicktofocus) { + 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) || HIDDEN(c)) + for (c = selmon->stack; c && (!ISVISIBLE(c) || HIDDEN(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[SchemeSelBorder][ColBorder].pixel); + setfocus(c); + opacity(c, activeopacity); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + if (selmon->lt[selmon->sellt]->arrange == monocle) + arrangemon(selmon); + drawbars(); +} + +void +hide(Client *c) { + if (!c || HIDDEN(c)) + return; + + Window w = c->win; + static XWindowAttributes ra, ca; + + // more or less taken directly from blackbox's hide() function + XGrabServer(dpy); + XGetWindowAttributes(dpy, root, &ra); + XGetWindowAttributes(dpy, w, &ca); + // prevent UnmapNotify events + XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); + XSelectInput(dpy, w, ca.your_event_mask & ~StructureNotifyMask); + XUnmapWindow(dpy, w); + setclientstate(c, IconicState); + XSelectInput(dpy, root, ra.your_event_mask); + XSelectInput(dpy, w, ca.your_event_mask); + XUngrabServer(dpy); + + focus(c->snext); + arrange(c->mon); +} + +/* 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 +focusstackvis(const Arg *arg) +{ + focusstack(arg->i, 0); +} + +void +focusstackhid(const Arg *arg) +{ + focusstack(arg->i, 1); +} + + +void +focusstack(int inc, int hid) +{ + Client *c = NULL, *i; + + if (!selmon->sel && (!hid || (selmon->sel->isfullscreen && lockfullscreen))) + return; + if (!selmon->clients) + return; + + if (inc > 0) { + if (selmon->sel) + for (c = selmon->sel->next; + c && (!ISVISIBLE(c) || (!hid && HIDDEN(c))); + c = c->next); + if (!c) + for (c = selmon->clients; + c && (!ISVISIBLE(c) && (!hid && HIDDEN(c))); + c = c->next); + } else { + if (selmon->sel) { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i) && !(!hid && HIDDEN(i))) + c = i; + } else + c = selmon->clients; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i) && !HIDDEN(i)) + c = i; + } + + if (c) { + focus(c); + restack(selmon); + + + if (HIDDEN(c)) { + showwin(c); + c->mon->hidsel = 1; + } + } +} + +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; +} + +static uint32_t prealpha(uint32_t p) { + uint8_t a = p >> 24u; + uint32_t rb = (a * (p & 0xFF00FFu)) >> 8u; + uint32_t g = (a * (p & 0x00FF00u)) >> 8u; + return (rb & 0xFF00FFu) | (g & 0x00FF00u) | (a << 24u); +} + +Picture +geticonprop(Window win, unsigned int *picw, unsigned int *pich) +{ + int format; + unsigned long n, extra, *p = NULL; + Atom real; + + if (XGetWindowProperty(dpy, win, netatom[NetWMIcon], 0L, LONG_MAX, False, AnyPropertyType, + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return None; + if (n == 0 || format != 32) { XFree(p); return None; } + + unsigned long *bstp = NULL; + uint32_t w, h, sz; + { + unsigned long *i; const unsigned long *end = p + n; + uint32_t bstd = UINT32_MAX, d, m; + for (i = p; i < end - 1; i += sz) { + if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; } + if ((sz = w * h) > end - i) break; + if ((m = w > h ? w : h) >= ICONSIZE && (d = m - ICONSIZE) < bstd) { bstd = d; bstp = i; } + } + if (!bstp) { + for (i = p; i < end - 1; i += sz) { + if ((w = *i++) >= 16384 || (h = *i++) >= 16384) { XFree(p); return None; } + if ((sz = w * h) > end - i) break; + if ((d = ICONSIZE - (w > h ? w : h)) < bstd) { bstd = d; bstp = i; } + } + } + if (!bstp) { XFree(p); return None; } + } + + if ((w = *(bstp - 2)) == 0 || (h = *(bstp - 1)) == 0) { XFree(p); return None; } + + uint32_t icw, ich; + if (w <= h) { + ich = ICONSIZE; icw = w * ICONSIZE / h; + if (icw == 0) icw = 1; + } + else { + icw = ICONSIZE; ich = h * ICONSIZE / w; + if (ich == 0) ich = 1; + } + *picw = icw; *pich = ich; + + uint32_t i, *bstp32 = (uint32_t *)bstp; + for (sz = w * h, i = 0; i < sz; ++i) bstp32[i] = prealpha(bstp[i]); + + Picture ret = drw_picture_create_resized(drw, (char *)bstp, w, h, icw, ich); + XFree(p); + + return ret; +} + +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 (!clicktofocus) { + 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); + } else { + 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; + KeyCode chain; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < LENGTH(keys); i++) + if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) { + if (keys[i].chain != -1 && + ((chain = XKeysymToKeycode(dpy, keys[i].chain)))) + code = chain; + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, + True, GrabModeAsync, GrabModeAsync); + } + } +} + +void +incnmaster(const Arg *arg) +{ + if (i3nmaster) { + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = !selmon->nmaster; + } else { + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + } + arrange(selmon); +} + +#ifdef XINERAMA +static int +isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +{ + while (n--) + if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org + && unique[n].width == info->width && unique[n].height == info->height) + return 0; + return 1; +} +#endif /* XINERAMA */ + +void +keypress(XEvent *e) +{ + unsigned int i, j; + KeySym keysym; + XKeyEvent *ev; + int current = 0; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for (i = 0; i < LENGTH(keys); i++) { + if (keysym == keys[i].keysym && keys[i].chain == -1 + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); + else if (keysym == keys[i].chain && keychain == -1 + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) { + current = 1; + keychain = keysym; + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, AnyKey, 0 | modifiers[j], root, + True, GrabModeAsync, GrabModeAsync); + } else if (!current && keysym == keys[i].keysym + && keychain != -1 + && keys[i].chain == keychain + && keys[i].func) + keys[i].func(&(keys[i].arg)); + } + if (!current) { + keychain = -1; + grabkeys(); + } +} + +int +fake_signal(void) +{ + char fsignal[256]; + char indicator[9] = "fsignal:"; + char str_signum[16]; + int i, v, signum; + size_t len_fsignal, len_indicator = strlen(indicator); + + // 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) { + memcpy(str_signum, &fsignal[len_indicator], len_fsignal - len_indicator); + str_signum[len_fsignal - len_indicator] = '\0'; + + // Convert string value into managable integer + for (i = signum = 0; i < strlen(str_signum); i++) { + v = str_signum[i] - '0'; + if (v >= 0 && v <= 9) { + signum = signum * 10 + v; + } + } + + // Check if a signal was found, and if so handle it + if (signum) + for (i = 0; i < LENGTH(signals); i++) + if (signum == signals[i].signum && signals[i].func) + signals[i].func(&(signals[i].arg)); + + // A fake signal was sent + return 1; + } + } + + // No fake signal was sent, so proceed with update + return 0; +} + +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); +} + +void +getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc) +{ + unsigned int n, oe, ie; + #if PERTAG_PATCH + oe = ie = selmon->pertag->enablegaps[selmon->pertag->curtag]; + #else + oe = ie = enablegaps; + #endif // PERTAG_PATCH + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if (smartgaps && n == 1) { + oe = smartgapsize; // outer gaps disabled 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 +} + +void +togglegaps(const Arg *arg) +{ + enablegaps = !enablegaps; + arrange(selmon); +} + +void +defaultgaps(const Arg *arg) +{ + setgaps(gappoh, gappov, gappih, gappiv); +} + +void +incrgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +void +incrigaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +void +incrogaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +/* +void +incrohgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov, + selmon->gappih, + selmon->gappiv + ); +} + +void +incrovgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +void +incrihgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + ); +} + +void +incrivgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih, + selmon->gappiv + arg->i + ); +} +*/ + +void +killclient(const Arg *arg) +{ + if (!selmon->sel || selmon->sel->ispermanent) + 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, *term = NULL; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + c->pid = winpid(w); + + if (getatomprop(c, netatom[NetWMWindowType]) == netatom[NetWMWindowTypeDesktop]) { + XMapWindow(dpy, c->win); + XLowerWindow(dpy, c->win); + free(c); + return; + } + + /* 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; + c->cfact = 1.0; + + updateicon(c); + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + } else { + c->mon = selmon; + applyrules(c); + term = termforwin(c); + } + + if (unmanaged) { + XMapWindow(dpy, c->win); + if (unmanaged == 1) + XRaiseWindow(dpy, c->win); + else if (unmanaged == 2) + XLowerWindow(dpy, c->win); + free(c); + unmanaged = 0; + return; + } + + 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->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); + c->bw = borderpx; + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, scheme[SchemeNormBorder][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); + { + int format; + unsigned long *data, n, extra; + Monitor *m; + Atom atom; + if (XGetWindowProperty(dpy, c->win, netatom[NetClientInfo], 0L, 2L, False, XA_CARDINAL, + &atom, &format, &n, &extra, (unsigned char **)&data) == Success && n == 2) { + c->tags = *data; + for (m = mons; m; m = m->next) { + if (m->num == *(data+1)) { + c->mon = m; + break; + } + } + } + if (n > 0) + XFree(data); + } + setclienttagprop(c); + + if (savefloat) + { + c->sfx = c->x; + c->sfy = c->y; + c->sfw = c->w; + c->sfh = c->h; + } + + if (centerfloating) + { + c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2; + c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2; + } + + updatemotifhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + if (!c->isfloating) + c->isfloating = c->oldstate = t || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); + switch(attachdirection){ + case 1: + attachabove(c); + break; + case 2: + attachaside(c); + break; + case 3: + attachbelow(c); + break; + case 4: + attachbottom(c); + break; + case 5: + attachtop(c); + break; + default: + attach(c); + } + attachstack(c); + setclienttagprop(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XChangeProperty(dpy, root, netatom[NetClientListStacking], XA_WINDOW, 32, PropModePrepend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * tw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); + if (!HIDDEN(c)) + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); + if (!HIDDEN(c)) + XMapWindow(dpy, c->win); + if (term) + swallow(term, c); + focus(NULL); +} + +void +managealtbar(Window win, XWindowAttributes *wa) +{ + Monitor *m; + if (!(m = recttomon(wa->x, wa->y, wa->width, wa->height))) + return; + + m->barwin = win; + m->by = wa->y; + bh = m->bh = wa->height; + updatebarpos(m); + arrange(m); + XSelectInput(dpy, win, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + XMoveResizeWindow(dpy, win, wa->x, wa->y, wa->width, wa->height); + XMapWindow(dpy, win); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &win, 1); +} + +void +managetray(Window win, XWindowAttributes *wa) +{ + Monitor *m; + if (!(m = recttomon(wa->x, wa->y, wa->width, wa->height))) + return; + + m->traywin = win; + m->tx = wa->x; + m->tw = wa->width; + updatebarpos(m); + arrange(m); + XSelectInput(dpy, win, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + XMoveResizeWindow(dpy, win, wa->x, wa->y, wa->width, wa->height); + XMapWindow(dpy, win); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &win, 1); +} + +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.depth) + return; + if (wa.override_redirect) + return; + if (wmclasscontains(ev->window, altbarclass, "")) + managealtbar(ev->window, &wa); + else if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +void +motionnotify(XEvent *e) +{ + Monitor *m; + Client *c; + m = 0; + + static Monitor *mon = NULL; + XMotionEvent *ev = &e->xmotion; + unsigned int i, x = 0, occ = 0; + + if (ev->window == selmon->barwin) { + i = x = 0; + + for (c = selmon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + do { + if (!(occ & 1 << i || selmon->tagset[selmon->seltags] & 1 << i)) + continue; + + x += TEXTW(tags[i]); + + } while (ev->x >= x && ++i < (LENGTH(tags))); + + if (!leftlayout && mousepreview && !hidetags) { + if (i < LENGTH(tags)) { + if ((i + 1) != selmon->previewshow && !(selmon->tagset[selmon->seltags] & 1 << i)) { + selmon->previewshow = i + 1; + showtagpreview(i); + } else if (selmon->tagset[selmon->seltags] & 1 << i) { + selmon->previewshow = 0; + XUnmapWindow(dpy, selmon->tagwin); + } + } else if (selmon->previewshow) { + selmon->previewshow = 0; + XUnmapWindow(dpy, selmon->tagwin); + } + } else if (selmon->previewshow) { + showtagpreview(0); + XUnmapWindow(dpy, selmon->tagwin); + } + } + 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 +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; + /* make sure the cursor stays inside the window */ + if ((msx + nmx) > c->x && (msy + nmy) > c->y) + XWarpPointer(dpy, None, None, 0, 0, 0, 0, nmx, nmy); + } +} + +void +moveresizeedge(const Arg *arg) { + /* move or resize floating window to edge of screen */ + Client *c; + c = selmon->sel; + char e; + int nx, ny, nw, nh, ox, oy, ow, oh, bp; + int msx, msy, dx, dy, nmx, nmy; + int starty; + unsigned int dui; + Window dummy; + + nx = c->x; + ny = c->y; + nw = c->w; + nh = c->h; + + starty = selmon->showbar && barposition ? bh : 0; + bp = selmon->showbar && !barposition ? bh : 0; + + if (!c || !arg) + return; + if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) + return; + if(sscanf((char *)arg->v, "%c", &e) != 1) + return; + + if(e == 't') + ny = starty; + + if(e == 'b') + ny = c->h > selmon->mh - 2 * c->bw ? c->h - bp : selmon->mh - c->h - 2 * c->bw - bp; + + if(e == 'l') + nx = selmon->mx; + + if(e == 'r') + nx = c->w > selmon->mw - 2 * c->bw ? selmon->mx + c->w : selmon->mx + selmon->mw - c->w - 2 * c->bw; + + if(e == 'T') { + /* if you click to resize again, it will return to old size/position */ + if(c->h + starty == c->oldh + c->oldy) { + nh = c->oldh; + ny = c->oldy; + } else { + nh = c->h + c->y - starty; + ny = starty; + } + } + + if(e == 'B') + nh = c->h + c->y + 2 * c->bw + bp == selmon->mh ? c->oldh : selmon->mh - c->y - 2 * c->bw - bp; + + if(e == 'L') { + if(selmon->mx + c->w == c->oldw + c->oldx) { + nw = c->oldw; + nx = c->oldx; + } else { + nw = c->w + c->x - selmon->mx; + nx = selmon->mx; + } + } + + if(e == 'R') + nw = c->w + c->x + 2 * c->bw == selmon->mx + selmon->mw ? c->oldw : selmon->mx + selmon->mw - c->x - 2 * c->bw; + + 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; + /* make sure the cursor stays inside the window */ + if ((msx + nmx) > c->x && (msy + nmy) > c->y) + XWarpPointer(dpy, None, None, 0, 0, 0, 0, nmx, nmy); + } +} + +Client * +nexttagged(Client *c) { + Client *walked = c->mon->clients; + for(; + walked && (walked->isfloating || !ISVISIBLEONTAG(walked, c->tags)); + walked = walked->next + ); + return walked; +} + +void +moveorplace(const Arg *arg) { + if ((!selmon->lt[selmon->sellt]->arrange || (selmon->sel && selmon->sel->isfloating))) + movemouse(arg); + else + placemouse(arg); +} + +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); + } +} + +void +resizemouse(const Arg *arg) +{ + int ocx, ocy, nw, nh; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + + if (!mousemfact) { + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + } else { + if (c->isfloating || NULL == c->mon->lt[c->mon->sellt]->arrange) { + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + } else { + XWarpPointer(dpy, None, root, 0, 0, 0, 0, + selmon->mx + (selmon->ww * selmon->mfact), + selmon->my + (selmon->wh / 2) + ); + } + } + + 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 (!mousemfact) { + 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); + + if (c->isfloating || NULL == c->mon->lt[c->mon->sellt]->arrange) { + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + } else { + if (mousemfact) { + selmon->mfact = (double) (ev.xmotion.x_root - selmon->mx) / (double) selmon->ww; + arrange(selmon); + XWarpPointer(dpy, None, root, 0, 0, 0, 0, + selmon->mx + (selmon->ww * selmon->mfact), + selmon->my + (selmon->wh / 2) + ); + } + } + + 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); + } +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c) || HIDDEN(c)); c = c->next); + return c; +} + +void +opacity(Client *c, double opacity) +{ + if(bUseOpacity && opacity > 0 && opacity < 1 && fadeinactive) { + unsigned long real_opacity[] = { opacity * 0xffffffff }; + XChangeProperty(dpy, c->win, netatom[NetWMWindowsOpacity], XA_CARDINAL, + 32, PropModeReplace, (unsigned char *)real_opacity, + 1); + } else + XDeleteProperty(dpy, c->win, netatom[NetWMWindowsOpacity]); +} + +void +placemouse(const Arg *arg) +{ + int x, y, px, py, ocx, ocy, nx = -9999, ny = -9999, freemove = 0; + Client *c, *r = NULL, *at, *prevr; + Monitor *m; + XEvent ev; + XWindowAttributes wa; + Time lasttime = 0; + int attachmode, prevattachmode; + attachmode = prevattachmode = -1; + + if (!(c = selmon->sel) || !c->mon->lt[c->mon->sellt]->arrange) /* no support for placemouse when floating layout is used */ + return; + if (c->isfullscreen) /* no support placing fullscreen windows by mouse */ + return; + restack(selmon); + prevr = c; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + + c->isfloating = 0; + c->beingmoved = 1; + + XGetWindowAttributes(dpy, c->win, &wa); + ocx = wa.x; + ocy = wa.y; + + if (arg->i == 2) // warp cursor to client center + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, WIDTH(c) / 2, HEIGHT(c) / 2); + + 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 (!freemove && (abs(nx - ocx) > snap || abs(ny - ocy) > snap)) + freemove = 1; + + if (freemove) + XMoveWindow(dpy, c->win, nx, ny); + + if ((m = recttomon(ev.xmotion.x, ev.xmotion.y, 1, 1)) && m != selmon) + selmon = m; + + if (arg->i == 1) { // tiled position is relative to the client window center point + px = nx + wa.width / 2; + py = ny + wa.height / 2; + } else { // tiled position is relative to the mouse cursor + px = ev.xmotion.x; + py = ev.xmotion.y; + } + + r = recttoclient(px, py, 1, 1); + + if (!r || r == c) + break; + + attachmode = 0; // below + if (((float)(r->y + r->h - py) / r->h) > ((float)(r->x + r->w - px) / r->w)) { + if (abs(r->y - py) < r->h / 2) + attachmode = 1; // above + } else if (abs(r->x - px) < r->w / 2) + attachmode = 1; // above + + if ((r && r != prevr) || (attachmode != prevattachmode)) { + detachstack(c); + detach(c); + if (c->mon != r->mon) { + arrangemon(c->mon); + c->tags = r->mon->tagset[r->mon->seltags]; + } + + c->mon = r->mon; + r->mon->sel = r; + + if (attachmode) { + if (r == r->mon->clients) + attach(c); + else { + for (at = r->mon->clients; at->next != r; at = at->next); + c->next = at->next; + at->next = c; + } + } else { + c->next = r->next; + r->next = c; + } + + attachstack(c); + arrangemon(r->mon); + prevr = r; + prevattachmode = attachmode; + } + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + + if ((m = recttomon(ev.xmotion.x, ev.xmotion.y, 1, 1)) && m != c->mon) { + detach(c); + detachstack(c); + arrangemon(c->mon); + c->mon = m; + c->tags = m->tagset[m->seltags]; + attach(c); + attachstack(c); + selmon = m; + } + + focus(c); + c->beingmoved = 0; + + if (nx != -9999) + resize(c, nx, ny, c->w, c->h, 0); + arrangemon(c->mon); +} + +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->ignoretransient && !c->isfloating && + (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + updatesizehints(c); + break; + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + updaterules(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + else if (ev->atom == netatom[NetWMIcon]) { + updateicon(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + if (ev->atom == motifatom) + updatemotifhints(c); + if (ev->atom == wmatom[WMClass]) { + if (wmclass) { + applyrules(c); + } + } + } +} + +void +saveSession(void) +{ + FILE *fw = fopen(SESSION_FILE, "w"); + for (Client *c = selmon->clients; c != NULL; c = c->next) { // get all the clients with their tags and write them to the file + fprintf(fw, "%lu %u\n", c->win, c->tags); + } + fclose(fw); +} + +void +restoreSession(void) +{ + // restore session + FILE *fr = fopen(SESSION_FILE, "r"); + if (!fr) + return; + + char *str = malloc(23 * sizeof(char)); // allocate enough space for excepted input from text file + while (fscanf(fr, "%[^\n] ", str) != EOF) { // read file till the end + long unsigned int winId; + unsigned int tagsForWin; + int check = sscanf(str, "%lu %u", &winId, &tagsForWin); // get data + if (check != 2) // break loop if data wasn't read correctly + break; + + for (Client *c = selmon->clients; c ; c = c->next) { // add tags to every window by winId + if (c->win == winId) { + c->tags = tagsForWin; + break; + } + } + } + + for (Client *c = selmon->clients; c ; c = c->next) { // refocus on windows + focus(c); + restack(c->mon); + } + + for (Monitor *m = selmon; m; m = m->next) // rearrange all monitors + arrange(m); + + free(str); + fclose(fr); + + // delete a file + remove(SESSION_FILE); +} + +void +quit(const Arg *arg) +{ + + Monitor *m; + Client *c; + + for (m = mons; m; m = m->next) { + if (m) { + for (c = m->stack; c; c = c->next) + if (c && HIDDEN(c)) show(c); + } + } + + if(arg->i) restart = 1; + running = 0; + + if (restart == 1) + saveSession(); +} + +Client * +recttoclient(int x, int y, int w, int h) +{ + Client *c, *r = NULL; + int a, area = 0; + + for (c = nexttiled(selmon->clients); c; c = nexttiled(c->next)) { + if ((a = INTERSECTC(x, y, w, h, c)) > area) { + area = a; + r = c; + } + } + return r; +} + +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 +resetlayout(const Arg *arg) +{ + if (defaultlayout) { + Arg default_layout = {.v = &layouts[0]}; + Arg default_mfact = {.f = mfact + 1}; + + setlayout(&default_layout); + setmfact(&default_mfact); + } +} + +void +reorganizetags(const Arg *arg) { + Client *c; + unsigned int occ, unocc, i; + unsigned int tagdest[LENGTH(tags)]; + + occ = 0; + for (c = selmon->clients; c; c = c->next) + occ |= (1 << (ffs(c->tags)-1)); + unocc = 0; + for (i = 0; i < LENGTH(tags); ++i) { + while (unocc < i && (occ & (1 << unocc))) + unocc++; + if (occ & (1 << i)) { + tagdest[i] = unocc; + occ &= ~(1 << i); + occ |= 1 << unocc; + } + } + + for (c = selmon->clients; c; c = c->next) + c->tags = 1 << tagdest[ffs(c->tags)-1]; + if (selmon->sel) + selmon->tagset[selmon->seltags] = selmon->sel->tags; + arrange(selmon); +} + +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; + + if (c->beingmoved) + return; + + wc.border_width = c->bw; + + if ((nexttiled(c->mon->clients) == c) && !(nexttiled(c->next))) + resetlayout(NULL); + + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +restack(Monitor *m) +{ + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + + if (warpcursor) + { + if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && selmon->lt[selmon->sellt] != &layouts[2]) + warp(m->sel); + } +} + +void +run(void) +{ + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ +} + +void spawnscratch(const Arg *arg) +{ + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(((char **)arg->v)[1], ((char **)arg->v)+1); + fprintf(stderr, "speedwm: execvp %s", ((char **)arg->v)[1]); + perror(" failed"); + exit(EXIT_SUCCESS); + } +} + +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 (altbar && wmclasscontains(wins[i], altbarclass, "")) + managealtbar(wins[i], &wa); + else 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); + } +} + +static void scratchpad_hide () +{ + if (selmon -> sel) + { + selmon -> sel -> tags = SCRATCHPAD_MASK; + if (floatscratchpad) + selmon -> sel -> isfloating = 1; + + focus(NULL); + arrange(selmon); + } +} + +static _Bool scratchpad_last_showed_is_killed (void) +{ + _Bool killed = 1; + for (Client * c = selmon -> clients; c != NULL; c = c -> next) + { + if (c == scratchpad_last_showed) + { + killed = 0; + break; + } + } + return killed; +} + +static void scratchpad_remove () +{ + if (selmon -> sel && scratchpad_last_showed != NULL && selmon -> sel == scratchpad_last_showed) + scratchpad_last_showed = NULL; +} + +static void scratchpad_show () +{ + if (scratchpad_last_showed == NULL || scratchpad_last_showed_is_killed ()) + scratchpad_show_first (); + else + { + if (scratchpad_last_showed -> tags != SCRATCHPAD_MASK) + { + scratchpad_last_showed -> tags = SCRATCHPAD_MASK; + focus(NULL); + arrange(selmon); + } + else + { + _Bool found_current = 0; + _Bool found_next = 0; + for (Client * c = selmon -> clients; c != NULL; c = c -> next) + { + if (found_current == 0) + { + if (c == scratchpad_last_showed) + { + found_current = 1; + continue; + } + } + else + { + if (c -> tags == SCRATCHPAD_MASK) + { + found_next = 1; + scratchpad_show_client (c); + break; + } + } + } + if (found_next == 0) scratchpad_show_first (); + } + } +} + +static void scratchpad_show_client (Client * c) +{ + scratchpad_last_showed = c; + c -> tags = selmon->tagset[selmon->seltags]; + focus(c); + arrange(selmon); +} + +static void scratchpad_show_first (void) +{ + for (Client * c = selmon -> clients; c != NULL; c = c -> next) + { + if (c -> tags == SCRATCHPAD_MASK) + { + scratchpad_show_client (c); + break; + } + } +} + +void +scantray(void) +{ + unsigned int num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (unsigned int i = 0; i < num; i++) { + if (wmclasscontains(wins[i], altbarclass, alttrayname)) { + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + break; + managetray(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] ? m->tagset[m->seltags] : 1); + //attach(c); + switch(attachdirection){ + case 1: + attachabove(c); + break; + case 2: + attachaside(c); + break; + case 3: + attachbelow(c); + break; + case 4: + attachbottom(c); + break; + case 5: + attachtop(c); + break; + default: + 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); +} +void +setcurrentdesktop(void){ + long data[] = { 0 }; + XChangeProperty(dpy, root, netatom[NetCurrentDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} +void +setdesktopnames(void) +{ + XTextProperty text; + Xutf8TextListToTextProperty(dpy, tags, TAGSLENGTH, XUTF8StringStyle, &text); + XSetTextProperty(dpy, root, &text, netatom[NetDesktopNames]); +} + +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 +setnumdesktops(void){ + long data[] = { TAGSLENGTH }; + XChangeProperty(dpy, root, netatom[NetNumberOfDesktops], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +void +setfocus(Client *c) +{ + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } + sendevent(c, wmatom[WMTakeFocus]); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; + c->oldstate = c->isfloating; + c->oldbw = c->bw; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; + c->isfloating = c->oldstate; + c->bw = c->oldbw; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; + if (arg && arg->v) + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +void +setcfact(const Arg *arg) { + float f; + Client *c; + + c = selmon->sel; + + if(!arg || !c || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f + c->cfact; + if(arg->f == 0.0) + f = 1.0; + else if(f < 0.25 || f > 4.0) + return; + c->cfact = f; + arrange(selmon); +} + +/* arg > 1.0 will set mfact absolutely */ +void +setmfact(const Arg *arg) +{ + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); +} + +void +toggleopacity(const Arg *arg) { + bUseOpacity = !bUseOpacity; + for (Monitor* m = mons; m; m = m->next) + for (Client* c = m->clients; c; c = c->next) + opacity(c, (bUseOpacity && c != selmon->sel) ? inactiveopacity : activeopacity); +} + +void +setup(void) +{ + XSetWindowAttributes wa; + Atom utf8string; + + /* clean up any zombies immediately */ + sigchld(0); + + signal(SIGHUP, sighup); + signal(SIGTERM, sigterm); + + /* init screen */ + screen = DefaultScreen(dpy); + tw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + xinitvisual(); + drw = drw_create(dpy, screen, root, tw, sh, visual, depth, cmap); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = altbar ? 0 : drw->fonts->h + barheight; + sp = barpaddingh; + vp = (barposition == 1) ? barpaddingv : - barpaddingv; + updategeom(); + + int i = 0; + + /* 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[NetWMIcon] = XInternAtom(dpy, "_NET_WM_ICON", 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[NetWMWindowTypeDesktop] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + netatom[NetClientInfo] = XInternAtom(dpy, "_NET_CLIENT_INFO", False); + netatom[NetClientListStacking] = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False); + netatom[NetWMWindowsOpacity] = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); + netatom[NetDesktopViewport] = XInternAtom(dpy, "_NET_DESKTOP_VIEWPORT", False); + netatom[NetNumberOfDesktops] = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False); + netatom[NetCurrentDesktop] = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); + netatom[NetDesktopNames] = XInternAtom(dpy, "_NET_DESKTOP_NAMES", False); + motifatom = XInternAtom(dpy, "_MOTIF_WM_HINTS", 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) + 1, sizeof(Clr *)); + scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], alphas[i], 3); + if (LENGTH(tags) > LENGTH(tagsel)) + die("too few color schemes for the tags"); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], alphas[i], 3); + tagscheme = ecalloc(LENGTH(tagsel), sizeof(Clr *)); + for (i = 0; i < LENGTH(tagsel); i++) + tagscheme[i] = drw_scm_create(drw, tagsel[i], tagalpha, 2); + /* init bars */ + updatebars(); + updatestatus(); + updatepreview(); + /* 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 *) "speedwm", 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); + setnumdesktops(); + setcurrentdesktop(); + setdesktopnames(); + setviewport(); + XDeleteProperty(dpy, root, netatom[NetClientList]); + XDeleteProperty(dpy, root, netatom[NetClientInfo]); + XDeleteProperty(dpy, root, netatom[NetClientListStacking]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask|KeyPressMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + focus(NULL); + spawnbar(); +} + +void +setviewport(void){ + long data[] = { 0, 0 }; + XChangeProperty(dpy, root, netatom[NetDesktopViewport], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 2); +} + + +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 +show(Client *c) +{ + if (!c || !HIDDEN(c)) + return; + + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); + arrange(c->mon); +} + +void +showwin(Client *c) +{ + if (!c || !HIDDEN(c)) + return; + + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); + arrange(c->mon); +} + +void +showhide(Client *c) +{ + if (!c) + return; + if (ISVISIBLE(c)) { + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if (c->needresize) { + c->needresize = 0; + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else { + 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 +togglewin(const Arg *arg) +{ + Client *c = (Client *)arg->v; + + if (c == selmon->sel) { + hidewin(NULL); + focus(c); + arrange(c->mon); + } else { + if (HIDDEN(c)) + showwin(c); + focus(c); + restack(selmon); + } +} + +void +showtagpreview(unsigned int i) +{ + + if (!selmon->previewshow || !selmon->tagmap[i]) { + XUnmapWindow(dpy, selmon->tagwin); + return; + } + + if (tagpreview) { + XSetWindowBackgroundPixmap(dpy, selmon->tagwin, selmon->tagmap[i]); + XCopyArea(dpy, selmon->tagmap[i], selmon->tagwin, drw->gc, 0, 0, + selmon->mw / scalepreview, selmon->mh / scalepreview, + 0, 0); + XSync(dpy, False); + XMapWindow(dpy, selmon->tagwin); + } +} + +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; + } + } + + } +} + +#define SPAWN_CWD_DELIM " []{}()<>\"':" + +void +getpreview(void) +{ + unsigned int occ = 0, i; + Client *c; + Imlib_Image image; + + for (c = selmon->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + for (i = 0; i < LENGTH(tags); i++) { + /* searching for tags that are occupied && selected */ + if (!(occ & 1 << i) || !(selmon->tagset[selmon->seltags] & 1 << i)) + continue; + + if (selmon->tagmap[i]) { /* tagmap exist, clean it */ + XFreePixmap(dpy, selmon->tagmap[i]); + selmon->tagmap[i] = 0; + } + + if (!(image = imlib_create_image(tw, sh))) { + fprintf(stderr, "speedwm: imlib: failed to create image, skipping.\n"); + continue; + } + imlib_context_set_image(image); + imlib_context_set_display(dpy); + imlib_image_set_has_alpha(1); + imlib_context_set_blend(0); + imlib_context_set_visual(visual); + imlib_context_set_drawable(root); + imlib_copy_drawable_to_image(0, selmon->wx, selmon->wy, selmon->ww ,selmon->wh, 0, 0, 1); + selmon->tagmap[i] = XCreatePixmap(dpy, selmon->tagwin, selmon->mw / scalepreview, selmon->mh / scalepreview, depth); + imlib_context_set_drawable(selmon->tagmap[i]); + imlib_render_image_part_on_drawable_at_size(0, 0, selmon->mw, selmon->mh, 0, 0, selmon->mw / scalepreview, selmon->mh / scalepreview); + imlib_free_image(); + } +} + +void +sighup(int unused) +{ + Arg a = {.i = 1}; + quit(&a); +} + +void +sigterm(int unused) +{ + Arg a = {.i = 0}; + quit(&a); +} + +#ifdef XINERAMA +void +sortscreens(XineramaScreenInfo *screens, int n) +{ + int i, j; + XineramaScreenInfo *screen = ecalloc(1, sizeof(XineramaScreenInfo)); + + for (i = 0; i < n; i++) + for (j = i + 1; j < n; j++) + if (RIGHTOF(screens[i], screens[j])) { + memcpy(&screen[0], &screens[i], sizeof(XineramaScreenInfo)); + memcpy(&screens[i], &screens[j], sizeof(XineramaScreenInfo)); + memcpy(&screens[j], &screen[0], sizeof(XineramaScreenInfo)); + } + XFree(screen); +} +#endif /* XINERAMA */ + +void +spawn(const Arg *arg) +{ + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + if (arg->v == statuscmd) { + for (int i = 0; i < LENGTH(statuscmds); i++) { + if (statuscmdn == statuscmds[i].id) { + statuscmd[2] = statuscmds[i].cmd; + setenv("BUTTON", lastbutton, 1); + break; + } + } + if (!statuscmd[2]) + exit(EXIT_SUCCESS); + } + + if(spawncd) { + if(selmon->sel) { + const char* const home = getenv("HOME"); + assert(home && strchr(home, '/')); + const size_t homelen = strlen(home); + char *cwd, *pathbuf = NULL; + struct stat statbuf; + + cwd = strtok(selmon->sel->name, SPAWN_CWD_DELIM); + /* NOTE: strtok() alters selmon->sel->name in-place, + * but that does not matter because we are going to + * exec() below anyway; nothing else will use it */ + while(cwd) { + if(*cwd == '~') { /* replace ~ with $HOME */ + if(!(pathbuf = malloc(homelen + strlen(cwd)))) /* ~ counts for NULL term */ + die("fatal: could not malloc() %u bytes\n", homelen + strlen(cwd)); + strcpy(strcpy(pathbuf, home) + homelen, cwd + 1); + cwd = pathbuf; + } + + if(strchr(cwd, '/') && !stat(cwd, &statbuf)) { + if(!S_ISDIR(statbuf.st_mode)) + cwd = dirname(cwd); + + if(!chdir(cwd)) + break; + } + + cwd = strtok(NULL, SPAWN_CWD_DELIM); + } + + free(pathbuf); + } + } + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + fprintf(stderr, "speedwm: execvp %s", ((char **)arg->v)[0]); + perror(" failed"); + exit(EXIT_SUCCESS); + } +} + +void +spawnbar() +{ + if (*altbarcmd) + if (system(altbarcmd) == 0) + return; + +} + +void +tagmon(const Arg *arg) +{ + Client *c = selmon->sel; + if (!c || !mons->next) + return; + if (movefullscreenmon) { + if (c->isfullscreen) { + c->isfullscreen = 0; + sendmon(c, dirtomon(arg->i)); + c->isfullscreen = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else + sendmon(c, dirtomon(arg->i)); + } else { + sendmon(selmon->sel, dirtomon(arg->i)); + } +} + +void +viewtoleft(const Arg *arg) { + if(__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] > 1) { + selmon->seltags ^= 1; /* toggle sel tagset */ + selmon->tagset[selmon->seltags] = selmon->tagset[selmon->seltags ^ 1] >> 1; + focus(NULL); + arrange(selmon); + } +} + +void +viewtoright(const Arg *arg) { + if(__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 + && selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { + selmon->seltags ^= 1; /* toggle sel tagset */ + selmon->tagset[selmon->seltags] = selmon->tagset[selmon->seltags ^ 1] << 1; + focus(NULL); + arrange(selmon); + } +} + +void +togglebar(const Arg *arg) +{ + + /** + * Polybar tray does not raise maprequest event. It must be manually scanned + * for. Scanning it too early while the tray is being populated would give + * wrong dimensions. + */ + if (altbar && !selmon->traywin) + scantray(); + + selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, selmon->bh); + XMoveResizeWindow(dpy, selmon->traywin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, selmon->bh); + + if (altbar) + XMoveResizeWindow(dpy, selmon->traywin, selmon->tx, selmon->by, selmon->tw, selmon->bh); + arrange(selmon); +} + +void +togglescratch(const Arg *arg) +{ + Client *c; + unsigned int found = 0; + + for (c = selmon->clients; c && !(found = c->scratchkey == ((char**)arg->v)[0][0]); c = c->next); + if (found) { + c->tags = ISVISIBLE(c) ? 0 : selmon->tagset[selmon->seltags]; + focus(NULL); + arrange(selmon); + + if (ISVISIBLE(c)) { + focus(c); + restack(selmon); + } + + } else{ + spawnscratch(arg); + } +} + +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) + { + if (savefloat) + { + /* restore last known float dimensions */ + resize(selmon->sel, selmon->sel->sfx, selmon->sel->sfy, selmon->sel->sfw, selmon->sel->sfh, False); + } + else + { + resize(selmon->sel, selmon->sel->x, selmon->sel->y, selmon->sel->w, selmon->sel->h, 0); + } + } + else + { + selmon->sel->sfx = selmon->sel->x; + selmon->sel->sfy = selmon->sel->y; + selmon->sel->sfw = selmon->sel->w; + selmon->sel->sfh = selmon->sel->h; + } + + arrange(selmon); +} + +void hidewin(const Arg *arg) { + if (!selmon->sel) + return; + Client *c = (Client *)selmon->sel; + hide(c); + hiddenWinStack[++hiddenWinStackTop] = c; +} + +void restorewin(const Arg *arg) { + int i = hiddenWinStackTop; + while (i > -1) { + if (HIDDEN(hiddenWinStack[i]) && + hiddenWinStack[i]->tags == selmon->tagset[selmon->seltags]) { + show(hiddenWinStack[i]); + focus(hiddenWinStack[i]); + restack(selmon); + for (int j = i; j < hiddenWinStackTop; ++j) { + hiddenWinStack[j] = hiddenWinStack[j + 1]; + } + --hiddenWinStackTop; + return; + } + --i; + } +} + +void hideotherwins(const Arg *arg) { + Client *c = NULL, *i; + if (!selmon->sel) + return; + c = (Client *)selmon->sel; + for (i = selmon->clients; i; i = i->next) { + if (i != c && ISVISIBLE(i)) { + hide(i); + hiddenWinStack[++hiddenWinStackTop] = i; + } + } +} + +void restoreotherwins(const Arg *arg) { + int i; + for (i = 0; i <= hiddenWinStackTop; ++i) { + if (HIDDEN(hiddenWinStack[i]) && + hiddenWinStack[i]->tags == selmon->tagset[selmon->seltags]) { + show(hiddenWinStack[i]); + restack(selmon); + memcpy(hiddenWinStack + i, hiddenWinStack + i + 1, + (hiddenWinStackTop - i) * sizeof(Client *)); + --hiddenWinStackTop; + --i; + } + } +} + +/* +int issinglewin(const Arg *arg) { + Client *c = NULL; + int cot = 0; + int tag = selmon->tagset[selmon->seltags]; + for (c = selmon->clients; c; c = c->next) { + if (ISVISIBLE(c) && !HIDDEN(c) && c->tags == tag) { + cot++; + } + if (cot > 1) { + return 0; + } + } + return 1; +} +*/ + +/* +void focuswin(const Arg *arg) { + Client *c = NULL, *i; + int j; + + if (arg->i > 0) { + for (c = selmon->sel->next; + c && !(c->tags == selmon->tagset[selmon->seltags]); c = c->next) + ; + if (!c) + for (c = selmon->clients; + c && !(c->tags == selmon->tagset[selmon->seltags]); + c = c->next) + ; + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (i->tags == selmon->tagset[selmon->seltags]) + c = i; + if (!c) + for (; i; i = i->next) + if (i->tags == selmon->tagset[selmon->seltags]) + c = i; + } + + i = selmon->sel; + + if (c && c != i) { + hide(i); + for (j = 0; j <= hiddenWinStackTop; ++j) { + if (HIDDEN(hiddenWinStack[j]) && + hiddenWinStack[j]->tags == selmon->tagset[selmon->seltags] && + hiddenWinStack[j] == c) { + show(c); + focus(c); + restack(selmon); + memcpy(hiddenWinStack + j, hiddenWinStack + j + 1, + (hiddenWinStackTop - j) * sizeof(Client *)); + hiddenWinStack[hiddenWinStackTop] = i; + return; + } + } + } +} +*/ + +void +togglesticky(const Arg *arg) +{ + if (!selmon->sel) + return; + selmon->sel->issticky = !selmon->sel->issticky; + arrange(selmon); +} + +void +togglefullscr(const Arg *arg) +{ + if(selmon->sel) + setfullscreen(selmon->sel, !selmon->sel->isfullscreen); +} + +void +freeicon(Client *c) +{ + if (c->icon) { + XRenderFreePicture(dpy, c->icon); + c->icon = None; + } + updatecurrentdesktop(); +} + +/* +void +togglewin(const Arg *arg) +{ + Client *c = (Client*)arg->v; + if (c == selmon->sel) + hide(c); + else { + if (HIDDEN(c)) + show(c); + focus(c); + restack(selmon); + } +} +*/ + + +void +unfocus(Client *c, int setfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + opacity(c, inactiveopacity); + XSetWindowBorder(dpy, c->win, scheme[SchemeNormBorder][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + updatecurrentdesktop(); +} + +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); + freeicon(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + if (scratchpad_last_showed == c) + scratchpad_last_showed = NULL; + free(c); + + if (!s) { + arrange(m); + focus(NULL); + updateclientlist(); + } +} + +void +unmanagealtbar(Window w) +{ + Monitor *m = wintomon(w); + + if (!m) + return; + + m->barwin = 0; + m->by = 0; + m->bh = 0; + updatebarpos(m); + arrange(m); +} + +void +unmanagetray(Window w) +{ + Monitor *m = wintomon(w); + + if (!m) + return; + + m->traywin = 0; + m->tx = 0; + m->tw = 0; + updatebarpos(m); + arrange(m); +} + + +void +unmapnotify(XEvent *e) +{ + Client *c; + Monitor *m; + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + } else if (altbar && (m = wintomon(ev->window)) && m->barwin == ev->window) + unmanagealtbar(ev->window); + else if (altbar && m->traywin == ev->window) + unmanagetray(ev->window); +} + +void +updatebars(void) +{ + if (altbar) + return; + + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixel = 0, + .border_pixel = 0, + .colormap = cmap, + .event_mask = ButtonPressMask|ExposureMask|PointerMotionMask + }; + XClassHint ch = {"speedwm", "speeddwm"}; + 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, depth, + InputOutput, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|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 - barpaddingv - m->bh; + m->by = m->barposition ? m->wy : m->wy + m->wh + barpaddingv; + m->wy = m->barposition ? m->wy + m->bh + vp : m->wy; + } else + m->by = -m->bh - vp; +} + +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); + + XDeleteProperty(dpy, root, netatom[NetClientListStacking]); + for (m = mons; m; m = m->next) + for (c = m->stack; c; c = c->snext) + XChangeProperty(dpy, root, netatom[NetClientListStacking], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +} + +void updatecurrentdesktop(void){ + long rawdata[] = { selmon->tagset[selmon->seltags] }; + int i=0; + while(*rawdata >> (i+1)){ + i++; + } + long data[] = { i }; + XChangeProperty(dpy, root, netatom[NetCurrentDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 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; + sortscreens(unique, nn); + if (n <= nn) { /* new monitors available */ + for (i = 0; i < (nn - n); 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); + } + } else { /* less monitors available nn < n */ + 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; + switch(attachdirection){ + case 1: + attachabove(c); + break; + case 2: + attachaside(c); + break; + case 3: + attachbelow(c); + break; + case 4: + attachbottom(c); + break; + case 5: + attachtop(c); + break; + default: + 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 != tw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = tw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void +updatemotifhints(Client *c) +{ + Atom real; + int format; + unsigned char *p = NULL; + unsigned long n, extra; + unsigned long *motif; + int width, height; + + if (!decorhints) + return; + + if (XGetWindowProperty(dpy, c->win, motifatom, 0L, 5L, False, motifatom, + &real, &format, &n, &extra, &p) == Success && p != NULL) { + motif = (unsigned long*)p; + if (motif[MWM_HINTS_FLAGS_FIELD] & MWM_HINTS_DECORATIONS) { + width = WIDTH(c); + height = HEIGHT(c); + + if (motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_ALL || + motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_BORDER || + motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_TITLE) + c->bw = c->oldbw = borderpx; + else + c->bw = c->oldbw = 0; + + resize(c, c->x, c->y, width - (2*c->bw), height - (2*c->bw), 0); + } + XFree(p); + } +} + +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); +} + +void +updatestatus(void) +{ + Monitor* m; + if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) + strcpy(stext, defaultname); + else + copyvalidchars(stext, rawstext); + if (statusallmons) { + for(m = mons; m; m = m->next) + drawbar(m); + } else { + drawbar(selmon); + } +} + +void +updaterules(Client *c) +{ + if (refreshrules) + { + const char *class, *instance; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + char found_rule = 0; + + 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; + + if(!found_rule) + { + c->tags=0; + found_rule=1; + } + 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]; + + // end apply rules + if (c->isfloating) + resize(c, c->x, c->y, + c->w, c->h, 0); + arrange(c->mon); + } +} + +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') + strcpy(c->name, broken); +} + +void +updateicon(Client *c) +{ + freeicon(c); + c->icon = geticonprop(c->win, &c->icw, &c->ich); +} + +void +updatepreview(void) +{ + Monitor *m; + + XSetWindowAttributes wa = { + .background_pixel = 0, + .border_pixel = 0, + .colormap = cmap, + .override_redirect = True, + //.background_pixmap = ParentRelative, + .event_mask = ButtonPressMask|ExposureMask + }; + + for (m = mons; m; m = m->next) { + m->tagwin = XCreateWindow(dpy, root, m->wx, m->by + bh, m->mw / scalepreview, m->mh / scalepreview, 0, + depth, CopyFromParent, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + //DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), + //CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->tagwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->tagwin); + XUnmapWindow(dpy, m->tagwin); + } +} + +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) +{ + Monitor *m; + int i; + unsigned int tmptag; + + if(arg->ui && (arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + getpreview(); + //selmon->seltags ^= 1; /* toggle sel tagset */ + for (m = mons; m; m = m->next) + m->seltags ^= 1; + if (arg->ui & TAGMASK) { + //selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + for (m = mons; m; m = m->next) + m->tagset[m->seltags] = arg->ui & TAGMASK; + + if (pertag) { + selmon->pertag->prevtag = selmon->pertag->curtag; + + if (arg->ui == ~0) + selmon->pertag->curtag = 0; + else { + for (i = 0; !(arg->ui & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + } + + focus(NULL); + arrange(NULL); + updatecurrentdesktop(); +} + +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->barposition && !y)) + return; + + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2); +} + +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", 0), 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 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; + + 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; +} + +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 || w == m->traywin) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +int +wmclasscontains(Window win, const char *class, const char *name) +{ + XClassHint ch = { NULL, NULL }; + int res = 1; + + if (XGetClassHint(dpy, win, &ch)) { + if (ch.res_name && strstr(ch.res_name, name) == NULL) + res = 0; + if (ch.res_class && strstr(ch.res_class, class) == NULL) + res = 0; + } else + res = 0; + + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + + return res; +} + +/* 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, "speedwm: 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("speedwm: Do not start speedwm by running 'speedwm'. Your X session should run 'speedwm_run'."); + return -1; +} + +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); + } +} + +void +zoom(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange + || (selmon->sel && selmon->sel->isfloating)) + return; + if (c == nexttiled(selmon->clients)) + if (!c || !(c = nexttiled(c->next))) + return; + pop(c); +} + +void +resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst) +{ + char *sdst = NULL; + int *idst = NULL; + float *fdst = NULL; + + sdst = dst; + idst = dst; + fdst = dst; + + char fullname[256]; + char *type; + XrmValue ret; + + snprintf(fullname, sizeof(fullname), "%s.%s", "speedwm", name); + fullname[sizeof(fullname) - 1] = '\0'; + + XrmGetResource(db, fullname, "*", &type, &ret); + if (!(ret.addr == NULL || strncmp("String", type, 64))) + { + switch (rtype) { + case STRING: + strcpy(sdst, ret.addr); + break; + case INTEGER: + *idst = strtoul(ret.addr, NULL, 10); + break; + case FLOAT: + *fdst = strtof(ret.addr, NULL); + break; + } + } +} + +void +load_xresources(void) +{ + Display *display; + char *resm; + XrmDatabase db; + ResourcePref *p; + + display = XOpenDisplay(NULL); + resm = XResourceManagerString(display); + if (!resm) + return; + + db = XrmGetStringDatabase(resm); + for (p = resources; p < resources + LENGTH(resources); p++) + resource_load(db, p->name, p->type, p->dst); + XCloseDisplay(display); +} + +/* Thanks to https://codeberg.org/explosion-mental/demwm for this! */ +void +previewtag(const Arg *arg) +{ + if (selmon->previewshow != (arg->ui + 1)) + selmon->previewshow = arg->ui + 1; + else + selmon->previewshow = 0; + showtagpreview(arg->ui); +} + +void +setclienttagprop(Client *c) +{ + long data[] = { (long) c->tags, (long) c->mon->num }; + XChangeProperty(dpy, c->win, netatom[NetClientInfo], XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) data, 2); +} + +int +main(int argc, char *argv[]) +{ + if (argc == 2 && !strcmp("-v", argv[1])) + die("speedwm-"VERSION); + else if (argc != 1) + die("usage: speedwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("speedwm: cannot open display"); + if (!(xcon = XGetXCBConnection(dpy))) + die("speedwm: cannot get xcb connection\n"); + checkotherwm(); + autostart_exec(); + XrmInitialize(); + load_xresources(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec ps", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + restoreSession(); + run(); + if(restart) execvp(argv[0], argv); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} + + +void +livereloadxrdb(const Arg *arg) +{ + load_xresources(); + int i; + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], alphas[i], 3); + focus(NULL); + arrange(NULL); +} + +void +insertclient(Client *item, Client *insertItem, int after) { + Client *c; + if (item == NULL || insertItem == NULL || item == insertItem) return; + detach(insertItem); + if (!after && selmon->clients == item) { + attach(insertItem); + return; + } + if (after) { + c = item; + } else { + for (c = selmon->clients; c; c = c->next) { if (c->next == item) break; } + } + insertItem->next = c->next; + c->next = insertItem; +} + +void +inplacerotate(const Arg *arg) +{ + if(!selmon->sel || (selmon->sel->isfloating && !arg->f)) return; + + unsigned int selidx = 0, i = 0; + Client *c = NULL, *stail = NULL, *mhead = NULL, *mtail = NULL, *shead = NULL; + + // Determine positionings for insertclient + for (c = selmon->clients; c; c = c->next) { + if (ISVISIBLE(c) && !(c->isfloating)) { + if (selmon->sel == c) { selidx = i; } + if (i == selmon->nmaster - 1) { mtail = c; } + if (i == selmon->nmaster) { shead = c; } + if (mhead == NULL) { mhead = c; } + stail = c; + i++; + } + } + + // All clients rotate + if (arg->i == 2) insertclient(selmon->clients, stail, 0); + if (arg->i == -2) insertclient(stail, selmon->clients, 1); + // Stack xor master rotate + if (arg->i == -1 && selidx >= selmon->nmaster) insertclient(stail, shead, 1); + if (arg->i == 1 && selidx >= selmon->nmaster) insertclient(shead, stail, 0); + if (arg->i == -1 && selidx < selmon->nmaster) insertclient(mtail, mhead, 1); + if (arg->i == 1 && selidx < selmon->nmaster) insertclient(mhead, mtail, 0); + + // Restore focus position + i = 0; + for (c = selmon->clients; c; c = c->next) { + if (!ISVISIBLE(c) || (c->isfloating)) continue; + if (i == selidx) { focus(c); break; } + i++; + } + arrange(selmon); + focus(c); +} diff --git a/speedwm.html b/speedwm.html new file mode 100644 index 0000000..94e916f --- /dev/null +++ b/speedwm.html @@ -0,0 +1,439 @@ + +
+Dynamic window manager // speedie's website + + + + + +

speedie's website

+ +🏠 Home +💾 Projects +📘 Blog +📕 Guides +📧 Email +🐧 Dotfiles +📰 RSS +💰Donate + +
+
+ + +

image

+

https://speedie.gq/speedwm

+

https://speedie.gq/donate

+

What is speedwm?

+

speedwm is a window manager forked from suckless.org's dwm or dynamic window manager. It manages the user's open windows and tiles them according to a set layout (dynamic).
Unlike dwm, speedwm tries to be minimal just dwm but also has functionality and aesthetics as a goal. It is also much more minimal than other window managers like i3-gaps while offering many more features.

+

Tiling window managers (unlike floating window managers that you may be used to) tile windows based on a set layout making them easy to get productive on.
They also encourage the user to use their keyboard instead of the mouse so that the user doesn't have to move their hands much but there are mouse keybinds and more can be added.

+

Installation

+

In order to install this build of speedwm, all dependencies must be installed.
You can see (Dependencies) for a list of all dependencies required to use this fork.

+
    +
  • git clone https://codeberg.org/speedie/speedwm
  • +
  • cd speedwm
  • +
  • make clean install
      +
    • If any warnings show up, fix it by installing the missing dependency.
    • +
    +
  • +
  • If a .xinitrc is used, add 'speedwm_run' to the end.
      +
    • NOTE: Your .xinitrc should ONLY contain 'speedwm_run'. speedwm will automatically start everything else. If not, edit autostart.h and 'make clean install'. If a .xinitrc is not used then you don't need to worry. A .desktop file is automatically created when you run 'make clean install'.
    • +
    +
  • +
+

Layouts

+

speedwm comes with the following layouts:

+
    +
  • Tile
  • +
  • Monocle
  • +
  • Grid
  • +
  • Deck
  • +
  • Centered Master
  • +
  • Centered Floating Master
  • +
  • Spiral
  • +
  • Dwindle
  • +
  • Three Column
  • +
  • Bottom Stack
  • +
  • Horizontal Bottom Stack
  • +
  • Horizonal Grid
  • +
  • Tatami
  • +
  • Tilewide
  • +
  • Stairs
  • +
  • Tiling (5:4)
  • +
  • Column
  • +
  • Dynamic Grid
  • +
+

They can be switched between using a little menu (See Keybinds for more information) or by right clicking the Layout indicator.
The more commonly used layouts can be switched between using a quick keybind.

+

Keybinds

+

Below is a list of all speedwm keybinds.

+

Applications

+

Keybinds for regular applications

+
    +
  • Super+Shift+Enter | Opens a terminal
  • +
  • Super+Shift+Colon | Opens a dmenu prompt
  • +
  • Super+Shift+s | Opens 'maim' to take a screenshot and copies it to the clipboard using 'xclip'
  • +
  • Super+Shift+f | Opens the defined file manager
  • +
  • Super+Shift+w | Opens the defined web browser
  • +
  • Super+Shift+o | Opens the bundled dfmpeg dmenu script to record your screen.
  • +
  • Super+Shift+e | Opens the dboard dmenu script in dmenu which can copy text to your clipboard
  • +
  • Super+Shift+t | Opens the defined editor in your terminal
  • +
  • Super+Shift+p | Kills the defined web browser
  • +
  • Super+Shift+m | Kills the defined music player
  • +
  • Super+Shift+a | Opens the defined mixer in your terminal
  • +
  • Super+Shift+m | Opens the defined music player
  • +
  • Super+Shift+x | Opens the defined system process viewer in your terminal
  • +
  • Super+Control+Shift+m | Opens the defined email client
  • +
  • Super+Control+u | Opens the defined RSS reader

    + +

    These keybinds are for navigating speedwm

    +
  • +
  • Super+t | Reorganize tags and move clients

    +
  • +
  • Super+f | Full-screen the selected window
  • +
  • Super+b | Show/hide the speedwm bar
  • +
  • Super+s | Show/hide the systray (If trayer is installed)
  • +
  • Super+j/k | Move focus between visible windows
  • +
  • Super+Alt+j/k | Increase/decrease gaps between windows in tiling layout by 1
  • +
  • Super+Alt+u/d | Increase/decrease gaps between windows in tiling layout by 5
  • +
  • Super+Shift+j/k | Move focus between hidden windows
  • +
  • Super+a/d | Increase/decrease size of each window
  • +
  • Super+o | Hide a window
  • +
  • Super+Control+o | Show a window
  • +
  • Super+w | Hide all windows except focused
  • +
  • Super+Control+w | Show all windows except focused
  • +
  • Super+a/d | Move to the next/previous tag
  • +
  • Super+Minus | Show the scratchpad
  • +
  • Super+Equal | Remove the scratchpad
  • +
  • Super+Enter | Switch order of windows
  • +
  • Super+Shift+q | Close the current window
  • +
  • Super+Space | Set layout
  • +
  • Super+t | Disable inactive fade
  • +
  • Super+Shift+Equal | Toggle scratchpads
  • +
  • Super+Shift+Minus | Hide the scratchpad
  • +
  • Super+Shift+Space | Unfloat floating windows
  • +
  • Super+Shift+Arrow | Resizes a window in floating mode
  • +
  • Super+1 | Move to tag 1
  • +
  • Super+2 | Move to tag 2
  • +
  • Super+3 | Move to tag 3
  • +
  • Super+4 | Move to tag 4
  • +
  • Super+5 | Move to tag 5
  • +
  • Super+6 | Move to tag 6
  • +
  • Super+7 | Move to tag 7
  • +
  • Super+8 | Move to tag 8
  • +
  • Super+9 | Move to tag 9
  • +
  • Super+Shift+1 | Preview tag 1
  • +
  • Super+Shift+2 | Preview tag 2
  • +
  • Super+Shift+3 | Preview tag 3
  • +
  • Super+Shift+4 | Preview tag 4
  • +
  • Super+Shift+5 | Preview tag 5
  • +
  • Super+Shift+6 | Preview tag 6
  • +
  • Super+Shift+7 | Preview tag 7
  • +
  • Super+Shift+8 | Preview tag 8
  • +
  • Super+Shift+9 | Preview tag 9
  • +
  • Super+Shift+h/j/k/l | Rotates a stack.
  • +
  • Super+Shift+Escape | Ask the user if they want to shutdown or reboot or nothing
  • +
  • Super+Shift+i | Open a dmenu prompt and open the file the user picks in Zathura
  • +
  • Super+Shift+p | Open a dmenu prompt and open the file the user picks in Vim
  • +
  • Super+Control+e | Switch to layout 3 (Grid)
  • +
  • Super+Control+r | Switch to layout 1 (Monocle)
  • +
  • Super+Control+t | Switch to layout 0 (Master & stack)
  • +
  • Super+Control+y | Switch to layout 4 (Deck)
  • +
  • Super+Control+0 | Set all windows to use the same tag
  • +
  • Super+Control+Arrow | Moves a window to any corner of your screen (Arrow key)
  • +
  • Super+Control+Tab | Open a dmenu prompt asking the user what layout to switch to
  • +
  • Super+Control+h | Open a list of all keybinds in your terminal using less
  • +
  • Super+Control+Shift+a/d | Move between available layouts
  • +
  • Super+Alt+s | Make the current selected window sticky
  • +
  • Super+Control+Shift+Esc | Open speedwm-utils (Main menu)
  • +
  • Super+Control+Shift+Arrow | Resize the window to the screen size.
  • +
  • Super+Control+Shift+s | Set a wallpaper
  • +
  • Super+Control+Shift+n | Connect to wifi (Requires iwd)
  • +
  • Super+Control+Shift+b | Connect to a bluetooth device (Requires bluez and bluez-utils)
  • +
  • Super+Control+Shift+e | Open up a list of dotfiles in dmenu that you can edit.
  • +
  • Alt+Tab | Switch windows quickly and easily
  • +
  • Alt+Control+j/k | Change window size vertically (cfact)

    +

    Chained keybinds

    +
  • +
  • Super+c & w | Curl wttr.in and open in less
  • +
  • Super+c & m | Ask the user for a topic and curl cheat.sh
  • +
  • Super+g & t | Toggle gaps
  • +
  • Super+g & 0 | Reset gaps
  • +
  • Super+g & i | Increase inner gaps by 1
  • +
  • Super+Shift+g & i | Decrease inner gaps by 1
  • +
  • Super+g & o | Increase outer gaps by 1
  • +
  • Super+Shift+g & o | Decrease outer gaps by 1
  • +
  • Super+r+v | Open the defined music visualizer

    +

    Extras

    +

    These will only work if your keyboard has special multimedia buttons.

    +
  • +
  • Mute button | Mutes your audio

    +
  • +
  • Up Volume button | Increases your volume
  • +
  • Down Volume button | Decreases your volume
  • +
  • Stop button | Stops your defined music player
  • +
  • Browser button | Opens your defined web browser
  • +
  • Power button | Ask if you wanna shut down, restart or lock your computer.
  • +
  • Email button | Open your defined email client
  • +
  • System button | Open your defined status viewer in a terminal

    +

    Mouse

    +

    These binds can be activated using your mouse

    +
  • +
  • Tag <num> (Left click) | Switch to tag <num>

    +
  • +
  • Layout indicator (Left click) | Switch to the next layout
  • +
  • Layout indicator (Middle click) | Switch to the next layout
  • +
  • Layout indicator (Right click) | Open a dmenu list of all layouts
  • +
  • Window title (Left click) | Hide/Show the window
  • +
  • Window title (Right click) | Open speedwm-utils
  • +
  • Focused window (Super+Alt+Left click) | Move the focused window around
  • +
  • Focused window (Super+Alt+Middle click) | Make the focused window floating
  • +
  • Focused window title (Middle click) | Rotate stack
  • +
  • Dragging (Super+Right click) | Increase/decrease size of each window

    +

    There are also keybinds for statuscmd, but you must implement it into your own status bar.
    See mouse.h for more information.

    +
  • +
+

Dependencies

+

These are absolutely necessary, speedwm will NOT compile without them

+
    +
  • libxft-bgra (Can be installed through 'make <distro>-libxftfix')
      +
    • NOTE: libXft will do but will cause speedwm and as such all your applications to crash if a colored emoji is displayed in the status bar.
    • +
    +
  • +
  • libXinerama
      +
    • Can be disabled through editing config.mk if you're not interested in multiple monitors.
    • +
    +
  • +
  • imlib2

    +

    Features

    +

    These are dependencies if you wanna use certain features
    NOTE: Do not add any of these to .xinitrc or similar. They are going to be autostarted by speedwm.
    If you want to use an alternative, change it in options.h.

    +
  • +
  • dmenu
      +
    • NOTE: dmenu is required for most scripts included with this build of speedwm. My build is required for Pywal support.
    • +
    • NOTE 2: The build must have the 'grid' patch. If yours does not have this, you can patch it in or get my build here: https://codeberg.org/speedie/dmenu
    • +
    +
  • +
  • picom
  • +
  • xclip (Required for clipboard support by a few scripts, will start automatically)
  • +
  • xwallpaper (Required to set wallpapers automatically)
  • +
  • xmodmap (Install if you want Escape instead of Caps Lock and Right Super+hjkl for arrow keys)
  • +
  • xrdb (Install if you want .Xresources support)
  • +
  • pywal (Install if you want pywal support. Requires swal aka the default way to set wallpapers)
  • +
  • wmctrl (Needed for proper window management)
  • +
  • xsetroot (Needed for most scripts including Pywal support)
  • +
  • slock (Required for screen locking)
  • +
  • maim (Required for built in 'speedwm-screenshotutil' script)

    +

    Software

    +

    This build of speedwm comes with binds for software.
    These must be installed by default but you can change what software is required by editing 'options.h' and running 'make clean install'.
    You can also remove keybinds by editing 'keybinds.h' and running 'make clean install'.

    +
  • +
  • st (Terminal)
  • +
  • firefox (Web browser)
  • +
  • htop (Status monitor)
  • +
  • newsboat (RSS reader)
  • +
  • zathura (PDF reader)
  • +
  • alsa-utils (Required for audio controls)
  • +
  • mocp (Default music player)
  • +
  • vim (Text editor)
  • +
  • neomutt (Email client)
  • +
  • maim (Screenshot tool, automatically copies to clipboard using xclip)
  • +
  • vifm (File manager)
  • +
  • slock (Lock screen)
    And everything under 'Features'.
  • +
+

Important

+

If you're used to dwm, speedwm might be a little unfamiliar to you at first. This is because speedwm doesn't use config.h (or config.def.h).
Instead, config.h is split into different parts to make it easier to edit. Instead of editing config.h you'll want to edit:

+
    +
  • autostart.h for starting stuff right before speedwm (For example xclip, pywal, etc.)
  • +
  • options.h for changing colors and applications to use with keybinds.
  • +
  • fsignal.h for adding fake signals
  • +
  • colors.h for changing alpha options, most users won't need to edit it.
  • +
  • xresources.h for adding .Xresources options
  • +
  • rules.h for adding rules
  • +
  • keybinds.h for adding/removing keybinds.
  • +
+

After you've edited one of the files, you need to run 'make clean install' to reinstall speedwm.
Remember that you can change colors through your .Xresources file (see .Xresources and Pywal) meaning you do not need to recompile speedwm.

+

Another important detail you must keep in mind is that this build comes with a status bar simply named 'status'.
It can be found in the speedwm source code directory. It is just a shell script which adds stuff to your status bar. It will automatically be started when speedwm starts.

+

You can edit the status bar simply by editing 'status' and running 'make clean install'.
You can also configure it by editing '~/.config/speedwm-de/status/config'.
Please note that most status bars including the built in 'status' depends on xsetroot which must be installed. speedwm-compatcheck is going to tell you about this when compiling.

+

If you want to change status bar, edit options.h and set 'static char status' to your status bar binary (must be in $PATH).
Alternatively, you can also set speedwm.status: <statusbar> in .Xresources (See .Xresources and Pywal)

+

.Xresources and Pywal

+

This fork of speedwm has .Xresources support thanks to the .Xresources patch.
It also has pywal support (tool which grabs colors based on your wallpaper).

+

Colors reload automagically because of a reloadxresources function this build has combined with fsignal and a wallpaper script I wrote.
Therefore, if you want colors to reload instantly, you're unfortunately forced to use the bundled wallpaper script.

+

If you want to use another script, you can open up speedwm-utils and select 'Reload .Xresources' to reload .Xresources.
Alternatively, you can write a script yourself (18 reloads the colors)

+

Below is a list of all .Xresources values you can define. The .Xresources file should be placed in ~ or ~/.config by the user.
If it is not or you want it somewhere else, you can edit 'autostart.h' and 'make clean install'.

+

Note that the 'xrdb' dependency is required for both pywal and .Xresources support and 'xsetroot' is required for automatic reloading of colors, the built in status bar, and more so you should install this.

+
    +
  • speedwm.nmaster: 1
  • +
  • speedwm.font: fontawesome:size=8
  • +
  • speedwm.font2: NotoSans-Regular:size=8:antialiasing=true
  • +
  • speedwm.font3: Noto Emoji:size=8
  • +
  • speedwm.col_background: #222222
  • +
  • speedwm.col_backgroundmid: #222222
  • +
  • speedwm.col_textnorm: #bbbbbb
  • +
  • speedwm.col_textsel: #eeeeee
  • +
  • speedwm.col_windowbordersel: #eeeeee
  • +
  • speedwm.col_windowbordernorm: #000000
  • +
  • speedwm.col_tag1: #333333
  • +
  • speedwm.col_tag1_text: #eeeeee
  • +
  • speedwm.col_tag2: #333333
  • +
  • speedwm.col_tag2_text: #eeeeee
  • +
  • speedwm.col_tag3: #333333
  • +
  • speedwm.col_tag3_text: #eeeeee
  • +
  • speedwm.col_tag4: #333333
  • +
  • speedwm.col_tag4_text: #eeeeee
  • +
  • speedwm.col_tag5: #333333
  • +
  • speedwm.col_tag5_text: #eeeeee
  • +
  • speedwm.col_tag6: #333333
  • +
  • speedwm.col_tag6_text: #eeeeee
  • +
  • speedwm.col_tag7: #333333
  • +
  • speedwm.col_tag7_text: #eeeeee
  • +
  • speedwm.col_tag8: #333333
  • +
  • speedwm.col_tag8_text: #eeeeee
  • +
  • speedwm.col_tag9: #333333
  • +
  • speedwm.col_tag9_text: #eeeeee
  • +
  • speedwm.col_layouttext: #000000
  • +
  • speedwm.col_layoutbgnorm: #222222
  • +
  • speedwm.col_layoutbgsel: #bbbbbb
  • +
  • speedwm.col_status0: #131210
  • +
  • speedwm.col_status1: #bf616a
  • +
  • speedwm.col_status2: #A16F9D
  • +
  • speedwm.col_status3: #68ABAA
  • +
  • speedwm.col_status4: #A89F93
  • +
  • speedwm.col_status5: #D3A99B
  • +
  • speedwm.col_status6: #AFC9AC
  • +
  • speedwm.col_status7: #eae1cb
  • +
  • speedwm.col_status8: #a39d8e
  • +
  • speedwm.col_status9: #6D5E8E
  • +
  • speedwm.col_status10: #A16F9D
  • +
  • speedwm.col_status11: #D3A99B
  • +
  • speedwm.col_status12: #AFC9AC
  • +
  • speedwm.col_status13: #eae1cb
  • +
  • speedwm.col_status14: #6D5E8E
  • +
  • speedwm.col_status15: #ffffff
  • +
  • speedwm.borderpx: 1
  • +
  • speedwm.snap: 20
  • +
  • speedwm.showbar: 1
  • +
  • speedwm.resizehints: 0
  • +
  • speedwm.mousemfact: 1
  • +
  • speedwm.mfact: 0.50
  • +
  • speedwm.startontag: 1
  • +
  • speedwm.enablegaps: 1
  • +
  • speedwm.smartgaps: 1
  • +
  • speedwm.smartgapsize: 0
  • +
  • speedwm.gappih: 10
  • +
  • speedwm.gappiv: 10
  • +
  • speedwm.gappoh: 10
  • +
  • speedwm.gappov: 10
  • +
  • speedwm.attachdirection: 3
  • +
  • speedwm.shell: /bin/sh
  • +
  • speedwm.sizeicon: 10
  • +
  • speedwm.spacingicon: 5
  • +
  • speedwm.status: status
  • +
  • speedwm.defaultname:
  • +
  • speedwm.refreshrules: 1
  • +
  • speedwm.decorhints: 1
  • +
  • speedwm.barpaddingv: 0
  • +
  • speedwm.barpaddingh: 0
  • +
  • speedwm.barheight: 5
  • +
  • speedwm.altbar: 0
  • +
  • speedwm.altbarclass: Polybar
  • +
  • speedwm.alttrayname: tray
  • +
  • speedwm.altbarcmd: polybar &
  • +
  • speedwm.centerfloating: 1
  • +
  • speedwm.savefloat: 1
  • +
  • speedwm.warpcursor: 1
  • +
  • speedwm.pertag: 1
  • +
  • speedwm.i3nmaster: 0
  • +
  • speedwm.scalepreview: 4
  • +
  • speedwm.tagpreview: 1
  • +
  • speedwm.mousepreview: 1
  • +
  • speedwm.monocleclientcount: 0
  • +
  • speedwm.monoclecount: 0
  • +
  • speedwm.monocleformat: [%d/%d]
  • +
  • speedwm.statusallmons: 1
  • +
  • speedwm.hidelayout: 0
  • +
  • speedwm.hidetitle: 0
  • +
  • speedwm.hidestatus: 0
  • +
  • speedwm.hidetags: 0
  • +
  • speedwm.hidesticky: 0
  • +
  • speedwm.hidefloating: 0
  • +
  • speedwm.leftlayout: 1
  • +
  • speedwm.fadeinactive: 1
  • +
  • speedwm.defaultlayout: 1
  • +
  • speedwm.wmclass: 1
  • +
  • speedwm.clicktofocus: 0
  • +
  • speedwm.stairpx: 20
  • +
  • speedwm.stairdirection: 1
  • +
  • speedwm.stairsamesize: 1
  • +
  • speedwm.deckcount: 0
  • +
  • speedwm.deckformat: D %d
  • +
  • speedwm.roundedcorners: 0
  • +
  • speedwm.cornerradius: 3
  • +
  • speedwm.swallowclients: 1
  • +
  • speedwm.swallowfloating: 1
  • +
  • speedwm.movefullscreenmon: 0
  • +
  • speedwm.lockfullscreen: 1
  • +
  • speedwm.underline: 0
  • +
  • speedwm.underlineall: 0
  • +
  • speedwm.underlinepad: 5
  • +
  • speedwm.underlinestroke: 2
  • +
  • speedwm.underlinevoffset: 0
  • +
  • speedwm.forcevsplit: 1
  • +
  • speedwm.floatscratchpad: 0
  • +
+

Fsignal

+

Thanks to the 'fsignal' patch available on suckless.org's website, we can easily write shell scripts to interact with dwm and therefore speedwm.
This is exactly what I did and speedwm-utils, speedwm-swal, speedwm-shutdown and more take advantage of it.

+

In order to use 'fsignal', your system must have 'xsetroot' installed.
Then simply use the dwm-utils script. Syntax is dwm-utils -exec <signum>

+

Below is a list of all signums and what they do.

+
    +
  • 1 | Switch to the Tiling layout
  • +
  • 2 | Switch to the Floating layout
  • +
  • 3 | Switch to the Monocle layout
  • +
  • 4 | Switch to the Grid layout
  • +
  • 5 | Switch to the Deck layout
  • +
  • 6 | Switch to the Centered Master layout
  • +
  • 7 | Switch to the Centered Floating Master layout
  • +
  • 8 | Switch to the Fibonacci Spiral layout
  • +
  • 9 | Switch to the Fibonacci Dwindle layout
  • +
  • 10 | Switch to the Three Column layout
  • +
  • 11 | Switch to the Bottom Stack Vertical layout
  • +
  • 12 | Switch to the Bottom Stack Horizontal layout
  • +
  • 13 | Switch to the Horizontal Grid layout
  • +
  • 14 | Switch to the Tatami layout
  • +
  • 15 | To be added
  • +
  • 16 | Cycle layout (Previous)
  • +
  • 17 | Cycle layout (Next)
  • +
  • 18 | Reload colors from .Xresources
  • +
  • 19 | Set mfact (-0.05)
  • +
  • 20 | Set mfact (+0.05)
  • +
  • 21 | Toggle Scratchpad
  • +
  • 22 | Toggle Sticky
  • +
  • 23 | Toggle Bar
  • +
  • 24 | Toggle Fullscreen
  • +
  • 25 | Restart speedwm keeping all your applications open.
  • +
  • 26 | Unused at the moment.
  • +
  • 27 | Switch to the Stairs layout
  • +
  • 28 | Reset layout and mfact
  • +
  • 29 | Reorganize tags
  • +
  • 30 | Restart speedwm
  • +
  • 31 | Shutdown speedwm
  • +
  • 32 | To be added
  • +
  • 33 | To be added
  • +
  • 34 | To be added
  • +
  • 35 | Switch to the Tiling (5:4) layout
  • +
  • 36 | Switch to the Column layout
  • +
  • 37 | Switch to the Dynamic Grid layout
  • +
+

Switching run launcher

+

Some users may prefer to use a different run launcher than dmenu.
Previously all scripts bundled would only run dmenu from $PATH but you can now switch run launcher very easily.

+
    +
  • Edit options.h and change RUN to your run launcher
  • +
  • Add export RUNLAUNCHER=<runlauncher> to your .<shell>rc
  • +
+

Run launchers must support dmenu arguments because otherwise scripts are going to be incompatible.
It must also support the additional '-g' argument that the dmenu grid patch provides unless you modify the scripts bundled.
Keep in mind that if you use a different run launcher, it may not support Pywal/.Xresources.

+

Auto generated.

+

This page was auto generated by the dwm-help script bundled with dwm. It acts as the help script and it writes documentation to HTML, Markdown and plain text from documentation in the docs folder and data grabbed from your current system.

+ +
+

Website made by speedie, proudly written in Vim on Gentoo Linux.

+

This website is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.

+
+ + diff --git a/speedwm.png b/speedwm.png new file mode 100644 index 0000000..e908731 Binary files /dev/null and b/speedwm.png differ diff --git a/status b/status new file mode 100755 index 0000000..0534278 --- /dev/null +++ b/status @@ -0,0 +1,521 @@ +#!/bin/sh +# Status bar for speedwm +# Written by speedie.gq in POSIX sh for https://speedie.gq/speedwm +# Licensed under the GNU GPLv3 free software license. +# +# NOTE: Anything in the DISPLAY function will be printed to your status bar. +# This means stdout will be printed regardless. +# You can output to /dev/null to silence most commands. +# +# Once you're done editing, run 'make clean install'. +# +# CONFIG +# This is the configuration for the status bar +# +# I highly recommend editing ~/.config/speedwm-de/status/config instead though +# All settings with the exception of changing the order of items can be +# changed there. Otherwise, carefully edit the file and recompile speedwm! +# +# General settings +USEWAL=true # Use Pywal if possible (true/false) +RUNONCE=false # Only run status bar once (true/false) +SLEEPTIME=7 # How long delay in seconds between each reload (int) +DATEBINDIR="/bin/" # Directory where your 'date' binary is. Default should be fine for most people +SETMETHOD="xsetroot -name" # Method used to set the status bar +SEP="<" # Separator to use +COLORFG=true # Color foreground (true/false) +COLORBG=false # Color background (true/false) +USEBUILTINCOL=false # Use built in speedwm colors (col_status). This also supports Pywal. (true/false) + +# Status bar modules +# Set to true/false to enable/disable. +# Keep in mind, if the dependencies are not installed, then they will not be used regardless of what the option is set to. +ENABLE_ITEM1=true # Enable RAM usage (true/false) +ENABLE_ITEM2=true # Enable Time (HH:MM) (true/false) +ENABLE_ITEM3=true # Enable Date (DD/MM/YY) (true/false) +ENABLE_ITEM4=true # Enable Volume/Mute status (%) (true/false) +ENABLE_ITEM5=true # Enable Weather (true/false) +ENABLE_ITEM6=true # Enable Network Traffic (B/s (true/false) +ENABLE_ITEM7=true # Enable dfmpeg status (true/false) +ENABLE_ITEM8=true # Enable CPU temp (true/false) +ENABLE_ITEM9=true # Enable music status (true/false) +ENABLE_ITEM10=false # Enable newsboat unread (true/false) +ENABLE_ITEM11=true # Enable battery percentage and charging status (true/false) +ENABLE_ITEM12=false # Enable #ff IRC backlog in status bar (true/false) + +ITEM1_ICON="" # Icon for ITEM1 +ITEM2_ICON="" # Icon for ITEM2 +ITEM3_ICON="" # Icon for ITEM3 +ITEM4_ICON="" # Icon for ITEM4 +ITEM5_ICON="" # Icon for ITEM5 +ITEM6_ICON="" # Icon for ITEM6 +ITEM7_ICON="" # Icon for ITEM7 +ITEM8_ICON="" # Icon for ITEM8 +ITEM9_ICON="" # Icon for ITEM9 +ITEM10_ICON="" # Icon for ITEM10 +ITEM11_ICON="" # Icon for ITEM11 +ITEM12_ICON="" # Icon for ITEM12 + +ITEM9_BACKEND="auto" # Backend for the music status (cmus/mocp/auto) + +# Systray options +HIDE_STATUS_SYSTRAY=true # Hide the status when a systray is running (true/false) +SYSTRAY="trayer" # Systray to use (). +STATUS_WHEN_HIDDEN="" # Status to print when status is hidden (char) + +# Order of colors and items in the status bar +PRINT() { +$SETMETHOD "\ +$FSETCOLORCMD11\ +$BSETCOLORCMD11\ +$(ITEM2) \ +$FSETCOLORCMD10\ +$BSETCOLORCMD10\ +$(ITEM3) \ +$FSETCOLORCMD13\ +$BSETCOLORCMD13\ +$(ITEM11) \ +$FSETCOLORCMD8\ +$BSETCOLORCMD8\ +$(ITEM4) \ +$FSETCOLORCMD7\ +$BSETCOLORCMD7\ +$(ITEM1) \ +$FSETCOLORCMD6\ +$BSETCOLORCMD6\ +$(ITEM6) \ +$FSETCOLORCMD4\ +$BSETCOLORCMD4\ +$(ITEM8) \ +$FSETCOLORCMD3\ +$BSETCOLORCMD3\ +$(ITEM5) \ +$FSETCOLORCMD2\ +$BSETCOLORCMD2\ +$(ITEM9) \ +$FSETCOLORCMD5\ +$BSETCOLORCMD5\ +$(ITEM7) \ +$FSETCOLORCMD9\ +$BSETCOLORCMD9\ +$(ITEM10) \ +$FSETCOLORCMD14\ +$BSETCOLORCMD14\ +$(ITEM12)" +} + +# Colors +#################################### +# The colors under SETCOLORS will be used if Pywal is not enabled. + +# Default colorscheme, for when pywal is not enabled or available. +# I recommend using Vim, vim-plugin and colorizer so that you can actually see the colors. +SETCOLORS() { + COLOR1=#131210 + COLOR2=#bf616a + COLOR3=#A16F9D + COLOR4=#68ABAA + COLOR5=#A89F93 + COLOR6=#D3A99B + COLOR7=#AFC9AC + COLOR8=#eae1cb + COLOR9=#a39d8e + COLOR10=#6D5E8E + COLOR11=#A16F9D + COLOR12=#D3A99B + COLOR13=#AFC9AC + COLOR14=#eae1cb + COLOR15=#6D5E8E +} + +# Add colors to status2d compatible variable to use with xsetroot later. +SETCOLORS_CMD() { + if [ "$COLORFG" = "true" ]; then + FSETCOLORCMD1="^c${COLOR1}^" + FSETCOLORCMD2="^c${COLOR2}^" + FSETCOLORCMD3="^c${COLOR3}^" + FSETCOLORCMD4="^c${COLOR4}^" + FSETCOLORCMD5="^c${COLOR5}^" + FSETCOLORCMD6="^c${COLOR6}^" + FSETCOLORCMD7="^c${COLOR7}^" + FSETCOLORCMD8="^c${COLOR8}^" + FSETCOLORCMD9="^c${COLOR9}^" + FSETCOLORCMD10="^c${COLOR10}^" + FSETCOLORCMD11="^c${COLOR11}^" + FSETCOLORCMD12="^c${COLOR12}^" + FSETCOLORCMD13="^c${COLOR13}^" + FSETCOLORCMD14="^c${COLOR14}^" + FSETCOLORCMD15="^c${COLOR15}^" + fi + + if [ "$COLORBG" = "true" ]; then + BSETCOLORCMD1="^b${COLOR1}^" + BSETCOLORCMD2="^b${COLOR2}^" + BSETCOLORCMD3="^b${COLOR3}^" + BSETCOLORCMD4="^b${COLOR4}^" + BSETCOLORCMD5="^b${COLOR5}^" + BSETCOLORCMD6="^b${COLOR6}^" + BSETCOLORCMD7="^b${COLOR7}^" + BSETCOLORCMD8="^b${COLOR8}^" + BSETCOLORCMD9="^b${COLOR9}^" + BSETCOLORCMD10="^b${COLOR10}^" + BSETCOLORCMD11="^b${COLOR11}^" + BSETCOLORCMD12="^b${COLOR12}^" + BSETCOLORCMD13="^b${COLOR13}^" + BSETCOLORCMD14="^b${COLOR14}^" + BSETCOLORCMD15="^b${COLOR15}^" + fi + + if [ "$USEBUILTINCOL" = "true" ]; then + if [ "$COLORFG" = "true" ]; then + FSETCOLORCMD1="^C1^" + FSETCOLORCMD2="^C2^" + FSETCOLORCMD3="^C3^" + FSETCOLORCMD4="^C4^" + FSETCOLORCMD5="^C5^" + FSETCOLORCMD6="^C6^" + FSETCOLORCMD7="^C7^" + FSETCOLORCMD8="^C8^" + FSETCOLORCMD9="^C9^" + FSETCOLORCMD10="^C10^" + FSETCOLORCMD11="^C11^" + FSETCOLORCMD12="^C12^" + FSETCOLORCMD13="^C13^" + FSETCOLORCMD14="^C14^" + FSETCOLORCMD15="^C15^" + fi + + if [ "$COLORBG" = "true" ]; then + BSETCOLORCMD1="^B1^" + BSETCOLORCMD2="^B2^" + BSETCOLORCMD3="^B3^" + BSETCOLORCMD4="^B4^" + BSETCOLORCMD5="^B5^" + BSETCOLORCMD6="^B6^" + BSETCOLORCMD7="^B7^" + BSETCOLORCMD8="^B8^" + BSETCOLORCMD9="^B9^" + BSETCOLORCMD10="^B10^" + BSETCOLORCMD11="^B11^" + BSETCOLORCMD12="^B12^" + BSETCOLORCMD13="^B13^" + BSETCOLORCMD14="^B14^" + BSETCOLORCMD15="^B15^" + fi + fi +} + +# Grab colors from pywal if possible +# For example, 1,1p means the first line +SETCOLORS_WAL() { + if [ "$USEWAL" = "true" ]; then + if [ -e "$HOME/.cache/wal/colors" ]; then + COLOR1=$(sed -n 1,1p $HOME/.cache/wal/colors) + COLOR2=$(sed -n 2,2p $HOME/.cache/wal/colors) + COLOR3=$(sed -n 3,3p $HOME/.cache/wal/colors) + COLOR4=$(sed -n 4,4p $HOME/.cache/wal/colors) + COLOR5=$(sed -n 5,5p $HOME/.cache/wal/colors) + COLOR6=$(sed -n 6,6p $HOME/.cache/wal/colors) + COLOR7=$(sed -n 7,7p $HOME/.cache/wal/colors) + COLOR8=$(sed -n 8,8p $HOME/.cache/wal/colors) + COLOR9=$(sed -n 9,9p $HOME/.cache/wal/colors) + COLOR10=$(sed -n 10,10p $HOME/.cache/wal/colors) + COLOR11=$(sed -n 11,11p $HOME/.cache/wal/colors) + COLOR12=$(sed -n 12,12p $HOME/.cache/wal/colors) + COLOR13=$(sed -n 13,13p $HOME/.cache/wal/colors) + COLOR14=$(sed -n 14,14p $HOME/.cache/wal/colors) + COLOR15=$(sed -n 15,15p $HOME/.cache/wal/colors) + USINGWAL=true + fi + fi +} + +# End of configuration +######################################################################### + +# Set bindir +case "$BINDIR" in +"") BINDIR=$(cat /usr/share/speedwm-bindir) ;; +esac + +mkdir -p $HOME/.config/speedwm-de/status + +# Load config and create options if it does not exist +LOADCONFIG() { +if [ -e "$HOME/.config/speedwm-de/status/config" ]; then + . $HOME/.config/speedwm-de/status/config && echo "Loaded configuration file" +else + touch $HOME/.config/speedwm-de/status/config && echo "Created configuration file" + printf "USEWAL=$USEWAL # Use Pywal if possible (true/false)" > $HOME/.config/speedwm-de/status/config + printf "\nRUNONCE=$RUNONCE # Only run status bar once (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nSLEEPTIME=$SLEEPTIME # How long delay in seconds between each reload (int)" >> $HOME/.config/speedwm-de/status/config + printf "\nSEP='$SEP' # Separator to use" >> $HOME/.config/speedwm-de/status/config + printf "\n\nENABLE_ITEM1=$ENABLE_ITEM1 # Enable RAM usage (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nENABLE_ITEM2=$ENABLE_ITEM2 # Enable Time (HH:MM) (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nENABLE_ITEM3=$ENABLE_ITEM3 # Enable Date (DD/MM/YY) (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nENABLE_ITEM4=$ENABLE_ITEM4 # Enable Volume/Mute status (%) (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nENABLE_ITEM5=$ENABLE_ITEM5 # Enable Weather (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nENABLE_ITEM6=$ENABLE_ITEM6 # Enable Network Traffic (B/s) (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nENABLE_ITEM7=$ENABLE_ITEM7 # Enable dfmpeg status (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nENABLE_ITEM8=$ENABLE_ITEM8 # Enable CPU temp (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nENABLE_ITEM9=$ENABLE_ITEM9 # Enable music status (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nENABLE_ITEM10=$ENABLE_ITEM10 # Enable newsboat unread (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nENABLE_ITEM11=$ENABLE_ITEM11 # Enable battery percentage and charging status (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nENABLE_ITEM12=$ENABLE_ITEM12 # Enable #ff IRC backlogs in status bar (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\n\nITEM1_ICON=$ITEM1_ICON # Icon for ITEM1" >> $HOME/.config/speedwm-de/status/config + printf "\nITEM2_ICON=$ITEM2_ICON # Icon for ITEM2" >> $HOME/.config/speedwm-de/status/config + printf "\nITEM3_ICON=$ITEM3_ICON # Icon for ITEM3" >> $HOME/.config/speedwm-de/status/config + printf "\nITEM4_ICON=$ITEM4_ICON # Icon for ITEM4" >> $HOME/.config/speedwm-de/status/config + printf "\nITEM5_ICON=$ITEM5_ICON # Icon for ITEM5" >> $HOME/.config/speedwm-de/status/config + printf "\nITEM6_ICON=$ITEM6_ICON # Icon for ITEM6" >> $HOME/.config/speedwm-de/status/config + printf "\nITEM7_ICON=$ITEM7_ICON # Icon for ITEM7" >> $HOME/.config/speedwm-de/status/config + printf "\nITEM8_ICON=$ITEM8_ICON # Icon for ITEM8" >> $HOME/.config/speedwm-de/status/config + printf "\nITEM9_ICON=$ITEM9_ICON # Icon for ITEM9" >> $HOME/.config/speedwm-de/status/config + printf "\nITEM10_ICON=$ITEM10_ICON # Icon for ITEM10" >> $HOME/.config/speedwm-de/status/config + printf "\nITEM11_ICON=$ITEM11_ICON # Icon for ITEM11" >> $HOME/.config/speedwm-de/status/config + printf "\nITEM12_ICON=$ITEM12_ICON # Icon for ITEM12" >> $HOME/.config/speedwm-de/status/config + printf "\n\nITEM9_BACKEND=$ITEM9_BACKEND # Backend for the music status (cmus/mocp/auto)" >> $HOME/.config/speedwm-de/status/config + printf "\n\nHIDE_STATUS_SYSTRAY=$HIDE_STATUS_SYSTRAY # Hide the status when a systray is running (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nSYSTRAY=$SYSTRAY # Systray to use ()" >> $HOME/.config/speedwm-de/status/config + printf "\nSTATUS_WHEN_HIDDEN=$STATUS_WHEN_HIDDEN # Status to print when status is hidden (char)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLORFG=$COLORFG # Color foreground (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLORBG=$COLORBG # Color background (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\nUSEBUILTINCOL=$USEBUILTINCOL # Use built in speedwm colors (col_status). This also supports Pywal. (true/false)" >> $HOME/.config/speedwm-de/status/config + printf "\n\nCOLOR1=$COLOR1 # Color for ITEM1 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR2=$COLOR2 # Color for ITEM2 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR3=$COLOR3 # Color for ITEM3 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR4=$COLOR4 # Color for ITEM4 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR5=$COLOR5 # Color for ITEM5 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR6=$COLOR6 # Color for ITEM6 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR7=$COLOR7 # Color for ITEM7 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR8=$COLOR8 # Color for ITEM8 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR9=$COLOR9 # Color for ITEM9 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR10=$COLOR10 # Color for ITEM10 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR11=$COLOR11 # Color for ITEM11 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR12=$COLOR12 # Color for ITEM12 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR13=$COLOR13 # Color for ITEM13 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR14=$COLOR14 # Color for ITEM14 (hex color)" >> $HOME/.config/speedwm-de/status/config + printf "\nCOLOR15=$COLOR15 # Color for ITEM15 (hex color)\n" >> $HOME/.config/speedwm-de/status/config +fi +} + +# Set it to /usr/bin if it was not possible to get it through /usr/share/speedwm-bindir +case "$BINDIR" in +"") BINDIR="/usr/bin" ;; +esac + +# Check if muted, for use with volume status bar. Unmute if muted. +case "$MUTED" in +"") MUTED=false ;; +"true") MUTED=false ;; +esac + +# Systray stuff +PRINT_SYSTRAY() { + if [ "$SYSTRAY" = "trayer" ]; then + grep "USE_SRG=true" $HOME/.config/speedwm-de/systray/config && SYSTRAY="trayer-srg" + fi + + if [ "$HIDE_STATUS_SYSTRAY" = "true" ]; then + pgrep -x $SYSTRAY > /dev/null && SYSTRAYRUNNING=true + case "$SYSTRAYRUNNING" in + "true") $SETMETHOD "$STATUS_WHEN_HIDDEN" && exit 0 ;; + esac + fi + + if [ "$HIDE_SYSTEM_SYSTRAY" = "true" ]; then + if [ "$SYSTRAYRUNNING" != "true" ]; then + PRINT + fi + else + PRINT + fi +} + +# RAM usage +ITEM1() { + if [ "$ENABLE_ITEM1" = "true" ]; then + if [ -e "${BINDIR}free" ]; then + echo "$SEP $ITEM1_ICON $(echo $(free -h --giga | awk '/^Mem/ {print $3}')B)" + fi + fi +} + +# Time +ITEM2() { + if [ "$ENABLE_ITEM2" = "true" ]; then + if [ -e "${DATEBINDIR}date" ]; then + echo "$SEP $ITEM2_ICON $(date +%H:%M)" + fi + fi +} + +# Date +ITEM3() { + if [ "$ENABLE_ITEM3" = "true" ]; then + if [ -e "${DATEBINDIR}date" ]; then + echo "$SEP $ITEM3_ICON $(date +%d/%m/%Y)" + fi + fi +} + +# Volume +ITEM4() { + if [ "$ENABLE_ITEM4" = "true" ]; then + if [ -e "${BINDIR}pulsemixer" ]; then + echo "$SEP $ITEM4_ICON $(echo $(pulsemixer --get-volume | awk '{ print $1 }')%)" + test /tmp/speedwm-audioctrl-mutestatus && grep "Not muted" /tmp/speedwm-audioctrl-mutestatus > /dev/null && MUTED=false + test /tmp/speedwm-audioctrl-mutestatus && grep "Not muted" /tmp/speedwm-audioctrl-mutestatus > /dev/null || MUTED=true + test /tmp/speedwm-audioctrl-mutestatus || MUTED=false + else + echo "$SEP $ITEM4_ICON $(echo $(amixer -c 0 get Master | tail -n1 | sed -r "s/.*\[(.*)%\].*/\1/")%)" + fi + + if [ "$MUTED" != "false" ]; then + printf "\n (Muted)" + fi + fi +} + +# Weather +ITEM5() { + if [ "$ENABLE_ITEM5" = "true" ]; then + if [ -e "${BINDIR}curl" ]; then + curl -so /tmp/hacky_internet_test wttr.in && echo "$SEP $ITEM5_ICON $(echo $(curl -s wttr.in/?format="%C"), $(curl -s wttr.in/?format=3 | sed 's/.* //' | sed 's/.*\(.....\)/\1/'))" + rm -f /tmp/hacky_internet_test + fi + fi +} + +# Network traffic +ITEM6() { + if [ "$ENABLE_ITEM6" = "true" ]; then + if [ -e "${BINDIR}awk" ]; then + echo "$SEP $ITEM6_ICON $(awk '{$1=$1/1024000; print $1"B";}' /sys/class/net/[ew]*/statistics/tx_bytes | sed 's/.*\(....\)/\1/' | sed "s|B|B/s |")" + fi + fi +} + +# Dfmpeg status +ITEM7() { + # Check if we're recording with dfmpeg or not + if [ "$ENABLE_ITEM7" = "true" ]; then + if [ -e "/tmp/dfmpeg-recording" ]; then + echo "$SEP $ITEM7_ICON Recording" + fi + fi +} + +# CPU temp +ITEM8() { + if [ "$ENABLE_ITEM8" = "true" ]; then + if [ -e "${BINDIR}sensors" ]; then + echo "$SEP $ITEM8_ICON $(sensors | grep "temp1" | sed 's/(.*//' | sed "s/temp1.//" | sed -r 's/\s+//g' | awk '{ print $1 }')" + fi + fi +} + +# mocp/cmus status +ITEM9() { + # Auto set backend if set to auto + if [ "$ITEM9_BACKEND" = "auto" ]; then + if [ -e "${BINDIR}cmus" ]; then + ITEM9_BACKEND="cmus" + elif [ -e "${BINDIR}mocp" ]; then + ITEM9_BACKEND="mocp" + fi + fi + + if [ "$ENABLE_ITEM9" = "true" ]; then + if [ "$ITEM9_BACKEND" = "mocp" ]; then + if [ -e "${BINDIR}mocp" ]; then + if [ "$(echo $(mocp -Q %file))" != "" ]; then + echo "$SEP $ITEM9_ICON $(basename "$(mocp -Q %file)" | sed 's|\(.*\)[.].*|\1|')" + fi + fi + fi + fi + + if [ "$ENABLE_ITEM9" = "true" ]; then + if [ -e "${BINDIR}cmus-remote" ]; then + if [ "$(cmus-remote -C status | head -n 1 | awk '{ print $2 }')" = "playing" ]; then + echo "$SEP $ITEM9_ICON $(basename "$(cmus-remote -C status | grep file)"| sed 's|\(.*\)[.].*|\1|')" + fi + fi + fi +} + +# Newsboat unreads +ITEM10() { + if [ "$ENABLE_ITEM10" = "true" ]; then + if [ -e "${BINDIR}newsboat" ]; then + pgrep -x newsboat > /dev/null || echo "$SEP $ITEM10_ICON $(newsboat -x print-unread | sed "s| unread articles||") unread!" + fi + fi +} + +# Battery percentage/charging status +ITEM11() { + if [ "$ENABLE_ITEM11" = "true" ]; then + if [ -e "/sys/class/power_supply/BAT0/capacity" ]; then + echo "$SEP $ITEM11_ICON $(cat /sys/class/power_supply/BAT0/capacity)%" + fi + + if [ -e "${BINDIR}acpi" ]; then + CHARGESTATUS=$(echo ", $(acpi | awk '{ print $3 }' | sed "s|,||g" | sed "s|Discharging|Not charging|g")") && echo $CHARGESTATUS + fi + fi +} + +# IRC #ff backlogs +ITEM12() { + if [ "$ENABLE_ITEM12" = "true" ]; then + if [ -e "${BINDIR}curl" ]; then + curl -so /tmp/ff-backlogs https://donut.gq/ff/read.php + if [ -e "/tmp/ff-backlogs" ]; then + echo "$SEP $ITEM12_ICON $(cat /tmp/ff-backlogs | tail -n 1)" + fi + fi + fi +} + +BASE() { + # Determine whether or not + if [ "$USEWAL" = "true" ]; then + if [ "$USINGWAL" = "true" ]; then + if [ "$COLOR1" != "$(sed 1,1p $HOME/.cache/wal/colors)" ]; then + SETCOLORS_WAL && SETCOLORS_CMD + fi + fi + fi + + # Run once if set to false (useful for maybe a shell) + case "$RUNONCE" in + "false") PRINT_SYSTRAY && sleep $SLEEPTIME ;; + "true") exit 0 ;; + esac + + BASE +} + +# Set base colors +SETCOLORS + +# Use pywal once if possible +USE_PYWAL_COLORS() { +if [ -e "${BINDIR}xsetroot" ]; then + if [ -e "${BINDIR}xrdb" ]; then + if [ "$USEWAL" = "true" ]; then + wal -v > /dev/null && SETCOLORS_WAL + fi + fi +fi +} + +LOADCONFIG +USE_PYWAL_COLORS +SETCOLORS_CMD +PRINT_SYSTRAY +BASE + +exit 0 diff --git a/transient.c b/transient.c new file mode 100644 index 0000000..040adb5 --- /dev/null +++ b/transient.c @@ -0,0 +1,42 @@ +/* cc transient.c -o transient -lX11 */ + +#include +#include +#include +#include + +int main(void) { + Display *d; + Window r, f, t = None; + XSizeHints h; + XEvent e; + + d = XOpenDisplay(NULL); + if (!d) + exit(1); + r = DefaultRootWindow(d); + + f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); + h.min_width = h.max_width = h.min_height = h.max_height = 400; + h.flags = PMinSize | PMaxSize; + XSetWMNormalHints(d, f, &h); + XStoreName(d, f, "floating"); + XMapWindow(d, f); + + XSelectInput(d, f, ExposureMask); + while (1) { + XNextEvent(d, &e); + + if (t == None) { + sleep(5); + t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); + XSetTransientForHint(d, t, f); + XStoreName(d, t, "transient"); + XMapWindow(d, t); + XSelectInput(d, t, ExposureMask); + } + } + + XCloseDisplay(d); + exit(0); +} diff --git a/util.c b/util.c new file mode 100644 index 0000000..fe044fc --- /dev/null +++ b/util.c @@ -0,0 +1,35 @@ +/* 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/util.h b/util.h new file mode 100644 index 0000000..f633b51 --- /dev/null +++ b/util.h @@ -0,0 +1,8 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/xresources.h b/xresources.h new file mode 100644 index 0000000..46313b3 --- /dev/null +++ b/xresources.h @@ -0,0 +1,171 @@ +/* +* Xresources preferences to load at startup +* You can add more if you know what you're doing. +* +* When you're done with your edits, run 'make clean install'. +* +* Value in .Xresources Type Value internally */ +ResourcePref resources[] = { + { "font", STRING, &font }, + { "font2", STRING, &font2 }, + { "font3", STRING, &font3 }, + { "col_background", STRING, &col_background }, + { "col_backgroundmid", STRING, &col_backgroundmid }, + { "col_windowbordersel", STRING, &col_windowbordersel }, + { "col_windowbordernorm", STRING, &col_windowbordernorm }, + { "col_layoutbgsel", STRING, &col_layoutbgsel }, + { "col_layoutbgnorm", STRING, &col_layoutbgnorm }, + { "col_layouttext", STRING, &col_layouttext }, + { "col_textnorm", STRING, &col_textnorm }, + { "col_textsel", STRING, &col_textsel }, + { "col_tag1", STRING, &col_tag1 }, + { "col_tag1_text", STRING, &col_tag1_text }, + { "col_tag2", STRING, &col_tag2 }, + { "col_tag2_text", STRING, &col_tag2_text }, + { "col_tag3", STRING, &col_tag3 }, + { "col_tag3_text", STRING, &col_tag3_text }, + { "col_tag4", STRING, &col_tag4 }, + { "col_tag4_text", STRING, &col_tag4_text }, + { "col_tag5", STRING, &col_tag5 }, + { "col_tag5_text", STRING, &col_tag5_text }, + { "col_tag6", STRING, &col_tag6 }, + { "col_tag6_text", STRING, &col_tag6_text }, + { "col_tag7", STRING, &col_tag7 }, + { "col_tag7_text", STRING, &col_tag7_text }, + { "col_tag8", STRING, &col_tag8 }, + { "col_tag8_text", STRING, &col_tag8_text }, + { "col_tag9", STRING, &col_tag9 }, + { "col_tag9_text", STRING, &col_tag9_text }, + { "col_status0", STRING, &col_status0 }, + { "col_status1", STRING, &col_status1 }, + { "col_status2", STRING, &col_status2 }, + { "col_status3", STRING, &col_status3 }, + { "col_status4", STRING, &col_status4 }, + { "col_status5", STRING, &col_status5 }, + { "col_status6", STRING, &col_status6 }, + { "col_status7", STRING, &col_status7 }, + { "col_status8", STRING, &col_status8 }, + { "col_status9", STRING, &col_status9 }, + { "col_status10", STRING, &col_status10 }, + { "col_status11", STRING, &col_status11 }, + { "col_status12", STRING, &col_status12 }, + { "col_status13", STRING, &col_status13 }, + { "col_status14", STRING, &col_status14 }, + { "col_status15", STRING, &col_status15 }, + + /* pywal support */ + { "color0", STRING, &col_background }, + { "color4", STRING, &col_backgroundmid }, + { "color8", STRING, &col_windowbordersel }, + { "color0", STRING, &col_windowbordernorm }, + { "color4", STRING, &col_textnorm }, + { "color0", STRING, &col_textsel }, + { "color0", STRING, &col_layouttext }, + { "color2", STRING, &col_layoutbgnorm }, + { "color4", STRING, &col_layoutbgsel }, + { "color1", STRING, &col_tag1 }, + { "color0", STRING, &col_tag1_text }, + { "color2", STRING, &col_tag2 }, + { "color0", STRING, &col_tag2_text }, + { "color3", STRING, &col_tag3 }, + { "color0", STRING, &col_tag3_text }, + { "color4", STRING, &col_tag4 }, + { "color0", STRING, &col_tag4_text }, + { "color5", STRING, &col_tag5 }, + { "color0", STRING, &col_tag5_text }, + { "color6", STRING, &col_tag6 }, + { "color0", STRING, &col_tag6_text }, + { "color7", STRING, &col_tag7 }, + { "color0", STRING, &col_tag7_text }, + { "color8", STRING, &col_tag8 }, + { "color0", STRING, &col_tag8_text }, + { "color9", STRING, &col_tag9 }, + { "color0", STRING, &col_tag9_text }, + { "color0", STRING, &col_status0 }, + { "color1", STRING, &col_status1 }, + { "color2", STRING, &col_status2 }, + { "color3", STRING, &col_status3 }, + { "color4", STRING, &col_status4 }, + { "color5", STRING, &col_status5 }, + { "color6", STRING, &col_status6 }, + { "color7", STRING, &col_status7 }, + { "color8", STRING, &col_status8 }, + { "color9", STRING, &col_status9 }, + { "color10", STRING, &col_status10 }, + { "color11", STRING, &col_status11 }, + { "color12", STRING, &col_status12 }, + { "color13", STRING, &col_status13 }, + { "color14", STRING, &col_status14 }, + { "color15", STRING, &col_status15 }, + + { "shell", STRING, &shell }, + { "status", STRING, &status }, + { "defaultname", STRING, &defaultname }, + { "monocleformat", STRING, &monocleformat }, + { "deckformat", STRING, &deckformat }, + { "altbarclass", STRING, &altbarclass }, + { "altbarcmd", STRING, &altbarcmd }, + { "alttrayname", STRING, &alttrayname }, + { "lockfullscreen", INTEGER, &lockfullscreen }, + { "movefullscreenmon", INTEGER, &movefullscreenmon }, + { "refreshrules", INTEGER, &refreshrules }, + { "borderpx", INTEGER, &borderpx }, + { "snap", INTEGER, &snap }, + { "showbar", INTEGER, &showbar }, + { "nmaster", INTEGER, &nmaster }, + { "attachdirection", INTEGER, &attachdirection }, + { "resizehints", INTEGER, &resizehints }, + { "startontag", INTEGER, &startontag }, + { "sizeicon", INTEGER, &sizeicon }, + { "decorhints", INTEGER, &decorhints }, + { "swallowclients", INTEGER, &swallowclients }, + { "swallowfloating", INTEGER, &swallowfloating }, + { "barpaddingv", INTEGER, &barpaddingv }, + { "barpaddingh", INTEGER, &barpaddingh }, + { "barheight", INTEGER, &barheight }, + { "centerfloating", INTEGER, ¢erfloating }, + { "savefloat", INTEGER, &savefloat }, + { "warpcursor", INTEGER, &warpcursor }, + { "pertag", INTEGER, &pertag }, + { "i3nmaster", INTEGER, &i3nmaster }, + { "monocleclientcount", INTEGER, &monocleclientcount }, + { "monoclecount", INTEGER, &monoclecount }, + { "scalepreview", INTEGER, &scalepreview }, + { "tagpreview", INTEGER, &tagpreview }, + { "mousepreview", INTEGER, &mousepreview }, + { "leftlayout", INTEGER, &leftlayout }, + { "hidelayout", INTEGER, &hidelayout }, + { "hidetitle", INTEGER, &hidetitle }, + { "hidetags", INTEGER, &hidetags }, + { "hidestatus", INTEGER, &hidestatus }, + { "hidesticky", INTEGER, &hidesticky }, + { "hidefloating", INTEGER, &hidefloating }, + { "statusallmons", INTEGER, &statusallmons }, + { "fadeinactive", INTEGER, &fadeinactive }, + { "stairpx", INTEGER, &stairpx }, + { "stairdirection", INTEGER, &stairdirection }, + { "stairsamesize", INTEGER, &stairsamesize }, + { "deckcount", INTEGER, &deckcount }, + { "defaultlayout", INTEGER, &defaultlayout }, + { "wmclass", INTEGER, &wmclass }, + { "clicktofocus", INTEGER, &clicktofocus }, + { "roundedcorners", INTEGER, &roundedcorners }, + { "cornerradius", INTEGER, &cornerradius }, + { "underline", INTEGER, &underline }, + { "underlinepad", INTEGER, &underlinepad }, + { "underlinestroke", INTEGER, &underlinestroke }, + { "underlinevoffset", INTEGER, &underlinevoffset }, + { "enablegaps", INTEGER, &enablegaps }, + { "gappih", INTEGER, &gappih }, + { "gappiv", INTEGER, &gappiv }, + { "gappoh", INTEGER, &gappoh }, + { "gappov", INTEGER, &gappov }, + { "smartgaps", INTEGER, &smartgaps }, + { "smartgapsize", INTEGER, &smartgapsize }, + { "forcevsplit", INTEGER, &forcevsplit }, + { "mousemfact", INTEGER, &mousemfact }, + { "floatscratchpad", INTEGER, &floatscratchpad }, + { "altbar", INTEGER, &altbar }, + { "mfact", FLOAT, &mfact }, +/* value in .Xresources type value in speedwm */ +};