iwd-dmenu

An interactive iwd menu using dmenu.
git clone git://mattcarlson.org/repos/iwd-dmenu.git
Log | Files | Refs | README

commit 2d5ae7524ae9183b96e7286bb3a271412dc02679
Author: Matthew Carlson <matt@mcarlson.xyz>
Date:   Tue,  7 Sep 2021 04:29:56 -0400

Initial commit

Diffstat:
AMakefile | 17+++++++++++++++++
AREADME.md | 23+++++++++++++++++++++++
Aiwd-dmenu | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 182 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,17 @@ +PREFIX ?= /usr + +all: + @echo Run \'make install\' as root to install iwd-dmenu. + +install: + @mkdir -p $(DESTDIR)$(PREFIX)/bin/ + @echo Copying iwd-dmenu to $(DESTDIR)$(PREFIX)/bin/... + @cp -p iwd-dmenu $(DESTDIR)$(PREFIX)/bin/iwd-dmenu + @echo Running chmod 755 on $(DESTDIR)$(PREFIX)/bin/iwd-dmenu... + @chmod 755 $(DESTDIR)$(PREFIX)/bin/iwd-dmenu + @echo Installation successful. + +uninstall: + @ echo Removing /usr/bin/iwd-dmenu... + @rm -f $(PREFIX)/bin/iwd-dmenu + @echo iwd-dmenu uninstalled. diff --git a/README.md b/README.md @@ -0,0 +1,23 @@ +# iwd-dmenu +An interactive iwd menu using dmenu. + +![iwd-dmenu](https://mcarlson.xyz/img/iwd-dmenu.png) + +Used with my [dotfiles](https://www.git.mcarlson.xyz/dots/). This script has some hacky solutions, will maybe improve them ¯\\\_(ツ)_/¯ + +Aside from grep word boundaries, this script should be pretty portable. + +Some dependencies: +- iwd (duh) +- dmenu (with center and password patches) +- [herbe](https://github.com/dudik/herbe) + +# Installation +```shell +$ git clone https://github.com/mslcarlson/iwd-dmenu/ +$ cd iwd-dmenu +$ sudo make install +``` + +# Usage +Just run `iwd-dmenu` from the command line. diff --git a/iwd-dmenu b/iwd-dmenu @@ -0,0 +1,142 @@ +#!/bin/sh +# +# iwd-dmenu + +NEWLINE=' +' + +iwd_dmenu() { + main_menu() { + while :; do + # get first 802.11 capable device from /sys/class/net + interface="$(for dev in /sys/class/net/*; do + [ -d "${dev}/wireless/" ] && printf '%s\n' "${dev##*/}" + done | head -n 1)" + + # user can connect, disconnect, or forget network + # don't care bout access points or other stuff + if ! show_menu 'iwd dmenu' 'Connect' 'Disconnect' 'Forget'; then break; fi + done + } + + connect() { + get_network_info "iwctl station ${interface} get-networks" + + # user can choose network to connect to + network="$(show_menu 'Connect' "${network_names}")" + + # if network starts with '> ' it means user is already on that network + case "${network}" in '> '*) herbe 'Already connected to this network' & return ;; esac + + get_network_info 'iwctl known-networks list' + + # if network is known connect with no passphrase + if printf '%s\n' "${network_info}" | awk -F '|' '{print $1}' | grep -iq "\<${network}\>"; then + iwctl station "${interface}" connect "${network}" + return + fi + + get_network_info "iwctl station ${interface} get-networks" + + # if network is wep, psk, or 8021x get passphrase + case "$(printf '%s\n' "${network_info}" | grep -i "\<${network}\>" | awk -F '|' '{print $2}')" in + 'wep'|'psk'|'8021x') get_passphrase ;; + # if open just connect without passphrase + 'open') iwctl station "${interface}" connect "${network}" ;; + esac + + # connect with passphrase + [ -n "${passphrase}" ] && iwctl --passphrase "${passphrase}" station "${interface}" connect "${network}" + } + + disconnect() { + connected_network="$(iwctl station "${interface}" show | grep -i '\<Connected network\>' | awk '{ $1=""; $2=""; sub(" ", " "); { $1=$1; print } }')" + if [ -n "${connected_network}" ]; then + answer="$(show_menu "Disconnect from ${connected_network}?" 'Yes' 'No')" + case "$(printf '%s\n' "${answer}" | tr '[:upper:]' '[:lower:]')" in + 'yes') iwctl station "${interface}" disconnect ;; + 'no') return ;; + esac + fi + } + + forget() { + get_network_info 'iwctl known-networks list' + + # if there are known networks + if [ -n "${network_names}" ]; then + # get network + network="$(show_menu 'Forget' "${network_names}")" + + # get confirmation + answer="$(show_menu "Forget ${network}?" 'Yes' 'No')" + + # forget if answer is yes + [ "${answer}" = 'Yes' ] && iwctl known-networks "${network}" forget + fi + } + + # get network column separately + get_network_column() { + printf '%s\n' "${info}" | awk -v var="${1}" 'BEGIN { FS = "open|wep|psk|8021x" } { print $var }' | sed 's/^[ \t]*//;s/[ \t]*$//' + } + + get_network_info() { + # scan interface + iwctl station "${interface}" scan + + info="$(eval "${1}" | grep '\s' | tail +3 | awk '{ $1 = $1 }; 1' | sed -e 's/\x1b\[[0-9;]*m//g')" + + network_names="$(get_network_column '1')" + network_types="$(printf '%s\n' "${info}" | awk '{ for (i=1;i<=NF;i++){ if ($i ~ /open|wep|psk|8021x/) { print $i } } }')" + network_metas="$(get_network_column '2')" + + i=1 + network_info="$(printf '%s\n' "${network_names}" | ( while read -r name; do + type_field="$(printf '%s\n' "${network_types}" | sed -n ${i}p)" + meta_field="$(printf '%s\n' "${network_metas}" | sed -n ${i}p)" + + if [ ${i} -eq 1 ]; then network_info="${name}|${type_field}|${meta_field}" + else network_info="${network_info}${NEWLINE}${name}|${type_field}|${meta_field}" + fi + + i=$((i+1)) + done + printf '%s\n' "${network_info}" ))" + } + + # hide input with dmenu -P flag + get_passphrase() { passphrase="$(dmenu -c -p 'Passphrase:' -P)" ; } + + show_menu() { + # first arg is prompt + prompt="${1}" + shift + + # remaining opts are options + options="$(for option in "${@}"; do + printf '%s\n' "${option}" + done)" + + # print using dmenu + if ! answer="$(printf '%s\n' "${options}" | dmenu -c -l 10 -p "${prompt}")"; then return 1; fi + + # get user's answer + printf '%s\n' "${options}" | while read -r line; do + if [ "${line}" = "${answer}" ]; then + # if answer is a function run it else just print it + cmd="$(printf '%s\n' "${answer}" | tr '[:upper:]' '[:lower:]')" + if command -v "${cmd}" >/dev/null 2>&1 && [ "${cmd}" != 'yes' ]; then "${cmd}" + else printf '%s\n' "${answer}" + fi + break + fi + done + } + + main_menu +} + +main() { iwd_dmenu ; } + +main "${@}"