├── screenshot.png ├── Makefile ├── LICENSE ├── README.md └── dmenu-win /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NikitaIvanovV/dmenu-win/HEAD/screenshot.png -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr/local 2 | BINPREFIX := $(DESTDIR)$(PREFIX)/bin 3 | 4 | SCRIPT = dmenu-win 5 | 6 | all: 7 | 8 | install: 9 | install -d ${BINPREFIX} 10 | install ${SCRIPT} ${BINPREFIX} 11 | 12 | uninstall: 13 | rm -f $(PREFIX)/bin/${SCRIPT} 14 | 15 | .PHONY: all install uninstall 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nikita Ivanov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dmenu-win 2 | 3 | This small shell script allows you to switch between windows with [dmenu](https://tools.suckless.org/dmenu/). 4 | 5 | ![showcase](screenshot.png) 6 | 7 | ## Dependencies 8 | 9 | * [dmenu](https://tools.suckless.org/dmenu/) 10 | * [xprop](https://gitlab.freedesktop.org/xorg/app/xprop/) 11 | * [xdotool](https://github.com/jordansissel/xdotool/) 12 | * ([focusonnetactive patch](https://dwm.suckless.org/patches/focusonnetactive/) if you use [dwm](https://dwm.suckless.org/)) 13 | 14 | ## Installation 15 | 16 | ### Manual 17 | 18 | ```sh 19 | git clone https://github.com/NikitaIvanovV/dmenu-win 20 | cd dmenu-win 21 | sudo make install 22 | ``` 23 | 24 | Uninstall with `sudo make uninstall` 25 | 26 | ### AUR 27 | 28 | If you are an Arch Linux user, you can install [`dmenu-win-git`](https://aur.archlinux.org/packages/dmenu-win-git/) AUR package. 29 | 30 | ```sh 31 | yay -S dmenu-win-git 32 | ``` 33 | 34 | ## Usage 35 | 36 | Just run: 37 | 38 | ```sh 39 | dmenu-win 40 | ``` 41 | 42 | dmenu is called with these options by default: `-i -l 10 -p Windows`. You can set your options via `DMENU` environmental variable: 43 | 44 | ```sh 45 | DMENU='dmenu -l 5 -p Type...' dmenu-win 46 | ``` 47 | 48 | It's also possible to replace dmenu altogether (e.g. with [fzf](https://github.com/junegunn/fzf)): 49 | 50 | ```sh 51 | DMENU='fzf --border=rounded --layout=reverse-list' dmenu-win 52 | ``` 53 | -------------------------------------------------------------------------------- /dmenu-win: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo_err() { 4 | echo "$0: $1" >&2 5 | } 6 | 7 | err() { 8 | echo_err "$1" 9 | exit 1 10 | } 11 | 12 | check_dep() { 13 | command -v "$1" > /dev/null || err "$1 is required: $2" 14 | } 15 | 16 | # We don't check if dmenu is installed because someone 17 | # may want to replace it via DMENU var 18 | check_dep xdotool https://github.com/jordansissel/xdotool 19 | check_dep xprop https://gitlab.freedesktop.org/xorg/app/xprop 20 | 21 | get_windows_ids() { 22 | list="$(xprop -root _NET_CLIENT_LIST 2> /dev/null)" || err "Failed to get windows list" 23 | echo "$list" | sed 's/^.\+# //; s/, \+/\n/g' | sort | uniq 24 | } 25 | 26 | get_window_name() { 27 | wm_name="$(xprop -id "$1" WM_NAME 2> /dev/null)" || return 1 28 | echo "$wm_name" | sed 's/^.\+ = //; s/^"//; s/"$//' 29 | } 30 | 31 | # Get windows in format "$num $id $name" 32 | get_windows() { 33 | get_windows_ids | while read -r id; do 34 | if ! name="$(get_window_name "$id")"; then 35 | echo_err "Failed to get window name of $id" 36 | continue 37 | fi 38 | echo "$id $name" 39 | done | nl -w 1 -s ' ' 40 | } 41 | 42 | get_window_id() { 43 | cut -d ' ' -f 2 44 | } 45 | 46 | get_windows_menu() { 47 | cut -d ' ' -f '1,3-' | sed 's/\([[:digit:]]\+\) \+/\1 /' | column -t -s ' ' 48 | } 49 | 50 | get_num_from_selection() { 51 | echo "$1" | cut -d ' ' -f 1 52 | } 53 | 54 | get_selected_window() { 55 | grep "^$(get_num_from_selection "$1") " 56 | } 57 | 58 | focus_window() { 59 | xdotool windowactivate "$1" 60 | } 61 | 62 | [ -z "$DMENU" ] && DMENU='dmenu -i -l 10 -p Windows' 63 | 64 | windows="$(get_windows)" 65 | 66 | selection="$(echo "$windows" | get_windows_menu | eval "$DMENU")" 67 | [ -z "$selection" ] && exit 0 68 | 69 | focus_window "$(echo "$windows" | get_selected_window "$selection" | get_window_id)" 70 | --------------------------------------------------------------------------------